Skip to main content

Minimalist high-speed Python-to-Zig FFI bridge via comptime trampolines.

Project description

Nano-FFI

Minimalist Python-to-Zig FFI bridge via comptime trampolines.

Philosophy: Zig Performance. Python Brain. Comptime Safety.


What it is

Nano-FFI lets Python call Zig functions with near-zero overhead. Instead of runtime type inspection (like ctypes/cffi), it uses Zig's comptime engine to generate type-safe wrapper functions at compile time — no branches, no dynamic dispatch, no surprises.

import nano_ffi

result = nano_ffi.call("add", 3, 4)      # → 7
result = nano_ffi.call("mul", 2.5, 4.0)  # → 10.0

Benchmark

Library Avg call overhead vs Nano-FFI
ctypes ~340 ns 4.28x slower
Nano-FFI 79.6 ns baseline

Measured on x86_64 Windows, ReleaseFast build, 100k iterations. Run python tests/test_python.py to reproduce on your machine.


Requirements

  • Python >= 3.10
  • Zig 0.13.0 (pinned — do not use nightly)
  • A C compiler (for linking against libpython)

Installation

From PyPI (pre-built wheels)

pip install nano-ffi

No Zig required — wheels are pre-compiled for Linux, macOS, and Windows.

From source

git clone https://github.com/fcarvajalbrown/Nano-FII.git
cd Nano-FII
pip install scikit-build-core
pip install -e .

Or build manually:

zig build -Doptimize=ReleaseFast \
  -Dpython-include=<path/to/python/include> \
  -Dpython-lib=<path/to/python/libs>

The compiled library lands in zig-out/lib/.


Usage

Calling a registered function

import nano_ffi

# Integer addition (built-in example)
result = nano_ffi.call("add", 3, 4)        # → 7

# Float multiplication (built-in example)
result = nano_ffi.call("mul", 2.5, 4.0)   # → 10.0

# Check library version
print(nano_ffi.version())                  # → "0.1.0"

Registering your own Zig function

In your Zig code, use comptime_bridge.makeTrampoline to generate a wrapper and register it:

const bridge = @import("nano_ffi").bridge;
const reg    = @import("nano_ffi").registry;

fn add(a: i64, b: i64) i64 { return a + b; }

const AddTrampoline = bridge.makeTrampoline(add, .{
    .args = &.{
        .{ .name = "a", .typ = .i64 },
        .{ .name = "b", .typ = .i64 },
    },
    .ret = .i64,
});

// Register at init time:
try my_registry.register("add", AddTrampoline.asPtr(), AddTrampoline.sig);

Architecture

src/
├── root.zig            # Entry point, exports PyInit_nano_ffi
├── registry.zig        # StringHashMap storing FnPtr + Signature
├── comptime_bridge.zig # Comptime trampoline generator (core)
├── allocator.zig       # Python-Zig memory boundary management
└── python_ext.zig      # CPython C extension (only file touching Python.h)

Key constraint: only python_ext.zig is allowed to import <Python.h>. Everything else is pure Zig with C-ABI compatible types.

How the speed works: makeTrampoline uses Zig's inline for over the signature at compile time — the compiler sees explicit typed assignments, not a loop. No branches, no runtime type checks in the hot path.


Running tests

# Zig unit tests
zig build test

# Python end-to-end + benchmark (debug)
zig build
python tests/test_python.py

# Benchmark (production numbers)
zig build -Doptimize=ReleaseFast \
  -Dpython-include=<path> \
  -Dpython-lib=<path>
python tests/test_python.py

Cross-compilation (wheels)

From a single Linux machine:

zig build -Doptimize=ReleaseFast -Dtarget=x86_64-linux-gnu
zig build -Doptimize=ReleaseFast -Dtarget=aarch64-apple-macos
zig build -Doptimize=ReleaseFast -Dtarget=x86_64-windows-gnu

Current limitations

  • Supported argument types: i64, f64, bool — slices and strings planned for v0.2.0
  • Max 8 arguments per function call

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 Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

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

nano_ffi-0.1.0-py3-none-any.whl (3.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: nano_ffi-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 3.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for nano_ffi-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8fbbdc79422f50dc1dcced0c98acfc51d45ea7ac83be2378216683f3969109a5
MD5 bf1eba478f4e347b09cc45092953af8a
BLAKE2b-256 7d5a47978b17ff5618ddf3f4049f53a62be1959c953ef28a91c49798480455dc

See more details on using hashes here.

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