docs/integrations/python/python-from-codon.md
There are two ways to call Python from Codon:
from python import allows importing and calling Python functions
from existing Python modules.@python allows writing Python code directly in Codon.In order to use these features, the CODON_PYTHON environment variable
must be set to the appropriate Python shared library:
export CODON_PYTHON=/path/to/libpython.X.Y.so
For example, with a Homebrew-installed Python 3.9 on macOS, this might be
/usr/local/opt/[email protected]/Frameworks/Python.framework/Versions/3.9/lib/libpython3.9.dylib
Note that only Python versions 3.6 and later are supported.
!!! tip "Tip: Finding libpython"
You can use
[this](https://raw.githubusercontent.com/exaloop/codon/refs/heads/develop/test/python/find-python-library.py)
script to locate the Python shared library for the `CODON_PYTHON` environment variable. Simply run it as
`python3 find-python-library.py` and it will print the library path.
!!! info "Info: Using virtual environments (venv)"
If you are using a virtual environment created with `venv`, set `PYTHON_PATH` to the `site-packages`
directory inside your virtual environment (e.g. `.venv/lib/python3.11/site-packages`).
!!! info "Info: Using virtual environments (uv)"
If you using a virtual environment created with `uv`, you can use the following steps to set up the necessary
environment variables. From the parent directory of your python project (where `pyproject.toml` is):
1. Run `uv python find --system` and set `PYTHON_HOME` to the result.
2. Set `CODON_PYTHON` to the `libpython.dylib` (or `.so`) file found in the folder from the previous
step (e.g. `lib/python3.11.dylib`) as an absolute path.
3. Set `PYTHON_PATH` to the `site-packages` folder inside your virtual environment (e.g `.venv/lib/python3.11/site-packages`).
Python modules can be imported and used in Codon-compiled programs through
a from python import <module> import statement. For example:
from python import sys # imports Python's 'sys' module
print(sys.version) # 3.11.12 (main, Apr 8 2025, 14:15:29) [Clang 17.0.0 (clang-1700.0.13.3)]
You can also import third-party libraries. Here is an example that imports Matplotlib to create a simple plot:
from python import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [2, 5, 3, 6, 4]
fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()
Objects created from imported Python modules can be manipulated and operated on from Codon. Internally, such operations are implemented by using CPython's C API. For example, we can create a Pandas dataframe in Codon, and perform operations on it:
from python import pandas as pd
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}) # internally a Python object
print(df['B'].max()) # 6
It is also possible to annotate functions with the @python decorator in order
to have them execute in Python, instead of being compiled by Codon:
@python
def version():
# the following runs in plain Python
import sys
print(sys.version)
version() # 3.11.12 (main, Apr 8 2025, 14:15:29) [Clang 17.0.0 (clang-1700.0.13.3)]
@python functions can specify return types, in which case returned values will
be checked and converted to native Codon types:
@python
def foo():
return 2 + 2
@python
def bar() -> int:
return 2 + 2
@python
def baz() -> int:
return 'abc'
print(foo()) # 4 (Python object)
print(bar()) # 4 (native Codon int)
print(baz()) # error: Python object did not have type 'int'
Similarly, arguments can be type-annotated as well:
@python
def square(n: int) -> int:
return n * n
print(square(4)) # 16
Codon uses two new magic methods to transfer data to and from Python:
__to_py__: Produces a Python object (PyObject* in C) given a Codon object.__from_py__: Produces a Codon object given a Python object.For example:
import python # needed to initialize the Python runtime
o = (42).__to_py__() # type of 'o' is 'Ptr', equivalent to a pointer in C
print(o) # 0x100e00610
n = int.__from_py__(o) # converts Python object 'o' to native Codon integer
print(n) # 42
Codon stores the results of __to_py__ calls by wrapping them in an instance of a new
class called pyobj, which correctly handles the underlying Python object's reference
count. All operations on pyobjs then go through CPython's API.