Correct handling of LOAD_PATH in Julia script for running with julia - -project= . .

Hello, I have the following questions.
Below is a MWE script that I want to run with julia --project=myLocalEnv .\mySimpleScript.jl. What I’ve discovered is that even if the package DataFrames is not installed in myLocalEnv, the script will run correctly since "@#.#" usually belongs to the LOAD_PATH and in my case DataFrames were installed in that global environment.

# LOAD_PATH = ["@", "@stdlib"]
# direct assignment to that variable has no effect

# these lines help to prevent "leaking" 
# packages from 
empty!(LOAD_PATH)
push!(LOAD_PATH, "@")
push!(LOAD_PATH, "@stdlib")

using DataFrames

function main() 
    println("HELLO WORLD FROM MAIN FUNCTION!")
    println("$ARGS")
    @show Base.active_project()
    @show Base.current_project()
    @show pathof(DataFrames)
    @show LOAD_PATH
    dat = DataFrame("a"=>[1, 2, 3], "b"=>[4, 5, 6] )
    @show dat
end 

if abspath(PROGRAM_FILE) == @__FILE__
    main()
end 

So, my questions are:

  1. Is this a correct (idiomatic?) way to prevent using packages from a global environment?
  2. Why the line
    LOAD_PATH = ["@", "@stdlib"] 
    
    has no effect, while
    empty!(LOAD_PATH)
    push!(LOAD_PATH, "@")
    push!(LOAD_PATH, "@stdlib")
    
    produces the desired effect?

Thanks in advance for your help!

What you have encountered is the environment stack. The idiomatic approach to this is to keep your version specific global environment mostly empty. Specifically, we do not recommend having a package such as DataFrames.jl in your global default environment.

As an alternative, you can add it to another shared environment such as “@datascience”. The “@” will install the package in a shared space in your ~/.julia/environments that you can access from anywhere.

Also note that Julia is stricter here if you build a Julia package rather than a script.

2 Likes

Because only the second version modifies the existing variable Base.LOAD_PATH, which determines where packages can be loaded from. The first creates a new variable Main.LOAD_PATH that julia’s package loading won’t know or care about. (Main is the implicit module in which your script code runs.)

3 Likes

Interesting… So, in layman terms, by writing LOAD_PATH = ... (with a scope in Main) I’ve accidentally created a new variable that shadowed Base.LOAD_PATH. Is my understanding correct? By the way, is there any special function that can show the scope (module? function?) to which a variable belongs?

1 Like

I don’t think so. The closest thing to that is @isdefined as far as I know.

Sounds like you’re looking for @which:

help?> @which
  @which

  Applied to a function or macro call, it evaluates the arguments to the specified call, and returns the Method object for the method that would be called for those arguments.
  Applied to a variable, it returns the module in which the variable was bound. It calls out to the which function.
2 Likes

Correct, unless you’ve already referenced LOAD_PATH and thus implicitly bound that name to Base.LOAD_PATH:

julia> println(LOAD_PATH)
["@", "@v#.#", "@stdlib"]

julia> LOAD_PATH = []
ERROR: cannot assign a value to imported variable Base.LOAD_PATH from module Main
Stacktrace:
 [1] top-level scope
   @ REPL[2]:1

As @evanfields mentioned, @which:

julia> @which LOAD_PATH
Base

Note that simply using @which is sufficient to establish the binding:

julia> @which LOAD_PATH  # In a fresh session
Base

julia> LOAD_PATH = []
ERROR: cannot assign a value to imported variable Base.LOAD_PATH from module Main
Stacktrace:
 [1] top-level scope
   @ REPL[1]:1
2 Likes

Thanks for the explanation! @which LOAD_PATH, unfortunately, didn’t work in my script, but what it showed through REPL is perfectly enough for my understanding of inner workings for now.

That’s because @which is defined in the InteractiveUtils standard library, which is automatically loaded in an interactive repl session but not when running a script. You could do using InteractiveUtils at the top of your script, but it’s probably not the best idea, it’s meant specifically for interactive exploration.

2 Likes