marimo-cython
Cython compilation support for marimo notebooks. Write Cython-accelerated functions directly in notebook cells and run them at native speed — no build system boilerplate required.
This is the marimo equivalent of Jupyter's %%cython magic, redesigned for marimo's reactive DAG model.
Installation
Requires Python 3.10+.
uv add marimo-cython
Or with pip:
pip install marimo-cython
Runtime dependencies: cython>=3.0 and setuptools (for C extension building).
Quick start
import cython
from marimo_cython import cy
@cy.compile
def fib(n: cython.int) -> cython.int:
a: cython.int = 0
b: cython.int = 1
i: cython.int
for i in range(n):
a, b = b, a + b
return a
fib(50) # runs compiled native code
API
Three entry points, all available via cy.* or as direct imports from marimo_cython:
@cy.compile — decorator
Compiles a pure-Python Cython function. The function must use Cython's pure Python syntax (cython.int, cython.double, typed memoryviews, etc.).
@cy.compile
def fib(n: cython.int) -> cython.int:
a: cython.int = 0
b: cython.int = 1
i: cython.int
for i in range(n):
a, b = b, a + b
return a
With options:
@cy.compile(boundscheck=False, wraparound=False, cdivision=True)
def mandelbrot(
out: cython.int[:, :],
xmin: cython.double,
xmax: cython.double,
ymin: cython.double,
ymax: cython.double,
max_iter: cython.int,
) -> None:
rows: cython.Py_ssize_t = out.shape[0]
cols: cython.Py_ssize_t = out.shape[1]
# ...
Using C library functions
To call C library functions (like sqrt, fabs, etc.) from a decorated function, use from cython.cimports.* at cell/module level above the function. Cython provides runtime Python stubs for these, so they are valid Python imports:
from cython.cimports.libc.math import sqrt, fabs
@cy.compile(boundscheck=False)
def fast_sqrt(x: cython.double) -> cython.double:
return sqrt(fabs(x))
The decorator automatically picks up from cython.cimports.* imports written above the function in the same cell or file, and includes them in the compiled module.
As a fallback, the cimports parameter accepts raw cimport strings (which use Cython syntax, not valid Python):
@cy.compile(cimports=["from libc.math cimport sqrt, fabs"])
def fast_sqrt(x: cython.double) -> cython.double:
return sqrt(fabs(x))
cy.compile_module(source) — compile a string
Compiles a Cython source string into a module. Supports full .pyx syntax including cdef, cimport, and typed function signatures.
mod = cy.compile_module("""
from libc.math cimport sqrt
def norm(double[:] v):
cdef Py_ssize_t i
cdef double s = 0.0
for i in range(v.shape[0]):
s += v[i] * v[i]
return sqrt(s)
""", boundscheck=False, wraparound=False)
mod.norm(np.array([3.0, 4.0])) # => 5.0
cy.compile_file(path) — compile a .pyx file
mod = cy.compile_file("solver.pyx", cplus=True, libraries=["lapack"])
mod.solve(matrix)
cy.clear_cache() — remove compiled artifacts
cy.clear_cache() # removes .marimo_cython_cache/ and all contents
cy.clear_cache(cache_dir="custom_cache/") # custom cache directory
Returns the number of files removed.
Compile options
All three entry points accept the same keyword arguments. These map to the CompileOptions dataclass:
C/C++ compilation
| Option | Type | Default | Description |
|---|---|---|---|
cplus |
bool |
False |
Compile as C++ instead of C |
compile_args |
list[str] |
[] |
Extra compiler flags (e.g. ["-O3", "-march=native"]) |
link_args |
list[str] |
[] |
Extra linker flags |
libraries |
list[str] |
[] |
Libraries to link against |
include_dirs |
list[str] |
[] |
Additional include directories |
library_dirs |
list[str] |
[] |
Additional library directories |
runtime_library_dirs |
list[str] |
[] |
Runtime library search paths |
define_macros |
list[tuple] |
[] |
Preprocessor macros |
undef_macros |
list[str] |
[] |
Macros to undefine |
extra_objects |
list[str] |
[] |
Extra object files to link |
Cython compiler directives
| Option | Type | Default | Description |
|---|---|---|---|
boundscheck |
bool |
None |
Array bounds checking |
wraparound |
bool |
None |
Negative indexing support |
cdivision |
bool |
None |
C-style division (no zero-division check) |
nonecheck |
bool |
None |
Check for None on extension types |
initializedcheck |
bool |
None |
Check memoryview initialization |
overflowcheck |
bool |
None |
Integer overflow checking |
infer_types |
bool |
None |
Automatic type inference |
profile |
bool |
None |
Enable profiling hooks |
linetrace |
bool |
None |
Enable line tracing |
embedsignature |
bool |
None |
Embed function signatures in docstrings |
emit_code_comments |
bool |
None |
Emit source comments in generated C |
freethreading_compatible |
bool |
None |
Mark as free-threading compatible |
language_level |
int|str |
3 |
Cython language level |
compiler_directives |
dict |
{} |
Catch-all for any Cython directive |
None means "use Cython's default". Set to True/False to override.
Other options
| Option | Type | Default | Description |
|---|---|---|---|
module_name |
str |
auto | Name for the compiled module |
cimports |
list[str]|str |
[] |
Fallback: raw cimport statements (for @cy.compile) |
annotate |
bool|str |
False |
Generate Cython annotation HTML |
nthreads |
int |
0 |
Parallel cythonization threads |
force |
bool |
False |
Force recompilation (ignore cache) |
cache_dir |
str|Path |
.marimo_cython_cache/ |
Build artifact directory |
How it works
Pipeline
All three APIs converge into the same path: normalize source, compute cache key, build or load.
Source → Normalize → Cache check → Cythonize (.pyx → .c) → Build (.c → .so) → Load
Source extraction (@cy.compile)
The decorator uses inspect.getsource(fn) to get the function source. This works in marimo because marimo populates linecache for exec'd cell code (the same mechanism Python uses for tracebacks — not a private API). Decorator lines are stripped before compilation.
Cimport context recovery
inspect.getsource only returns the function body. from cython.cimports.* imports written above the function in the same cell are not included. To recover them, _collect_context_cimports AST-parses all lines above the function definition, finds ImportFrom nodes matching cython.cimports.*, and prepends them to the source.
Auto import cython
import cython is auto-prepended if missing (needed for cython.int, cython.double annotations). Detection uses ast.parse, not regex. Critically, from cython.cimports.* does NOT count as a cython import — it imports C symbols, not the cython module. Getting this wrong causes cython.double to fail with "Unknown type declaration".
Content-addressed caching
The cache key is a SHA-256 of: source (post-normalization), all C/C++ flags, all compiler directives, EXT_SUFFIX (Python ABI), and Cython.__version__. Module name becomes {prefix}_{hash[:16]}. On hit, the .so loads directly via importlib.
Build
On cache miss: write .pyx → cythonize() to .c → setuptools build_ext to .so → load with importlib. Build temp uses a short-lived tempfile.mkdtemp to avoid path-length issues. Intermediate .c/.cpp files are cleaned after success; .pyx is kept for debugging.
Other details
- Numpy: auto-detected from source;
numpy.get_include()added toinclude_dirsautomatically - Linker warnings:
-Wl,-wsuppresses CPython 3.14's staleModules/_haclpath warnings CythonModule: wrapper that delegates attribute access to the compiled module and supports marimo rich display (_mime_()) for annotation HTML- Cache dir:
.marimo_cython_cache/by default (add to.gitignore)
Annotations
Compile with annotate=True to get Cython's annotation HTML rendered in marimo cell output:
mod = cy.compile_module(source, annotate=True)
mod # displays annotation HTML in marimo
Cross-cell interaction
Compiled modules and functions work across marimo cells like any other Python object. Define a module in one cell and call its functions from another:
# Cell 1
linalg = cy.compile_module("""
from libc.math cimport sqrt
def norm(double[:] v):
...
""")
# Cell 2 — uses linalg from Cell 1
result = linalg.norm(my_array)
Running the demos
uv sync --extra dev
uv run marimo edit demo.py
The demo covers the core API: fibonacci benchmark, cimport patterns, compile_module, and cross-cell interaction.
Examples
The examples/ directory contains standalone notebooks:
examples/mandelbrot.py— Interactive Mandelbrot set with a resolution slider. Runs Cython and Python side-by-side with progressive rendering (Cython result appears instantly, Python fills in when done). PEP 723 compatible — run withuv run --sandbox examples/mandelbrot.py.
uv sync --extra dev
uv run marimo run examples/mandelbrot.py
Development
uv sync --extra dev
uv run ruff check src/
uv run pytest
License
MIT
Metadata
Release files for marimo-cython 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| marimo_cython-0.1.2.tar.gz | 13.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| marimo_cython-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.6 kB
Release files / marimo_cython-0.1.2.tar.gz
| Download URL | marimo_cython-0.1.2.tar.gz |
|---|---|
| Size | 13.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8c8135f5b3ccca142b56c1bc67f8613dbec82f051d785bfc9f16e147356573d9
|
|
BLAKE2b-256 checksum How to use checksums |
dabba4bc1e739a8e434791c6f1d8e0c8576214892b66a4b595312e4b6b74faf4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 4, 2026.
Transparency logRelease files / marimo_cython-0.1.2-py3-none-any.whl
| Download URL | marimo_cython-0.1.2-py3-none-any.whl |
|---|---|
| Size | 15.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
700c4e586af31d1a50e12d53c986cf77f4097a2903924145418ecc9b9dfab9ee
|
|
BLAKE2b-256 checksum How to use checksums |
9a47356b4bdc897ef41233ec2aa1a0a3bcbd9d3cf4d1319940e301eae22a9190
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 4, 2026.
Transparency logSecurity for marimo-cython 0.1.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for marimo_cython-0.1.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 4, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.