From 5eeaaae7341b6652a640790c847f948ff3dbad0c Mon Sep 17 00:00:00 2001 From: tqchen Date: Fri, 5 Dec 2025 17:11:30 -0500 Subject: [PATCH 1/4] Update exchange API to be capsule following new convention This PR updates the exchange API to be capsule following the new convention. Note that the exchange API python recommendation is not yet updated on the array-api side and so far there hasn't yet been downstream implementation, so this change should be safe in generally. --- include/dlpack/dlpack.h | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/include/dlpack/dlpack.h b/include/dlpack/dlpack.h index 9d7be97..d4d46fd 100644 --- a/include/dlpack/dlpack.h +++ b/include/dlpack/dlpack.h @@ -19,7 +19,7 @@ #define DLPACK_MAJOR_VERSION 1 /*! \brief The current minor version of dlpack */ -#define DLPACK_MINOR_VERSION 2 +#define DLPACK_MINOR_VERSION 3 /*! \brief DLPACK_DLL prefix for windows */ #ifdef _WIN32 @@ -375,7 +375,7 @@ typedef struct DLManagedTensorVersioned { } DLManagedTensorVersioned; //---------------------------------------------------------------------- -// DLPack `__c_dlpack_exchange_api__` fast exchange protocol definitions +// DLPack `__dlpack_c_exchange_api__` fast exchange protocol definitions //---------------------------------------------------------------------- /*! * \brief Request a producer library to create a new tensor. @@ -391,7 +391,7 @@ typedef struct DLManagedTensorVersioned { * \param error_ctx Context for `SetError`. * \param SetError The function to set the error. * \return The owning DLManagedTensorVersioned* or NULL on failure. - * SetError is called exactly when NULL is returned (the implementor + * SetError is called exactly when NULL is returned (the implementer * must ensure this). * \note - As a C function, must not thrown C++ exceptions. * - Error propagation via SetError to avoid any direct need @@ -432,11 +432,11 @@ typedef int (*DLPackManagedTensorFromPyObjectNoSync)( // * \brief Exports a PyObject* Tensor/NDArray to a provided DLTensor. * * This function provides a faster interface for temporary, non-owning, exchange. - * The producer (implementor) still owns the memory of data, strides, shape. + * The producer (implementer) still owns the memory of data, strides, shape. * The liveness of the DLTensor and the data it views is only guaranteed until * control is returned. * - * This function currently assumes that the producer (implementor) can fill + * This function currently assumes that the producer (implementer) can fill * in the DLTensor shape and strides without the need for temporary allocations. * * This function does not perform any stream synchronization. The consumer should query @@ -488,7 +488,7 @@ typedef int (*DLPackCurrentWorkStream)( // * \brief Imports a DLManagedTensorVersioned to a PyObject* Tensor/NDArray. * * Convert an owning DLManagedTensorVersioned* to the Python tensor of the - * producer (implementor) library with the correct type. + * producer (implementer) library with the correct type. * * This function does not perform any stream synchronization. * @@ -532,16 +532,17 @@ typedef struct DLPackExchangeAPIHeader { * \brief Framework-specific function pointers table for DLPack exchange. * * Additionally to `__dlpack__()` we define a C function table sharable by - * Python implementations via `__c_dlpack_exchange_api__`. - * This attribute must be set on the type as a Python integer compatible - * with `PyLong_FromVoidPtr`/`PyLong_AsVoidPtr`. + * + * Python implementations via `__dlpack_c_exchange_api__`. + * This attribute must be set on the type as a Python PyCapsule + * with name "dlpack_exchange_api". * * A consumer library may use a pattern such as: * * \code * - * PyObject *api_obj = type(tensor_obj).__c_dlpack_exchange_api__; // as C-code - * MyDLPackExchangeAPI *api = PyLong_AsVoidPtr(api_obj); + * PyObject *api_obj = type(tensor_obj).__dlpack_c_exchange_api__; // as C-code + * MyDLPackExchangeAPI *api = PyCapsule_GetPointer(api_obj, "dlpack_exchange_api"); * if (api == NULL && PyErr_Occurred()) { goto handle_error; } * * \endcode From 1f18a9e29d60d1ee2ff1a75ab4cf3ef2f25ac6c2 Mon Sep 17 00:00:00 2001 From: tqchen Date: Thu, 11 Dec 2025 20:10:55 -0500 Subject: [PATCH 2/4] [DOCS] Update the exchange api spec --- docs/source/python_spec.rst | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/docs/source/python_spec.rst b/docs/source/python_spec.rst index f4844f0..05cb42d 100644 --- a/docs/source/python_spec.rst +++ b/docs/source/python_spec.rst @@ -179,6 +179,42 @@ refer to `github.com/dmlc/dlpack `_. guaranteed to be in a certain order or not. +DLPack C Exchange API +~~~~~~~~~~~~~~~~~~~~~ + +Starting with DLPack 1.3, a new C Exchange API is introduced to enable faster +data exchange than the Python ``__dlpack__`` API at the C extension level. +Producer array frameworks must provide a ``__dlpack_c_exchange_api__`` +attribute on the array type. +The attribute should be a ``PyCapsule`` with name ``"dlpack_exchange_api"``. +The consumer can query whether this attribute exists and use it at the C extension level. +Notably, consumer frameworks can always start implementing by only using the Python ``__dlpack__`` API, +and then upgrade to the C Exchange API later when faster data exchange is needed. + +.. code-block:: C + + PyObject *api_obj = type(tensor_obj).__dlpack_c_exchange_api__; // as C code. + MyDLPackExchangeAPI *api = PyCapsule_GetPointer(api_obj, "dlpack_exchange_api"); + if (api == NULL && PyErr_Occurred()) { goto handle_error; } + + +.. note:: Implementation of the C Exchange API + + Producer framework should implement the C Exchange API in a static way either + through Cython, Python C extensions, or Python binding mechanism. Importantly, + because the DLPack C exchange API operates at the C extension level, we need + direct interaction between the array framework PyObject* and DLPack, + as a result it is harder to implement the C Exchange API through ctypes (because + ctypes releases GIL by default and sometimes in non-free-threading environment, + GIL is needed to interact with the Python C API). + +A reference implementations of the C Exchange API in frameworks: + + +* PyTorch: `C++ `__ +* Paddle: `C++ `__ + + Reference Implementations ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -198,3 +234,4 @@ ctypes, cffi, etc: * mpi4py: `Cython `_ * Paddle: `C++ `__, `Python wrapper using Python C API `__ * Hidet: `ctypes `__ + From bd96c5f9885ddbada9aa6cae8972a22c7f12ad56 Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Wed, 24 Dec 2025 07:44:33 -0500 Subject: [PATCH 3/4] Update docs/source/python_spec.rst Co-authored-by: Leo Fang --- docs/source/python_spec.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/python_spec.rst b/docs/source/python_spec.rst index 05cb42d..d41ff75 100644 --- a/docs/source/python_spec.rst +++ b/docs/source/python_spec.rst @@ -184,7 +184,7 @@ DLPack C Exchange API Starting with DLPack 1.3, a new C Exchange API is introduced to enable faster data exchange than the Python ``__dlpack__`` API at the C extension level. -Producer array frameworks must provide a ``__dlpack_c_exchange_api__`` +Producer array frameworks may provide a ``__dlpack_c_exchange_api__`` attribute on the array type. The attribute should be a ``PyCapsule`` with name ``"dlpack_exchange_api"``. The consumer can query whether this attribute exists and use it at the C extension level. From 4d25c02231af0384bd61def8604b567a954be7d6 Mon Sep 17 00:00:00 2001 From: Tianqi Chen Date: Wed, 24 Dec 2025 07:45:14 -0500 Subject: [PATCH 4/4] Update docs/source/python_spec.rst Co-authored-by: Leo Fang --- docs/source/python_spec.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/python_spec.rst b/docs/source/python_spec.rst index d41ff75..4e2d162 100644 --- a/docs/source/python_spec.rst +++ b/docs/source/python_spec.rst @@ -203,7 +203,7 @@ and then upgrade to the C Exchange API later when faster data exchange is needed Producer framework should implement the C Exchange API in a static way either through Cython, Python C extensions, or Python binding mechanism. Importantly, because the DLPack C exchange API operates at the C extension level, we need - direct interaction between the array framework PyObject* and DLPack, + direct interaction between the array framework ``PyObject*`` and DLPack, as a result it is harder to implement the C Exchange API through ctypes (because ctypes releases GIL by default and sometimes in non-free-threading environment, GIL is needed to interact with the Python C API).