From 49f20746d2eac2bda145121024347b9bdbce3405 Mon Sep 17 00:00:00 2001 From: nstarman Date: Thu, 21 Sep 2023 10:51:48 -0400 Subject: [PATCH 1/8] refactor Array as a Protocol Signed-off-by: nstarman --- src/array_api_stubs/_draft/_types.py | 10 +- src/array_api_stubs/_draft/array_object.py | 189 ++++++++++++--------- src/array_api_stubs/_draft/data_types.py | 35 ++-- 3 files changed, 137 insertions(+), 97 deletions(-) diff --git a/src/array_api_stubs/_draft/_types.py b/src/array_api_stubs/_draft/_types.py index f2fa356f2..1630ad68b 100644 --- a/src/array_api_stubs/_draft/_types.py +++ b/src/array_api_stubs/_draft/_types.py @@ -33,6 +33,7 @@ from dataclasses import dataclass from typing import ( + TYPE_CHECKING, Any, List, Literal, @@ -46,9 +47,14 @@ ) from enum import Enum -array = TypeVar("array") + +if TYPE_CHECKING: + from .array_object import Array + from .data_types import DType + +array = TypeVar("array", bound="Array") device = TypeVar("device") -dtype = TypeVar("dtype") +dtype = TypeVar("dtype", bound="DType") SupportsDLPack = TypeVar("SupportsDLPack") SupportsBufferProtocol = TypeVar("SupportsBufferProtocol") PyCapsule = TypeVar("PyCapsule") diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 3c6fa8763..6f44d8549 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -2,26 +2,29 @@ __all__ = ["array"] -from ._types import ( - array, - dtype as Dtype, - device as Device, - Optional, - Tuple, - Union, - Any, - PyCapsule, - Enum, - ellipsis, -) - - -class _array: - def __init__(self: array) -> None: +from typing import TYPE_CHECKING, Protocol, TypeVar + +if TYPE_CHECKING: + from ._types import ( + dtype as Dtype, + device as Device, + Any, + PyCapsule, + Enum, + ellipsis, + ) + +Self = TypeVar("Self", bound="Array") +# NOTE: when working with py3.11+ this can be ``typing.Self``. + + +class Array(Protocol): + def __init__(self) -> None: """Initialize the attributes for the array object class.""" + ... @property - def dtype(self: array) -> Dtype: + def dtype(self) -> Dtype: """ Data type of the array elements. @@ -30,9 +33,10 @@ def dtype(self: array) -> Dtype: out: dtype array data type. """ + ... @property - def device(self: array) -> Device: + def device(self) -> Device: """ Hardware device the array data resides on. @@ -41,9 +45,10 @@ def device(self: array) -> Device: out: device a ``device`` object (see :ref:`device-support`). """ + ... @property - def mT(self: array) -> array: + def mT(self: Self) -> Self: """ Transpose of a matrix (or a stack of matrices). @@ -54,9 +59,10 @@ def mT(self: array) -> array: out: array array whose last two dimensions (axes) are permuted in reverse order relative to original array (i.e., for an array instance having shape ``(..., M, N)``, the returned array must have shape ``(..., N, M)``). The returned array must have the same data type as the original array. """ + ... @property - def ndim(self: array) -> int: + def ndim(self) -> int: """ Number of array dimensions (axes). @@ -65,9 +71,10 @@ def ndim(self: array) -> int: out: int number of array dimensions (axes). """ + ... @property - def shape(self: array) -> Tuple[Optional[int], ...]: + def shape(self) -> tuple[int | None, ...]: """ Array dimensions. @@ -83,9 +90,10 @@ def shape(self: array) -> Tuple[Optional[int], ...]: .. note:: The returned value should be a tuple; however, where warranted, an array library may choose to return a custom shape object. If an array library returns a custom shape object, the object must be immutable, must support indexing for dimension retrieval, and must behave similarly to a tuple. """ + ... @property - def size(self: array) -> Optional[int]: + def size(self) -> int | None: """ Number of elements in an array. @@ -101,9 +109,10 @@ def size(self: array) -> Optional[int]: .. note:: For array libraries having graph-based computational models, an array may have unknown dimensions due to data-dependent operations. """ + ... @property - def T(self: array) -> array: + def T(self: Self) -> Self: """ Transpose of the array. @@ -118,8 +127,9 @@ def T(self: array) -> array: .. note:: Limiting the transpose to two-dimensional arrays (matrices) deviates from the NumPy et al practice of reversing all axes for arrays having more than two-dimensions. This is intentional, as reversing all axes was found to be problematic (e.g., conflicting with the mathematical definition of a transpose which is limited to matrices; not operating on batches of matrices; et cetera). In order to reverse all axes, one is recommended to use the functional ``permute_dims`` interface found in this specification. """ + ... - def __abs__(self: array, /) -> array: + def __abs__(self: Self, /) -> Self: """ Calculates the absolute value for each element of an array instance. @@ -147,8 +157,9 @@ def __abs__(self: array, /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __add__(self: array, other: Union[int, float, array], /) -> array: + def __add__(self: Self, other: int | float | Self, /) -> Self: """ Calculates the sum for each element of an array instance with the respective element of the array ``other``. @@ -173,8 +184,9 @@ def __add__(self: array, other: Union[int, float, array], /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __and__(self: array, other: Union[int, bool, array], /) -> array: + def __and__(self: Self, other: int | bool | Self, /) -> Self: """ Evaluates ``self_i & other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -194,10 +206,9 @@ def __and__(self: array, other: Union[int, bool, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_and`. """ + ... - def __array_namespace__( - self: array, /, *, api_version: Optional[str] = None - ) -> Any: + def __array_namespace__(self, /, *, api_version: str | None = None) -> Any: """ Returns an object that has all the array API functions on it. @@ -213,8 +224,9 @@ def __array_namespace__( out: Any an object representing the array API namespace. It should have every top-level function defined in the specification as an attribute. It may contain other public names as well, but it is recommended to only include those names that are part of the specification. """ + ... - def __bool__(self: array, /) -> bool: + def __bool__(self, /) -> bool: """ Converts a zero-dimensional array to a Python ``bool`` object. @@ -251,8 +263,9 @@ def __bool__(self: array, /) -> bool: .. versionchanged:: 2023.12 Allowed lazy implementations to error. """ + ... - def __complex__(self: array, /) -> complex: + def __complex__(self, /) -> complex: """ Converts a zero-dimensional array to a Python ``complex`` object. @@ -292,15 +305,16 @@ def __complex__(self: array, /) -> complex: .. versionchanged:: 2023.12 Allowed lazy implementations to error. """ + ... def __dlpack__( - self: array, + self, /, *, - stream: Optional[Union[int, Any]] = None, - max_version: Optional[tuple[int, int]] = None, - dl_device: Optional[tuple[Enum, int]] = None, - copy: Optional[bool] = None, + stream: int | Any | None = None, + max_version: tuple[int, int] | None = None, + dl_device: tuple[Enum, int] | None = None, + copy: bool | None = None, ) -> PyCapsule: """ Exports the array for consumption by :func:`~array_api.from_dlpack` as a DLPack capsule. @@ -465,8 +479,9 @@ def __dlpack__( .. versionchanged:: 2023.12 Added recommendation for handling read-only arrays. """ + ... - def __dlpack_device__(self: array, /) -> Tuple[Enum, int]: + def __dlpack_device__(self, /) -> tuple[Enum, int]: """ Returns device type and device ID in DLPack format. Meant for use within :func:`~array_api.from_dlpack`. @@ -493,8 +508,9 @@ def __dlpack_device__(self: array, /) -> Tuple[Enum, int]: CUDA_MANAGED = 13 ONE_API = 14 """ + ... - def __eq__(self: array, other: Union[int, float, bool, array], /) -> array: + def __eq__(self: Self, other: int | float | bool | Self, /) -> Self: r""" Computes the truth value of ``self_i == other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -517,8 +533,9 @@ def __eq__(self: array, other: Union[int, float, bool, array], /) -> array: .. note:: Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ + ... - def __float__(self: array, /) -> float: + def __float__(self, /) -> float: """ Converts a zero-dimensional array to a Python ``float`` object. @@ -555,8 +572,9 @@ def __float__(self: array, /) -> float: .. versionchanged:: 2023.12 Allowed lazy implementations to error. """ + ... - def __floordiv__(self: array, other: Union[int, float, array], /) -> array: + def __floordiv__(self: Self, other: int | float | Self, /) -> Self: """ Evaluates ``self_i // other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -579,8 +597,9 @@ def __floordiv__(self: array, other: Union[int, float, array], /) -> array: .. note:: Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.floor_divide`. """ + ... - def __ge__(self: array, other: Union[int, float, array], /) -> array: + def __ge__(self: Self, other: int | float | Self, /) -> Self: """ Computes the truth value of ``self_i >= other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -606,19 +625,13 @@ def __ge__(self: array, other: Union[int, float, array], /) -> array: .. note:: Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ + ... def __getitem__( - self: array, - key: Union[ - int, - slice, - ellipsis, - None, - Tuple[Union[int, slice, ellipsis, None], ...], - array, - ], + self: Self, + key: int | slice | ellipsis | tuple[int | slice | ellipsis | None, ...] | Self, /, - ) -> array: + ) -> Self: """ Returns ``self[key]``. @@ -640,8 +653,9 @@ def __getitem__( When ``__getitem__`` is defined on an object, Python will automatically define iteration (i.e., the behavior from ``iter(x)``) as ``x[0]``, ``x[1]``, ..., ``x[N-1]``. This can also be implemented directly by defining ``__iter__``. Therefore, for a one-dimensional array ``x``, iteration should produce a sequence of zero-dimensional arrays ``x[0]``, ``x[1]``, ..., ``x[N-1]``, where ``N`` is the number of elements in the array. Iteration behavior for arrays having zero dimensions or more than one dimension is unspecified and thus implementation-defined. """ + ... - def __gt__(self: array, other: Union[int, float, array], /) -> array: + def __gt__(self: Self, other: int | float | Self, /) -> Self: """ Computes the truth value of ``self_i > other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -667,8 +681,9 @@ def __gt__(self: array, other: Union[int, float, array], /) -> array: .. note:: Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ + ... - def __index__(self: array, /) -> int: + def __index__(self, /) -> int: """ Converts a zero-dimensional integer array to a Python ``int`` object. @@ -695,8 +710,9 @@ def __index__(self: array, /) -> int: .. versionchanged:: 2023.12 Allowed lazy implementations to error. """ + ... - def __int__(self: array, /) -> int: + def __int__(self, /) -> int: """ Converts a zero-dimensional array to a Python ``int`` object. @@ -745,8 +761,9 @@ def __int__(self: array, /) -> int: .. versionchanged:: 2023.12 Allowed lazy implementations to error. """ + ... - def __invert__(self: array, /) -> array: + def __invert__(self: Self, /) -> Self: """ Evaluates ``~self_i`` for each element of an array instance. @@ -764,8 +781,9 @@ def __invert__(self: array, /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_invert`. """ + ... - def __le__(self: array, other: Union[int, float, array], /) -> array: + def __le__(self: Self, other: int | float | Self, /) -> Self: """ Computes the truth value of ``self_i <= other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -791,8 +809,9 @@ def __le__(self: array, other: Union[int, float, array], /) -> array: .. note:: Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ + ... - def __lshift__(self: array, other: Union[int, array], /) -> array: + def __lshift__(self: Self, other: int | Self, /) -> Self: """ Evaluates ``self_i << other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -812,8 +831,9 @@ def __lshift__(self: array, other: Union[int, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_left_shift`. """ + ... - def __lt__(self: array, other: Union[int, float, array], /) -> array: + def __lt__(self: Self, other: int | float | Self, /) -> Self: """ Computes the truth value of ``self_i < other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -839,8 +859,9 @@ def __lt__(self: array, other: Union[int, float, array], /) -> array: .. note:: Comparison of arrays without a corresponding promotable data type (see :ref:`type-promotion`) is undefined and thus implementation-dependent. """ + ... - def __matmul__(self: array, other: array, /) -> array: + def __matmul__(self: Self, other: Self, /) -> Self: """ Computes the matrix product. @@ -887,8 +908,9 @@ def __matmul__(self: array, other: array, /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __mod__(self: array, other: Union[int, float, array], /) -> array: + def __mod__(self: Self, other: int | float | Self, /) -> Self: """ Evaluates ``self_i % other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -911,8 +933,9 @@ def __mod__(self: array, other: Union[int, float, array], /) -> array: .. note:: Element-wise results, including special cases, must equal the results returned by the equivalent element-wise function :func:`~array_api.remainder`. """ + ... - def __mul__(self: array, other: Union[int, float, array], /) -> array: + def __mul__(self: Self, other: int | float | Self, /) -> Self: r""" Calculates the product for each element of an array instance with the respective element of the array ``other``. @@ -940,8 +963,9 @@ def __mul__(self: array, other: Union[int, float, array], /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __ne__(self: array, other: Union[int, float, bool, array], /) -> array: + def __ne__(self: Self, other: int | float | bool | Self, /) -> Self: """ Computes the truth value of ``self_i != other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -970,8 +994,9 @@ def __ne__(self: array, other: Union[int, float, bool, array], /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __neg__(self: array, /) -> array: + def __neg__(self: Self, /) -> Self: """ Evaluates ``-self_i`` for each element of an array instance. @@ -1000,8 +1025,9 @@ def __neg__(self: array, /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __or__(self: array, other: Union[int, bool, array], /) -> array: + def __or__(self: Self, other: int | bool | Self, /) -> Self: """ Evaluates ``self_i | other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1021,8 +1047,9 @@ def __or__(self: array, other: Union[int, bool, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_or`. """ + ... - def __pos__(self: array, /) -> array: + def __pos__(self: Self, /) -> Self: """ Evaluates ``+self_i`` for each element of an array instance. @@ -1045,8 +1072,9 @@ def __pos__(self: array, /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __pow__(self: array, other: Union[int, float, array], /) -> array: + def __pow__(self: Self, other: int | float | Self, /) -> Self: r""" Calculates an implementation-dependent approximation of exponentiation by raising each element (the base) of an array instance to the power of ``other_i`` (the exponent), where ``other_i`` is the corresponding element of the array ``other``. @@ -1076,8 +1104,9 @@ def __pow__(self: array, other: Union[int, float, array], /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __rshift__(self: array, other: Union[int, array], /) -> array: + def __rshift__(self: Self, other: int | Self, /) -> Self: """ Evaluates ``self_i >> other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1097,13 +1126,12 @@ def __rshift__(self: array, other: Union[int, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_right_shift`. """ + ... def __setitem__( - self: array, - key: Union[ - int, slice, ellipsis, Tuple[Union[int, slice, ellipsis], ...], array - ], - value: Union[int, float, bool, array], + self: Self, + key: int | slice | ellipsis | tuple[int | slice | ellipsis, ...] | Self, + value: int | float | bool | Self, /, ) -> None: """ @@ -1129,8 +1157,9 @@ def __setitem__( When ``value`` is an ``array`` of a different data type than ``self``, how values are cast to the data type of ``self`` is implementation defined. """ + ... - def __sub__(self: array, other: Union[int, float, array], /) -> array: + def __sub__(self: Self, other: int | float | Self, /) -> Self: """ Calculates the difference for each element of an array instance with the respective element of the array ``other``. @@ -1157,8 +1186,9 @@ def __sub__(self: array, other: Union[int, float, array], /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __truediv__(self: array, other: Union[int, float, array], /) -> array: + def __truediv__(self: Self, other: int | float | Self, /) -> Self: r""" Evaluates ``self_i / other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1188,8 +1218,9 @@ def __truediv__(self: array, other: Union[int, float, array], /) -> array: .. versionchanged:: 2022.12 Added complex data type support. """ + ... - def __xor__(self: array, other: Union[int, bool, array], /) -> array: + def __xor__(self: Self, other: int | bool | Self, /) -> Self: """ Evaluates ``self_i ^ other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1209,10 +1240,11 @@ def __xor__(self: array, other: Union[int, bool, array], /) -> array: .. note:: Element-wise results must equal the results returned by the equivalent element-wise function :func:`~array_api.bitwise_xor`. """ + ... def to_device( - self: array, device: Device, /, *, stream: Optional[Union[int, Any]] = None - ) -> array: + self: Self, device: Device, /, *, stream: int | Any | None = None + ) -> Self: """ Copy the array from the device on which it currently resides to the specified ``device``. @@ -1240,6 +1272,7 @@ def to_device( .. versionchanged:: 2023.12 Clarified behavior when a provided ``device`` object corresponds to the device on which an array instance resides. """ + ... -array = _array +array = Array diff --git a/src/array_api_stubs/_draft/data_types.py b/src/array_api_stubs/_draft/data_types.py index d15f4a9f7..186a5e76c 100644 --- a/src/array_api_stubs/_draft/data_types.py +++ b/src/array_api_stubs/_draft/data_types.py @@ -1,22 +1,23 @@ -__all__ = ["__eq__"] +__all__ = ["DType"] +from typing import Protocol -from ._types import dtype +class DType(Protocol): + def __eq__(self, other: "DType", /) -> bool: + """ + Computes the truth value of ``self == other`` in order to test for data type object equality. -def __eq__(self: dtype, other: dtype, /) -> bool: - """ - Computes the truth value of ``self == other`` in order to test for data type object equality. + Parameters + ---------- + self: dtype + data type instance. May be any supported data type. + other: dtype + other data type instance. May be any supported data type. - Parameters - ---------- - self: dtype - data type instance. May be any supported data type. - other: dtype - other data type instance. May be any supported data type. - - Returns - ------- - out: bool - a boolean indicating whether the data type objects are equal. - """ + Returns + ------- + out: bool + a boolean indicating whether the data type objects are equal. + """ + ... From 5a052135733b74243f145d3d92e987451bb754d5 Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Tue, 21 Feb 2023 20:59:59 +0000 Subject: [PATCH 2/8] Add a couple of `type: ignore` comments for `__eq__` and `__ne__` --- src/array_api_stubs/_draft/array_object.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 6f44d8549..03dfeb11a 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -510,7 +510,10 @@ def __dlpack_device__(self, /) -> tuple[Enum, int]: """ ... - def __eq__(self: Self, other: int | float | bool | Self, /) -> Self: + # Note that __eq__ returns an array while `object.__eq__` returns a bool. + # Hence Mypy will complain that this violates the Liskov substitution + # principle - ignore that. + def __eq__(self: Self, other: int | float | bool | Self, /) -> Self: # xtype: ignore r""" Computes the truth value of ``self_i == other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -965,7 +968,8 @@ def __mul__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __ne__(self: Self, other: int | float | bool | Self, /) -> Self: + # See note above __eq__ method for explanation of the `type: ignore` + def __ne__(self: Self, other: int | float | bool | Self, /) -> Self: # type: ignore """ Computes the truth value of ``self_i != other_i`` for each element of an array instance with the respective element of the array ``other``. From d678336b2239da6f7095ada97930151997ecb1cc Mon Sep 17 00:00:00 2001 From: Ralf Gommers Date: Tue, 21 Feb 2023 21:28:25 +0000 Subject: [PATCH 3/8] Resolve some Sphinx doc build errors Sphinx doesn't set `TYPE_CHECKING`, but does use the type annotations. `Self` is unknown to Sphinx, so should be filtered out to prevent lots of errors. --- src/_array_api_conf.py | 1 + src/array_api_stubs/_draft/array_object.py | 17 ++++++++--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/_array_api_conf.py b/src/_array_api_conf.py index 08929cc43..af578526e 100644 --- a/src/_array_api_conf.py +++ b/src/_array_api_conf.py @@ -68,6 +68,7 @@ ("py:class", ".*array"), ("py:class", ".*device"), ("py:class", ".*dtype"), + ("py:class", ".*Self"), ("py:class", ".*NestedSequence"), ("py:class", ".*SupportsBufferProtocol"), ("py:class", ".*PyCapsule"), diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 03dfeb11a..723951cc7 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -4,15 +4,14 @@ from typing import TYPE_CHECKING, Protocol, TypeVar -if TYPE_CHECKING: - from ._types import ( - dtype as Dtype, - device as Device, - Any, - PyCapsule, - Enum, - ellipsis, - ) +from ._types import ( + dtype as Dtype, + device as Device, + Any, + PyCapsule, + Enum, + ellipsis, +) Self = TypeVar("Self", bound="Array") # NOTE: when working with py3.11+ this can be ``typing.Self``. From 19afbaeb132ebcde3cfc29721e7081a6780fd4ca Mon Sep 17 00:00:00 2001 From: nstarman Date: Thu, 21 Sep 2023 10:52:34 -0400 Subject: [PATCH 4/8] static typing and documentation Signed-off-by: nstarman --- .pre-commit-config.yaml | 26 ++++ pyproject.toml | 10 ++ spec/draft/API_specification/array_object.rst | 126 +++++++++--------- spec/draft/purpose_and_scope.md | 2 +- src/_array_api_conf.py | 2 + src/array_api_stubs/_draft/_types.py | 4 +- src/array_api_stubs/_draft/array_object.py | 104 +++++++-------- src/array_api_stubs/_draft/data_types.py | 5 +- src/array_api_stubs/_draft/linalg.py | 4 +- 9 files changed, 161 insertions(+), 122 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7ec7f02a8..0ec2b8ada 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -48,3 +48,29 @@ repos: rev: 23.7.0 hooks: - id: black + + - repo: https://github.com/pre-commit/mirrors-mypy + rev: "v1.0.0" + hooks: + - id: mypy + additional_dependencies: [typing_extensions>=4.4.0] + args: + - --ignore-missing-imports + - --config=pyproject.toml + files: ".*(_draft.*)$" + exclude: | + (?x)^( + .*creation_functions.py| + .*data_type_functions.py| + .*elementwise_functions.py| + .*fft.py| + .*indexing_functions.py| + .*linalg.py| + .*linear_algebra_functions.py| + .*manipulation_functions.py| + .*searching_functions.py| + .*set_functions.py| + .*sorting_functions.py| + .*statistical_functions.py| + .*utility_functions.py| + )$ diff --git a/pyproject.toml b/pyproject.toml index 57af04207..ae63cad21 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,5 +30,15 @@ doc = [ requires = ["setuptools"] build-backend = "setuptools.build_meta" + [tool.black] line-length = 88 + + +[tool.mypy] +python_version = "3.9" +mypy_path = "$MYPY_CONFIG_FILE_DIR/src/array_api_stubs/_draft/" +files = [ + "src/array_api_stubs/_draft/**/*.py" +] +follow_imports = "silent" diff --git a/spec/draft/API_specification/array_object.rst b/spec/draft/API_specification/array_object.rst index f8a586ade..2d9df85f1 100644 --- a/spec/draft/API_specification/array_object.rst +++ b/spec/draft/API_specification/array_object.rst @@ -30,47 +30,47 @@ Arithmetic Operators A conforming implementation of the array API standard must provide and support an array object supporting the following Python arithmetic operators. -- ``+x``: :meth:`.array.__pos__` +- ``+x``: :meth:`.Array.__pos__` - `operator.pos(x) `_ - `operator.__pos__(x) `_ -- `-x`: :meth:`.array.__neg__` +- `-x`: :meth:`.Array.__neg__` - `operator.neg(x) `_ - `operator.__neg__(x) `_ -- `x1 + x2`: :meth:`.array.__add__` +- `x1 + x2`: :meth:`.Array.__add__` - `operator.add(x1, x2) `_ - `operator.__add__(x1, x2) `_ -- `x1 - x2`: :meth:`.array.__sub__` +- `x1 - x2`: :meth:`.Array.__sub__` - `operator.sub(x1, x2) `_ - `operator.__sub__(x1, x2) `_ -- `x1 * x2`: :meth:`.array.__mul__` +- `x1 * x2`: :meth:`.Array.__mul__` - `operator.mul(x1, x2) `_ - `operator.__mul__(x1, x2) `_ -- `x1 / x2`: :meth:`.array.__truediv__` +- `x1 / x2`: :meth:`.Array.__truediv__` - `operator.truediv(x1,x2) `_ - `operator.__truediv__(x1, x2) `_ -- `x1 // x2`: :meth:`.array.__floordiv__` +- `x1 // x2`: :meth:`.Array.__floordiv__` - `operator.floordiv(x1, x2) `_ - `operator.__floordiv__(x1, x2) `_ -- `x1 % x2`: :meth:`.array.__mod__` +- `x1 % x2`: :meth:`.Array.__mod__` - `operator.mod(x1, x2) `_ - `operator.__mod__(x1, x2) `_ -- `x1 ** x2`: :meth:`.array.__pow__` +- `x1 ** x2`: :meth:`.Array.__pow__` - `operator.pow(x1, x2) `_ - `operator.__pow__(x1, x2) `_ @@ -82,7 +82,7 @@ Array Operators A conforming implementation of the array API standard must provide and support an array object supporting the following Python array operators. -- `x1 @ x2`: :meth:`.array.__matmul__` +- `x1 @ x2`: :meth:`.Array.__matmul__` - `operator.matmul(x1, x2) `_ - `operator.__matmul__(x1, x2) `_ @@ -94,34 +94,34 @@ Bitwise Operators A conforming implementation of the array API standard must provide and support an array object supporting the following Python bitwise operators. -- `~x`: :meth:`.array.__invert__` +- `~x`: :meth:`.Array.__invert__` - `operator.inv(x) `_ - `operator.invert(x) `_ - `operator.__inv__(x) `_ - `operator.__invert__(x) `_ -- `x1 & x2`: :meth:`.array.__and__` +- `x1 & x2`: :meth:`.Array.__and__` - `operator.and(x1, x2) `_ - `operator.__and__(x1, x2) `_ -- `x1 | x2`: :meth:`.array.__or__` +- `x1 | x2`: :meth:`.Array.__or__` - `operator.or(x1, x2) `_ - `operator.__or__(x1, x2) `_ -- `x1 ^ x2`: :meth:`.array.__xor__` +- `x1 ^ x2`: :meth:`.Array.__xor__` - `operator.xor(x1, x2) `_ - `operator.__xor__(x1, x2) `_ -- `x1 << x2`: :meth:`.array.__lshift__` +- `x1 << x2`: :meth:`.Array.__lshift__` - `operator.lshift(x1, x2) `_ - `operator.__lshift__(x1, x2) `_ -- `x1 >> x2`: :meth:`.array.__rshift__` +- `x1 >> x2`: :meth:`.Array.__rshift__` - `operator.rshift(x1, x2) `_ - `operator.__rshift__(x1, x2) `_ @@ -133,32 +133,32 @@ Comparison Operators A conforming implementation of the array API standard must provide and support an array object supporting the following Python comparison operators. -- `x1 < x2`: :meth:`.array.__lt__` +- `x1 < x2`: :meth:`.Array.__lt__` - `operator.lt(x1, x2) `_ - `operator.__lt__(x1, x2) `_ -- `x1 <= x2`: :meth:`.array.__le__` +- `x1 <= x2`: :meth:`.Array.__le__` - `operator.le(x1, x2) `_ - `operator.__le__(x1, x2) `_ -- `x1 > x2`: :meth:`.array.__gt__` +- `x1 > x2`: :meth:`.Array.__gt__` - `operator.gt(x1, x2) `_ - `operator.__gt__(x1, x2) `_ -- `x1 >= x2`: :meth:`.array.__ge__` +- `x1 >= x2`: :meth:`.Array.__ge__` - `operator.ge(x1, x2) `_ - `operator.__ge__(x1, x2) `_ -- `x1 == x2`: :meth:`.array.__eq__` +- `x1 == x2`: :meth:`.Array.__eq__` - `operator.eq(x1, x2) `_ - `operator.__eq__(x1, x2) `_ -- `x1 != x2`: :meth:`.array.__ne__` +- `x1 != x2`: :meth:`.Array.__ne__` - `operator.ne(x1, x2) `_ - `operator.__ne__(x1, x2) `_ @@ -252,13 +252,13 @@ Attributes :toctree: generated :template: property.rst - array.dtype - array.device - array.mT - array.ndim - array.shape - array.size - array.T + Array.dtype + Array.device + Array.mT + Array.ndim + Array.shape + Array.size + Array.T ------------------------------------------------- @@ -272,37 +272,37 @@ Methods :toctree: generated :template: property.rst - array.__abs__ - array.__add__ - array.__and__ - array.__array_namespace__ - array.__bool__ - array.__complex__ - array.__dlpack__ - array.__dlpack_device__ - array.__eq__ - array.__float__ - array.__floordiv__ - array.__ge__ - array.__getitem__ - array.__gt__ - array.__index__ - array.__int__ - array.__invert__ - array.__le__ - array.__lshift__ - array.__lt__ - array.__matmul__ - array.__mod__ - array.__mul__ - array.__ne__ - array.__neg__ - array.__or__ - array.__pos__ - array.__pow__ - array.__rshift__ - array.__setitem__ - array.__sub__ - array.__truediv__ - array.__xor__ - array.to_device + Array.__abs__ + Array.__add__ + Array.__and__ + Array.__array_namespace__ + Array.__bool__ + Array.__complex__ + Array.__dlpack__ + Array.__dlpack_device__ + Array.__eq__ + Array.__float__ + Array.__floordiv__ + Array.__ge__ + Array.__getitem__ + Array.__gt__ + Array.__index__ + Array.__int__ + Array.__invert__ + Array.__le__ + Array.__lshift__ + Array.__lt__ + Array.__matmul__ + Array.__mod__ + Array.__mul__ + Array.__ne__ + Array.__neg__ + Array.__or__ + Array.__pos__ + Array.__pow__ + Array.__rshift__ + Array.__setitem__ + Array.__sub__ + Array.__truediv__ + Array.__xor__ + Array.to_device diff --git a/spec/draft/purpose_and_scope.md b/spec/draft/purpose_and_scope.md index f375c9512..5ca511181 100644 --- a/spec/draft/purpose_and_scope.md +++ b/spec/draft/purpose_and_scope.md @@ -317,7 +317,7 @@ namespace (e.g. `import package_name.array_api`). This has two issues though: To address both issues, a uniform way must be provided by a conforming implementation to access the API namespace, namely a [method on the array -object](array.__array_namespace__): +object](Array.__array_namespace__): ``` xp = x.__array_namespace__() diff --git a/src/_array_api_conf.py b/src/_array_api_conf.py index af578526e..c58aa52af 100644 --- a/src/_array_api_conf.py +++ b/src/_array_api_conf.py @@ -67,6 +67,7 @@ nitpick_ignore_regex = [ ("py:class", ".*array"), ("py:class", ".*device"), + ("py:class", ".*Device"), ("py:class", ".*dtype"), ("py:class", ".*Self"), ("py:class", ".*NestedSequence"), @@ -85,6 +86,7 @@ "array": "array", "Device": "device", "Dtype": "dtype", + "DType": "dtype", } # Make autosummary show the signatures of functions in the tables using actual diff --git a/src/array_api_stubs/_draft/_types.py b/src/array_api_stubs/_draft/_types.py index 1630ad68b..45b3685d4 100644 --- a/src/array_api_stubs/_draft/_types.py +++ b/src/array_api_stubs/_draft/_types.py @@ -72,7 +72,7 @@ class finfo_object: max: float min: float smallest_normal: float - dtype: dtype + dtype: DType @dataclass @@ -82,7 +82,7 @@ class iinfo_object: bits: int max: int min: int - dtype: dtype + dtype: DType _T_co = TypeVar("_T_co", covariant=True) diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 723951cc7..8a618b483 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -1,19 +1,20 @@ from __future__ import annotations -__all__ = ["array"] +__all__ = ["Array"] from typing import TYPE_CHECKING, Protocol, TypeVar - -from ._types import ( - dtype as Dtype, - device as Device, - Any, - PyCapsule, - Enum, - ellipsis, -) - -Self = TypeVar("Self", bound="Array") +from enum import Enum +from .data_types import DType +from ._types import device as Device + +if TYPE_CHECKING: + from ._types import ( + Any, + PyCapsule, + ellipsis, + ) + +array = TypeVar("array", bound="Array") # NOTE: when working with py3.11+ this can be ``typing.Self``. @@ -23,7 +24,7 @@ def __init__(self) -> None: ... @property - def dtype(self) -> Dtype: + def dtype(self) -> DType: """ Data type of the array elements. @@ -35,7 +36,7 @@ def dtype(self) -> Dtype: ... @property - def device(self) -> Device: + def device(self) -> "Device": # type: ignore[type-var] """ Hardware device the array data resides on. @@ -47,7 +48,7 @@ def device(self) -> Device: ... @property - def mT(self: Self) -> Self: + def mT(self: array) -> array: """ Transpose of a matrix (or a stack of matrices). @@ -111,7 +112,7 @@ def size(self) -> int | None: ... @property - def T(self: Self) -> Self: + def T(self: array) -> array: """ Transpose of the array. @@ -128,7 +129,7 @@ def T(self: Self) -> Self: """ ... - def __abs__(self: Self, /) -> Self: + def __abs__(self: array, /) -> array: """ Calculates the absolute value for each element of an array instance. @@ -158,7 +159,7 @@ def __abs__(self: Self, /) -> Self: """ ... - def __add__(self: Self, other: int | float | Self, /) -> Self: + def __add__(self: array, other: int | float | array, /) -> array: """ Calculates the sum for each element of an array instance with the respective element of the array ``other``. @@ -185,7 +186,7 @@ def __add__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __and__(self: Self, other: int | bool | Self, /) -> Self: + def __and__(self: array, other: int | bool | array, /) -> array: """ Evaluates ``self_i & other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -314,7 +315,7 @@ def __dlpack__( max_version: tuple[int, int] | None = None, dl_device: tuple[Enum, int] | None = None, copy: bool | None = None, - ) -> PyCapsule: + ) -> PyCapsule: # type: ignore[type-var] """ Exports the array for consumption by :func:`~array_api.from_dlpack` as a DLPack capsule. @@ -512,7 +513,7 @@ def __dlpack_device__(self, /) -> tuple[Enum, int]: # Note that __eq__ returns an array while `object.__eq__` returns a bool. # Hence Mypy will complain that this violates the Liskov substitution # principle - ignore that. - def __eq__(self: Self, other: int | float | bool | Self, /) -> Self: # xtype: ignore + def __eq__(self: array, other: int | float | bool | array, /) -> array: # type: ignore[override] r""" Computes the truth value of ``self_i == other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -576,7 +577,7 @@ def __float__(self, /) -> float: """ ... - def __floordiv__(self: Self, other: int | float | Self, /) -> Self: + def __floordiv__(self: array, other: int | float | array, /) -> array: """ Evaluates ``self_i // other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -601,7 +602,7 @@ def __floordiv__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __ge__(self: Self, other: int | float | Self, /) -> Self: + def __ge__(self: array, other: int | float | array, /) -> array: """ Computes the truth value of ``self_i >= other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -630,10 +631,10 @@ def __ge__(self: Self, other: int | float | Self, /) -> Self: ... def __getitem__( - self: Self, - key: int | slice | ellipsis | tuple[int | slice | ellipsis | None, ...] | Self, + self: array, + key: int | slice | ellipsis | tuple[int | slice | ellipsis | None, ...] | array, /, - ) -> Self: + ) -> array: """ Returns ``self[key]``. @@ -657,7 +658,7 @@ def __getitem__( """ ... - def __gt__(self: Self, other: int | float | Self, /) -> Self: + def __gt__(self: array, other: int | float | array, /) -> array: """ Computes the truth value of ``self_i > other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -765,7 +766,7 @@ def __int__(self, /) -> int: """ ... - def __invert__(self: Self, /) -> Self: + def __invert__(self: array, /) -> array: """ Evaluates ``~self_i`` for each element of an array instance. @@ -785,7 +786,7 @@ def __invert__(self: Self, /) -> Self: """ ... - def __le__(self: Self, other: int | float | Self, /) -> Self: + def __le__(self: array, other: int | float | array, /) -> array: """ Computes the truth value of ``self_i <= other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -813,7 +814,7 @@ def __le__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __lshift__(self: Self, other: int | Self, /) -> Self: + def __lshift__(self: array, other: int | array, /) -> array: """ Evaluates ``self_i << other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -835,7 +836,7 @@ def __lshift__(self: Self, other: int | Self, /) -> Self: """ ... - def __lt__(self: Self, other: int | float | Self, /) -> Self: + def __lt__(self: array, other: int | float | array, /) -> array: """ Computes the truth value of ``self_i < other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -863,7 +864,7 @@ def __lt__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __matmul__(self: Self, other: Self, /) -> Self: + def __matmul__(self: array, other: array, /) -> array: """ Computes the matrix product. @@ -912,7 +913,7 @@ def __matmul__(self: Self, other: Self, /) -> Self: """ ... - def __mod__(self: Self, other: int | float | Self, /) -> Self: + def __mod__(self: array, other: int | float | array, /) -> array: """ Evaluates ``self_i % other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -937,7 +938,7 @@ def __mod__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __mul__(self: Self, other: int | float | Self, /) -> Self: + def __mul__(self: array, other: int | float | array, /) -> array: r""" Calculates the product for each element of an array instance with the respective element of the array ``other``. @@ -968,7 +969,7 @@ def __mul__(self: Self, other: int | float | Self, /) -> Self: ... # See note above __eq__ method for explanation of the `type: ignore` - def __ne__(self: Self, other: int | float | bool | Self, /) -> Self: # type: ignore + def __ne__(self: array, other: int | float | bool | array, /) -> array: # type: ignore[override] """ Computes the truth value of ``self_i != other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -999,7 +1000,7 @@ def __ne__(self: Self, other: int | float | bool | Self, /) -> Self: # type: ig """ ... - def __neg__(self: Self, /) -> Self: + def __neg__(self: array, /) -> array: """ Evaluates ``-self_i`` for each element of an array instance. @@ -1030,7 +1031,7 @@ def __neg__(self: Self, /) -> Self: """ ... - def __or__(self: Self, other: int | bool | Self, /) -> Self: + def __or__(self: array, other: int | bool | array, /) -> array: """ Evaluates ``self_i | other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1052,7 +1053,7 @@ def __or__(self: Self, other: int | bool | Self, /) -> Self: """ ... - def __pos__(self: Self, /) -> Self: + def __pos__(self: array, /) -> array: """ Evaluates ``+self_i`` for each element of an array instance. @@ -1077,7 +1078,7 @@ def __pos__(self: Self, /) -> Self: """ ... - def __pow__(self: Self, other: int | float | Self, /) -> Self: + def __pow__(self: array, other: int | float | array, /) -> array: r""" Calculates an implementation-dependent approximation of exponentiation by raising each element (the base) of an array instance to the power of ``other_i`` (the exponent), where ``other_i`` is the corresponding element of the array ``other``. @@ -1109,7 +1110,7 @@ def __pow__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __rshift__(self: Self, other: int | Self, /) -> Self: + def __rshift__(self: array, other: int | array, /) -> array: """ Evaluates ``self_i >> other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1132,9 +1133,9 @@ def __rshift__(self: Self, other: int | Self, /) -> Self: ... def __setitem__( - self: Self, - key: int | slice | ellipsis | tuple[int | slice | ellipsis, ...] | Self, - value: int | float | bool | Self, + self: array, + key: int | slice | ellipsis | tuple[int | slice | ellipsis, ...] | array, + value: int | float | bool | array, /, ) -> None: """ @@ -1162,11 +1163,11 @@ def __setitem__( """ ... - def __sub__(self: Self, other: int | float | Self, /) -> Self: + def __sub__(self: array, other: int | float | array, /) -> array: """ Calculates the difference for each element of an array instance with the respective element of the array ``other``. - The result of ``self_i - other_i`` must be the same as ``self_i + (-other_i)`` and must be governed by the same floating-point rules as addition (see :meth:`array.__add__`). + The result of ``self_i - other_i`` must be the same as ``self_i + (-other_i)`` and must be governed by the same floating-point rules as addition (see :meth:`Array.__add__`). Parameters ---------- @@ -1191,7 +1192,7 @@ def __sub__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __truediv__(self: Self, other: int | float | Self, /) -> Self: + def __truediv__(self: array, other: int | float | array, /) -> array: r""" Evaluates ``self_i / other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1223,7 +1224,7 @@ def __truediv__(self: Self, other: int | float | Self, /) -> Self: """ ... - def __xor__(self: Self, other: int | bool | Self, /) -> Self: + def __xor__(self: array, other: int | bool | array, /) -> array: """ Evaluates ``self_i ^ other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1246,8 +1247,8 @@ def __xor__(self: Self, other: int | bool | Self, /) -> Self: ... def to_device( - self: Self, device: Device, /, *, stream: int | Any | None = None - ) -> Self: + self: array, device: "Device", /, *, stream: int | Any | None = None # type: ignore[type-var] + ) -> array: """ Copy the array from the device on which it currently resides to the specified ``device``. @@ -1258,7 +1259,7 @@ def to_device( device: device a ``device`` object (see :ref:`device-support`). stream: Optional[Union[int, Any]] - stream object to use during copy. In addition to the types supported in :meth:`array.__dlpack__`, implementations may choose to support any library-specific stream object with the caveat that any code using such an object would not be portable. + stream object to use during copy. In addition to the types supported in :meth:`Array.__dlpack__`, implementations may choose to support any library-specific stream object with the caveat that any code using such an object would not be portable. Returns ------- @@ -1276,6 +1277,3 @@ def to_device( Clarified behavior when a provided ``device`` object corresponds to the device on which an array instance resides. """ ... - - -array = Array diff --git a/src/array_api_stubs/_draft/data_types.py b/src/array_api_stubs/_draft/data_types.py index 186a5e76c..a0615bfeb 100644 --- a/src/array_api_stubs/_draft/data_types.py +++ b/src/array_api_stubs/_draft/data_types.py @@ -1,10 +1,13 @@ +from __future__ import annotations + __all__ = ["DType"] + from typing import Protocol class DType(Protocol): - def __eq__(self, other: "DType", /) -> bool: + def __eq__(self, other: DType, /) -> bool: """ Computes the truth value of ``self == other`` in order to test for data type object equality. diff --git a/src/array_api_stubs/_draft/linalg.py b/src/array_api_stubs/_draft/linalg.py index a1c9fe028..227baee24 100644 --- a/src/array_api_stubs/_draft/linalg.py +++ b/src/array_api_stubs/_draft/linalg.py @@ -301,7 +301,7 @@ def matrix_norm( /, *, keepdims: bool = False, - ord: Optional[Union[int, float, Literal[inf, -inf, "fro", "nuc"]]] = "fro", + ord: Optional[Union[int, float, Literal[inf, -inf, "fro", "nuc"]]] = "fro", # type: ignore ) -> array: """ Computes the matrix norm of a matrix (or a stack of matrices) ``x``. @@ -791,7 +791,7 @@ def vector_norm( *, axis: Optional[Union[int, Tuple[int, ...]]] = None, keepdims: bool = False, - ord: Union[int, float, Literal[inf, -inf]] = 2, + ord: Union[int, float, Literal[inf, -inf]] = 2, # type: ignore ) -> array: r""" Computes the vector norm of a vector (or batch of vectors) ``x``. From 6e8c89d08813b2f67ec56092ca39caf46cf1fe8d Mon Sep 17 00:00:00 2001 From: nstarman Date: Thu, 21 Sep 2023 10:54:17 -0400 Subject: [PATCH 5/8] Add device protocol to fix type ignore Signed-off-by: nstarman --- src/array_api_stubs/_draft/_types.py | 10 +++++++++- src/array_api_stubs/_draft/array_object.py | 4 ++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/array_api_stubs/_draft/_types.py b/src/array_api_stubs/_draft/_types.py index 45b3685d4..188cfb9f2 100644 --- a/src/array_api_stubs/_draft/_types.py +++ b/src/array_api_stubs/_draft/_types.py @@ -52,8 +52,16 @@ from .array_object import Array from .data_types import DType + +class Device(Protocol): + """Protocol for device objects.""" + + def __eq__(self, value: Any) -> bool: + ... + + array = TypeVar("array", bound="Array") -device = TypeVar("device") +device = TypeVar("device", bound=Device) dtype = TypeVar("dtype", bound="DType") SupportsDLPack = TypeVar("SupportsDLPack") SupportsBufferProtocol = TypeVar("SupportsBufferProtocol") diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 8a618b483..f757d8933 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Protocol, TypeVar from enum import Enum from .data_types import DType -from ._types import device as Device +from ._types import Device if TYPE_CHECKING: from ._types import ( @@ -36,7 +36,7 @@ def dtype(self) -> DType: ... @property - def device(self) -> "Device": # type: ignore[type-var] + def device(self) -> Device: """ Hardware device the array data resides on. From 2feaad9251893bc66244f051aff5a1d71816f600 Mon Sep 17 00:00:00 2001 From: nstarman Date: Thu, 21 Sep 2023 10:56:58 -0400 Subject: [PATCH 6/8] use general Array protocol for other in comparisons Signed-off-by: nstarman --- src/_array_api_conf.py | 1 + src/array_api_stubs/_draft/array_object.py | 26 +++++++++++----------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/_array_api_conf.py b/src/_array_api_conf.py index c58aa52af..13c898637 100644 --- a/src/_array_api_conf.py +++ b/src/_array_api_conf.py @@ -66,6 +66,7 @@ ] nitpick_ignore_regex = [ ("py:class", ".*array"), + ("py:class", ".*Array"), ("py:class", ".*device"), ("py:class", ".*Device"), ("py:class", ".*dtype"), diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index f757d8933..6bd5305cd 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -159,7 +159,7 @@ def __abs__(self: array, /) -> array: """ ... - def __add__(self: array, other: int | float | array, /) -> array: + def __add__(self: array, other: int | float | Array, /) -> array: """ Calculates the sum for each element of an array instance with the respective element of the array ``other``. @@ -513,7 +513,7 @@ def __dlpack_device__(self, /) -> tuple[Enum, int]: # Note that __eq__ returns an array while `object.__eq__` returns a bool. # Hence Mypy will complain that this violates the Liskov substitution # principle - ignore that. - def __eq__(self: array, other: int | float | bool | array, /) -> array: # type: ignore[override] + def __eq__(self: array, other: int | float | bool | Array, /) -> array: # type: ignore[override] r""" Computes the truth value of ``self_i == other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -577,7 +577,7 @@ def __float__(self, /) -> float: """ ... - def __floordiv__(self: array, other: int | float | array, /) -> array: + def __floordiv__(self: array, other: int | float | Array, /) -> array: """ Evaluates ``self_i // other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -602,7 +602,7 @@ def __floordiv__(self: array, other: int | float | array, /) -> array: """ ... - def __ge__(self: array, other: int | float | array, /) -> array: + def __ge__(self: array, other: int | float | Array, /) -> array: """ Computes the truth value of ``self_i >= other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -658,7 +658,7 @@ def __getitem__( """ ... - def __gt__(self: array, other: int | float | array, /) -> array: + def __gt__(self: array, other: int | float | Array, /) -> array: """ Computes the truth value of ``self_i > other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -786,7 +786,7 @@ def __invert__(self: array, /) -> array: """ ... - def __le__(self: array, other: int | float | array, /) -> array: + def __le__(self: array, other: int | float | Array, /) -> array: """ Computes the truth value of ``self_i <= other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -836,7 +836,7 @@ def __lshift__(self: array, other: int | array, /) -> array: """ ... - def __lt__(self: array, other: int | float | array, /) -> array: + def __lt__(self: array, other: int | float | Array, /) -> array: """ Computes the truth value of ``self_i < other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -913,7 +913,7 @@ def __matmul__(self: array, other: array, /) -> array: """ ... - def __mod__(self: array, other: int | float | array, /) -> array: + def __mod__(self: array, other: int | float | Array, /) -> array: """ Evaluates ``self_i % other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -938,7 +938,7 @@ def __mod__(self: array, other: int | float | array, /) -> array: """ ... - def __mul__(self: array, other: int | float | array, /) -> array: + def __mul__(self: array, other: int | float | Array, /) -> array: r""" Calculates the product for each element of an array instance with the respective element of the array ``other``. @@ -969,7 +969,7 @@ def __mul__(self: array, other: int | float | array, /) -> array: ... # See note above __eq__ method for explanation of the `type: ignore` - def __ne__(self: array, other: int | float | bool | array, /) -> array: # type: ignore[override] + def __ne__(self: array, other: int | float | bool | Array, /) -> array: # type: ignore[override] """ Computes the truth value of ``self_i != other_i`` for each element of an array instance with the respective element of the array ``other``. @@ -1078,7 +1078,7 @@ def __pos__(self: array, /) -> array: """ ... - def __pow__(self: array, other: int | float | array, /) -> array: + def __pow__(self: array, other: int | float | Array, /) -> array: r""" Calculates an implementation-dependent approximation of exponentiation by raising each element (the base) of an array instance to the power of ``other_i`` (the exponent), where ``other_i`` is the corresponding element of the array ``other``. @@ -1163,7 +1163,7 @@ def __setitem__( """ ... - def __sub__(self: array, other: int | float | array, /) -> array: + def __sub__(self: array, other: int | float | Array, /) -> array: """ Calculates the difference for each element of an array instance with the respective element of the array ``other``. @@ -1192,7 +1192,7 @@ def __sub__(self: array, other: int | float | array, /) -> array: """ ... - def __truediv__(self: array, other: int | float | array, /) -> array: + def __truediv__(self: array, other: int | float | Array, /) -> array: r""" Evaluates ``self_i / other_i`` for each element of an array instance with the respective element of the array ``other``. From 950fe94a775ac48b2e94508803399bfa91e0ffcd Mon Sep 17 00:00:00 2001 From: nstarman Date: Thu, 21 Sep 2023 10:55:19 -0400 Subject: [PATCH 7/8] Correct type hints in to_device and dlpack Signed-off-by: nstarman --- src/array_api_stubs/_draft/array_object.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index 6bd5305cd..ebbfc2ab5 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -311,7 +311,7 @@ def __dlpack__( self, /, *, - stream: int | Any | None = None, + stream: Any | None = None, max_version: tuple[int, int] | None = None, dl_device: tuple[Enum, int] | None = None, copy: bool | None = None, @@ -1247,7 +1247,7 @@ def __xor__(self: array, other: int | bool | array, /) -> array: ... def to_device( - self: array, device: "Device", /, *, stream: int | Any | None = None # type: ignore[type-var] + self: array, device: "Device", /, *, stream: Any | None = None # type: ignore[type-var] ) -> array: """ Copy the array from the device on which it currently resides to the specified ``device``. @@ -1258,7 +1258,7 @@ def to_device( array instance. device: device a ``device`` object (see :ref:`device-support`). - stream: Optional[Union[int, Any]] + stream: Optional[Any] stream object to use during copy. In addition to the types supported in :meth:`Array.__dlpack__`, implementations may choose to support any library-specific stream object with the caveat that any code using such an object would not be portable. Returns From 65170a702331dccf6feba9b9b265a9f1e43eb2e2 Mon Sep 17 00:00:00 2001 From: nstarman Date: Sun, 24 Nov 2024 16:21:05 -0500 Subject: [PATCH 8/8] fixes for ci after rebase Signed-off-by: nstarman --- spec/draft/API_specification/array_object.rst | 2 +- src/_array_api_conf.py | 1 + src/array_api_stubs/_draft/_types.py | 8 ++++---- src/array_api_stubs/_draft/array_object.py | 9 +++++---- src/array_api_stubs/_draft/creation_functions.py | 2 +- src/array_api_stubs/_draft/info.py | 14 ++++++++------ 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/spec/draft/API_specification/array_object.rst b/spec/draft/API_specification/array_object.rst index 2d9df85f1..a4e44a2c2 100644 --- a/spec/draft/API_specification/array_object.rst +++ b/spec/draft/API_specification/array_object.rst @@ -163,7 +163,7 @@ A conforming implementation of the array API standard must provide and support a - `operator.ne(x1, x2) `_ - `operator.__ne__(x1, x2) `_ -:meth:`.array.__lt__`, :meth:`.array.__le__`, :meth:`.array.__gt__`, :meth:`.array.__ge__` are only defined for arrays having real-valued data types. Other comparison operators should be defined for arrays having any data type. +:meth:`.Array.__lt__`, :meth:`.Array.__le__`, :meth:`.Array.__gt__`, :meth:`.Array.__ge__` are only defined for arrays having real-valued data types. Other comparison operators should be defined for arrays having any data type. For backward compatibility, conforming implementations may support complex numbers; however, inequality comparison of complex numbers is unspecified and thus implementation-dependent (see :ref:`complex-number-ordering`). In-place Operators diff --git a/src/_array_api_conf.py b/src/_array_api_conf.py index 13c898637..9e7f951e7 100644 --- a/src/_array_api_conf.py +++ b/src/_array_api_conf.py @@ -62,6 +62,7 @@ ("py:obj", "typing.Union[int, float, typing.Literal[inf, - inf]]"), ("py:class", "int | float | ~typing.Literal[inf, -inf]"), ("py:class", "enum.Enum"), + ("py:class", "Enum"), ("py:class", "ellipsis"), ] nitpick_ignore_regex = [ diff --git a/src/array_api_stubs/_draft/_types.py b/src/array_api_stubs/_draft/_types.py index 188cfb9f2..158e63b2c 100644 --- a/src/array_api_stubs/_draft/_types.py +++ b/src/array_api_stubs/_draft/_types.py @@ -110,17 +110,17 @@ class Info(Protocol): def capabilities(self) -> Capabilities: ... - def default_device(self) -> device: + def default_device(self) -> Device: ... - def default_dtypes(self, *, device: Optional[device]) -> DefaultDataTypes: + def default_dtypes(self, *, device: Optional[Device]) -> DefaultDataTypes: ... - def devices(self) -> List[device]: + def devices(self) -> List[Device]: ... def dtypes( - self, *, device: Optional[device], kind: Optional[Union[str, Tuple[str, ...]]] + self, *, device: Optional[Device], kind: Optional[Union[str, Tuple[str, ...]]] ) -> DataTypes: ... diff --git a/src/array_api_stubs/_draft/array_object.py b/src/array_api_stubs/_draft/array_object.py index ebbfc2ab5..27c2d4a33 100644 --- a/src/array_api_stubs/_draft/array_object.py +++ b/src/array_api_stubs/_draft/array_object.py @@ -2,8 +2,9 @@ __all__ = ["Array"] -from typing import TYPE_CHECKING, Protocol, TypeVar from enum import Enum +from typing import TYPE_CHECKING, Protocol, TypeVar + from .data_types import DType from ._types import Device @@ -311,7 +312,7 @@ def __dlpack__( self, /, *, - stream: Any | None = None, + stream: Any | None = None, max_version: tuple[int, int] | None = None, dl_device: tuple[Enum, int] | None = None, copy: bool | None = None, @@ -370,7 +371,7 @@ def __dlpack__( dl_device: Optional[tuple[enum.Enum, int]] the DLPack device type. Default is ``None``, meaning the exported capsule should be on the same device as ``self`` is. When specified, the format - must be a 2-tuple, following that of the return value of :meth:`array.__dlpack_device__`. + must be a 2-tuple, following that of the return value of :meth:`Array.__dlpack_device__`. If the device type cannot be handled by the producer, this function must raise ``BufferError``. @@ -492,7 +493,7 @@ def __dlpack_device__(self, /) -> tuple[Enum, int]: Returns ------- - device: Tuple[Enum, int] + device: Tuple[enum.Enum, int] a tuple ``(device_type, device_id)`` in DLPack format. Valid device type enum members are: :: diff --git a/src/array_api_stubs/_draft/creation_functions.py b/src/array_api_stubs/_draft/creation_functions.py index 6de79268e..07ed482f0 100644 --- a/src/array_api_stubs/_draft/creation_functions.py +++ b/src/array_api_stubs/_draft/creation_functions.py @@ -268,7 +268,7 @@ def from_dlpack( Notes ----- - See :meth:`array.__dlpack__` for implementation suggestions for `from_dlpack` in + See :meth:`Array.__dlpack__` for implementation suggestions for `from_dlpack` in order to handle DLPack versioning correctly. A way to move data from two array libraries to the same device (assumed supported by both libraries) in diff --git a/src/array_api_stubs/_draft/info.py b/src/array_api_stubs/_draft/info.py index 6177fb12f..dce4ccf8f 100644 --- a/src/array_api_stubs/_draft/info.py +++ b/src/array_api_stubs/_draft/info.py @@ -18,10 +18,11 @@ DataTypes, Capabilities, Info, + Device, ) -def __array_namespace_info__() -> Info: +def __array_namespace_info__() -> Info: # type: ignore[empty-body] """ Returns a namespace with Array API namespace inspection utilities. @@ -48,9 +49,10 @@ def __array_namespace_info__() -> Info: .. versionadded: 2023.12 """ + ... -def capabilities() -> Capabilities: +def capabilities() -> Capabilities: # type: ignore[empty-body] """ Returns a dictionary of array library capabilities. @@ -72,7 +74,7 @@ def capabilities() -> Capabilities: """ -def default_device() -> device: +def default_device() -> Device: # type: ignore[empty-body] """ Returns the default device. @@ -88,7 +90,7 @@ def default_device() -> device: """ -def default_dtypes( +def default_dtypes( # type: ignore[empty-body] *, device: Optional[device] = None, ) -> DefaultDataTypes: @@ -124,7 +126,7 @@ def default_dtypes( """ -def dtypes( +def dtypes( # type: ignore[empty-body] *, device: Optional[device] = None, kind: Optional[Union[str, Tuple[str, ...]]] = None, @@ -179,7 +181,7 @@ def dtypes( """ -def devices() -> List[device]: +def devices() -> List[device]: # type: ignore[empty-body] """ Returns a list of supported devices which are available at runtime.