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.

5 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

For anyone who found this topic through Google you can now (1.9) do

help?> Pkg.dependencies
  Pkg.dependencies()::Dict{UUID, PackageInfo}

  This feature is considered experimental.

  Query the dependency graph of the active project. The result is a Dict that maps a package UUID to a PackageInfo struct representing the dependency (a package).

  PackageInfo fields
  โ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰กโ‰ก

  Field                Description
  โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“ โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“โ€“
  name                 The name of the package
  version              The version of the package (this is Nothing for stdlibs)
  tree_hash            A file hash of the package directory tree
  is_direct_dep        The package is a direct dependency
  is_pinned            Whether a package is pinned
  is_tracking_path     Whether a package is tracking a path
  is_tracking_repo     Whether a package is tracking a repository
  is_tracking_registry Whether a package is being tracked by registry i.e. not by path nor by repository
  git_revision         The git revision when tracking by repository
  git_source           The git source when tracking by repository
  source               The directory containing the source code for that package
  dependencies         The dependencies of that package as a vector of UUIDs

1 Like

Wait, doesnโ€™t this just give the dependencies of the currently active project? If I understood the question correctly, itโ€™s about finding the packages that depend on a given package. So, e.g. finding all the packages that use Plots or something like that.

Ah, yeah I misread.

This might do the trick for direct deps:

import Pkg
ctx = Pkg.Types.Context();

function reverse_deps(name)
    nuuid = Pkg.Types.registered_uuid(ctx.registries, name)
    dd = Dict{String,Base.UUID}()
    isnothing(nuuid) && "$name not known"
    for reg in Pkg.Registry.reachable_registries()
        for (uuid, regpkg) in reg
            pkg = Pkg.Registry.registry_info(regpkg)
            deps = reduce(merge, values(pkg.deps), init=Dict{String,Base.UUID}())
            x = get(deps, name, nothing)
            if !isnothing(x) && x == nuuid
                dd[regpkg.name] = uuid
            end

        end
    end
    dd
end

julia> reverse_deps("Plots")
Dict{String, Base.UUID} with 386 entries:
  "Kinetic"                          => UUID("82403725-3cee-4f7c-b214-1ce71af4a797")
  "SchumakerSpline"                  => UUID("65e68595-3a03-5ff5-a6a2-f05fa774f32e")
  "PicoQuant"                        => UUID("2db47837-5a65-4a20-8d0f-8ec2491baa53")
  "CellularPotts"                    => UUID("11b7dee7-cd95-48a5-a457-717aae94d0bd")
  "StockFlow"                        => UUID("58c4a0e8-2944-4d18-9fa2-e17726aee9e5")
  "Dyn3d"                            => UUID("d8047ffa-7f43-11e9-2204-adfbb6974ec5")
  "AirfoilGmsh"                      => UUID("e68ec710-8e48-43c5-af1a-c38c4248fd7b")
  "FlightGNC"                        => UUID("f141b7d0-6781-47a7-9266-a0707263841d")
  "AgentsPlots"                      => UUID("7820620d-95fb-4739-93b3-b0a14dd83f9a")
  "ItsLive"                          => UUID("774cd0c4-ba78-41e4-92fb-f5f90e3791d1")
  "BifurcationKit"                   => UUID("0f109fa4-8a5d-4b75-95aa-f515264e7665")
  "Population"                       => UUID("11b83ad2-e65e-47ba-870b-1ff818f3e30a")
  "LaplaceRedux"                     => UUID("c52c1a26-f7c5-402b-80be-ba1e638ad478")
  "ScenTreesMakie"                   => UUID("9bd120df-771c-42ec-88b3-c561e7179d1b")
  "AbstractPDEInterfaces"            => UUID("db474457-f03d-4fd7-b196-5f323c96c803")
  "SemanticModels"                   => UUID("f5ac2a72-33c7-5caf-b863-f02fefdcf428")
  "VisClaw"                          => UUID("d5c85fe6-8620-4d3a-96f0-ac478d4f5d38")
  "AtariAlgos"                       => UUID("faabce77-3242-5060-abab-4a4767a0df42")
  "Smg2s"                            => UUID("36e52ba8-0d61-4da2-bc57-6d85c7a279e7")
  "FundamentalsNumericalComputation" => UUID("e48b7b93-d2a8-4750-aba7-f743ccd5256c")
  "Soss"                             => UUID("8ce77f84-9b61-11e8-39ff-d17a774bf41c")
  "DFTK"                             => UUID("acf6eb54-70d9-11e9-0013-234b7a5f5337")
  "CycleSolver"                      => UUID("3b43121a-1122-4a83-b2b6-7ec1eb6161aa")
  "CausalForest"                     => UUID("31bdf679-24a7-499a-9be9-b214c66174ea")
  "LineIntegralConvolution"          => UUID("954431ae-a746-4b0b-bf75-a8381ca95b21")
  "TreesHeaps"                       => UUID("11f3e0e9-035a-4796-9f15-e39fd2303e8e")
  "Huginn"                           => UUID("9cb796e9-d0ea-421b-b37d-eb97bc1add55")
  "AlgebraicDynamics"                => UUID("5fd6ff03-a254-427e-8840-ba658f502e32")
  "PosteriorPlots"                   => UUID("196f2941-2d58-45ba-9f13-43a2532b2fa8")
  "Bioinformatics"                   => UUID("c429c406-9079-5a14-8339-dedc8835de90")
  "SSMPlots"                         => UUID("b9734e3f-1fb5-4b0f-895a-a4ed7ba034cc")
  "VoronoiFVM"                       => UUID("82b139dc-5afc-11e9-35da-9b9bdfd336f3")
  "LazySets"                         => UUID("b4f0291d-fe17-52bc-9479-3d1a343d9043")
  "IndirectTrajOpt"                  => UUID("99e47ffd-a99c-4d17-8b51-d4481b168213")
  "Skyler"                           => UUID("804faec8-72f5-11ea-307d-47b816e9b733")
  "MOSLab"                           => UUID("646a8480-3455-4dcb-93e7-a10dc646a0af")
  "Trixi2Img"                        => UUID("821991e7-028e-4e54-884c-f74f8833bc95")
  "MonotoneSplines"                  => UUID("92812004-5f8d-4354-96af-0c8b7c0637d0")
  "BoseWormPIMC"                     => UUID("bd945c55-3bff-4c7d-84e7-fefb69b5105e")
  "ConScape"                         => UUID("2ba2d6f2-f651-5162-bda8-601e45ce5244")
  "Clines"                           => UUID("b03ef683-e087-4e84-9649-40693305c655")
  "AlphaZero"                        => UUID("8ed9eb0b-7496-408d-8c8b-2119aeea02cd")
  "Solaris"                          => UUID("c31bc6c7-79d6-424e-a85f-5b42ee086500")
  "KnetMetrics"                      => UUID("33081891-0f5e-47f5-81c7-76d5d1fed490")
  "Bingomatic"                       => UUID("5d0f211d-823e-4d77-a844-586f1e6d1e5a")
  "ConservationLawsParticles"        => UUID("dbb53eb6-f4e4-11e9-1aca-071dd871ed0a")
  "RvSpectMLPlots"                   => UUID("6ad363e8-653f-4efd-a04b-f033e69a984c")
  "ClimaCore"                        => UUID("d414da3d-4745-48bb-8d80-42e94e092884")
  "TightBindingToolkit"              => UUID("2233325a-6eb3-486f-aff0-670e0939fa7e")
  "LassoPlot"                        => UUID("21d151f5-1f6d-55ec-8524-1c9aee35b0a4")
  "FixedPointToolkit"                => UUID("081f7a28-ac82-48c1-a10e-fa8b3349a52a")
  "BifurcationInference"             => UUID("7fe238d6-d31e-4646-aa16-9d8429fd6da8")
  โ‹ฎ                                  => โ‹ฎ
2 Likes

Since this 7 year old thread was still the first relevant one in my google search, Iโ€™ll tack on an alternate take: A simpler solution for a simpler question.

When Iโ€™m looking at โ€œreverse dependenciesโ€ it is for a single package, not for all the packages. So rather than โ€œFor every package, what packages depend on it?โ€, this answers โ€œWhy is PackageXYZ installed?โ€. I think this is the same scope as the original question.

for example, i was trying to figure out which Package was using IntervalSets
From the Pkg repl:

 pkg> why IntervalSets
  DimensionalData โ†’ IntervalSets
  Images โ†’ ImageAxes โ†’ AxisArrays โ†’ IntervalSets
  Images โ†’ ImageMetadata โ†’ AxisArrays โ†’ IntervalSets
  Images โ†’ ImageMetadata โ†’ ImageAxes โ†’ AxisArrays โ†’ IntervalSets
  Images โ†’ IntegralArrays โ†’ IntervalSets
  Makie โ†’ ImageIO โ†’ Netpbm โ†’ ImageMetadata โ†’ AxisArrays โ†’ IntervalSets
... 

why lists the dependency chain from the packages in the environment to the package in question.

2 Likes

That site is defunct, but you can use JuliaHub

1 Like