Skip to content

How the module is built

The part of sharedbox that reads and writes shared memory is compiled C++, in a module called sharedbox._native, and update and snapshot are compiled too unless your class defines or inherits its own. Compiled code has to be built for each Python version it runs on, which could mean a long list of builds. This page explains how one build line gives a wheel for every supported Python instead.

One line, three wheels

The module is built with nanobind, a library that connects C++ to Python, as C++20 against sharedbox::headers, the CMake target of include/sharedbox/sharedbox.hpp:

nanobind_add_module(_native
    STABLE_ABI
    FREE_THREADED
    LTO
    NB_DOMAIN sharedbox
    src/sharedbox/_native/codec.cpp
    src/sharedbox/_native/module.cpp
    src/sharedbox/_native/scalars.cpp
    src/sharedbox/_native/segment.cpp
    src/sharedbox/_native/types.cpp)
target_link_libraries(_native PRIVATE sharedbox::headers)

STABLE_ABI asks for a module that keeps working on later Python versions without being rebuilt, which nanobind can do from Python 3.12.1 FREE_THREADED asks for a module that runs without the global interpreter lock (GIL), on the free-threaded Python that has none.2 nanobind quietly ignores whichever option doesn't fit the Python doing the build,12 so the same line, run on three Pythons, gives three wheels:

One line, three wheels
nanobind_add_module(STABLE_ABI FREE_THREADED)CPython 3.11Too old for the stable ABI, so both options are ignored.CPython 3.12Takes STABLE_ABI. FREE_THREADED does nothing on a Python with a GIL.free-threaded 3.14Takes FREE_THREADED. It can't load stable-ABI modules, so STABLE_ABI is ignored.cp311-cp3113.11 onlycp312-abi33.12 and latercp314-cp314tfree-threaded 3.14 Too old for the stable ABI, so both options are ignored. Takes STABLE_ABI. FREE_THREADED does nothing on a Python with a GIL. Takes FREE_THREADED. It can't load stable-ABI modules, so STABLE_ABI is ignored.
nanobind_add_module(STABLE_ABI FREE_THREADED)CPython 3.11Too old for the stable ABI, so both options are ignored.CPython 3.12Takes STABLE_ABI. FREE_THREADED does nothing on a Python with a GIL.free-threaded 3.14Takes FREE_THREADED. It can't load stable-ABI modules, so STABLE_ABI is ignored.cp311-cp3113.11 onlycp312-abi33.12 and latercp314-cp314tfree-threaded 3.14 Too old for the stable ABI, so both options are ignored. Takes STABLE_ABI. FREE_THREADED does nothing on a Python with a GIL. Takes FREE_THREADED. It can't load stable-ABI modules, so STABLE_ABI is ignored.

The free-threaded build needs its own wheel because it can't load stable-ABI modules.2 Python 3.15 starts a stable ABI for free-threaded builds (abi3t), which could later fold that wheel into the others.3

The header in the wheel

The build also puts include/sharedbox/ and the CMake config into the wheel, so your own extensions can compile against the very header the module was built with. get_include returns the folder that holds it.

Sources


  1. nanobind, CMake interface: STABLE_ABI (CPython 3.12 or newer), NB_DOMAIN. https://nanobind.readthedocs.io/en/latest/api_cmake.html ↩↩

  2. nanobind, "Free-threaded Python": FREE_THREADED, ignored on builds that do not support it; no stable ABI for free-threaded linked builds. https://nanobind.readthedocs.io/en/latest/free_threaded.html ↩↩↩

  3. PEP 803, the stable ABI for free-threaded builds (abi3t). https://peps.python.org/pep-0803/ ↩