PyJulia provides a high-level interface which assumes a “normal” setup (e.g., julia program is in your PATH) and a low-level interface which can be used in a customized setup.

High-level interface

To call a Julia function in a Julia module, import the Julia module (say Base) with:

>>> from julia import Base

and then call Julia functions in Base from python, e.g.,

>>> Base.sind(90)

Other variants of Python import syntax also work:

>>> import julia.Base
>>> from julia.Base import Enums    # import a submodule
>>> from julia.Base import sin      # import a function from a module

The global namespace of Julia’s interpreter can be accessed via a special module julia.Main:

>>> from julia import Main

You can set names in this module to send Python values to Julia:

>>> Main.xs = [1, 2, 3]

which allows it to be accessed directly from Julia code, e.g., it can be evaluated at Julia side using Julia syntax:

>>> Main.eval("sin.(xs)")

Low-level interface

If you need a custom setup for PyJulia, it must be done before importing any Julia modules. For example, to use the Julia executable named custom_julia, run:

>>> from julia import Julia
>>> jl = Julia(runtime="custom_julia")

You can then use, e.g.,

>>> from julia import Base

See also the API documentation for Julia.

IPython magic

In IPython (and therefore in Jupyter), you can directly execute Julia code using %julia magic:

In [1]: %load_ext julia.magic
Initializing Julia runtime. This may take some time...

In [2]: %julia [1 2; 3 4] .+ 1
array([[2, 3],
       [4, 5]], dtype=int64)

You can call Python code from inside of %julia blocks via $var for accessing single variables or py"..." for more complex expressions:

In [3]: arr = [1, 2, 3]

In [4]: %julia $arr .+ 1
array([2, 3, 4], dtype=int64)

In [5]: %julia sum(py"[x**2 for x in arr]")
Out[5]: 14

Inside of strings and quote blocks, $var and py"..." don’t call Python and instead retain their usual Julia behavior. To call Python code in these cases, you can “escape” one extra time:

In [6]: foo = "Python"
        %julia foo = "Julia"
        %julia ("this is $foo", "this is $($foo)")
Out[6]: ('this is Julia', 'this is Python')

Expressions in macro arguments also always retain the Julia behavior:

In [7]: %julia @eval $foo
Out[7]: 'Julia'

Results are automatically converted between equivalent Python/Julia types (should they exist). You can turn this off by appending o to the Python string:

In [8]: %julia typeof(py"1"), typeof(py"1"o)
Out[8]: (<PyCall.jlwrap Int64>, <PyCall.jlwrap PyObject>)

Code inside %julia blocks obeys the Python scope:

In [9]: x = "global"
   ...: def f():
   ...:     x = "local"
   ...:     ret = %julia py"x"
   ...:     return ret
   ...: f()
Out[9]: 'local'

IPython configuration

PyJulia-IPython integration can be configured via IPython’s configuration system. For the non-default behaviors, add the following lines in, e.g., ~/.ipython/profile_default/ (see Introduction to IPython configuration).

To disable code completion in %julia and %%julia magics, use

c.JuliaMagics.completion = False  # default: True

To disable code highlighting in %%julia magic for terminal (non-Jupyter) IPython, use

c.JuliaMagics.highlight = False  # default: True

To enable Revise.jl automatically, use

c.JuliaMagics.revise = True  # default: False

Virtual environments

PyJulia can be used in Python virtual environments created by virtualenv, venv, and any tools wrapping them such as pipenv, provided that Python executable used in such environments are linked to identical libpython used by PyCall. If this is not the case, initializing PyJulia (e.g., import julia.Main) prints an informative error message with detected paths to libpython. See PyCall documentation for how to configure Python executable.

Note that Python environment created by conda is not supported.