Skip to main content

easybind

Simple self-registering helpers for distributed nanobind bindings.

Install

pip install easybind
import easybind
from easybind import sample  # optional demo module

PyPI project: easybind (same name as the import).

Version: Set by Git tags at build time (v0.1.0, …) via setuptools-scm; see RELEASING.md. At runtime, easybind._version.__version__ is written when the wheel is built (or use importlib.metadata.version("easybind")).

Local dev / clangd

An editable install configures CMake under ./build/ and generates build/compile_commands.json, which clangd picks up via .clangd — same database as the Python build, no second configure step:

uv pip install -e .    # or: pip install -e .

If you need compile_commands.json without pip, run scripts/clangd-update.sh (plain cmake -S . -B build …).

Python layer

The Python package is implemented as native extensions. It exposes:

  • easybind (core helpers and macros)
  • easybind.module (module tree API)
  • easybind.sample (demo bindings)

Build-time SDK

Installed wheels ship CMake helpers under easybind/cmake/:

  • easybind_pip.cmake — easybind_pip_setup() finds Python, nanobind (pip), libeasybind, and include roots for #include <easybind/...>, then pulls in easybind_dependencies.cmake. Helpers: easybind_pip_link_magic_enum(target), easybind_pip_set_rpath_next_to_easybind(target easybind_pkg_dir).
  • easybind_dependencies.cmake — pins nanobind, magic_enum, reflect-cpp (same tags as this repo). Use easybind_fetch_third_party_deps() to pull all three, or easybind_fetch_nanobind() / easybind_fetch_magic_enum() / easybind_fetch_reflect_cpp() when you only need a subset (e.g. pip already provides nanobind, so call only easybind_fetch_magic_enum()).

Typical consumer bootstrap:

find_package(Python REQUIRED COMPONENTS Interpreter Development.Module)
execute_process(COMMAND "${Python_EXECUTABLE}" -c
  "import pathlib, easybind; print(pathlib.Path(easybind.__file__).resolve().parent / 'cmake' / 'easybind_pip.cmake')"
  OUTPUT_VARIABLE _eb_pip OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY)
include("${_eb_pip}")
easybind_pip_setup()
easybind_fetch_magic_enum()   # if you include easybind headers that need magic_enum

When developing inside this repository, easybind_add_extension(...) is defined in the top-level CMakeLists.txt (not shipped in the wheel).

Bumping {distribution}~=… pins (downstream projects)

Use easybind.devtools or the easybind-pin-pyproject CLI to rewrite every compatible-release line {name}~=X.Y.Z in a pyproject.toml for any PyPI distribution name name (not only easybind). Examples:

  • cppdantic pinning easybind in several tables.
  • A future project pinning cppdantic the same way: pass --distribution cppdantic.

CLI (defaults: distribution=easybind, version = highest v* tag on GitHub — repo URL taken from that distribution’s PyPI metadata; run from the tree that contains pyproject.toml):

easybind-pin-pyproject --dry-run
easybind-pin-pyproject
easybind-pin-pyproject --from-pypi              # PyPI “latest published” instead of GitHub tags
easybind-pin-pyproject --from-github             # same as no flag; optional OWNER/REPO override
easybind-pin-pyproject --from-github ORG/REPO
easybind-pin-pyproject --installed
easybind-pin-pyproject --version 0.2.3
easybind-pin-pyproject --distribution cppdantic
easybind-pin-pyproject --pyproject /path/to/pyproject.toml

Use GITHUB_TOKEN for private GitHub repos or higher API rate limits.

GitHub tags API: responses can be a few seconds behind right after you push a new v* tag. If easybind-pin-pyproject --dry-run still shows the previous release, wait briefly and run again (or pin with --version until the API catches up).

Other devtools CLIs (install easybind first — pip install -e . from this repo, or PyPI):

easybind-release-tag --dry-run    # tag message uses [project].name from pyproject.toml
easybind-wait-pypi                # poll PyPI until pins resolve (downstream CI)

Without installing, run the same modules with PYTHONPATH pointing at this repo’s src (e.g. PYTHONPATH=src python -m easybind.devtools.release_tag).

Library:

from easybind.devtools import (
    bump_compatible_pins_in_file,
    fetch_pypi_version,
    github_owner_repo_from_pypi_distribution,
    latest_release_version_from_github,
)

# Match CLI default: latest v* tag on GitHub (same as easybind-pin-pyproject with no version flags)
or_ = github_owner_repo_from_pypi_distribution("easybind")
ver = latest_release_version_from_github(or_)
bump_compatible_pins_in_file("pyproject.toml", "easybind", ver)

# Or: latest published on PyPI (CLI: --from-pypi)
ver = fetch_pypi_version("easybind")
bump_compatible_pins_in_file("pyproject.toml", "easybind", ver)

Shorthands bump_easybind_compatible_pins / bump_easybind_compatible_pins_in_file remain for distribution=\"easybind\" only.

CI (downstream): easybind-wait-pypi wraps wait_pypi_for_compatible_pin in easybind.devtools — poll PyPI until the pinned version exists.

Core idea

  • Each namespace/module defines a ModuleNode and a bind callback.
  • The module entry point calls apply_init to run the callback and recurse.
  • Submodules are created on demand and registered in sys.modules.
  • Shared-object modules are marked so recursion stops at their boundary.
  • A minimal sample module lives at easybind.sample.

Developer note: layout rules

  • __init__.cpp marks the Python boundary (NB_MODULE) for a package/module.
  • node.cpp/.hpp is the pure C++ module-tree core.
  • ns_module.hpp defines the EASYBIND_NS_MODULE* macros.
  • Directory layout mirrors namespaces and Python modules.

Smallest possible example

1) Define a C++ type (normal code)

#pragma once

#include <string>

struct PeerInfo {
    std::string peer_id;
    int transport = 0;
};

2) Bind it in a separate file (module node)

#include <easybind/bind.hpp>

struct PeerInfo;  // forward declare or include the header

EASYBIND_NS_MODULE(my_pkg, m, false, {
    nanobind::class_<PeerInfo>(m, "PeerInfo")
        .def(nanobind::init<>())
        .def_rw("peer_id", &PeerInfo::peer_id)
        .def_rw("transport", &PeerInfo::transport);
});

3) Module entry point (shared-object boundary)

Use this only for the package that has its own .so and NB_MODULE entry point. Do not pair it with EASYBIND_NS_MODULE for the same my_pkg name. If you need to add bindings from another file, use EASYBIND_NS_MODULE_EXTEND to extend the same module node instead.

#include <easybind/bind.hpp>

EASYBIND_NS_MODULE_SHARED_OBJECT(my_pkg, my_pkg, m, true, {
    m.doc() = "my_pkg module";
});

4) Extend from another file

#include <easybind/bind.hpp>

EASYBIND_NS_MODULE_EXTEND(my_pkg, m, {
    m.def("ping", [] { return "pong"; });
});

Metadata

Release files for easybind 0.2.15

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for easybind 0.2.15
File Size Uploaded
easybind-0.2.15.tar.gz 35.9 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for easybind 0.2.15
File
easybind-0.2.15-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.14 CPython 3.14 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
easybind-0.2.15-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.13 CPython 3.13 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
easybind-0.2.15-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.12 CPython 3.12 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details
easybind-0.2.15-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl CPython 3.11 CPython 3.11 Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 Details

Total release size: 1.3 MB

Release files / easybind-0.2.15.tar.gz

Download URL easybind-0.2.15.tar.gz
Size 35.9 kB
Tags Source
SHA-256 checksum
How to use checksums
98f2910b0527ece878ce60410b34efb3b824be359afc7034bd4ba8d59f22cfa7
BLAKE2b-256 checksum
How to use checksums
4bf362edaf3b5990b9614ce9cb77187bb265d5c9aadebf159f5498ae2a09fa17
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / easybind-0.2.15-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL easybind-0.2.15-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 305.3 kB
Tags CPython 3.14 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
b0f9f6db1e852fde7a1c745b6bef43911dd733c40588fd4a43e2fd7b74ad8265
BLAKE2b-256 checksum
How to use checksums
e535faeb73a624a592d91ab34ad40a70b1ceb5ac9a643490c4c2d5e69923c381
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / easybind-0.2.15-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL easybind-0.2.15-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 305.2 kB
Tags CPython 3.13 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
7f95181ca29092b60695f9653888a49a1f8a35893ddc182c80b383860468c6bd
BLAKE2b-256 checksum
How to use checksums
12494c9a75c5def02fb3339c806480a410aeb831461e4de58888b5d98e18d5d8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / easybind-0.2.15-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL easybind-0.2.15-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 305.2 kB
Tags CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
711e2da7bce9fe3bc3d14880aaedade14537993c31d66d4e641594e2845d9dfd
BLAKE2b-256 checksum
How to use checksums
b174c52ee7e1992dce7962f95b3207ed1ef054544c8af35bfc53f87e76b12702
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / easybind-0.2.15-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl

Download URL easybind-0.2.15-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Size 305.6 kB
Tags CPython 3.11 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64
SHA-256 checksum
How to use checksums
3aca06c3617c977615e218b9a49895abfa27511cd321518d2921e73b6a121c1d
BLAKE2b-256 checksum
How to use checksums
77a886f8145dfaac7ff07ab316006da297f7f9c1a0ad92f0138fd4c8d9464d19
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release history Release notifications | RSS feed

This release

0.2.15 This release

5 release files

0.2.13

5 release files

0.2.12

5 release files

0.2.11

5 release files

0.2.10

5 release files

0.2.9

5 release files

0.2.8

5 release files

0.2.7

5 release files

0.2.5

5 release files

0.2.4

5 release files

0.2.3

5 release files

0.2.2

5 release files

0.2.0

5 release files

0.1.6

5 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page