FYI: Julia 1.12's REPL warns on non-owning, non-public qualified accesses

Some terminology:

  • Qualified access: like Module.name, e.g. DataFrames.VERSION
  • non-owning: VERSION is defined in Base, not in DataFrames
  • non-public: VERSION is not marked public in DataFrames (it is public in Base)

So on Julia 1.12, we get:

julia> DataFrames.VERSION
[ Warning: VERSION is defined in Base and is not public in DataFrames
v"1.12.2"

whereas in 1.11

julia> DataFrames.VERSION
v"1.11.7"

The issue this is trying to prevent is that users can guess .VERSION gives them the package version (in python, mypkg.__version__ does give the package version) and get it wrong with no warning.

This doesn’t just apply to names from Base though. Let’s say you have say foo defined in some package XBase.jl, then in X.jl you do using XBase: foo and want users to write X.foo. If foo is not marked public in X (regardless of its publicity in XBase), users will get a warning in the REPL. The solution is to mark foo public in X (i.e. add public foo to X’s source code); any name in your module X that should be used by users should be marked public.

This is due to my PR #54872.

Some other notes:

  • this only applies to the REPL; it is implemented as a REPL feature, and is meant to help users navigate the ecosystem better. For package developers, my package ExplicitImports.jl tries to solve similar issues
  • the warning only shows up the first time per REPL session per name. So if you run DataFrames.VERSION again a second time in the same REPL session, there will be no warning. This is to try to make it less annoying when you do need to access these names for whatever reason.
  • the warning only occurs for both non-owning & non-public. If you make a qualified access to a non-public name which is defined in that module, you won’t get a warning. This was again to try to reduce the spaminess and annoyance of the feature, especially as public is somewhat new (or at least, it was in summer 2024 when I made my PR) and not all packages have updated. Additionally, in the owning-but-non-public case, it’s not really a cross-package confusion issue (like the DataFrames.VERSION thing), so it might be less bad than the non-owning non-public case.

I wanted to advertise this here since new warnings showing up may be confusing. And to say: package developers, especially those with functionality divided among different packages like Core/Base packages and more user-facing packages: mark names as public in any package you want users to access them from!

8 Likes

Focusing on package versions, I think there should be a clear protocol for package versions to be identified. While packages having a VERSION constant is simple, I am not sure if this is the best solution.

Is there a better way to identify the version of a loaded package?

pkgversion(m::Module)

3 Likes

I definitely think they should not have a VERSION constant, since that opens the door further to mixing up Base.VERSION and MyPkg.VERSION, since VERSION is exported from Base. pkgversion is the right solution of course :slight_smile:

This is nice because I personally hate qualifying with non-owning modules. In the general case though, it won’t be unusual to find variables of the same name across different modules. Is there a reflection function for finding the owning module given a possibly non-owning module and a symbol, as this warning does? For some reason I keep expecting parentmodule to do that but it does not.