This is really an idea which comes from Python. (Rust uses it too, actually.) Please don’t write it off just for this reason, as there is a practical problem to be solved here.
Consider the following example package.
MyRepoName/
PackageC/
src/
PackageC.jl
Module1.jl
test/
runtests.jl
test_PackageC.jl
With a few details:
# test_PackageC.jl
using PackageC
using Test
@test PackageC.exampleC("helloworld") == "helloworld"
@test PackageC.Module1.exampleModule1() == 1
# runtests.jl
println("running tests for PackageC")
include("test_PackageC.jl")
# PackageC.jl
module PackageC
using Module1
function exampleC(arg::String)
return arg
end
end
# Module1.jl
module Module1
function exampleModule1()
return 1
end
end
Finally, you would need to know something about how the environment has been setup.
The idea is to simply use JULIA_LOAD_PATH
to set the installation location of the repository MyRepoName
, regardless of what type of system it is installed to.
There are two good example usecases for this.
- Developers own machine. The dev clones the repo and
export
sJULIA_LOAD_PATH
to wherever he has cloned it. He can runjulia
from anywhere in the command line, and by runningusing PackageC
, he can loadPackageC
. He can then test it, play with it, measure its performance, etc. - Deployment with Dockerfile. This one is simpler. Just write a Dockerfile. Copy
MyRepoName
to the container, and exportJULIA_LOAD_PATH
, pointing to the destination location whereMyRepoName
has been copied to.
Either way, what this means is:
- There is an entry in
LOAD_PATH
which points toMyRepoName
. So,using PackageX
works, because this directory will be searched forPackageX
.
Ok that’s all the minimal information required to see what we are trying to do, and to undersand what doesn’t work.
Why does this not work?
using Module1
does not work, becauseModule1
cannot be loaded through the same mechanism by whichusing PackageC
is loaded.- The reason for it is Julia does not search the working directory of the source file containing a
using
orimport
statement.
Difference with Python
In contrast, this will work in Python, because import
will also search the directory containing the file from which a using
or import
statement is executed.
(Actually, to be fully accurate here, the import
statement has to be of the form import PackageName.ModuleName
, or import .ModuleName
if using a relative import path.)
Why does it matter?
The obvious solution here is to use include
. However, this creates a problem.
The use of include
explicitly causes code to be loaded from a file.
In contrast, with import X
, Python looks to see if it already knows what X
is, and if it does, it creates a reference to it. If it does not know what X
is, then it will fall back to loading from file, searching the various locations specified in PYTHON_PATH
. (Which might include the current working directory, or a directory local to the current working directory of the Python interpreter.)
The reason why we care about this is it would be useful for us to be able to dynamically load and replace code in a similar way to which Python is able to.
With include
currently “locked” to loading code from files, and using
and import
currently “locked” to searching only the locations specified in LOAD_PATH
, this currently isn’t possible - at least not in a straightforward way.
Python example
It might be useful to see a short Python example which demonstrates this behavior.
Directory structure:
MyRepoNamePython/
src/
PackagePy/
__init__.py
__main__.py
Module1.py
# __init__.py
import PackagePy.Module1
# __main__.py
# Module1.py
def exampleModule1():
return 1
Run python3
from src
:
>>> import PackagePy
>>> PackagePy.Module1.exampleModule1()
1
>>> def exampleModule1b():
return 2
>>> PackagePy.Module1.exampleModule1 = exampleModule1b
>>> PackagePy.Module1.exampleModule1()
2
Attempts at a Julia solution
Here I show some of my attempts to resolve this, by hacking around with LOAD_PATH
. This doesn’t seem to work. I am not sure exactly why.
module PackageC
push!(LOAD_PATH, "$(@__DIR__)")
using Module1
export exampleC
function exampleC(arg::String)
return arg
end
pop!(LOAD_PATH)
end
However, even with these dynamic changes to LOAD_PATH
, Julia still does not seem to be able to find Module1.jl
.
Does anyone know why this is the case?
If you don’t find anything presented here compelling, perhaps the more succinct argument is more compelling:
- Since Julia is a dynamic language, it should be possible to dynamically replace types, functions, modules and packages.
Such a thing isn’t possible in Rust of course because it is statically typed.