Local Python file in module?

Hi! I’m trying to create a Julia module which uses functions from a Python file in the same directory. The setup is this: In the same directory I have two files. A mypythonfile.py

def myfunction():
	print("Hello!")

and myjuliamodule.jl

module myjuliamodule

using PyCall

pushfirst!(PyVector(pyimport("sys")."path"), "")

function testfun()
	pyimport("mypythonfile").myfunction()
end

export testfun

end

In the REPL, I then run

julia> push!(LOAD_PATH, "")
4-element Vector{String}:
 "@"
 "@v#.#"
 "@stdlib"
 ""

julia> using myjuliamodule

julia> testfun()
ERROR: PyError (PyImport_ImportModule

The Python package mypythonfile could not be imported by pyimport. Usually this means
that you did not install mypythonfile in the Python version being used by PyCall.

PyCall is currently configured to use the Python version at:

/usr/bin/python3

and you should use whatever mechanism you usually use (apt-get, pip, conda,
etcetera) to install the Python package containing the mypythonfile module.

One alternative is to re-configure PyCall to use a different Python
version on your system: set ENV["PYTHON"] to the path/name of the python
executable you want to use, run Pkg.build("PyCall"), and re-launch Julia.

Another alternative is to configure PyCall to use a Julia-specific Python
distribution via the Conda.jl package (which installs a private Anaconda
Python distribution), which has the advantage that packages can be installed
and kept up-to-date via Julia.  As explained in the PyCall documentation,
set ENV["PYTHON"]="", run Pkg.build("PyCall"), and re-launch Julia. Then,
To install the mypythonfile module, you can use `pyimport_conda("mypythonfile", PKG)`,
where PKG is the Anaconda package that contains the module mypythonfile,
or alternatively you can use the Conda package directly (via
`using Conda` followed by `Conda.add` etcetera).

) <class 'ModuleNotFoundError'>
ModuleNotFoundError("No module named 'mypythonfile'")

Stacktrace:
 [1] pyimport(name::String)
   @ PyCall ~/.julia/packages/PyCall/ygXW2/src/PyCall.jl:558
 [2] testfun()
   @ myjuliamodule /media/VM_shared/dev/Estun Vibrations/module_issue/myjuliamodule.jl:8
 [3] top-level scope
   @ REPL[3]:1

The pushfirst! line clearly doesn’t allow the module to find the local Python file. However, if I run the same code outside of a module it does. Consider the file myjuliafile.jl in the same directory.

using PyCall

pushfirst!(PyVector(pyimport("sys")."path"), "")

function testfun2()
	pyimport("mypythonfile").myfunction()
end

In the REPL:

julia> include("myjuliafile.jl")
testfun2 (generic function with 1 method)

julia> testfun2()
Hello!

So my question is why is the behaviour different inside the module?

The reason I want to do this is we have some code that is implemented in Python and translating it to Julia would be a massive task. For convenience reasons, I would like to avoid putting the entire thing inside a """python """ block in a Julia file. Also, rather than having a user get the Python repo separately and pointing to it outside of the module, I would like to include the relevant files directly in the package.

Try to place the sys.path manipulation in an __init__() function, which is run during module initialization.

That works! Thank you!

Why is this necessary though? I was under the impression that all code in the module would be run when I call using myjuliamodule regardless. I (clearly) don’t understand exactly how modules work.

Some things are run once and cached, which doesn’t work so well with external state. See this manual section.

1 Like