Skip to main content

Cython compilation support for marimo notebooks

Project description

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

  1. Source is extracted (via inspect.getsource or marimo's runtime context)
  2. import cython is auto-prepended if missing (detected via AST, not regex)
  3. from cython.cimports.* imports above the function are collected via AST and included
  4. A content-addressed cache key is computed (SHA-256 of source + all options + Python/Cython versions)
  5. On cache miss: write .pyx -> cythonize to .c -> compile to .so via setuptools -> load with importlib
  6. On cache hit: load the existing .so directly
  7. Intermediate build artifacts (.c, .cpp, temp dirs) are cleaned up after successful compilation

Compiled artifacts live in .marimo_cython_cache/ (add to .gitignore). Numpy includes are auto-detected if the source references numpy.

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 with uv 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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

marimo_cython-0.1.0.tar.gz (12.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

marimo_cython-0.1.0-py3-none-any.whl (14.5 kB view details)

Uploaded Python 3

File details

Details for the file marimo_cython-0.1.0.tar.gz.

File metadata

  • Download URL: marimo_cython-0.1.0.tar.gz
  • Upload date:
  • Size: 12.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for marimo_cython-0.1.0.tar.gz
Algorithm Hash digest
SHA256 1db9745090558a124878b75ba268f1a15d6a782b029483fa735e68f5d1bb0490
MD5 2eb28d1d88fbf85756098c643868b1d4
BLAKE2b-256 f52b039d31f832963ed8460d66a9cdd7e253ab5ed37ae3e9795c736d96ec71b8

See more details on using hashes here.

Provenance

The following attestation bundles were made for marimo_cython-0.1.0.tar.gz:

Publisher: publish.yml on cemrehancavdar/marimo-cython

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file marimo_cython-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: marimo_cython-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for marimo_cython-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9c10939c8373b02243d3d48771f9491c8fa4fa20855290ebb9609d88297f3b60
MD5 6ac489d42530dae67ffc7e49d2a5f684
BLAKE2b-256 89447f1c2d79b22fecbc812d8da4ec66aab4d7ad18ca48e1bee3e71ff6a25af8

See more details on using hashes here.

Provenance

The following attestation bundles were made for marimo_cython-0.1.0-py3-none-any.whl:

Publisher: publish.yml on cemrehancavdar/marimo-cython

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page