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; sole dirty pyproject.toml is auto-committed
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.12.tar.gz (35.4 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.12-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (304.9 kB view details)

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

easybind-0.2.12-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (304.8 kB view details)

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

easybind-0.2.12-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (304.8 kB view details)

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

easybind-0.2.12-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (305.2 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.12.tar.gz.

File metadata

  • Download URL: easybind-0.2.12.tar.gz
  • Upload date:
  • Size: 35.4 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.12.tar.gz
Algorithm Hash digest
SHA256 75194a3757d35693907dcba33f225332ec3ee0294d660b93204001298ec252cc
MD5 f97b3c378e3e631e28b5c7d8e4d5a485
BLAKE2b-256 f64493fb75200f5501d771adcc56188ef58cd539f4f6bb79f26fbc24384472a4

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.12-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 af9b3b222498c9e3e40dce7e719474be277a14f6512b314c0a4e3c078294c3ba
MD5 b51285a57073f4ab53cb47113ed4bd3f
BLAKE2b-256 64f6a2db4c836c8d9b4bee7d845fbb19827f0666e3c7b6cfc9392db201d15319

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.12-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 977626f3a4269ec4c16138ce1a9d31985394882eb5804d58c4b1337ab1eb1569
MD5 8187d1aaadce0031e1f228910bea2fcd
BLAKE2b-256 b250b2fb2de0a23ea79f67a51002fc788319ceb9846c09e2ba4c0c145c40c6d1

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.12-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 ab93bd16647cef5b01a50c2f3315ad74352aabf8b14b6288c98c2d48a8b4ddf1
MD5 3aa1617b750fc864deac733014b0079f
BLAKE2b-256 39535b8f7667444ea5f3f5c00cf3636aac003ebd631769abe677b21ff61414b7

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for easybind-0.2.12-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
Algorithm Hash digest
SHA256 2021a3dda3a6a64b4cdfe48fdc4ac9b2e6d88cc2522437166ffdd26b61e1ce53
MD5 d3cb46b375602a5aeee06acd6c5e7a52
BLAKE2b-256 7334130375f07d410040c8a8bcf4f61a57ec9b799a076fd33c349cc22ff74771

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