Describe the bug
The DLPackCompatible protocol in the type stubs declares its members as mutable attributes rather than methods:
https://github.com/ml-explore/mlx/blob/main/python/mlx/_stub_patterns.txt#L5-L7
class DLPackCompatible(Protocol):
__dlpack__: Callable[..., Any]
__dlpack_device__: Callable[..., Any]
A protocol member declared as a variable is invariant and settable, so a class that defines __dlpack__ as a normal (read-only) method — like numpy.ndarray — does not match structurally. mypy states it plainly:
note: Protocol member DLPackCompatible.__dlpack__ expected settable variable, got read-only attribute
The result is that passing a NumPy array to any of the three entry points that accept DLPackCompatible fails type checking, even though all three work perfectly at runtime:
mx.array — python/src/array.cpp
mx.asarray — python/src/ops.cpp
mx.from_dlpack — python/src/ops.cpp
This is a regression against 0.31.2, where array.__init__ was annotated with numpy.ndarray directly:
# 0.31.2
def __init__(self: array, val: Union[scalar, list, tuple, numpy.ndarray, array], dtype: Optional[Dtype] = None): ...
# 0.32.1
def __init__(self: array, val: scalar | list | tuple | DLPackCompatible | array, dtype: Dtype | None = None): ...
(This is separate from #3916 — the stubs are back in 0.32.1, thanks. This is about their content.)
To Reproduce
# probe.py
import mlx.core as mx
import numpy as np
a: np.ndarray = np.zeros((2, 3), dtype=np.float32)
mx.array(a)
mx.asarray(a)
mx.from_dlpack(a)
uv venv --python 3.12 && uv pip install "mlx==0.32.1" numpy mypy
uv run mypy probe.py
Result on 0.32.1 (identical failures from ty):
probe.py:5: error: Argument 1 to "array" has incompatible type "ndarray[tuple[Any, ...], dtype[Any]]";
expected "int | float | bool | complex | list[Any] | tuple[Any, ...] | DLPackCompatible | array" [arg-type]
probe.py:6: error: Argument 1 to "asarray" has incompatible type ... [arg-type]
probe.py:7: error: Argument 1 to "from_dlpack" has incompatible type "ndarray[...]";
expected "DLPackCompatible" [arg-type]
Found 3 errors in 1 file (checked 1 source file)
The same script on mlx==0.31.2:
Success: no issues found in 1 source file
And all three calls succeed at runtime on 0.32.1:
array (2, 3)
asarray (2, 3)
dlpack (2, 3)
Expected behavior
mx.array(np_array) type-checks, as it did in 0.31.2. Runtime behaviour is already correct — only the stub is wrong.
Suggested fix
Declare the protocol members as methods, which makes them read-only and matched covariantly:
class DLPackCompatible(Protocol):
def __dlpack__(self, *args: Any, **kwargs: Any) -> Any: ...
def __dlpack_device__(self, *args: Any, **kwargs: Any) -> Any: ...
Verified: with that patch applied to the installed mlx/core/__init__.pyi, the repro above is clean under both mypy and ty (Success: no issues found / All checks passed!), on the same 0.32.1 wheel.
If you want a CI gate for this class of regression, the probe.py above is a good end-to-end canary — it exercises the stubs the way downstream projects actually do, and it would have caught both #3916 (stubs missing) and this one (stubs wrong).
Desktop (please complete the following information):
- OS Version: macOS 15 (Darwin 25.5.0), Apple Silicon
- Version: mlx 0.32.1 (
mlx-0.32.1-cp312-cp312-macosx_15_0_arm64.whl), Python 3.12, mypy 2.3.1, ty 0.0.73
Describe the bug
The
DLPackCompatibleprotocol in the type stubs declares its members as mutable attributes rather than methods:https://github.com/ml-explore/mlx/blob/main/python/mlx/_stub_patterns.txt#L5-L7
A protocol member declared as a variable is invariant and settable, so a class that defines
__dlpack__as a normal (read-only) method — likenumpy.ndarray— does not match structurally. mypy states it plainly:The result is that passing a NumPy array to any of the three entry points that accept
DLPackCompatiblefails type checking, even though all three work perfectly at runtime:mx.array—python/src/array.cppmx.asarray—python/src/ops.cppmx.from_dlpack—python/src/ops.cppThis is a regression against 0.31.2, where
array.__init__was annotated withnumpy.ndarraydirectly:(This is separate from #3916 — the stubs are back in 0.32.1, thanks. This is about their content.)
To Reproduce
Result on 0.32.1 (identical failures from
ty):The same script on
mlx==0.31.2:And all three calls succeed at runtime on 0.32.1:
Expected behavior
mx.array(np_array)type-checks, as it did in 0.31.2. Runtime behaviour is already correct — only the stub is wrong.Suggested fix
Declare the protocol members as methods, which makes them read-only and matched covariantly:
Verified: with that patch applied to the installed
mlx/core/__init__.pyi, the repro above is clean under both mypy and ty (Success: no issues found/All checks passed!), on the same 0.32.1 wheel.If you want a CI gate for this class of regression, the
probe.pyabove is a good end-to-end canary — it exercises the stubs the way downstream projects actually do, and it would have caught both #3916 (stubs missing) and this one (stubs wrong).Desktop (please complete the following information):
mlx-0.32.1-cp312-cp312-macosx_15_0_arm64.whl), Python 3.12, mypy 2.3.1, ty 0.0.73