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.cmake—easybind_pip_setup()finds Python,nanobind(pip),libeasybind, and include roots for#include <easybind/...>, then pulls ineasybind_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). Useeasybind_fetch_third_party_deps()to pull all three, oreasybind_fetch_nanobind()/easybind_fetch_magic_enum()/easybind_fetch_reflect_cpp()when you only need a subset (e.g. pip already provides nanobind, so call onlyeasybind_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; run from the tree that contains pyproject.toml):
easybind-pin-pyproject --dry-run
easybind-pin-pyproject
easybind-pin-pyproject --from-github # GitHub OWNER/REPO from PyPI metadata for --distribution
easybind-pin-pyproject --from-github ORG/REPO # override when PyPI has no GitHub URL
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.
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
ModuleNodeand a bind callback. - The module entry point calls
apply_initto 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__.cppmarks the Python boundary (NB_MODULE) for a package/module.node.cpp/.hppis the pure C++ module-tree core.ns_module.hppdefines theEASYBIND_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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file easybind-0.2.8.tar.gz.
File metadata
- Download URL: easybind-0.2.8.tar.gz
- Upload date:
- Size: 34.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
51ceb9fc5909f9eecee85958cf3f2a33d320b834e87483e8fc3787c5c0c782f0
|
|
| MD5 |
13a2b6f58fd2e1d8327dfc599cf60633
|
|
| BLAKE2b-256 |
0a19360e5bd839b9493ea89ab7fc6e42e0dd6187bd382e34370a3adc839ec51e
|
File details
Details for the file easybind-0.2.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: easybind-0.2.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 303.5 kB
- Tags: CPython 3.14, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a727927fc5f34250ef8ba8becbb3c26e9f91c34c7490afdbfeeb6abec18c9e08
|
|
| MD5 |
6cf69afcc9b905f87e39a570cfe4e8a5
|
|
| BLAKE2b-256 |
673185f84036233b0b7bf385f80a91fc2294871f08326d9ff6dc9a284b777d01
|
File details
Details for the file easybind-0.2.8-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: easybind-0.2.8-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 303.4 kB
- Tags: CPython 3.13, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
23e5699a766042b91f5eaa91f0fb087db7125233e277a086b7421e0223c212ff
|
|
| MD5 |
9d88bc30983b73717f6b8aef0cd84fd0
|
|
| BLAKE2b-256 |
72b291810f784cfe2ebaefc4dcb1633a32fa58ec8259978c3e4cf753ec256946
|
File details
Details for the file easybind-0.2.8-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: easybind-0.2.8-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 303.4 kB
- Tags: CPython 3.12, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eecad0c494bc02e226290d64001e93cc46dca0f498b14b76ae469f4acd9244a4
|
|
| MD5 |
d7e83e1042ebc971b8bfbb66dabcd2cf
|
|
| BLAKE2b-256 |
cdf1e4b93be8b3d1a02976f4bb450ea05717a90dc7617a67c3745cf682e3ef47
|
File details
Details for the file easybind-0.2.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: easybind-0.2.8-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 303.8 kB
- Tags: CPython 3.11, manylinux: glibc 2.27+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4535393a50d18c7feca67ddaded8622317d750905b34254479cfcc24093f35ab
|
|
| MD5 |
dfd218d38622dd0f80d434b6e29782ba
|
|
| BLAKE2b-256 |
7fc913c036d3075cc75bfa32a25c2c9c78aa39cac901185686b4c4a7f4708111
|