Call Python from Codon
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/python@3.9/Frameworks/Python.framework/Versions/3.9/lib/libpython3.9.dylib
Note that only Python versions 3.6 and later are supported.
Tip: Finding libpython
You can use
this
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: 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: 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):
- Run
uv python find --system
and setPYTHON_HOME
to the result. - Set
CODON_PYTHON
to thelibpython.dylib
(or.so
) file found in the folder from the previous step (e.g.lib/python3.11.dylib
) as an absolute path. - Set
PYTHON_PATH
to thesite-packages
folder inside your virtual environment (e.g.venv/lib/python3.11/site-packages
).
Import Python modules in Codon¶
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
Run Python code directly in Codon¶
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
Data conversions¶
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 pyobj
s then go through CPython's API.