summaryrefslogtreecommitdiff
path: root/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'numpy')
-rw-r--r--numpy/_pytesttester.py2
-rw-r--r--numpy/core/src/multiarray/multiarraymodule.c35
-rw-r--r--numpy/core/src/multiarray/scalartypes.c.src9
-rw-r--r--numpy/core/tests/test_deprecations.py14
-rw-r--r--numpy/core/tests/test_records.py11
-rw-r--r--numpy/typing/__init__.py81
-rw-r--r--numpy/typing/_add_docstring.py96
7 files changed, 212 insertions, 36 deletions
diff --git a/numpy/_pytesttester.py b/numpy/_pytesttester.py
index 33fee9a14..813e069a4 100644
--- a/numpy/_pytesttester.py
+++ b/numpy/_pytesttester.py
@@ -6,7 +6,7 @@ boiler plate for doing that is to put the following in the module
``__init__.py`` file::
from numpy._pytesttester import PytestTester
- test = PytestTester(__name__).test
+ test = PytestTester(__name__)
del PytestTester
diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c
index 60b965845..af5949e73 100644
--- a/numpy/core/src/multiarray/multiarraymodule.c
+++ b/numpy/core/src/multiarray/multiarraymodule.c
@@ -1930,20 +1930,41 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
int alloc = 0;
void *dptr;
PyObject *ret;
-
+ PyObject *base = NULL;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!|O:scalar", kwlist,
&PyArrayDescr_Type, &typecode, &obj)) {
return NULL;
}
if (PyDataType_FLAGCHK(typecode, NPY_LIST_PICKLE)) {
- if (!PySequence_Check(obj)) {
- PyErr_SetString(PyExc_TypeError,
- "found non-sequence while unpickling scalar with "
- "NPY_LIST_PICKLE set");
+ if (typecode->type_num == NPY_OBJECT) {
+ /* Deprecated 2020-11-24, NumPy 1.20 */
+ if (DEPRECATE(
+ "Unpickling a scalar with object dtype is deprecated. "
+ "Object scalars should never be created. If this was a "
+ "properly created pickle, please open a NumPy issue. In "
+ "a best effort this returns the original object.") < 0) {
+ return NULL;
+ }
+ Py_INCREF(obj);
+ return obj;
+ }
+ /* We store the full array to unpack it here: */
+ if (!PyArray_CheckExact(obj)) {
+ /* We pickle structured voids as arrays currently */
+ PyErr_SetString(PyExc_RuntimeError,
+ "Unpickling NPY_LIST_PICKLE (structured void) scalar "
+ "requires an array. The pickle file may be corrupted?");
return NULL;
}
- dptr = &obj;
+ if (!PyArray_EquivTypes(PyArray_DESCR((PyArrayObject *)obj), typecode)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Pickled array is not compatible with requested scalar "
+ "dtype. The pickle file may be corrupted?");
+ return NULL;
+ }
+ base = obj;
+ dptr = PyArray_BYTES((PyArrayObject *)obj);
}
else if (PyDataType_FLAGCHK(typecode, NPY_ITEM_IS_POINTER)) {
@@ -1993,7 +2014,7 @@ array_scalar(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
dptr = PyBytes_AS_STRING(obj);
}
}
- ret = PyArray_Scalar(dptr, typecode, NULL);
+ ret = PyArray_Scalar(dptr, typecode, base);
/* free dptr which contains zeros */
if (alloc) {
diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src
index f04bdbaa8..b8976d08f 100644
--- a/numpy/core/src/multiarray/scalartypes.c.src
+++ b/numpy/core/src/multiarray/scalartypes.c.src
@@ -1742,13 +1742,8 @@ gentype_reduce(PyObject *self, PyObject *NPY_UNUSED(args))
if (arr == NULL) {
return NULL;
}
- /* arr.item() */
- PyObject *val = PyArray_GETITEM(arr, PyArray_DATA(arr));
- Py_DECREF(arr);
- if (val == NULL) {
- return NULL;
- }
- PyObject *tup = Py_BuildValue("NN", obj, val);
+ /* Use the whole array which handles sturctured void correctly */
+ PyObject *tup = Py_BuildValue("NN", obj, arr);
if (tup == NULL) {
return NULL;
}
diff --git a/numpy/core/tests/test_deprecations.py b/numpy/core/tests/test_deprecations.py
index 380b78f67..a67fe62c3 100644
--- a/numpy/core/tests/test_deprecations.py
+++ b/numpy/core/tests/test_deprecations.py
@@ -771,3 +771,17 @@ class TestDeprecateSubarrayDTypeDuringArrayCoercion(_DeprecationTestCase):
np.array(arr, dtype="(2,2)f")
self.assert_deprecated(check)
+
+
+class TestDeprecatedUnpickleObjectScalar(_DeprecationTestCase):
+ # Deprecated 2020-11-24, NumPy 1.20
+ """
+ Technically, it should be impossible to create numpy object scalars,
+ but there was an unpickle path that would in theory allow it. That
+ path is invalid and must lead to the warning.
+ """
+ message = "Unpickling a scalar with object dtype is deprecated."
+
+ def test_deprecated(self):
+ ctor = np.core.multiarray.scalar
+ self.assert_deprecated(lambda: ctor(np.dtype("O"), 1))
diff --git a/numpy/core/tests/test_records.py b/numpy/core/tests/test_records.py
index f28ad5ac9..4d4b4b515 100644
--- a/numpy/core/tests/test_records.py
+++ b/numpy/core/tests/test_records.py
@@ -424,7 +424,16 @@ class TestRecord:
# make sure we did not pickle the address
assert not isinstance(obj, bytes)
- assert_raises(TypeError, ctor, dtype, 13)
+ assert_raises(RuntimeError, ctor, dtype, 13)
+
+ # Test roundtrip:
+ dump = pickle.dumps(a[0])
+ unpickled = pickle.loads(dump)
+ assert a[0] == unpickled
+
+ # Also check the similar (impossible) "object scalar" path:
+ with pytest.warns(DeprecationWarning):
+ assert ctor(np.dtype("O"), data) is data
def test_objview_record(self):
# https://github.com/numpy/numpy/issues/2599
diff --git a/numpy/typing/__init__.py b/numpy/typing/__init__.py
index cbde75462..e72e8fb4d 100644
--- a/numpy/typing/__init__.py
+++ b/numpy/typing/__init__.py
@@ -11,14 +11,11 @@ Typing (:mod:`numpy.typing`)
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.
+addition a number of type aliases are available to users, most prominently
+the two below:
-- ``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``".
+- `ArrayLike`: objects that can be converted to arrays
+- `DTypeLike`: objects that can be converted to dtypes
.. _typing-extensions: https://pypi.org/project/typing-extensions/
@@ -34,13 +31,13 @@ differences.
ArrayLike
~~~~~~~~~
-The ``ArrayLike`` type tries to avoid creating object arrays. For
+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)
+ array(<generator object <genexpr> at ...>, dtype=object)
is valid NumPy code which will create a 0-dimensional object
array. Type checkers will complain about the above example when using
@@ -51,14 +48,14 @@ you can either use a ``# type: ignore`` comment:
>>> np.array(x**2 for x in range(10)) # type: ignore
-or explicitly type the array like object as ``Any``:
+or explicitly type the array like object as `~typing.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)
+ array(<generator object <genexpr> at ...>, dtype=object)
ndarray
~~~~~~~
@@ -75,10 +72,10 @@ 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.
-dtype
-~~~~~
+DTypeLike
+~~~~~~~~~
-The ``DTypeLike`` type tries to avoid creation of dtype objects using
+The `DTypeLike` type tries to avoid creation of dtype objects using
dictionary of fields like below:
.. code-block:: python
@@ -87,16 +84,43 @@ dictionary of fields like below:
Although this is valid Numpy code, the type checker will complain about it,
since its usage is discouraged.
-Please see : https://numpy.org/devdocs/reference/arrays.dtypes.html
+Please see : :ref:`Data type objects <arrays.dtypes>`
+
+Number Precision
+~~~~~~~~~~~~~~~~
+
+The precision of `numpy.number` subclasses is treated as a covariant generic
+parameter (see :class:`~NBitBase`), simplifying the annoting of proccesses
+involving precision-based casting.
+
+.. code-block:: python
+
+ >>> from typing import TypeVar
+ >>> import numpy as np
+ >>> import numpy.typing as npt
-NBitBase
-~~~~~~~~
+ >>> T = TypeVar("T", bound=npt.NBitBase)
+ >>> def func(a: "np.floating[T]", b: "np.floating[T]") -> "np.floating[T]":
+ ... ...
-.. autoclass:: numpy.typing.NBitBase
+Consequently, the likes of `~numpy.float16`, `~numpy.float32` and
+`~numpy.float64` are still sub-types of `~numpy.floating`, but, contrary to
+runtime, they're not necessarily considered as sub-classes.
+
+Timedelta64
+~~~~~~~~~~~
+
+The `~numpy.timedelta64` class is not considered a subclass of `~numpy.signedinteger`,
+the former only inheriting from `~numpy.generic` while static type checking.
+
+API
+---
"""
+# NOTE: The API section will be appended with additional entries
+# further down in this file
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, List
if TYPE_CHECKING:
import sys
@@ -107,6 +131,17 @@ if TYPE_CHECKING:
else:
def final(f): return f
+if not TYPE_CHECKING:
+ __all__ = ["ArrayLike", "DTypeLike", "NBitBase"]
+else:
+ # Ensure that all objects within this module are accessible while
+ # static type checking. This includes private ones, as we need them
+ # for internal use.
+ #
+ # Declare to mypy that `__all__` is a list of strings without assigning
+ # an explicit value
+ __all__: List[str]
+
@final # Dissallow the creation of arbitrary `NBitBase` subclasses
class NBitBase:
@@ -170,7 +205,7 @@ class _16Bit(_32Bit): ... # type: ignore[misc]
class _8Bit(_16Bit): ... # type: ignore[misc]
# Clean up the namespace
-del TYPE_CHECKING, final
+del TYPE_CHECKING, final, List
from ._scalars import (
_CharLike,
@@ -186,6 +221,12 @@ from ._array_like import _SupportsArray, ArrayLike
from ._shape import _Shape, _ShapeLike
from ._dtype_like import _SupportsDType, _VoidDTypeLike, DTypeLike
+if __doc__ is not None:
+ from ._add_docstring import _docstrings
+ __doc__ += _docstrings
+ __doc__ += '\n.. autoclass:: numpy.typing.NBitBase\n'
+ del _docstrings
+
from numpy._pytesttester import PytestTester
test = PytestTester(__name__)
del PytestTester
diff --git a/numpy/typing/_add_docstring.py b/numpy/typing/_add_docstring.py
new file mode 100644
index 000000000..8e39fe2c6
--- /dev/null
+++ b/numpy/typing/_add_docstring.py
@@ -0,0 +1,96 @@
+"""A module for creating docstrings for sphinx ``data`` domains."""
+
+import re
+import textwrap
+
+_docstrings_list = []
+
+
+def add_newdoc(name, value, doc):
+ _docstrings_list.append((name, value, doc))
+
+
+def _parse_docstrings():
+ type_list_ret = []
+ for name, value, doc in _docstrings_list:
+ s = textwrap.dedent(doc).replace("\n", "\n ")
+
+ # Replace sections by rubrics
+ lines = s.split("\n")
+ new_lines = []
+ indent = ""
+ for line in lines:
+ m = re.match(r'^(\s+)[-=]+\s*$', line)
+ if m and new_lines:
+ prev = textwrap.dedent(new_lines.pop())
+ if prev == "Examples":
+ indent = ""
+ new_lines.append(f'{m.group(1)}.. rubric:: {prev}')
+ else:
+ indent = 4 * " "
+ new_lines.append(f'{m.group(1)}.. admonition:: {prev}')
+ new_lines.append("")
+ else:
+ new_lines.append(f"{indent}{line}")
+ s = "\n".join(new_lines)
+
+ # Done.
+ type_list_ret.append(f""".. data:: {name}\n :value: {value}\n {s}""")
+ return "\n".join(type_list_ret)
+
+
+add_newdoc('ArrayLike', 'typing.Union[...]',
+ """
+ A `~typing.Union` representing objects that can be coerced into an `~numpy.ndarray`.
+
+ Among others this includes the likes of:
+
+ * Scalars.
+ * (Nested) sequences.
+ * Objects implementing the `~class.__array__` protocol.
+
+ See Also
+ --------
+ :term:`array_like`:
+ Any scalar or sequence that can be interpreted as an ndarray.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ >>> import numpy as np
+ >>> import numpy.typing as npt
+
+ >>> def as_array(a: npt.ArrayLike) -> np.ndarray:
+ ... return np.array(a)
+
+ """)
+
+add_newdoc('DTypeLike', 'typing.Union[...]',
+ """
+ A `~typing.Union` representing objects that can be coerced into a `~numpy.dtype`.
+
+ Among others this includes the likes of:
+
+ * :class:`type` objects.
+ * Character codes or the names of :class:`type` objects.
+ * Objects with the ``.dtype`` attribute.
+
+ See Also
+ --------
+ :ref:`Specifying and constructing data types <arrays.dtypes.constructing>`
+ A comprehensive overview of all objects that can be coerced into data types.
+
+ Examples
+ --------
+ .. code-block:: python
+
+ >>> import numpy as np
+ >>> import numpy.typing as npt
+
+ >>> def as_dtype(d: npt.DTypeLike) -> np.dtype:
+ ... return np.dtype(d)
+
+ """)
+
+_docstrings = _parse_docstrings()