Skip to content

Add __array__ and __dlpack__ protocol support to OrtValue - #27826

Closed
Rishi-Dave wants to merge 1 commit into
microsoft:mainfrom
Rishi-Dave:rishidave/feat/ortvalue-array-dlpack-protocols
Closed

Add __array__ and __dlpack__ protocol support to OrtValue#27826
Rishi-Dave wants to merge 1 commit into
microsoft:mainfrom
Rishi-Dave:rishidave/feat/ortvalue-array-dlpack-protocols

Conversation

@Rishi-Dave

@Rishi-Dave Rishi-Dave commented Mar 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add __array__, __dlpack__, __dlpack_device__, and from_dlpack to the Python OrtValue wrapper class
  • Enable idiomatic numpy and DLPack interop: np.array(ort_value) and OrtValue.from_dlpack(tensor) now work directly

Motivation

The C pybind layer (C.OrtValue) already exposes __dlpack__, __dlpack_device__, and from_dlpack (added in #23110), but the Python-level OrtValue wrapper class doesn't surface them. This means users must reach into ort_value._ortvalue.__dlpack__() for DLPack interop, and there's no __array__ protocol support at all.

This PR bridges that gap so that standard Python array interchange protocols work on OrtValue directly:

import numpy as np
import onnxruntime as ort

# numpy protocol
ort_val = ort.OrtValue.ortvalue_from_numpy(np.array([1.0, 2.0]))
arr = np.array(ort_val)  # works via __array__

# DLPack protocol
capsule = ort_val.__dlpack__()
device = ort_val.__dlpack_device__()

# from_dlpack: auto-calls __dlpack__() on source, handles bool detection
ort_val2 = ort.OrtValue.from_dlpack(some_numpy_or_torch_tensor)

Fixes #24071

Changes

  • onnxruntime/python/onnxruntime_inference_collection.py: Added four methods to OrtValue:

    • __array__(dtype, copy) — numpy array protocol; delegates to .numpy() with optional dtype casting. Accepts copy parameter for numpy 2.0 compatibility.
    • __dlpack__(*, stream) — delegates to C layer's __dlpack__
    • __dlpack_device__() — delegates to C layer's __dlpack_device__
    • from_dlpack(source) — classmethod that calls source.__dlpack__(), auto-detects bool tensors via C.is_dlpack_uint8_tensor, and constructs an OrtValue
  • onnxruntime/test/python/onnxruntime_test_python.py: Added 7 new test cases:

    • DLPack protocol methods on Python wrapper
    • from_dlpack with numpy arrays as source
    • OrtValue → OrtValue roundtrip via DLPack
    • __array__ protocol with float32, float64, int64, and bool tensors
    • dtype casting via np.array(ort_value, dtype=...)

Test Plan

  • All new tests verified against installed onnxruntime 1.24.4 — 9/9 pass
  • ruff format — both files pass
  • ruff check — no new warnings introduced
  • DLPack tests properly skip when ENABLE_DLPACK is not available in the build
  • CI pipeline

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR exposes NumPy __array__ and DLPack (__dlpack__, __dlpack_device__, from_dlpack) protocols on the public Python onnxruntime.OrtValue wrapper to enable idiomatic np.array(ortvalue)/np.asarray(ortvalue) and direct DLPack interop without using private _ortvalue access.

Changes:

  • Add OrtValue.__array__ to support NumPy array conversion (including dtype + NumPy 2.0 copy behavior).
  • Add public DLPack protocol methods and OrtValue.from_dlpack that delegate to the underlying pybind C.OrtValue.
  • Add Python unit tests covering NumPy array protocol and DLPack round-trips across multiple dtypes.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
onnxruntime/python/onnxruntime_inference_collection.py Adds NumPy and DLPack protocol methods to the public OrtValue wrapper.
onnxruntime/test/python/onnxruntime_test_python.py Adds new tests validating NumPy __array__ and wrapper-level DLPack behaviors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

data = data.__dlpack__()
return cls(C.OrtValue.from_dlpack(data, is_bool_tensor))

def __dlpack__(self, *, stream: int | None = None) -> object:

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OrtValue.__dlpack__ is defined with *, stream=..., which makes stream keyword-only. The underlying pybind C.OrtValue.__dlpack__ accepts a positional stream argument, and some consumers of the DLPack protocol (e.g., framework from_dlpack implementations) may call __dlpack__(stream) positionally. This wrapper would raise TypeError in that case and break interop. Make stream a normal (positional-or-keyword) argument to match the protocol and the underlying binding.

Suggested change
def __dlpack__(self, *, stream: int | None = None) -> object:
def __dlpack__(self, stream: int | None = None) -> object:

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current keyword-only signature matches the Python array API standard, which defines __dlpack__(self, *, stream=None, ...). NumPy, CuPy, and JAX all use keyword-only as well. Every major consumer (numpy.from_dlpack, torch.from_dlpack, cupy.from_dlpack, jax.dlpack.from_dlpack) passes stream as a keyword argument — I wasn't able to find any framework that calls __dlpack__(stream) positionally.

Keeping stream keyword-only here stays consistent with the spec and with how the protocol is consumed in practice.

Comment on lines +1207 to +1208
Valid only for OrtValues holding Tensors on CPU.
For device tensors the data is first copied to host memory.

Copilot AI Mar 29, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The __array__ docstring is internally inconsistent: it says this is "Valid only" for CPU tensors but then states device tensors are copied to host first. Since the implementation unconditionally calls self._ortvalue.numpy() (which can copy device tensors to CPU), please clarify the docstring to accurately describe supported devices/behavior (and any restrictions for non-tensor OrtValues).

Suggested change
Valid only for OrtValues holding Tensors on CPU.
For device tensors the data is first copied to host memory.
Valid only for OrtValues holding Tensors.
For tensors on non-CPU devices, the data is first copied to host memory.
OrtValues holding non-Tensor types (for example, SparseTensor or Tensor
Sequence) are not supported and will cause an exception to be raised by
the underlying ``numpy()`` call.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in a959fb5 — rewrote the docstring to accurately describe behavior across devices and non-Tensor types.

@Rishi-Dave
Rishi-Dave force-pushed the rishidave/feat/ortvalue-array-dlpack-protocols branch from a959fb5 to 0603480 Compare March 31, 2026 12:15
@Rishi-Dave Rishi-Dave changed the title Add numpy __array__ and DLPack protocols to OrtValue Add __array__ and __dlpack__ protocol support to OrtValue Mar 31, 2026
Expose the DLPack and numpy array interchange protocols on the
Python-level OrtValue wrapper class, enabling idiomatic interop:

- __array__(dtype, copy): allows np.array(ort_value) without explicit
  .numpy() calls, with optional dtype casting
- __dlpack__(stream): delegates to C layer for zero-copy tensor sharing
- __dlpack_device__(): returns (device_type, device_id) tuple
- from_dlpack(source): classmethod that auto-calls __dlpack__() on the
  source and handles bool tensor detection via is_dlpack_uint8_tensor

Fixes microsoft#24071
@Rishi-Dave
Rishi-Dave force-pushed the rishidave/feat/ortvalue-array-dlpack-protocols branch from 0603480 to ae2e63c Compare April 2, 2026 12:16
@tianleiwu tianleiwu closed this Apr 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Make OrtValue compatible with numpy __array__ and dlpack protocols

3 participants