|
|
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:
|
||
|
|
|
||
|
|
``` bash
|
||
|
|
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 "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`).
|
||
|
|
|
||
|
|
## 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:
|
||
|
|
|
||
|
|
``` python
|
||
|
|
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:
|
||
|
|
|
||
|
|
``` python
|
||
|
|
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:
|
||
|
|
|
||
|
|
``` python
|
||
|
|
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
|
||
|
|
@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
|
||
|
|
@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
|
||
|
|
@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:
|
||
|
|
|
||
|
|
``` python
|
||
|
|
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.
|