Library / SDK
JuliaPy/PyCall.jl avatar
JuliaPy/PyCall.jl

PyCall.jl: Calling Python from Julia

Package to call Python functions from the Julia language

1,513 stars186 forksJuliaMIT

At a glance

What is it?
PyCall.jl gives Julia programs direct access to Python modules, functions, classes, and large arrays. It suits Julia projects that need Python libraries without moving the whole application to Python.
Who is it for?
PyCall.jl is a practical choice when a Julia application must depend on Python code, especially for NumPy, SciPy, or plotting workflows. It is less suitable when the project must avoid native Python linkage, runtime configuration, or language boundaries around every computation.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Julia, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

PyCall.jl's Python-Julia Boundary

PyCall.jl solves a specific interoperability problem: calling Python code from a Julia program without rewriting that functionality as a Julia implementation. The target users are Julia developers who need an established Python library, want to combine Julia code with scientific Python packages, or depend on a Python dependency that is not available elsewhere. The package supports importing arbitrary Python modules, invoking Python functions, defining Python classes from Julia methods, passing functions in both directions, and sharing larger data structures.

The boundary is not limited to scalar function calls. The README documents automatic conversions for numeric, boolean, string, IO stream, date and period types, functions, tuples, arrays, lists, and dictionaries built from those types. Values that do not fit those categories remain available through the generic PyObject type. This distinction matters because interoperability does not mean every Python object becomes a native Julia object. A project that depends on precise Python object identity or implementation-specific attributes must account for the PyObject wrapper rather than assuming transparent conversion.

How PyCall.jl Connects Julia to Python

PyCall.jl links directly to Python's libpython library. During the build step, it locates that library by running a Python program, even though the linked library is what Julia uses at run time. The selected executable name is recorded in PyCall's dependency configuration, and the active Python details can be inspected through PyCall.pyversion, PyCall.libpython, and PyCall.pyprogramname. PyCall.conda reports whether the Conda-provided Python is in use.

Calls use Julia syntax over imported Python objects. The README's first example imports the math module and calls math.sin with math.pi divided by four. Keyword arguments are supported, so Python functions that expose options can retain their calling style. Julia functions can also be supplied to Python routines, including numerical routines such as scipy.optimize.newton. Python classes can be defined from Julia methods according to the package documentation. The architecture therefore combines native library linkage, Python object handles, conversion rules, and a Julia-facing interface.

Array Sharing and Conversion Costs

Multidimensional arrays use Python's NumPy array interface. The default direction has an important asymmetry: Julia-to-Python multidimensional arrays are passed without making a copy, while Python-to-Julia conversion makes a copy. For workloads that repeatedly move arrays in the Python-to-Julia direction, the default behavior can introduce memory use and copying costs that are easy to overlook. The README identifies PyArray as the mechanism for no-copy conversion of Python arrays into Julia arrays.

This design can be useful when a numerical Python library consumes large Julia arrays directly. It is also a reason to inspect data ownership before assuming that every cross-language operation is zero-copy. Scalar conversion and list or dictionary conversion have their own rules, and generic objects still require PyObject handling. PyCall is consequently more than an import statement: its adoption should include a decision about where data lives and which direction it must move.

Installing PyCall.jl and Selecting Python

Within Julia, the package manager installs PyCall.jl. The README states that Julia 0.7 or later is required. On Mac and Windows, the default build uses Conda.jl to install a private minimal Python distribution through Miniconda. On GNU/Linux, PyCall defaults to python3 in PATH, or python if python3 is unavailable. The following command installs the package inside Julia:

julia
Pkg.add("PyCall")

To select another Python executable, set PYTHON and rebuild PyCall. The path must point to the Python or Python 3 executable rather than to a vague environment name.

julia
ENV["PYTHON"] = "/usr/bin/python3.10"
Pkg.build("PyCall")

The README also documents an empty PYTHON value as the way to request the Julia-managed Conda Python. That configuration is useful when the project needs a private Python installation, although Python packages still need to be installed into that environment. The reader should check PyCall.pyversion after rebuilding to confirm which interpreter was selected.

A First PyCall.jl Use with SciPy

After installation, the shortest useful check imports Python's math module and calls a function. This verifies that Julia can load PyCall and reach the configured Python runtime:

julia
using PyCall
math = pyimport("math")
math.sin(math.pi / 4)

The README identifies the result as approximately 0.70710678. A more representative scientific example passes a Julia callback into SciPy. SciPy must be available in the Python environment selected during the build; installation of that dependency is not supplied by the example.

julia
using PyCall
so = pyimport("scipy.optimize")
so.newton(x -> cos(x) - x, 1)

The callback remains a Julia function while SciPy performs the root-finding call. This demonstrates why PyCall is useful beyond simple data exchange. It also exposes a practical requirement: the Julia build and Python package environment must agree on a usable interpreter and compatible native libraries.

PyCall.jl's Configuration and Upgrade Boundaries

PyCall stores its selected Python executable in a dependency file so later builds can reuse the same choice. After changing the Python executable significantly, the README says to rerun Pkg.build("PyCall"). Changing to another virtual environment also requires rebuilding unless runtime environment switching is configured as described in the README. If PYTHON is changed explicitly, later builds use that value instead of the recorded executable.

A significant operational constraint is that PyCall links to libpython. The Python distribution therefore needs the library files and platform configuration required by the selected environment. The README specifically mentions a pyenv configuration issue on macOS and says that the Enthought Canopy distribution is not supported. Those statements are narrower than a general compatibility guarantee, so a new deployment should verify the interpreter and library setup on its target platform.

The repository is not archived, and its last push was on 2026-10-13. The listed recent releases are v1.96.2, v1.96.3, and v1.96.4, with v1.96.4 dated 2023-12-16. That release information and push date indicate repository activity, but they do not replace compatibility testing for a particular Julia, Python, or native-library combination. Upgrade work may include rebuilding PyCall after Python changes.

Choosing PyCall.jl Instead of a Separate Service

PyCall.jl is the appropriate direction when one process needs to reuse Python libraries and Julia code together. A separate Python service is a different approach: it communicates through a network or process boundary instead of linking to libpython. That separation can make deployment and process isolation clearer, but it also changes how functions, arrays, errors, and performance-sensitive data cross the boundary. The README does not document a service interface or a benchmark comparing the two approaches, so the distinction here is architectural rather than a performance claim.

PyCall.jl is the wrong choice when the requirement is to keep Python entirely outside the Julia process, or when native linkage and Python build configuration are unacceptable. It can also be a poor fit for a small script whose only Python dependency could be replaced by a Julia package without adding a runtime boundary. For scientific or library-heavy workloads, however, its direct imports, callback support, class definitions, and NumPy array interface provide a focused reason to adopt it. Verify those specific capabilities against the Python package and deployment model before committing.

Editorial conclusion

PyCall.jl is a practical choice when a Julia application must depend on Python code, especially for NumPy, SciPy, or plotting workflows. It is less suitable when the project must avoid native Python linkage, runtime configuration, or language boundaries around every computation. Verify the selected Python executable, library availability, and array-copy behavior before adoption.

Frequently asked questions

How do I install PyCall.jl in Julia?

Run Pkg.add("PyCall") inside Julia. The README states that Julia 0.7 or later is required.

How do I choose which Python installation PyCall.jl uses?

Set the PYTHON environment variable to the path of the Python executable, then run Pkg.build("PyCall"). On Mac and Windows, the default build uses a private Conda Python through Miniconda.

Does PyCall.jl copy large arrays?

The README says multidimensional Julia arrays are passed to Python without copying by default, while Python-to-Julia conversion makes a copy. PyArray provides no-copy conversion for Python arrays into Julia arrays.

Official sources

  1. Issues
  2. JuliaPy/PyCall.jl on GitHub
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/juliapy-pycall-jl.svg)](https://hysenlabs.com/projects/juliapy-pycall-jl)

Was this useful?