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 = 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    # next v* tag + git push (easybind repo)
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"; });
});

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.11.tar.gz (34.6 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.11-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (304.0 kB view details)

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

easybind-0.2.11-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (303.9 kB view details)

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

easybind-0.2.11-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (303.9 kB view details)

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

easybind-0.2.11-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (304.3 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.11.tar.gz.

File metadata

  • Download URL: easybind-0.2.11.tar.gz
  • Upload date:
  • Size: 34.6 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.11.tar.gz
Algorithm Hash digest
SHA256 8c729b96cbb6184bc038ff54c0b050a60f430229f0cf479dc446228b1bb33a49
MD5 d1087e288f38310de7fc5c86069c6f6a
BLAKE2b-256 1c2f1d0c38b0615918a4ccacdb45055bece536bff2ec6ae7c1739d8284ca222c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.11-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 7ca51215da3dcaa7e7e322d967592978c73c6a88d333baf7d94d6cce7482f778
MD5 3c988959f5e49830775a7a7cac10ca71
BLAKE2b-256 b16fbe2ee9c5a681f7120108b818945a824ed2a0b8d1dbb543ee12acc279b336

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.11-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 beeb08ddb8badacea2318e6de25f91d8061c2d3b9c09365b31dbb510a9b5ba41
MD5 3a8b2df78315df062241cad4d3df325c
BLAKE2b-256 6a4bf15236761bc4cfdc46998ac9b0bb2ca0af759fed34adc767944f8cb50b86

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.11-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 e97dc9a58c885586add9c8883463e54aba873839571210c179ed410639e4865e
MD5 4f3b207e1068d532ee763199a8836cbd
BLAKE2b-256 2465882b5759fe5d654a0c8c78c87067e766606eb74f778bdb4f01e68403654d

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.11-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 9a730bab59827ffcfb2f6b648f5899570637343d8fb2cdf3ddd1f0e349bf293c
MD5 1c4d2f4ebcb96729478f15ef76c460e8
BLAKE2b-256 ef45cc77e59a2cafbc96f7f115f29da3a60d33a5bd97b40c1033e03132991649

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