Skip to main content

Self-registering nanobind helpers (import as easybind)

Project description

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.cmakeeasybind_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 = latest on PyPI for that distribution; run from the tree that contains pyproject.toml):

easybind-pin-pyproject --dry-run
easybind-pin-pyproject
easybind-pin-pyproject --installed
easybind-pin-pyproject --version 0.2.3
easybind-pin-pyproject --distribution cppdantic
easybind-pin-pyproject --pyproject /path/to/pyproject.toml

Other devtools CLIs (after pip install / uv pip install -e ., or run scripts/… shims from a git checkout):

easybind-release-tag --dry-run    # next v* tag + git push (easybind repo)
easybind-wait-pypi                # poll PyPI until pins resolve (downstream CI)

Library:

from easybind.devtools import (
    bump_compatible_pins_in_file,
    fetch_pypi_version,
)

# Pin all easybind~= lines to latest PyPI easybind
ver = fetch_pypi_version("easybind")
bump_compatible_pins_in_file("pyproject.toml", "easybind", ver)

# Pin all cppdantic~= lines to latest PyPI cppdantic (e.g. downstream of cppdantic)
ver = fetch_pypi_version("cppdantic")
bump_compatible_pins_in_file("pyproject.toml", "cppdantic", ver)

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

CI (downstream): easybind-wait-pypi (or scripts/wait_pypi_release.py) wraps wait_pypi_for_compatible_pin in easybind.devtools — poll PyPI until the pinned version exists. From a checkout without install, set PYTHONPATH to this repo’s src or use the shim script.

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"; });
});

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

easybind-0.2.5.tar.gz (31.7 kB view details)

Uploaded Source

Built Distributions

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

easybind-0.2.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (301.0 kB view details)

Uploaded CPython 3.14manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

easybind-0.2.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (300.9 kB view details)

Uploaded CPython 3.13manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

easybind-0.2.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (301.0 kB view details)

Uploaded CPython 3.12manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

easybind-0.2.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (301.4 kB view details)

Uploaded CPython 3.11manylinux: glibc 2.27+ x86-64manylinux: glibc 2.28+ x86-64

File details

Details for the file easybind-0.2.5.tar.gz.

File metadata

  • Download URL: easybind-0.2.5.tar.gz
  • Upload date:
  • Size: 31.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for easybind-0.2.5.tar.gz
Algorithm Hash digest
SHA256 c63279b899f9bc66995212acddfaa74ee2b030d1c2afa529f38b6d73b37bcc0e
MD5 731915c1720095128b3213f440927257
BLAKE2b-256 61479618799a4a24b7ac540d57304bb69503bdeb173037247136c70007723ac0

See more details on using hashes here.

File details

Details for the file easybind-0.2.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for easybind-0.2.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 2733646a72741165c9144ee550edcf918973d0aa4a352c5f976f89a97454ce47
MD5 b78655fca27502f7c6f22c07ed5c668c
BLAKE2b-256 801d776168c44463b4d39ded78c020609605913c14a5be7ee1f33c3e4bd8d2e3

See more details on using hashes here.

File details

Details for the file easybind-0.2.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for easybind-0.2.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 ca6a75d3e971982a1618e40ec61fbec5ef925d30604212c44386dab6be881c74
MD5 b9c4cfd87508a7ff6ae2e2344a132cba
BLAKE2b-256 e1aeb305b1e8fa4323eb42c393af0c0f39128c887288bdc30fad220e1eb6e787

See more details on using hashes here.

File details

Details for the file easybind-0.2.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for easybind-0.2.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 62b7d8a548fe0af8570b29d3f03efbb4fd837a0301aac6e32408538132bc8415
MD5 5d773cf5833727036386440e666b318c
BLAKE2b-256 cb505f9e10b65e5438fac361a9a475034b8669418c0c14849579b0956170ae0a

See more details on using hashes here.

File details

Details for the file easybind-0.2.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.

File metadata

File hashes

Hashes for easybind-0.2.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 1a9e8ce8615d22aef0c6b9b9bc79a5741d5c8febbf5e22e50bc5a941d31d77d4
MD5 5135695b4c131bf3b73c66c6ac7d40ed
BLAKE2b-256 681dbe4175623f5520c55a40a7efc6616809974cb4c05a450cd8b7f1ea7061fa

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