Avoid namespace pollution from startup.jl

I have startup.jl file that I’m mostly happy with. Any time Julia is updated and I start an interactive session without explicitly activating some environment, startup.jl adds a few key packages to the base environment. A few of these packages are auto-imported via using in every interactive session. I use a let block to avoid polluting the Main module namespace with any variables used to accomplish these tasks. However, I need to import Pkg to obtain its functionality for these same tasks, and therefore every interactive session has the Pkg module imported. Is there a way to avoid this? Listing of startup.jl follows…

if isinteractive()
    # If not already present, add selected packages to base (@v1.x) package environment.
    # Automatically use some of these in every REPL session.
    let # Avoid polluting global namespace
        import Pkg
        auto_used_packages = [:Revise, :PkgOnlineHelp, :OhMyREPL]
        manually_used_packages = [:Plots, :BenchmarkTools]
        vstr = string(VERSION)
        dots = findall('.', vstr)
        vstr = "v" * vstr[begin:dots[2]-1]
        baseenvpath = joinpath(first(DEPOT_PATH), "environments", vstr, "Project.toml")
        if Pkg.project().path == baseenvpath
            for package in union(auto_used_packages, manually_used_packages)
                (String(package) in keys(Pkg.project().dependencies)) || Pkg.add(String(package))
            end
        end
        
        for package in auto_used_packages
            @eval using $package
        end
    end

end

For me having Pkg available is actually a feature :slight_smile: but if you don’t want it then simply wrap the bits that use Pkg into a module so the import doesn’t leak.

Won’t the module name then be the pollution?

What about using Pkg: Pkg, will that pull the name in only locally?

This seemed quite awkward to me, but I was a bit surprised to find there isn’t a lot of convenience functions for working with version numbers. Is there anything better than

vstr = string('v', VERSION.major, '.', VERSION.minor)

?

2 Likes

Technically yes, but you can choose some other name to avoid any practical issues e.g.

module var"##super-secret-setup-space"
    import Pkg
    # rest of setup...
end

This won’t show up in any auto-complete and so is for practical purposes invisible I’d say.
Just checked: names(Main) and varinfo(Main) also do not show it.

2 Likes

No, Pkg is still seen in Main afterwards:

julia> Pkg
ERROR: UndefVarError: `Pkg` not defined

julia> let
           using Pkg: Pkg
           Pkg.status()
       end
Status `C:\Users\peter\.julia\environments\v1.10\Project.toml`
  [6e4b80f9] BenchmarkTools v1.5.0
  [7073ff75] IJulia v1.25.0
  [5903a43b] Infiltrator v1.8.3
  [c3a54625] JET v0.9.6
  [33e6dc65] MKL v0.7.0
  [5fb14364] OhMyREPL v0.5.27
  [021381c1] PkgOnlineHelp v0.2.3
  [91a5bcdd] Plots v1.40.5
  [295af30f] Revise v3.5.15

julia> Pkg
Pkg

I think the following, based on @rafael.guerra 's suggestion, also is pretty good:

vstr = "v"*first(splitext(string(VERSION)))

Even though it’s a bit involved, one doesn’t have to access the undocumented fields of a VersionNumber.

1 Like

You can also create an anonymous module with Module(), so you can do something like

@eval Module() begin
    import Pkg
    # ...
end
5 Likes

Thanks to all who replied. It’s such a pleasure learning from this forum!

1 Like

My main problem with the above is that it’s a bit indirect, and it’s not clear what it’s actually doing. My suggestion is very explicit.

Are the major and minor fields actually undocumented?

They are documented now as of Document the fields of `VersionNumber` by jakobnissen · Pull Request #50179 · JuliaLang/julia · GitHub

2 Likes