How to find out the version of a package from its module

Suppose I just did using Foo. How do I find out the version of Foo.jl given the module Foo ?

The only way I found was to know the UUID of the package and query Pkg. I am hoping I am just overlooking something simple? Ideally I’d be hoping for something like Foo.version or so.

Motivation: I’d like to keep my code compatible with both CxxWrap 0.9.1 and 0.10.1, but there are some differences I need to deal with, no matter what. So far, all ways to query the version I found require using Pkg.jl, the UUID, and different code depending on whether one is using Julia 1.3 or 1.4 (or newer).

Any pointers appreciated, thanks!

The only way to get the version of the currently loaded package (as opposed to what’s in the active environment) is something like

julia> pkgversion(m::Module) = Pkg.TOML.parsefile(joinpath(dirname(string(first(methods(m.eval)).file)), "..", "Project.toml"))["version"]
pkgversion (generic function with 1 method)

julia> pkgversion(Atom)
"0.12.10"

Needs some error handling, of course. Also hits the file system, but I don’t think there’s a way around that anyways.

8 Likes

First of, thanks for the quick and helpful reply. That is actually a bit nicer than what I have. I wonder why something like that isn’t part of Pkg.jl.

But actually, I was wondering why Pkg.jl doesn’t just inject the relevant info into the module when loading it? It could simply insert Foo.version (well, that probably would be a bad idea as package may already have a symbol version on the toplevel; but it could also store a dictionary with this info and then use that to implement a function that allows querying the version (or even a full PackageSpec) for the currently loaded version of a package. Surely it must have all this information at some point

I realize such functionality this is not in there, but it seems like a sensible feature request to me, isn’t it? I can submit it as an issue to Pkg.jl I guess? I’d even work on a PR for it, but I’d first like to know if the idea has any chance at all :wink:

I am not sure about this. Pkg and code loading work in a declarative way: you specify what you need (packages and their compat bounds), the resolver takes care of it. Any alternative gets chaotic very quickly.

As for your specific problem, it is a common workaround to test for the presence of various functions/types/values/etc in other modules, and condition code on this, eg with isdefined. That said, the cleanest solution may be just picking a version of CxxWrap you want to support, and moving on. Earlier versions of your package remain usable with other versions.

There is a registered package by @klacru that does this as well: PkgVersion.

julia> using PkgVersion, Atom

julia> PkgVersion.Version(Atom)
v"0.12.10"
9 Likes

Just to support @fingolfin:
we communicate with Julia by package name only (add Blah, using Blah)
we communicate with the users via name and version (cool feature in version 0.10.3 of Blah)
The uuid is neccessary for Project.toml (deps)
The version is neccessary to Project.toml (compat)

Yet, there is no “legal” way within Julia to obtain either the uuid, nor the version. Even worse, versions of indirect dependencies are not even shown with Pkg.status by default…
It is difficult to see why a package is not updated, let alone to see that it is not updated.

I like the package manager and the declarative way, but I still need to be able to see and test the information. It has been part of Julia for a long time to e.g. check the Julia version to enable/ disable features. I can test for signatures being present, but I cannot test for a new algorithm being there unless the API changes.

There are workarounds like the PkgVersion, or using undocumented features (Pkg.installed(), Pkg.dependencies()), but it feels like it should be exponsed in a legal way.

4 Likes

Maybe I misunderstood your question, but most packages provide their version in a VERSION variable. For example, CxxWrap.VERSION results in v"1.6.2" on my machine.

Thats the Julia version.

3 Likes

:joy: alright, I didn’t expect that! Would have been too easy I guess. So single-sourcing a version is not really any easier than in Python (I had hoped it was).

Just for reference, this is the code I ended up with:

# pkgdir was added in Julia 1.4
if VERSION < v"1.4"
   pkgdir(m::Core.Module) = abspath(Base.pathof(Base.moduleroot(m)), "..", "..")
else
   import Base.pkgdir
end
pkgproject(m::Core.Module) = Pkg.Operations.read_project(Pkg.Types.projectfile_path(pkgdir(m)))
pkgversion(m::Core.Module) = pkgproject(m).version
const VERSION_NUMBER = pkgversion(@__MODULE__)
1 Like

Perhaps VersionNumber(TOML.parsefile("../Project.toml")["version"]) is easier?

2 Likes

Depends on your use case? (I’d use Pkg.TOML.parsefile, though).

But I actually needed pkgversion() for other things, also pkgproject and pkgdir, so…

I meant, you are using a bunch of Pkg internals that can change at any point and then if you have this in a bunch of packages they will immediately stop working. That’s why I prefer what I wrote.

Better to just use TOML.

3 Likes

Adding another use case: for development purposes. I (probably) found a bug in IndexedTables and I could not figure out which version am I running. PkgVersion.Version() eventually helped so that I know which branch of the code to look at.

For what it’s worth I’d also find it quite useful if the Base loading code knew about package version numbers and baked them into modules at precompile time. (I’m not sure Pkg needs to be involved?)

I’m using a variant of the code posted above to do this manually for DataSets.jl. Here’s a generic version of that:

using TOML

const PACKAGE_VERSION = let
    project = TOML.parsefile(joinpath(pkgdir(@__MODULE__), "Project.toml"))
    VersionNumber(project["version"])
end
3 Likes

Seems Julia 1.9 adds pkgversion(m::Module), see Add `pkgversion(m::Module)` to get the version of the package that loaded a given module by IanButterworth · Pull Request #45607 · JuliaLang/julia · GitHub. Yay!

4 Likes