How to use Pkg.dependencies() instead of Pkg.installed()

From Julia v1.4, Pkg.installed() shows deprecation message.
Using Pkg.dependencies() is recommended instead of Pkg.installed() in this issue:
https://github.com/JuliaLang/Pkg.jl/issues/1724

However, Pkg.dependencies return all packages, not top level packages like Pkg.installed.
How can I get same results using Pkg.dependencies instead of Pkg.installed?

1 Like

Just take a look at the current implementation of Pkg.installed():


function installed()
    @warn "Pkg.installed() is deprecated"
    deps = dependencies()
    installs = Dict{String, VersionNumber}()
    for (uuid, dep) in deps
        dep.is_direct_dep || continue
        dep.version === nothing && continue
        installs[dep.name] = dep.version
    end
    return installs
end

You just need to look at the field is_direct_dep :smile:.

5 Likes

Why do you need Pkg.installed()?

Oh. Great!!. Your answer is totally what I want!!

Sometimes, I want to check a certain package is installed or not in a Julia script for dependency management.

1 Like

For such cases Requires is also super helpful by the way:

2 Likes

So, what is the best way to check if a package is already installed ? I often want to say “If package xxx is not present, add it”.

In my system:

haskey(Pkg.installed(), "StatsPlots") # true
haskey(Pkg.dependencies(), "StatsPlots") #false
1 Like

Use the UUID of StatsPlots as the key instead.

1 Like

Thank you, I found it:

haskey(Pkg.installed(), "StatsPlots") # true
haskey(Pkg.dependencies(), Base.UUID("f3b207a7-027a-5e70-b257-86293d7955fd")) # true

However on a usability prospective it is terrible, it is a regression compared to the first line… I need to know what a UUID is, where to find it,…

Why Pkg.installed(PackageName) has been deprecated ? Shouldn’t be a higher-level function like it (or better Pkg.isavailable("PackageName"), returning directly a bool ) made available ?

3 Likes

The first one had the problem in that it only showed packages that were direct dependencies. It’s API was fundamentally incompatible with showing packages in the manifest since you can have multiple packages with the same name there and the key for installed was the package name. To have an API that gives information about all dependencies (including recursive) you pretty much must use the UUID as the key since that is what identifies the package.

You can however easily recreate the old API as:

isinstalled(pkg::String) = any(x -> x.name == pkg && x.is_direct_dep, values(Pkg.dependencies()))

Note that this only checks if a package with the name pkg is installed, not that it actually is the package you want.

5 Likes

But I am interested in just knowing if a package is already available for using or not.

If name collision is a (remote, i.e. not possible if you just use the standard repository) possibility one could have just a function that returns the number of available packages by name and then use it as:

if Pkg.availablecount("pkgName") == 0
  Pkg.add("pkgName")
elseif Pkg.availablecount("pkgName") > 1
  @error "More than one package is available with the given name. Please use UUID"
end

using pkgName

You can use the snippet I gave you then.

It’s also possible to just try to use it and do something in a catch:

try
    using Foo
catch
   # do something
end
2 Likes

To add to this discussion:
when I start a program I’d like to enter a vector of packages
then, automatically/generically
-check if it’s installed
-install it if not
-using all packages in vector

Old way, gives deprec warning

pk = ["Knet","Colors","Images"]
using Pkg; for p in pk; haskey(Pkg.installed(),p) || Pkg.add(p); end
using Knet, Colors, Images

New way using @kristoffer.carlsson function

isinstalled(pkg::String) = any(x -> x.name == pkg && x.is_direct_dep, values(Pkg.dependencies()))

pk = ["Knet","Colors","Images", "StatsPlots"]
using Pkg; for p in pk; isinstalled(p) || Pkg.add(p); end
using Knet, Colors, Images

Also I’d like to replace
using Knet, Colors, Images
with
for p in pk; using(p); end
but it doesn’t work bc pk is a vector of strings

Adding the function isinstalled(pkg::String) each time is a bit of a pain …

1 Like

I am wondering if you are duplicating the work that is supposed to be done by Pkg.instantiate.

2 Likes
julia> packages = [:Example, ]
1-element Vector{Symbol}:
 :Example

julia> for pkg in packages
           @eval using $pkg
       end

julia> Example.hello("world")
"Hello, world"
1 Like

So is there no one-liner like Pkg.installed() now? I ran the for loop and I got the answer but is there any simpler way of getting the installed libraries?

I’m not aware of any. On the other hand it is a one-liner if you don’t insist on getting exactly the same datastructure as the ancient installed(). Like you could just do:

[dep for dep in dependencies() if dep.is_direct_dep]
1 Like

There is the Pkg.project() which contains a field named dependencies containing a dict with package names and UUID of direct dependencies. So keys(Pkg.project().dependencies) == keys(Pkg.installed()) if I’m not mistaken, and could for the purpose in this thread probably be used as a one line replacement.

This code of mine works wonderfully and hopefully solves almost all the issues raised here:

# Make sure all needed Pkg's are ready to go
neededPackages = [:Gtk4, :Cairo] 
using Pkg;
for neededpackage in neededPackages
    (String(neededpackage) in keys(Pkg.project().dependencies)) || Pkg.add(String(neededpackage))
    @eval using $neededpackage
end
3 Likes

Suddenly, you get frustrated with something that should be right away, but at least there is always a way to go. Thank you!