Skip to main content

Automatically build and install Python extension modules from C++ headers

Project description

magicbind

CI PyPI PyPI - Python Version License: MIT

The easy way to speed up Python bottlenecks with C++. No CMake, no build system, no boilerplate. Just point magicbind at your header and it takes care of the rest.

magicbind add mylib.h

magicbind parses the header, generates nanobind glue code, compiles it with a bundled Zig compiler, and installs the extension into your Python environment.

Install

uv add magicbind

A C++ compiler is bundled via the ziglang package, so you don't need one installed.

Basic usage

Given a header:

// math_utils.h
#include <vector>
#include <optional>

double sum(const std::vector<double>& values);
std::optional<double> mean(const std::vector<double>& values);
double clamp(double value, double lo, double hi);

If there is a math_utils.cpp next to the header, magicbind picks it up automatically. You can also pass source files explicitly. If everything is defined in the header, no source file is needed.

magicbind add math_utils.h                    # auto-detects math_utils.cpp
magicbind add math_utils.h --source other.cpp # explicit
magicbind add math_utils.h --source a.cpp --source b.cpp

Then use it from Python:

import math_utils

math_utils.sum([1, 2, 3])       # 6.0
math_utils.mean([])             # None
math_utils.clamp(10, 0, 5)      # 5.0

STL types are converted automatically: std::vector becomes a list, std::optional becomes None or a value, std::pair becomes a tuple.

System libraries

To use a library installed on your system, pass --pkg with its pkg-config name:

magicbind add image_ops.h --pkg opencv4 --system-compiler

--system-compiler is required when linking against system C++ libraries. The default Zig compiler is great for self-contained code, but system libraries like OpenCV were compiled with a different C++ runtime, so you need the system's g++ or clang++ to match.

You can also specify include and link paths manually:

magicbind add mylib.h \
  --include /opt/mylib/include \
  --lib /opt/mylib/lib \
  --link mylib

Rebuilding

When you change the header or source, run:

magicbind build          # rebuilds all modules
magicbind build mylib    # rebuilds one module

This replays the original add command with the same flags and compiler, without you having to remember them.

OpenCV

magicbind ships built-in type casters for common OpenCV types. Write normal C++ functions:

// image_ops.h
#include <opencv2/core.hpp>
#include <string>

cv::Mat blur(const cv::Mat& src, int kernel_size = 5);
cv::Mat to_grayscale(const cv::Mat& src);
cv::Size image_size(const cv::Mat& src);
cv::Mat crop(const cv::Mat& src, cv::Rect roi);
cv::Scalar mean_color(const cv::Mat& src);

And call them from Python with numpy arrays, no manual conversion needed:

import numpy as np
import image_ops

img = np.zeros((480, 640, 3), dtype=np.uint8)

blurred       = image_ops.blur(img, 11)          # numpy array
gray          = image_ops.to_grayscale(img)       # numpy array
w, h          = image_ops.image_size(img)         # tuple
cropped       = image_ops.crop(img, (10, 10, 100, 100))  # rect as tuple
b, g, r, _   = image_ops.mean_color(img)         # scalar as tuple

Supported types: cv::Matnumpy.ndarray, cv::Point / cv::Size / cv::Rect / cv::Scalar ↔ tuple, and their typed variants (cv::Point2f, cv::Rect2d, etc.).

Jupyter

Write C++ directly in a notebook cell:

%load_ext magicbind
%%magicbind math_utils
#include <vector>

double sum(const std::vector<double>& v) {
    double s = 0;
    for (auto x : v) s += x;
    return s;
}
math_utils.sum([1.0, 2.0, 3.0])  # 6.0

The module is compiled and imported automatically. Re-running the cell recompiles and reloads. Requires magicbind in your environment.

Open In Colab

How it works

magicbind uses libclang to parse the header into an intermediate representation, generates a nanobind binding file, and compiles everything in a single zig c++ (or system compiler) invocation. Build artifacts go into .magicbind/build/ and the compiled extension is installed directly into site-packages.

Templates

Template functions and classes are not bound directly. Expose concrete overloads in your header:

template <typename T>
T clamp(T value, T lo, T hi);

// Expose concrete overloads:
inline int    clamp(int v,    int lo,    int hi)    { return ::clamp(v, lo, hi); }
inline float  clamp(float v,  float lo,  float hi)  { return ::clamp(v, lo, hi); }
inline double clamp(double v, double lo, double hi) { return ::clamp(v, lo, hi); }

All three are available in Python as mylib.clamp. The right overload is picked automatically based on the argument types.

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

magicbind-0.2.1.tar.gz (20.9 kB view details)

Uploaded Source

Built Distribution

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

magicbind-0.2.1-py3-none-any.whl (18.3 kB view details)

Uploaded Python 3

File details

Details for the file magicbind-0.2.1.tar.gz.

File metadata

  • Download URL: magicbind-0.2.1.tar.gz
  • Upload date:
  • Size: 20.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for magicbind-0.2.1.tar.gz
Algorithm Hash digest
SHA256 f6f677f3464c52d510214000d18dd47406224ce2f3a02fcfff2caaa4945534ba
MD5 ccacf28a3bbdc939fce07801c9b51e9d
BLAKE2b-256 a50c27564071ed4acb4819b03b611364fffd3419c7e19e86f621c519326def62

See more details on using hashes here.

File details

Details for the file magicbind-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: magicbind-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 18.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for magicbind-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 b098f35df2f58d7c32cb5b648c2b44ad0d43b457791a49d1ba18ae0822a11566
MD5 6eeeb7e9dd0beffabe1814195f6a2de9
BLAKE2b-256 33286dafaeadd1de1187b2b9ac5dad60049f9e2b8f88a80a9d098a5421f02aa3

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