diff options
Diffstat (limited to 'numpy/typing')
-rw-r--r-- | numpy/typing/__init__.py | 81 | ||||
-rw-r--r-- | numpy/typing/_array_like.py | 34 | ||||
-rw-r--r-- | numpy/typing/_dtype_like.py | 46 | ||||
-rw-r--r-- | numpy/typing/_shape.py | 6 |
4 files changed, 167 insertions, 0 deletions
diff --git a/numpy/typing/__init__.py b/numpy/typing/__init__.py new file mode 100644 index 000000000..f2000823f --- /dev/null +++ b/numpy/typing/__init__.py @@ -0,0 +1,81 @@ +""" +============================ +Typing (:mod:`numpy.typing`) +============================ + +.. warning:: + + Some of the types in this module rely on features only present in + the standard library in Python 3.8 and greater. If you want to use + these types in earlier versions of Python, you should install the + typing-extensions_ package. + +Large parts of the NumPy API have PEP-484-style type annotations. In +addition, the following type aliases are available for users. + +- ``typing.ArrayLike``: objects that can be converted to arrays +- ``typing.DtypeLike``: objects that can be converted to dtypes + +Roughly speaking, ``typing.ArrayLike`` is "objects that can be used as +inputs to ``np.array``" and ``typing.DtypeLike`` is "objects that can +be used as inputs to ``np.dtype``". + +.. _typing-extensions: https://pypi.org/project/typing-extensions/ + +Differences from the runtime NumPy API +-------------------------------------- + +NumPy is very flexible. Trying to describe the full range of +possibilities statically would result in types that are not very +helpful. For that reason, the typed NumPy API is often stricter than +the runtime NumPy API. This section describes some notable +differences. + +ArrayLike +~~~~~~~~~ + +The ``ArrayLike`` type tries to avoid creating object arrays. For +example, + +.. code-block:: python + + >>> np.array(x**2 for x in range(10)) + array(<generator object <genexpr> at 0x10c004cd0>, dtype=object) + +is valid NumPy code which will create a 0-dimensional object +array. Type checkers will complain about the above example when using +the NumPy types however. If you really intended to do the above, then +you can either use a ``# type: ignore`` comment: + +.. code-block:: python + + >>> np.array(x**2 for x in range(10)) # type: ignore + +or explicitly type the array like object as ``Any``: + +.. code-block:: python + + >>> from typing import Any + >>> array_like: Any = (x**2 for x in range(10)) + >>> np.array(array_like) + array(<generator object <genexpr> at 0x1192741d0>, dtype=object) + +ndarray +~~~~~~~ + +It's possible to mutate the dtype of an array at runtime. For example, +the following code is valid: + +.. code-block:: python + + x = np.array([1, 2]) + x.dtype = np.bool_ + +This sort of mutation is not allowed by the types. Users who want to +write statically typed code should insted use the `numpy.ndarray.view` +method to create a view of the array with a different dtype. + +""" +from ._array_like import _SupportsArray, ArrayLike +from ._shape import _Shape, _ShapeLike +from ._dtype_like import DtypeLike diff --git a/numpy/typing/_array_like.py b/numpy/typing/_array_like.py new file mode 100644 index 000000000..76c0c839c --- /dev/null +++ b/numpy/typing/_array_like.py @@ -0,0 +1,34 @@ +import sys +from typing import Any, overload, Sequence, TYPE_CHECKING, Union + +from numpy import ndarray +from ._dtype_like import DtypeLike + +if sys.version_info >= (3, 8): + from typing import Protocol + HAVE_PROTOCOL = True +else: + try: + from typing_extensions import Protocol + except ImportError: + HAVE_PROTOCOL = False + else: + HAVE_PROTOCOL = True + +if TYPE_CHECKING or HAVE_PROTOCOL: + class _SupportsArray(Protocol): + @overload + def __array__(self, __dtype: DtypeLike = ...) -> ndarray: ... + @overload + def __array__(self, dtype: DtypeLike = ...) -> ndarray: ... +else: + _SupportsArray = Any + +# TODO: support buffer protocols once +# +# https://bugs.python.org/issue27501 +# +# is resolved. See also the mypy issue: +# +# https://github.com/python/typing/issues/593 +ArrayLike = Union[bool, int, float, complex, _SupportsArray, Sequence] diff --git a/numpy/typing/_dtype_like.py b/numpy/typing/_dtype_like.py new file mode 100644 index 000000000..b9df0af04 --- /dev/null +++ b/numpy/typing/_dtype_like.py @@ -0,0 +1,46 @@ +from typing import Any, Dict, List, Sequence, Tuple, Union + +from numpy import dtype +from ._shape import _ShapeLike + +_DtypeLikeNested = Any # TODO: wait for support for recursive types + +# Anything that can be coerced into numpy.dtype. +# Reference: https://docs.scipy.org/doc/numpy/reference/arrays.dtypes.html +DtypeLike = Union[ + dtype, + # default data type (float64) + None, + # array-scalar types and generic types + type, # TODO: enumerate these when we add type hints for numpy scalars + # TODO: add a protocol for anything with a dtype attribute + # character codes, type strings or comma-separated fields, e.g., 'float64' + str, + # (flexible_dtype, itemsize) + Tuple[_DtypeLikeNested, int], + # (fixed_dtype, shape) + Tuple[_DtypeLikeNested, _ShapeLike], + # [(field_name, field_dtype, field_shape), ...] + # + # The type here is quite broad because NumPy accepts quite a wide + # range of inputs inside the list; see the tests for some + # examples. + List[Any], + # {'names': ..., 'formats': ..., 'offsets': ..., 'titles': ..., + # 'itemsize': ...} + # TODO: use TypedDict when/if it's officially supported + Dict[ + str, + Union[ + Sequence[str], # names + Sequence[_DtypeLikeNested], # formats + Sequence[int], # offsets + Sequence[Union[bytes, str, None]], # titles + int, # itemsize + ], + ], + # {'field1': ..., 'field2': ..., ...} + Dict[str, Tuple[_DtypeLikeNested, int]], + # (base_dtype, new_dtype) + Tuple[_DtypeLikeNested, _DtypeLikeNested], +] diff --git a/numpy/typing/_shape.py b/numpy/typing/_shape.py new file mode 100644 index 000000000..4629046ea --- /dev/null +++ b/numpy/typing/_shape.py @@ -0,0 +1,6 @@ +from typing import Sequence, Tuple, Union + +_Shape = Tuple[int, ...] + +# Anything that can be coerced to a shape tuple +_ShapeLike = Union[int, Sequence[int]] |