@@ -275,12 +275,19 @@ desired Python type.
275275- :cpp:class: `nb::tensorflow <tensorflow> `: create a ``tensorflow.python.framework.ops.EagerTensor ``.
276276- :cpp:class: `nb::jax <jax> `: create a ``jaxlib.xla_extension.DeviceArray ``.
277277- :cpp:class: `nb::cupy <cupy> `: create a ``cupy.ndarray ``.
278+ - :cpp:class: `nb::memview <memview> `: create a Python ``memoryview ``.
279+ - :cpp:class: `nb::arrayapi <arrayapi> `: create an object that supports the
280+ Python buffer protocol (i.e., is accepted as an argument to ``memoryview() ``)
281+ and also has the DLPack attributes ``__dlpack__ `` and ``_dlpack_device__ ``
282+ (i.e., it is accepted as an argument to a framework's ``from_dlpack() ``
283+ function).
278284- No framework annotation. In this case, nanobind will create a raw Python
279285 ``dltensor `` `capsule <https://docs.python.org/3/c-api/capsule.html >`__
280- representing the `DLPack <https://github.com/dmlc/dlpack >`__ metadata.
286+ representing the `DLPack <https://github.com/dmlc/dlpack >`__ metadata of
287+ a ``DLManagedTensor ``.
281288
282289This annotation also affects the auto-generated docstring of the function,
283- which in this case becomes:
290+ which in this example's case becomes:
284291
285292.. code-block :: python
286293
@@ -458,6 +465,13 @@ interpreted as follows:
458465- :cpp:enumerator: `rv_policy::move ` is unsupported and demoted to
459466 :cpp:enumerator: `rv_policy::copy `.
460467
468+ Note that when a copy is returned, the copy is made by the framework, not by
469+ nanobind itself.
470+ For example, ``numpy.array `` is passed the keyword argument ``copy `` with
471+ value ``True ``, or the PyTorch ``clone `` member function is immediately
472+ called on the tensor to create the copy.
473+
474+
461475.. _ndarray-temporaries :
462476
463477Returning temporaries
@@ -643,26 +657,98 @@ support inter-framework data exchange, custom array types should implement the
643657- `__dlpack__ <https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__dlpack__.html#array_api.array.__dlpack_ _>`__ and
644658- `__dlpack_device__ <https://data-apis.org/array-api/latest/API_specification/generated/array_api.array.__dlpack_device__.html#array_api.array.__dlpack_device_ _>`__
645659
646- methods. This is easy thanks to the nd-array integration in nanobind. An example is shown below:
660+ methods.
661+ These, as well as the buffer protocol, are implemented in the object returned
662+ by nanobind when specifying :cpp:class: `nb::arrayapi <arrayapi> ` as the
663+ framework template parameter.
664+ For example:
647665
648666.. code-block :: cpp
649667
650- nb::class_<MyArray>(m, "MyArray")
651- // ...
652- .def("__dlpack__", [](nb::kwargs kwargs) {
653- return nb::ndarray<>( /* ... */);
654- })
655- .def("__dlpack_device__", []() {
656- return std::make_pair(nb::device::cpu::value, 0);
657- });
668+ class MyArray {
669+ double* data;
670+ public:
671+ MyArray() { data = new double[5] { 0.0, 1.0, 2.0, 3.0, 4.0 }; }
672+ ~MyArray() { delete[] data; }
673+ auto arrayapi() {
674+ return nb::ndarray<nb::arrayapi, double>(data, {5});
675+ }
676+ };
677+
678+ nb::class_<MyArray>(m, "MyArray")
679+ .def(nb::init<>())
680+ .def("arrayapi", &MyArray::arrayapi, nb::rv_policy::reference_internal);
681+
682+ which can be used as follows:
683+
684+ .. code-block :: pycon
685+
686+ >>> import my_extension
687+ >>> ma = my_extension.MyArray()
688+ >>> aa = ma.arrayapi()
689+ >>> aa.__dlpack_device__()
690+ (1, 0)
691+ >>> import numpy as np
692+ >>> x = np.from_dlpack(aa)
693+ >>> x
694+ array([0., 1., 2., 3., 4.])
695+
696+ The DLPack methods can also be provided in the class itself, by implementing
697+ ``__dlpack__() `` as a wrapper function.
698+ For example, add the following member functions to the ``MyArray `` class:
699+
700+ .. code-block :: cpp
658701
659- Returning a raw :cpp:class: `nb::ndarray <ndarray> ` without framework annotation
660- will produce a DLPack capsule, which is what the interface expects.
702+ auto dlpack(nb::kwargs kwargs) {
703+ nb::object aa =
704+ nb::cast(nb::ndarray<nb::arrayapi, double>(data, {5}),
705+ nb::rv_policy::reference_internal,
706+ nb::find(*this));
707+ nb::object max_version = kwargs.get("max_version", nb::none());
708+ return aa.attr("__dlpack__")(nb::arg("max_version") = max_version);
709+ }
710+ auto dlpack_device() {
711+ return std::make_pair(nb::device::cpu::value, 0);
712+ }
713+
714+ and add the following two lines to the binding:
715+
716+ .. code-block :: cpp
717+
718+ .def("__dlpack__", &MyArray::dlpack)
719+ .def("__dlpack_device__", &MyArray::dlpack_device)
720+
721+ Then the class can be used as follows:
722+
723+ .. code-block :: pycon
724+
725+ >>> import my_extension
726+ >>> ma = my_extension.MyArray()
727+ >>> ma.__dlpack_device__()
728+ (1, 0)
729+ >>> import numpy as np
730+ >>> y = np.from_dlpack(ma)
731+ >>> y
732+ array([0., 1., 2., 3., 4.])
733+
734+
735+ The ``kwargs `` argument in the implementation of ``__dlpack__ `` above can be
736+ used to support additional parameters (e.g., to allow the caller to request a
737+ copy). Please see the DLPack documentation for details.
738+
739+ The caller may or may not supply the keyword argument ``max_version ``.
740+ If it is not supplied or has the value ``None ``, nanobind will return an
741+ unversioned ``DLManagedTensor `` in a capsule named ``dltensor ``.
742+ If its value is a tuple of integers ``(major_version, minor_version) `` and the
743+ major version is at least 1, nanobind will return a ``DLManagedTensorVersioned ``
744+ in a capsule named ``dltensor_versioned ``.
745+ Nanobind ignores other keyword arguments.
746+ In particular, it cannot transfer the array's data to another device (such as
747+ a GPU), nor can it make a copy of the data.
748+ A custom class (such as ``MyArray `` above) could provide such functionality.
749+ Often, the caller framework takes care of copying and inter-device data
750+ transfer and does not ask the producer, ``MyArray ``, to perform them.
661751
662- The ``kwargs `` argument can be used to provide additional parameters (for
663- example to request a copy), please see the DLPack documentation for details.
664- Note that nanobind does not yet implement the versioned DLPack protocol. The
665- version number should be ignored for now.
666752
667753Frequently asked questions
668754--------------------------
@@ -708,7 +794,3 @@ be more restrictive. Presently supported dtypes include signed/unsigned
708794integers, floating point values, complex numbers, and boolean values. Some
709795:ref: `nonstandard arithmetic types <ndarray-nonstandard >` can be supported as
710796well.
711-
712- Nanobind can receive and return *read-only * arrays via the buffer protocol when
713- exhanging data with NumPy. The DLPack interface currently ignores this
714- annotation.
0 commit comments