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; 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.

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

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.9.tar.gz (34.5 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.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (303.8 kB view details)

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

easybind-0.2.9-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (303.7 kB view details)

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

easybind-0.2.9-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (303.7 kB view details)

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

easybind-0.2.9-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (304.1 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.9.tar.gz.

File metadata

  • Download URL: easybind-0.2.9.tar.gz
  • Upload date:
  • Size: 34.5 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.9.tar.gz
Algorithm Hash digest
SHA256 42f2b6263a128ee0669b2be5666f8fbe974e1cc7e7a56e2310ebc8929d34b2f7
MD5 71e54c5a6c562eae3d521f80ee142995
BLAKE2b-256 38312335a5b9d193d7b448e54fcd074b26249d34cb485d831b027f3e8640fe43

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.9-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 a2fe95e0a4304e12bdd3c62307dce7d3755c841cb1ec0f6d36ad16819db1ad06
MD5 e3cf109e31a9809652821f906d50ff1d
BLAKE2b-256 397bb3e37474e8315eea6ad3e046b8f86fbb6f0cd1f9d1ce0b7b22ed01cb10d1

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.9-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9fc9f728159e06a6525cefe41022ea863460577d6b99db59c26912280ea415ed
MD5 6a9dd1fa7eabfaa13c507b25ca6ef637
BLAKE2b-256 65759e949e3c0a315545d757dd13459caca160a13d898d05847f3ba5ebeb37a6

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.9-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 6e3f9e5953544ca1eddaa43f2d15ed23d26d407c06af0a1c664ffb234914db04
MD5 d13c2c0870985b90a7944b71a0ec0554
BLAKE2b-256 4390d17bf1043408ca05a48a13a57de28aee55ab3d1b6e8df45a76cac1464c44

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.9-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 851455658e29739b50c162b407e92a94c15186f96bda53863fd621274cfbf629
MD5 f096c891cad9f9a1a717c020f96210f7
BLAKE2b-256 eacec36a1ad98d1549217d27340c3f42063d6074022c0019c4608c295533ec5f

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