.. cpp:namespace:: nanobind
.. _porting:
Porting guide
=============
The API of nanobind is *extremely* similar to that of `pybind11
`_, which makes porting existing
projects easy. Parts of the nanobind documentation are almost verbatim copies
pybind11's documentation.
A number of noteworthy changes are documented below.
Namespace
---------
nanobind types and functions are located in the ``nanobind`` namespace. The
following shorthand alias is recommended and used throughout the documentation:
.. code-block:: cpp
namespace nb = nanobind;
Name changes
------------
The following macros, types, and functions were renamed:
.. list-table::
:widths: 42 48
:header-rows: 1
* - pybind11
- nanobind
* - ``PYBIND11_MODULE(..)``
- :c:macro:`NB_MODULE(..) `
* - ``PYBIND11_OVERRIDE_*(..)``
- :c:macro:`NB_OVERRIDE_*(..) `
* - ``error_already_set``
- :cpp:class:`python_error`
* - ``type::of()``
- :cpp:func:`type\() `
* - ``type``
- :cpp:class:`type_object`
* - ``reinterpret_borrow(x)``
- :cpp:func:`borrow\(x) `
* - ``reinterpret_steal(x)``
- :cpp:func:`steal\(x) `
* - ``.def_readwrite(..)``
- :cpp:func:`.def_rw(..) `
* - ``.def_readonly(..)``
- :cpp:func:`.def_ro(..) `
* - ``.def_property(..)``
- :cpp:func:`.def_prop_rw(..) `
* - ``.def_property_readonly(..)``
- :cpp:func:`.def_prop_ro(..) `
* - ``.def_readwrite_static(..)``
- :cpp:func:`.def_rw_static(..) `
* - ``.def_readonly_static(..)``
- :cpp:func:`.def_ro_static(..) `
* - ``.def_property_static(..)``
- :cpp:func:`.def_prop_rw_static(..) `
* - ``.def_property_readonly_static(..)``
- :cpp:func:`.def_prop_ro_static(..) `
* - ``register_exception``
- :cpp:class:`exception\ `
None/null arguments
-------------------
In contrast to pybind11, nanobind does *not* permit ``None``-valued function
arguments by default. You must enable them explicitly using the
:cpp:func:`arg::none() ` argument annotation, e.g.:
.. code-block:: cpp
m.def("func", &func, "arg"_a.none());
It is also possible to set a ``None`` default value, in which case the
:cpp:func:`.none() ` annotation can be omitted.
.. code-block:: cpp
m.def("func", &func, "arg"_a = nb::none());
``None``-valued arguments are only supported by two of the three parameter
passing styles described in the section on :ref:`information exchange
`. In particular, they are supported by :ref:`bindings `
and :ref:`wrappers `, *but not* by :ref:`type casters
`.
Shared pointers and holders
---------------------------
When nanobind instantiates a C++ type within Python, the resulting instance
data is stored *within* the created Python object ("``PyObject``").
Alternatively, when an already existing C++ instance is transferred to Python
via a function return value and :cpp:enumerator:`rv_policy::reference`,
:cpp:enumerator:`rv_policy::reference_internal`, or
:cpp:enumerator:`rv_policy::take_ownership`, nanobind creates a smaller wrapper
``PyObject`` that only stores a pointer to the instance data.
This is *very different* from pybind11, where the instance ``PyObject``
contained a *holder type* (typically ``std::unique_ptr``) storing a pointer
to the instance data. Dealing with holders caused inefficiencies and introduced
complexity; they were therefore removed in nanobind. This has implications on
object ownership, shared ownership, and interactions with C++ shared/unique
pointers. The :ref:`intermediate ` and :ref:`advanced
` sections on object ownership provide further detail.
The gist is that it is no longer necessary to specify holder types in the type
declaration:
*pybind11*:
.. code-block:: cpp
py::class_>(m, "MyType")
*nanobind*:
.. code-block:: cpp
nb::class_(m, "MyType")
To bind functions that exchange shared/unique pointers, you must add one or
both of the following include directives to your code:
.. code-block:: cpp
#include
#include
Binding functions that take ``std::unique_ptr`` arguments involves some
limitations that can be avoided by changing their signatures to
``std::unique_ptr>`` (:ref:`details `).
Use of ``std::enable_shared_from_this`` is permitted, but since
nanobind does not use holder types, an object
constructed in Python will typically not have any associated
``std::shared_ptr`` until it is passed to a C++ function that
accepts ``std::shared_ptr``. That means a C++ function that accepts
a raw ``T*`` and calls ``shared_from_this()`` on it might stop working
when ported from pybind11 to nanobind. You can solve this problem
by always passing such objects across the Python/C++ boundary as
``std::shared_ptr`` rather than as ``T*``. See the :ref:`advanced section
on object ownership ` for more details.
Custom constructors
-------------------
In pybind11, custom constructors (i.e. ones that do not already exist in the
C++ class) could be specified as a lambda function returning an instance of
the desired type.
.. code-block:: cpp
py::class_(m, "MyType")
.def(py::init([](int) { return MyType(...); }));
Unfortunately, the implementation of this feature was quite complex and
often required further internal calls to the move or copy
constructor. nanobind instead reverts to how pybind11 originally
implemented this feature using in-place construction (`placement
new `_):
.. code-block:: cpp
nb::class_(m, "MyType")
.def("__init__", [](MyType *t) { new (t) MyType(...); });
The provided lambda function will be called with a pointer to uninitialized
memory that has already been allocated (this memory region is co-located
with the Python object for reasons of efficiency). The lambda function can
then either run an in-place constructor and return normally (in which case
the instance is assumed to be correctly constructed) or fail by raising an
exception.
To turn an existing factory function into a constructor, you will need to
combine the above pattern with an invocation of the move/copy-constructor,
e.g.:
.. code-block:: cpp
nb::class_(m, "MyType")
.def("__init__", [](MyType *t) { new (t) MyType(MyType::create()); });
Implicit conversions
--------------------
In pybind11, implicit conversions were specified using a follow-up function
call. This also works in nanobind, but it is recommended that you already
specify them within the constructor declaration:
*pybind11*:
.. code-block:: cpp
py::class_(m, "MyType")
.def(py::init());
py::implicitly_convertible();
*nanobind*:
.. code-block:: cpp
nb::class_(m, "MyType")
.def(nb::init_implicit());
Trampoline classes
------------------
Trampolines, i.e., polymorphic class implementations that forward virtual
function calls to Python, now require an extra :c:macro:`NB_TRAMPOLINE(parent,
size) ` declaration, where ``parent`` refers to the parent class
and ``size`` is at least as big as the number of :c:macro:`NB_OVERRIDE_*() `
calls. nanobind caches information to enable efficient function dispatch, for
which it must know the number of trampoline "slots".
The macro ``PYBIND11_OVERRIDE_*(..)`` required the base type and return value
as the first two arguments. This information is no longer needed in nanobind,
and the arguments should be removed in :c:macro:`NB_OVERRIDE_*()
`:
An example:
.. code-block:: cpp
struct PyAnimal : Animal {
NB_TRAMPOLINE(Animal, 1);
std::string name() const override {
NB_OVERRIDE(name);
}
};
Trampoline declarations with an insufficient size may eventually trigger a
Python ``RuntimeError`` exception with a descriptive label, e.g.:
.. code-block:: text
nanobind::detail::get_trampoline('PyAnimal::what()'): the trampoline ran out of
slots (you will need to increase the value provided to the NB_TRAMPOLINE() macro)
Iterator bindings
-----------------
Use of the :cpp:func:`nb::make_iterator() `,
:cpp:func:`nb::make_key_iterator() `, and
:cpp:func:`nb::make_value_iterator() ` functions requires
including the additional header file ``nanobind/make_iterator.h``. The
interface of these functions has also slightly changed: all take a Python scope
and a name as first and second arguments, which are used to permanently
"install" the iterator type (which is created on demand). See the `test suite
`_
for a worked out example.
Type casters
------------
The API of custom type casters has changed *significantly*. The following
changes are needed:
- ``load()`` was renamed to ``from_python()``. The function now takes an extra
``uint8_t flags`` parameter (instead ``bool convert``, which is now
represented by the flag ``nb::detail::cast_flags::convert``). A
``cleanup_list *`` pointer keeps track of Python temporaries that are created
by the conversion, and which need to be deallocated after a function call has
taken place.
``flags`` and ``cleanup`` should be passed to any recursive usage of
``type_caster::from_python()``. If casting fails due to a Python exception,
the function should clear it (``PyErr_Clear()``) and return ``false``. If a
severe error condition arises that should be reported, use Python warning API
calls for this, e.g. ``PyErr_WarnFormat()``.
- ``cast()`` was renamed to ``from_cpp()``. The function takes a return value
policy (as before) and a ``cleanup_list *`` pointer. If casting fails due to
a Python exception, the function should *leave the error set* (note the
asymmetry compared to ``from_python()``) and return ``nullptr``.
Note that the cleanup list is only available when ``from_python()`` or
``from_cpp()`` are called as part of function dispatch, while usage by
:cpp:func:`nb::cast() ` may set ``cleanup`` to ``nullptr`` if implicit
conversions are not enabled. This case should be handled gracefully by refusing
the conversion if the cleanup list is absolutely required.
Type casters may not raise C++ exceptions. Both ``from_python()`` and
``from_cpp()`` must be annotated with ``noexcept``. Exceptions or failure
conditions caused by a conversion should instead be caught *within* the
function body and handled as follows:
- ``from_python()``: return ``false``. That's it. (Failed Python to C++
conversion occur all the time while dispatching calls, causing nanobind
to simply move to the next function overload. Dedicated error reporting would
add undesirable overheads). In case of a severe internal error, use the
CPython warning API (e.g., ``PyErr_Warn()``) to notify the user.
- ``from_cpp()``: a failure here is more serious, since a return value of a
successfully evaluated cannot be converted, causing the call to fail. To
provide further detail, use the CPython error API (e.g., ``PyErr_Format()``)
and return an invalid handle (``return nb::handle();``).
The ``std::pair`` type caster (`link
`_)
may be useful as a starting point of custom implementations.
.. _removed:
Removed features
----------------
A number of pybind11 features are unavailable in nanobind. The list below
uses the following symbols:
- ○: This removal is a design choice. Use pybind11 if you need this feature.
- ●: Unclear, to be discussed.
Removed features include:
- ○ **Multiple inheritance**: this feature was a persistent source of
complexity in pybind11 and it is one of the main casualties in creating
nanobind.
- ○ **Holders**: nanobind instances co-locate instance data with a Python
object instead of accessing it via a holder type. This is a major difference
compared to pybind11 and will require changes to binding code that used
custom holders (e.g. unique or shared pointers). The :ref:`intermediate
` and :ref:`advanced ` sections on object ownership
provide further detail.
- ○ **Multi-intepreter, Embedding**: The ability to embed Python in an
executable or run several independent Python interpreters in the same process
is unsupported. Nanobind caters to bindings only. Multi-interpreter support
would require TLS lookups for nanobind data structures, which is undesirable.
- ○ **Function binding annotations**: The ``pos_only`` argument
annotation was removed. However, the same behavior can be achieved by
creating unnamed arguments; see the discussion in the section on
:ref:`keyword-only arguments `.
- ○ **Metaclasses**: creating types with custom metaclasses is unsupported.
- ○ **Module-local bindings**: support was removed (both for types and exceptions).
- ○ **Custom allocation**: C++ classes with an overloaded or deleted ``operator
new`` / ``operator delete`` are not supported.
- ○ **Compilation**: workarounds for buggy or non-standard-compliant
compilers were removed and will not be reintroduced.
- ○ The ``options`` class for customizing docstring generation was removed.
- ○ The NumPy array class (``py::array``) was removed in exchange for a more
powerful alternative (:cpp:class:`nb::ndarray\<..\> `)
that additionally supports CPU/GPU tensors produced by various frameworks
(NumPy, PyTorch, TensorFlow, JAX, etc.). Its API is not compatible with
pybind11, however.
- ● Buffer protocol binding (``.def_buffer()``) was removed in favor of
:cpp:class:`nb::ndarray\<..\> `.
- ● Support for evaluating Python files was removed.
Bullet points marked with ● may be reintroduced eventually, but this will
need to be done in a careful opt-in manner that does not affect code
complexity, binary size, and compilation/runtime performance of basic bindings
that don't depend on these features.