Reverse dependencies of a package?

Are there existing tools for evaluating the reverse dependencies of a package? That is, for determining the packages that depend directly or indirectly on a particular package?

It occurred to me that, if not, this might be an occasion for me to learn a bit more about the Registries used with Pkg3. How would I begin exploring the TOML capabilities if I decided to try to parse a Registry?

1 Like

Pkg.dependents will give you the direct dependencies at least. Shouldn’t be hard to call it recursively if needed.

2 Likes

Thanks. It seems that I should have done a bit more research before asking the question.:grimacing:

https://juliaobserver.com will also give you this information.

2 Likes

This showed up in my Google search today. I don’t think the answers here still work, so I feel compelled to post, with apology, a kludge that does work. Perhaps someone will also post a more elegant solution which makes use of the dependency graph that I believe Pkg maintains in the first place.

import Pkg
ctx = Pkg.Types.Context()

latest_version(ctx, uuid) = maximum(keys(Pkg.Operations.load_versions(Pkg.Types.registered_paths(ctx.env, uuid)[])))

function latest_pkgspec(ctx, name)
  uuid = Pkg.Types.registered_uuid(ctx.env, name)
  Pkg.Types.PackageSpec(name, uuid, latest_version(ctx, uuid))
end

latest_deps(pkg_name) = Pkg.Operations.load_deps(ctx, latest_pkgspec(ctx, pkg_name))
latest_deps("Plots") # trigger populating of ctx.env

function reverse_deps(ctx, pkg_name_query)
  all_names = map(
    x->if length(x) == 1; x[]; else; nothing; end,
    values(ctx.env.names))
  all_names = filter(!isnothing, all_names)
  [pkg_name for pkg_name in all_names if pkg_name_query in keys(latest_deps(pkg_name))]
end

reverse_deps(ctx, "BinDeps")

Thanks for this snippet. Just a small note — in julia 1.4 I had to adjust this to use ctx rather than ctx.env.

1 Like

Updated to run w/ 1.5/1.6 below. I say to run because the output is wrong. Pkg.Operations.load_all_deps does not seem to be the correct function to get all dependencies for a package (assuming that’s what Pkg.Operations.load_deps did - it no longer exists). Rather, I believe load_all_deps returns all installed packages in the current environment.

julia> length(latest_deps("Revise"))
83
grep "\[\[" ~/.julia/environments/v1.6/Manifest.toml | wc -l
      83

Is there a function to return all dependencies based on a given package name or spec, ideally even if it is not installed?


import Pkg
ctx = Pkg.Types.Context()

latest_version(ctx, uuid) = maximum(keys(Pkg.Operations.load_versions(ctx, Pkg.Types.registered_paths(ctx, uuid)[])))

function latest_pkgspec(ctx, name)
  uuid = Pkg.Types.registered_uuid(ctx, name)
  Pkg.Types.PackageSpec(name, uuid, latest_version(ctx, uuid))
end

latest_deps(pkg_name) = map(x->x.name, Pkg.Operations.load_all_deps(ctx, [latest_pkgspec(ctx, pkg_name)]))
latest_deps("Plots") # trigger populating of ctx.env

function reverse_deps(ctx, pkg_name_query)
  all_names = map(
    x->if length(x) == 1; x[]; else; nothing; end,
    values(ctx.env.names))
  all_names = filter(!isnothing, all_names)
  [pkg_name for pkg_name in all_names if pkg_name_query in latest_deps(pkg_name)]
end