From 6c2eaa6ed445cb7bf56a9b310165d67217bcb43d Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Thu, 21 Mar 2024 14:54:46 +0100 Subject: [PATCH 1/9] GH-38325: [Python] Expand the Arrow PyCapsule Interface with C Device Data support --- .../CDataInterface/PyCapsuleInterface.rst | 67 +++++++++++++++++-- docs/source/format/CDeviceDataInterface.rst | 1 + 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index 03095aa2e9356..880457ae1dd8a 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -27,8 +27,8 @@ The Arrow PyCapsule Interface Rationale ========= -The :ref:`C data interface ` and -:ref:`C stream interface ` allow moving Arrow data between +The :ref:`C data interface `, :ref:`C stream interface ` +and :ref:`C device interface ` allow moving Arrow data between different implementations of Arrow. However, these interfaces don't specify how Python libraries should expose these structs to other libraries. Prior to this, many libraries simply provided export to PyArrow data structures, using the @@ -43,7 +43,7 @@ Goals ----- * Standardize the `PyCapsule`_ objects that represent ``ArrowSchema``, ``ArrowArray``, - and ``ArrowArrayStream``. + ``ArrowArrayStream``, ``ArrowDeviceArray`` and ``ArrowDeviceArrayStream``. * Define standard methods that export Arrow data into such capsule objects, so that any Python library wanting to accept Arrow data as input can call the corresponding method instead of hardcoding support for specific Arrow @@ -80,7 +80,10 @@ Arrow structures are recognized, the following names must be used: - ``arrow_array`` * - ArrowArrayStream - ``arrow_array_stream`` - + * - ArrowDeviceArray + - ``arrow_device_array`` + * - ArrowDeviceArrayStream + - ``arrow_device_array_stream`` Lifetime Semantics ------------------ @@ -95,6 +98,10 @@ the data and marked the release callback as null, so there isn’t a risk of releasing data the consumer is using. :ref:`Read more in the C Data Interface specification `. +In case of a device struct, the above mentioned release callback is the +``release`` member of the embedded ``ArrowArray`` structure. +:ref:`Read more in the C Device Interface specification `. + Just like in the C Data Interface, the PyCapsule objects defined here can only be consumed once. @@ -110,6 +117,11 @@ The interface consists of three separate protocols: * ``ArrowArrayExportable``, which defines the ``__arrow_c_array__`` method. * ``ArrowStreamExportable``, which defines the ``__arrow_c_stream__`` method. +Two additional protocols are defined for the Device interface: + +* ``ArrowDeviceArrayExportable``, which defines the ``__arrow_c_device_array__`` method. +* ``ArrowDeviceStreamExportable``, which defines the ``__arrow_c_device_stream__`` method. + ArrowSchema Export ------------------ @@ -142,6 +154,22 @@ Arrays and record batches (contiguous tables) can implement the method respectively. The schema capsule should have the name ``"arrow_schema"`` and the array capsule should have the name ``"arrow_array"``. +Libraries supporting the Device interface can implement a ``__arrow_c_device_array__`` +method on those objects, which works the same as ``__arrow_c_array__`` except +for returning a ArrowDeviceArray structure instead of a ArrowArray structure: + +.. py:method:: __arrow_c_device_array__(self, requested_schema: object | None = None) -> Tuple[object, object] + + Export the object as a pair of ArrowSchema and ArrowDeviceArray structures. + + :param requested_schema: A PyCapsule containing a C ArrowSchema representation + of a requested schema. Conversion to this schema is best-effort. See + `Schema Requests`_. + :type requested_schema: PyCapsule or None + + :return: A pair of PyCapsules containing a C ArrowSchema and ArrowDeviceArray, + respectively. The schema capsule should have the name ``"arrow_schema"`` + and the array capsule should have the name ``"arrow_device_array"``. ArrowStream Export ------------------ @@ -160,6 +188,23 @@ Tables / DataFrames and streams can implement the method ``__arrow_c_stream__``. :return: A PyCapsule containing a C ArrowArrayStream representation of the object. The capsule must have a name of ``"arrow_array_stream"``. +Libraries supporting the Device interface can implement a ``__arrow_c_device_stream__`` +method on those objects, which works the same as ``__arrow_c_stream__`` except +for returning a ArrowDeviceArrayStream structure instead of a ArrowArrayStream +structure: + +.. py:method:: __arrow_c_device_stream__(self, requested_schema: object | None = None) -> object + + Export the object as an ArrowDeviceArrayStream. + + :param requested_schema: A PyCapsule containing a C ArrowSchema representation + of a requested schema. Conversion to this schema is best-effort. See + `Schema Requests`_. + :type requested_schema: PyCapsule or None + + :return: A PyCapsule containing a C ArrowDeviceArrayStream representation of the + object. The capsule must have a name of ``"arrow_device_array_stream"``. + Schema Requests --------------- @@ -217,6 +262,20 @@ function accepts an object implementing one of these protocols. ) -> object: ... + class ArrowDeviceArrayExportable(Protocol): + def __arrow_c_device_array__( + self, + requested_schema: object | None = None + ) -> Tuple[object, object]: + ... + + class ArrowDeviceStreamExportable(Protocol): + def __arrow_c_device_stream__( + self, + requested_schema: object | None = None + ) -> object: + ... + Examples ======== diff --git a/docs/source/format/CDeviceDataInterface.rst b/docs/source/format/CDeviceDataInterface.rst index b5b7229a679e1..9a8adfb24d05f 100644 --- a/docs/source/format/CDeviceDataInterface.rst +++ b/docs/source/format/CDeviceDataInterface.rst @@ -344,6 +344,7 @@ Notes: synchronization is needed for an extension device, the producer should document the type. +.. _c-device-data-interface-semantics: Semantics ========= From 4e6f140b9f11889eb62c4693a144dd3537fc5c0e Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 26 Mar 2024 13:59:22 +0100 Subject: [PATCH 2/9] add section on guidelines around device support --- .../CDataInterface/PyCapsuleInterface.rst | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index 880457ae1dd8a..ec113ccf2b59a 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -230,6 +230,34 @@ raise an exception. The requested schema mechanism is only meant to negotiate between different representations of the same data and not to allow arbitrary schema transformations. +Device Support +-------------- + +Th PyCapsule interface has cross hardware support through using the +:ref:`C device interface `. This means it is possible +to exchange data on non-CPU devices (e.g. CUDA GPUs) and to inspect on what +device the exchanged data lives. + +For exchanging the data structures, this interface has two sets of protocol +methods: the standard CPU-only versions (:meth:`__arrow_c_array__` and +:meth:`__arrow_c_stream__`) and the equivalent device-aware versions +(:meth:`__arrow_c_device_array__```, and :meth:`__arrow_c_device_stream__`). + +For CPU-only libraries, it is allowed to either implement only the standard +protocol methods, or either implement both the standard and device-aware +methods. The absence of the device version methods implies CPU-only data. For +CPU-only consumers, it is encouraged to be able to consume both versions of the +protocol. + +For a device-aware library, and for data structures that can only reside in +non-CPU memory, it is recommeded to _only_ implement the device version of the +protocol (e.g. only add ``__arrow_c_device_array__``, and not add ``__arrow_c_array__``). +Libraries that have data structures that can live both on CPU or non-CPU devices +can implement both versions of the protocol (in that case, the standard methods +should raise an error when trying to export non-CPU data). + +Producing the ``ArrowDeviceArray`` and ``ArrowDeviceArrayStream`` structures +is expected to not involve any cross-device copying of data. .. _PyCapsule: https://docs.python.org/3/c-api/capsule.html From be14cf72eea6185f2d9fef5595f82c0c29afbc0f Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 26 Mar 2024 17:02:38 +0100 Subject: [PATCH 3/9] Apply suggestions from code review Co-authored-by: Antoine Pitrou --- .../format/CDataInterface/PyCapsuleInterface.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index ec113ccf2b59a..5670e4554d39a 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -233,7 +233,7 @@ schema transformations. Device Support -------------- -Th PyCapsule interface has cross hardware support through using the +The PyCapsule interface has cross hardware support through using the :ref:`C device interface `. This means it is possible to exchange data on non-CPU devices (e.g. CUDA GPUs) and to inspect on what device the exchanged data lives. @@ -243,16 +243,16 @@ methods: the standard CPU-only versions (:meth:`__arrow_c_array__` and :meth:`__arrow_c_stream__`) and the equivalent device-aware versions (:meth:`__arrow_c_device_array__```, and :meth:`__arrow_c_device_stream__`). -For CPU-only libraries, it is allowed to either implement only the standard +For CPU-only producers, it is allowed to either implement only the standard protocol methods, or either implement both the standard and device-aware methods. The absence of the device version methods implies CPU-only data. For CPU-only consumers, it is encouraged to be able to consume both versions of the protocol. -For a device-aware library, and for data structures that can only reside in -non-CPU memory, it is recommeded to _only_ implement the device version of the +For a device-aware producer whose data structures can only reside in +non-CPU memory, it is recommended to _only_ implement the device version of the protocol (e.g. only add ``__arrow_c_device_array__``, and not add ``__arrow_c_array__``). -Libraries that have data structures that can live both on CPU or non-CPU devices +Producers that have data structures that can live both on CPU or non-CPU devices can implement both versions of the protocol (in that case, the standard methods should raise an error when trying to export non-CPU data). From 5ac18585336f3ec2ff0aa4e6c43ec2ac5a05e3f4 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 26 Mar 2024 17:05:23 +0100 Subject: [PATCH 4/9] remove type hints in main spec --- .../format/CDataInterface/PyCapsuleInterface.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index 5670e4554d39a..2babef74ec6c5 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -127,7 +127,7 @@ ArrowSchema Export Schemas, fields, and data types can implement the method ``__arrow_c_schema__``. -.. py:method:: __arrow_c_schema__(self) -> object +.. py:method:: __arrow_c_schema__(self) Export the object as an ArrowSchema. @@ -141,7 +141,7 @@ ArrowArray Export Arrays and record batches (contiguous tables) can implement the method ``__arrow_c_array__``. -.. py:method:: __arrow_c_array__(self, requested_schema: object | None = None) -> Tuple[object, object] +.. py:method:: __arrow_c_array__(self, requested_schema=None) Export the object as a pair of ArrowSchema and ArrowArray structures. @@ -158,7 +158,7 @@ Libraries supporting the Device interface can implement a ``__arrow_c_device_arr method on those objects, which works the same as ``__arrow_c_array__`` except for returning a ArrowDeviceArray structure instead of a ArrowArray structure: -.. py:method:: __arrow_c_device_array__(self, requested_schema: object | None = None) -> Tuple[object, object] +.. py:method:: __arrow_c_device_array__(self, requested_schema=None) Export the object as a pair of ArrowSchema and ArrowDeviceArray structures. @@ -176,7 +176,7 @@ ArrowStream Export Tables / DataFrames and streams can implement the method ``__arrow_c_stream__``. -.. py:method:: __arrow_c_stream__(self, requested_schema: object | None = None) -> object +.. py:method:: __arrow_c_stream__(self, requested_schema=None) Export the object as an ArrowArrayStream. @@ -193,7 +193,7 @@ method on those objects, which works the same as ``__arrow_c_stream__`` except for returning a ArrowDeviceArrayStream structure instead of a ArrowArrayStream structure: -.. py:method:: __arrow_c_device_stream__(self, requested_schema: object | None = None) -> object +.. py:method:: __arrow_c_device_stream__(self, requested_schema=None) Export the object as an ArrowDeviceArrayStream. @@ -250,7 +250,7 @@ CPU-only consumers, it is encouraged to be able to consume both versions of the protocol. For a device-aware producer whose data structures can only reside in -non-CPU memory, it is recommended to _only_ implement the device version of the +non-CPU memory, it is recommended to only implement the device version of the protocol (e.g. only add ``__arrow_c_device_array__``, and not add ``__arrow_c_array__``). Producers that have data structures that can live both on CPU or non-CPU devices can implement both versions of the protocol (in that case, the standard methods From 0e94a00cff47ad3f82ad0bad36c69d002c7a5628 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Thu, 20 Jun 2024 18:01:27 +0200 Subject: [PATCH 5/9] add kwargs handling for potential future keywords --- .../CDataInterface/PyCapsuleInterface.rst | 57 +++++++++++++++---- 1 file changed, 45 insertions(+), 12 deletions(-) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index 2d7295e3ea7db..6da3ff28df75d 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -158,14 +158,17 @@ Libraries supporting the Device interface can implement a ``__arrow_c_device_arr method on those objects, which works the same as ``__arrow_c_array__`` except for returning a ArrowDeviceArray structure instead of a ArrowArray structure: -.. py:method:: __arrow_c_device_array__(self, requested_schema=None) +.. py:method:: __arrow_c_device_array__(self, requested_schema=None, **kwargs) Export the object as a pair of ArrowSchema and ArrowDeviceArray structures. - :param requested_schema: A PyCapsule containing a C ArrowSchema representation - of a requested schema. Conversion to this schema is best-effort. See + :param requested_schema: A PyCapsule containing a C ArrowSchema representation + of a requested schema. Conversion to this schema is best-effort. See `Schema Requests`_. :type requested_schema: PyCapsule or None + :param kwargs: Additional keyword arguments should only be accepted if they have + a default value of ``None``, to allow for future addition of new keywords. + See :ref:`arrow-pycapsule-interface-device-support` for more details. :return: A pair of PyCapsules containing a C ArrowSchema and ArrowDeviceArray, respectively. The schema capsule should have the name ``"arrow_schema"`` @@ -193,14 +196,17 @@ method on those objects, which works the same as ``__arrow_c_stream__`` except for returning a ArrowDeviceArrayStream structure instead of a ArrowArrayStream structure: -.. py:method:: __arrow_c_device_stream__(self, requested_schema=None) +.. py:method:: __arrow_c_device_stream__(self, requested_schema=None, **kwargs) Export the object as an ArrowDeviceArrayStream. - :param requested_schema: A PyCapsule containing a C ArrowSchema representation - of a requested schema. Conversion to this schema is best-effort. See + :param requested_schema: A PyCapsule containing a C ArrowSchema representation + of a requested schema. Conversion to this schema is best-effort. See `Schema Requests`_. :type requested_schema: PyCapsule or None + :param kwargs: Additional keyword arguments should only be accepted if they have + a default value of ``None``, to allow for future addition of new keywords. + See :ref:`arrow-pycapsule-interface-device-support` for more details. :return: A PyCapsule containing a C ArrowDeviceArrayStream representation of the object. The capsule must have a name of ``"arrow_device_array_stream"``. @@ -230,6 +236,11 @@ raise an exception. The requested schema mechanism is only meant to negotiate between different representations of the same data and not to allow arbitrary schema transformations. +.. _PyCapsule: https://docs.python.org/3/c-api/capsule.html + + +.. _arrow-pycapsule-interface-device-support: + Device Support -------------- @@ -241,7 +252,7 @@ device the exchanged data lives. For exchanging the data structures, this interface has two sets of protocol methods: the standard CPU-only versions (:meth:`__arrow_c_array__` and :meth:`__arrow_c_stream__`) and the equivalent device-aware versions -(:meth:`__arrow_c_device_array__```, and :meth:`__arrow_c_device_stream__`). +(:meth:`__arrow_c_device_array__`, and :meth:`__arrow_c_device_stream__`). For CPU-only producers, it is allowed to either implement only the standard protocol methods, or either implement both the standard and device-aware @@ -253,14 +264,34 @@ For a device-aware producer whose data structures can only reside in non-CPU memory, it is recommended to only implement the device version of the protocol (e.g. only add ``__arrow_c_device_array__``, and not add ``__arrow_c_array__``). Producers that have data structures that can live both on CPU or non-CPU devices -can implement both versions of the protocol (in that case, the standard methods -should raise an error when trying to export non-CPU data). +can implement both versions of the protocol, but the CPU-only versions +(:meth:`__arrow_c_array__` and :meth:`__arrow_c_stream__`) should be guaranteed +to contain valid pointers for CPU memory. Producing the ``ArrowDeviceArray`` and ``ArrowDeviceArrayStream`` structures is expected to not involve any cross-device copying of data. -.. _PyCapsule: https://docs.python.org/3/c-api/capsule.html +The device-aware methods (:meth:`__arrow_c_device_array__`, and :meth:`__arrow_c_device_stream__`) +should accept additional keyword arguments (``**kwargs``), if they have a +default value of ``None``. This allows for future addition of new optional +keywords, where the default value for such new keyword will always ``None``. +The implementor is responsible for raising a ``NotImplementedError`` for any +additional keyword being passed by the user which is not recognised. For +example: + +.. code-block:: python + + def __arrow_c_device_array__(self, requested_schema=None, **kwargs): + non_default_kwargs = [ + name for name, value in kwargs.items() if value is not None + ] + if non_default_kwargs: + raise NotImplementedError( + f"Received unsupported keyword argument(s): {non_default_kwargs}" + ) + + ... Protocol Typehints ------------------ @@ -293,14 +324,16 @@ function accepts an object implementing one of these protocols. class ArrowDeviceArrayExportable(Protocol): def __arrow_c_device_array__( self, - requested_schema: object | None = None + requested_schema: object | None = None, + **kwargs, ) -> Tuple[object, object]: ... class ArrowDeviceStreamExportable(Protocol): def __arrow_c_device_stream__( self, - requested_schema: object | None = None + requested_schema: object | None = None, + **kwargs, ) -> object: ... From 4c5c8b5f4f300594e159736d915d23110e38ed2b Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Thu, 20 Jun 2024 18:32:02 +0200 Subject: [PATCH 6/9] clarify CPU-only version of the protocol in case of non-CPU data --- docs/source/format/CDataInterface/PyCapsuleInterface.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index 6da3ff28df75d..34e23edac1eec 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -266,7 +266,8 @@ protocol (e.g. only add ``__arrow_c_device_array__``, and not add ``__arrow_c_ar Producers that have data structures that can live both on CPU or non-CPU devices can implement both versions of the protocol, but the CPU-only versions (:meth:`__arrow_c_array__` and :meth:`__arrow_c_stream__`) should be guaranteed -to contain valid pointers for CPU memory. +to contain valid pointers for CPU memory (thus, when trying to export non-CPU data, +either raise an error or make a to CPU memory). Producing the ``ArrowDeviceArray`` and ``ArrowDeviceArrayStream`` structures is expected to not involve any cross-device copying of data. From 4949f88fb58c721b1e7b2142fb2ba6f5ea8bcf0e Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Fri, 21 Jun 2024 09:08:12 +0200 Subject: [PATCH 7/9] Update docs/source/format/CDataInterface/PyCapsuleInterface.rst Co-authored-by: Dewey Dunnington --- docs/source/format/CDataInterface/PyCapsuleInterface.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index 34e23edac1eec..c56ec9f9ebee3 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -267,7 +267,7 @@ Producers that have data structures that can live both on CPU or non-CPU devices can implement both versions of the protocol, but the CPU-only versions (:meth:`__arrow_c_array__` and :meth:`__arrow_c_stream__`) should be guaranteed to contain valid pointers for CPU memory (thus, when trying to export non-CPU data, -either raise an error or make a to CPU memory). +either raise an error or make a copy to CPU memory). Producing the ``ArrowDeviceArray`` and ``ArrowDeviceArrayStream`` structures is expected to not involve any cross-device copying of data. From fded4d490379f43bf22681ff4de312c4664d99b9 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 25 Jun 2024 11:11:20 +0200 Subject: [PATCH 8/9] Update docs/source/format/CDataInterface/PyCapsuleInterface.rst Co-authored-by: Matt Topol --- docs/source/format/CDataInterface/PyCapsuleInterface.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index c56ec9f9ebee3..0ed1883f0a464 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -275,7 +275,7 @@ is expected to not involve any cross-device copying of data. The device-aware methods (:meth:`__arrow_c_device_array__`, and :meth:`__arrow_c_device_stream__`) should accept additional keyword arguments (``**kwargs``), if they have a default value of ``None``. This allows for future addition of new optional -keywords, where the default value for such new keyword will always ``None``. +keywords, where the default value for such a new keyword will always be ``None``. The implementor is responsible for raising a ``NotImplementedError`` for any additional keyword being passed by the user which is not recognised. For example: From a1ae0823684b9c9002cfb05d4a3a1602e223ded4 Mon Sep 17 00:00:00 2001 From: Joris Van den Bossche Date: Tue, 25 Jun 2024 11:16:57 +0200 Subject: [PATCH 9/9] fix typo and use CPU-only instead of standard --- docs/source/format/CDataInterface/PyCapsuleInterface.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/format/CDataInterface/PyCapsuleInterface.rst b/docs/source/format/CDataInterface/PyCapsuleInterface.rst index 0ed1883f0a464..d38ba2822da46 100644 --- a/docs/source/format/CDataInterface/PyCapsuleInterface.rst +++ b/docs/source/format/CDataInterface/PyCapsuleInterface.rst @@ -156,7 +156,7 @@ Arrays and record batches (contiguous tables) can implement the method Libraries supporting the Device interface can implement a ``__arrow_c_device_array__`` method on those objects, which works the same as ``__arrow_c_array__`` except -for returning a ArrowDeviceArray structure instead of a ArrowArray structure: +for returning an ArrowDeviceArray structure instead of an ArrowArray structure: .. py:method:: __arrow_c_device_array__(self, requested_schema=None, **kwargs) @@ -193,7 +193,7 @@ Tables / DataFrames and streams can implement the method ``__arrow_c_stream__``. Libraries supporting the Device interface can implement a ``__arrow_c_device_stream__`` method on those objects, which works the same as ``__arrow_c_stream__`` except -for returning a ArrowDeviceArrayStream structure instead of a ArrowArrayStream +for returning an ArrowDeviceArrayStream structure instead of an ArrowArrayStream structure: .. py:method:: __arrow_c_device_stream__(self, requested_schema=None, **kwargs) @@ -255,7 +255,7 @@ methods: the standard CPU-only versions (:meth:`__arrow_c_array__` and (:meth:`__arrow_c_device_array__`, and :meth:`__arrow_c_device_stream__`). For CPU-only producers, it is allowed to either implement only the standard -protocol methods, or either implement both the standard and device-aware +CPU-only protocol methods, or either implement both the CPU-only and device-aware methods. The absence of the device version methods implies CPU-only data. For CPU-only consumers, it is encouraged to be able to consume both versions of the protocol.