From 2e446a01f89533a24291e519724e0e855da83ede Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Thu, 13 Feb 2025 10:36:35 -0700 Subject: [PATCH 01/49] MAINT: Prepare 2.2.x for further development. - Create 2.2.4-notes.rst - Update release.rst - Update pavement.rst - Update pyproject.toml [skip azp] [skip cirrus] [skip actions] --- doc/source/release.rst | 1 + doc/source/release/2.2.4-notes.rst | 20 ++++++++++++++++++++ pavement.py | 2 +- pyproject.toml | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 doc/source/release/2.2.4-notes.rst diff --git a/doc/source/release.rst b/doc/source/release.rst index a22178a055ee..13413f3a1b83 100644 --- a/doc/source/release.rst +++ b/doc/source/release.rst @@ -5,6 +5,7 @@ Release notes .. toctree:: :maxdepth: 2 + 2.2.4 2.2.3 2.2.2 2.2.1 diff --git a/doc/source/release/2.2.4-notes.rst b/doc/source/release/2.2.4-notes.rst new file mode 100644 index 000000000000..9cde851301f9 --- /dev/null +++ b/doc/source/release/2.2.4-notes.rst @@ -0,0 +1,20 @@ +.. currentmodule:: numpy + +========================== +NumPy 2.2.4 Release Notes +========================== + +NumPy 2.2.4 is a patch release that fixes bugs found after the 2.2.3 release. + +Highlights +========== + +*We'll choose highlights for this release near the end of the release cycle.* + + +.. if release snippets have been incorporated already, uncomment the follow + line (leave the `.. include:: directive) + +.. **Content from release note snippets in doc/release/upcoming_changes:** + +.. include:: notes-towncrier.rst diff --git a/pavement.py b/pavement.py index 6b6a0668b7a1..e3e778d4bbfc 100644 --- a/pavement.py +++ b/pavement.py @@ -36,7 +36,7 @@ #----------------------------------- # Path to the release notes -RELEASE_NOTES = 'doc/source/release/2.2.3-notes.rst' +RELEASE_NOTES = 'doc/source/release/2.2.4-notes.rst' #------------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index b4f39af4d56c..3d9889d2eeed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ requires = [ [project] name = "numpy" -version = "2.2.3" +version = "2.2.4" # TODO: add `license-files` once PEP 639 is accepted (see meson-python#88) license = {file = "LICENSE.txt"} From 92d561e008c430bcacdf5e763084d08f06238ed0 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Thu, 13 Feb 2025 12:31:37 +0500 Subject: [PATCH 02/49] TYP: Fix missing typing arguments flags --- numpy/_core/numeric.pyi | 4 ++++ numpy/linalg/_linalg.pyi | 26 +++++++++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/numpy/_core/numeric.pyi b/numpy/_core/numeric.pyi index d23300752cd7..799ee7e17add 100644 --- a/numpy/_core/numeric.pyi +++ b/numpy/_core/numeric.pyi @@ -874,6 +874,8 @@ def array_equiv(a1: ArrayLike, a2: ArrayLike) -> bool: ... def astype( x: ndarray[_ShapeType, dtype[Any]], dtype: _DTypeLike[_SCT], + /, + *, copy: bool = ..., device: None | L["cpu"] = ..., ) -> ndarray[_ShapeType, dtype[_SCT]]: ... @@ -881,6 +883,8 @@ def astype( def astype( x: ndarray[_ShapeType, dtype[Any]], dtype: DTypeLike, + /, + *, copy: bool = ..., device: None | L["cpu"] = ..., ) -> ndarray[_ShapeType, dtype[Any]]: ... diff --git a/numpy/linalg/_linalg.pyi b/numpy/linalg/_linalg.pyi index d3ca3eb701b7..550cab44cf49 100644 --- a/numpy/linalg/_linalg.pyi +++ b/numpy/linalg/_linalg.pyi @@ -176,11 +176,11 @@ def matrix_power( ) -> NDArray[Any]: ... @overload -def cholesky(a: _ArrayLikeInt_co) -> NDArray[float64]: ... +def cholesky(a: _ArrayLikeInt_co, /, *, upper: bool = False) -> NDArray[float64]: ... @overload -def cholesky(a: _ArrayLikeFloat_co) -> NDArray[floating[Any]]: ... +def cholesky(a: _ArrayLikeFloat_co, /, *, upper: bool = False) -> NDArray[floating[Any]]: ... @overload -def cholesky(a: _ArrayLikeComplex_co) -> NDArray[complexfloating[Any, Any]]: ... +def cholesky(a: _ArrayLikeComplex_co, /, *, upper: bool = False) -> NDArray[complexfloating[Any, Any]]: ... @overload def outer(x1: _ArrayLikeUnknown, x2: _ArrayLikeUnknown) -> NDArray[Any]: ... @@ -373,12 +373,16 @@ def norm( @overload def matrix_norm( x: ArrayLike, + /, + *, ord: None | float | L["fro", "nuc"] = ..., keepdims: bool = ..., ) -> floating[Any]: ... @overload def matrix_norm( x: ArrayLike, + /, + *, ord: None | float | L["fro", "nuc"] = ..., keepdims: bool = ..., ) -> Any: ... @@ -386,6 +390,8 @@ def matrix_norm( @overload def vector_norm( x: ArrayLike, + /, + *, axis: None = ..., ord: None | float = ..., keepdims: bool = ..., @@ -393,6 +399,8 @@ def vector_norm( @overload def vector_norm( x: ArrayLike, + /, + *, axis: SupportsInt | SupportsIndex | tuple[int, ...] = ..., ord: None | float = ..., keepdims: bool = ..., @@ -407,11 +415,15 @@ def multi_dot( def diagonal( x: ArrayLike, # >= 2D array + /, + *, offset: SupportsIndex = ..., ) -> NDArray[Any]: ... def trace( x: ArrayLike, # >= 2D array + /, + *, offset: SupportsIndex = ..., dtype: DTypeLike = ..., ) -> Any: ... @@ -420,24 +432,32 @@ def trace( def cross( a: _ArrayLikeUInt_co, b: _ArrayLikeUInt_co, + /, + *, axis: int = ..., ) -> NDArray[unsignedinteger[Any]]: ... @overload def cross( a: _ArrayLikeInt_co, b: _ArrayLikeInt_co, + /, + *, axis: int = ..., ) -> NDArray[signedinteger[Any]]: ... @overload def cross( a: _ArrayLikeFloat_co, b: _ArrayLikeFloat_co, + /, + *, axis: int = ..., ) -> NDArray[floating[Any]]: ... @overload def cross( a: _ArrayLikeComplex_co, b: _ArrayLikeComplex_co, + /, + *, axis: int = ..., ) -> NDArray[complexfloating[Any, Any]]: ... From a1713351f152cbbe80ef3b886354af69f61d7049 Mon Sep 17 00:00:00 2001 From: Andrej730 Date: Sat, 15 Feb 2025 01:02:16 +0500 Subject: [PATCH 03/49] TYP: Fix mismatching np.cross and np.linalg.cross typing arguments names --- numpy/_core/numeric.pyi | 28 ++++++++++++++-------------- numpy/linalg/_linalg.pyi | 16 ++++++++-------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/numpy/_core/numeric.pyi b/numpy/_core/numeric.pyi index 799ee7e17add..d97ee6e4f649 100644 --- a/numpy/_core/numeric.pyi +++ b/numpy/_core/numeric.pyi @@ -699,8 +699,8 @@ def moveaxis( @overload def cross( - x1: _ArrayLikeUnknown, - x2: _ArrayLikeUnknown, + a: _ArrayLikeUnknown, + b: _ArrayLikeUnknown, axisa: int = ..., axisb: int = ..., axisc: int = ..., @@ -708,8 +708,8 @@ def cross( ) -> NDArray[Any]: ... @overload def cross( - x1: _ArrayLikeBool_co, - x2: _ArrayLikeBool_co, + a: _ArrayLikeBool_co, + b: _ArrayLikeBool_co, axisa: int = ..., axisb: int = ..., axisc: int = ..., @@ -717,8 +717,8 @@ def cross( ) -> NoReturn: ... @overload def cross( - x1: _ArrayLikeUInt_co, - x2: _ArrayLikeUInt_co, + a: _ArrayLikeUInt_co, + b: _ArrayLikeUInt_co, axisa: int = ..., axisb: int = ..., axisc: int = ..., @@ -726,8 +726,8 @@ def cross( ) -> NDArray[unsignedinteger[Any]]: ... @overload def cross( - x1: _ArrayLikeInt_co, - x2: _ArrayLikeInt_co, + a: _ArrayLikeInt_co, + b: _ArrayLikeInt_co, axisa: int = ..., axisb: int = ..., axisc: int = ..., @@ -735,8 +735,8 @@ def cross( ) -> NDArray[signedinteger[Any]]: ... @overload def cross( - x1: _ArrayLikeFloat_co, - x2: _ArrayLikeFloat_co, + a: _ArrayLikeFloat_co, + b: _ArrayLikeFloat_co, axisa: int = ..., axisb: int = ..., axisc: int = ..., @@ -744,8 +744,8 @@ def cross( ) -> NDArray[floating[Any]]: ... @overload def cross( - x1: _ArrayLikeComplex_co, - x2: _ArrayLikeComplex_co, + a: _ArrayLikeComplex_co, + b: _ArrayLikeComplex_co, axisa: int = ..., axisb: int = ..., axisc: int = ..., @@ -753,8 +753,8 @@ def cross( ) -> NDArray[complexfloating[Any, Any]]: ... @overload def cross( - x1: _ArrayLikeObject_co, - x2: _ArrayLikeObject_co, + a: _ArrayLikeObject_co, + b: _ArrayLikeObject_co, axisa: int = ..., axisb: int = ..., axisc: int = ..., diff --git a/numpy/linalg/_linalg.pyi b/numpy/linalg/_linalg.pyi index 550cab44cf49..8ac75a47f5f8 100644 --- a/numpy/linalg/_linalg.pyi +++ b/numpy/linalg/_linalg.pyi @@ -430,32 +430,32 @@ def trace( @overload def cross( - a: _ArrayLikeUInt_co, - b: _ArrayLikeUInt_co, + x1: _ArrayLikeUInt_co, + x2: _ArrayLikeUInt_co, /, *, axis: int = ..., ) -> NDArray[unsignedinteger[Any]]: ... @overload def cross( - a: _ArrayLikeInt_co, - b: _ArrayLikeInt_co, + x1: _ArrayLikeInt_co, + x2: _ArrayLikeInt_co, /, *, axis: int = ..., ) -> NDArray[signedinteger[Any]]: ... @overload def cross( - a: _ArrayLikeFloat_co, - b: _ArrayLikeFloat_co, + x1: _ArrayLikeFloat_co, + x2: _ArrayLikeFloat_co, /, *, axis: int = ..., ) -> NDArray[floating[Any]]: ... @overload def cross( - a: _ArrayLikeComplex_co, - b: _ArrayLikeComplex_co, + x1: _ArrayLikeComplex_co, + x2: _ArrayLikeComplex_co, /, *, axis: int = ..., From d8553b8588cf3adcd8948084f77fc6b7b83e67dc Mon Sep 17 00:00:00 2001 From: Abhishek Kumar <142383124+abhishek-iitmadras@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:38:11 +0530 Subject: [PATCH 04/49] CI: Update FreeBSD base image in `cirrus_arm.yml` (#28328) --- tools/ci/cirrus_arm.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ci/cirrus_arm.yml b/tools/ci/cirrus_arm.yml index 46fed5bbf0c4..180770451c44 100644 --- a/tools/ci/cirrus_arm.yml +++ b/tools/ci/cirrus_arm.yml @@ -67,7 +67,7 @@ freebsd_test_task: use_compute_credits: $CIRRUS_USER_COLLABORATOR == 'true' compute_engine_instance: image_project: freebsd-org-cloud-dev - image: family/freebsd-14-0 + image: family/freebsd-14-2 platform: freebsd cpu: 1 memory: 4G From 8937d9478c46af92ec6dc0dd71c6443aa7cc34ee Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Fri, 21 Feb 2025 16:55:32 +0100 Subject: [PATCH 05/49] MAINT: No need to check for check for FPEs in casts to/from object (#28358) * MAINT: No need to check for check for FPEs in casts to/from object Since these go via Python (in some form) and Python doesn't use FPEs we can be sure that we don't need to check for FPEs. Note that while it hides *almost always* spurious FPEs seen on some platforms, there could be certain chains or multiple cast situations where FPEs are checked for other reasons and the spurious FPE will show up. So it "somewhat": Closes gh-28351 * MAINT: Follow-up, got the wrong place (the other is OK). * DOC: Add a small comment as per review request I don't think it needs a comment that we can do this, but maybe it is nice to say that there was a reason for it. --- numpy/_core/src/multiarray/convert_datatype.c | 8 ++++++-- numpy/_core/src/multiarray/dtype_transfer.c | 7 ++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/numpy/_core/src/multiarray/convert_datatype.c b/numpy/_core/src/multiarray/convert_datatype.c index 00251af5bf68..158c9ed207b5 100644 --- a/numpy/_core/src/multiarray/convert_datatype.c +++ b/numpy/_core/src/multiarray/convert_datatype.c @@ -3517,7 +3517,9 @@ initialize_void_and_object_globals(void) { method->nin = 1; method->nout = 1; method->name = "object_to_any_cast"; - method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; + method->flags = (NPY_METH_SUPPORTS_UNALIGNED + | NPY_METH_REQUIRES_PYAPI + | NPY_METH_NO_FLOATINGPOINT_ERRORS); method->casting = NPY_UNSAFE_CASTING; method->resolve_descriptors = &object_to_any_resolve_descriptors; method->get_strided_loop = &object_to_any_get_loop; @@ -3532,7 +3534,9 @@ initialize_void_and_object_globals(void) { method->nin = 1; method->nout = 1; method->name = "any_to_object_cast"; - method->flags = NPY_METH_SUPPORTS_UNALIGNED | NPY_METH_REQUIRES_PYAPI; + method->flags = (NPY_METH_SUPPORTS_UNALIGNED + | NPY_METH_REQUIRES_PYAPI + | NPY_METH_NO_FLOATINGPOINT_ERRORS); method->casting = NPY_SAFE_CASTING; method->resolve_descriptors = &any_to_object_resolve_descriptors; method->get_strided_loop = &any_to_object_get_loop; diff --git a/numpy/_core/src/multiarray/dtype_transfer.c b/numpy/_core/src/multiarray/dtype_transfer.c index d7a5e80800b6..188a55a4b5f5 100644 --- a/numpy/_core/src/multiarray/dtype_transfer.c +++ b/numpy/_core/src/multiarray/dtype_transfer.c @@ -235,8 +235,8 @@ any_to_object_get_loop( NpyAuxData **out_transferdata, NPY_ARRAYMETHOD_FLAGS *flags) { - - *flags = NPY_METH_REQUIRES_PYAPI; /* No need for floating point errors */ + /* Python API doesn't use FPEs and this also attempts to hide spurious ones. */ + *flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_NO_FLOATINGPOINT_ERRORS; *out_loop = _strided_to_strided_any_to_object; *out_transferdata = PyMem_Malloc(sizeof(_any_to_object_auxdata)); @@ -342,7 +342,8 @@ object_to_any_get_loop( NpyAuxData **out_transferdata, NPY_ARRAYMETHOD_FLAGS *flags) { - *flags = NPY_METH_REQUIRES_PYAPI; + /* Python API doesn't use FPEs and this also attempts to hide spurious ones. */ + *flags = NPY_METH_REQUIRES_PYAPI | NPY_METH_NO_FLOATINGPOINT_ERRORS; /* NOTE: auxdata is only really necessary to flag `move_references` */ _object_to_any_auxdata *data = PyMem_Malloc(sizeof(*data)); From 363135470f2ea68950117cdf2469ac961430baa0 Mon Sep 17 00:00:00 2001 From: Giovanni Del Monte Date: Tue, 18 Feb 2025 15:48:48 +0100 Subject: [PATCH 06/49] BUG: numpy.loadtxt reads only 50000 lines when skip_rows >= max_rows (#28319) * fixed bug in function _read in numpy/lib/_npyio_impl.py, misnamed variable skiplines as skiprows; added test in numpy/lib/tests/test_loadtxt.py * fixed sintax in test_loadtxt.py * changed use of mkstemp with use of tmpdir provided by pytest * fixed bug in use of tmpdir in loadtxt test * Update numpy/lib/tests/test_loadtxt.py Co-authored-by: Sebastian Berg * Update file numpy/lib/tests/test_loadtxt.py * Update file numpy/lib/tests/test_loadtxt.py * Update numpy/lib/tests/test_loadtxt.py --------- Co-authored-by: Sebastian Berg --- numpy/lib/_npyio_impl.py | 2 +- numpy/lib/tests/test_loadtxt.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/numpy/lib/_npyio_impl.py b/numpy/lib/_npyio_impl.py index f0d1bb2b0c68..4dc3a4b9b7e2 100644 --- a/numpy/lib/_npyio_impl.py +++ b/numpy/lib/_npyio_impl.py @@ -1084,7 +1084,7 @@ def _read(fname, *, delimiter=',', comment='#', quote='"', # be adapted (in principle the concatenate could cast). chunks.append(next_arr.astype(read_dtype_via_object_chunks)) - skiprows = 0 # Only have to skip for first chunk + skiplines = 0 # Only have to skip for first chunk if max_rows >= 0: max_rows -= chunk_size if len(next_arr) < chunk_size: diff --git a/numpy/lib/tests/test_loadtxt.py b/numpy/lib/tests/test_loadtxt.py index 116cd1608da3..60717be3bd9a 100644 --- a/numpy/lib/tests/test_loadtxt.py +++ b/numpy/lib/tests/test_loadtxt.py @@ -1073,3 +1073,28 @@ def test_maxrows_exceeding_chunksize(nmax): res = np.loadtxt(fname, dtype=str, delimiter=" ", max_rows=nmax) os.remove(fname) assert len(res) == nmax + +@pytest.mark.parametrize("nskip", (0, 10000, 12345, 50000, 67891, 100000)) +def test_skiprow_exceeding_maxrows_exceeding_chunksize(tmpdir, nskip): + # tries to read a file in chunks by skipping a variable amount of lines, + # less, equal, greater than max_rows + file_length = 110000 + data = "\n".join(f"{i} a 0.5 1" for i in range(1, file_length + 1)) + expected_length = min(60000, file_length - nskip) + expected = np.arange(nskip + 1, nskip + 1 + expected_length).astype(str) + + # file-like path + txt = StringIO(data) + res = np.loadtxt(txt, dtype='str', delimiter=" ", skiprows=nskip, max_rows=60000) + assert len(res) == expected_length + # are the right lines read in res? + assert_array_equal(expected, res[:, 0]) + + # file-obj path + tmp_file = tmpdir / "test_data.txt" + tmp_file.write(data) + fname = str(tmp_file) + res = np.loadtxt(fname, dtype='str', delimiter=" ", skiprows=nskip, max_rows=60000) + assert len(res) == expected_length + # are the right lines read in res? + assert_array_equal(expected, res[:, 0]) From 4615d543b492b10c8935cf6ef97d3bc798fbb53a Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 20 Feb 2025 14:55:52 +0100 Subject: [PATCH 07/49] [ENH] add multi-threading test for np.nonzero --- numpy/_core/tests/test_multithreading.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index 133268d276ee..de283df6a44b 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -271,3 +271,21 @@ def closure(b): # Reducing the number of threads means the test doesn't trigger the # bug. Better to skip on some platforms than add a useless test. pytest.skip("Couldn't spawn enough threads to run the test") + + +def test_nonzero(): + # np.nonzero uses np.count_nonzero to determine the size of the output array + # In a second pass the indices of the non-zero elements are determined, but they can have changed + + for dtype in [bool, int, float]: + x= np.random.randint(4, size=10_000).astype(dtype) + + def func(seed): + x[::2] = np.random.randint(2) + try: + r = np.nonzero(x) + assert r[0].min() >= 0 + except RuntimeError as ex: + assert 'number of non-zero array elements changed during function execution' in str(ex) + + run_threaded(func, max_workers=10, pass_count=True, outer_iterations=10) From f5069056a336572611794fe1dd6e748f1d35c528 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 20 Feb 2025 16:08:21 +0100 Subject: [PATCH 08/49] lint --- numpy/_core/tests/test_multithreading.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index de283df6a44b..e49996ab887f 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -278,7 +278,7 @@ def test_nonzero(): # In a second pass the indices of the non-zero elements are determined, but they can have changed for dtype in [bool, int, float]: - x= np.random.randint(4, size=10_000).astype(dtype) + x = np.random.randint(4, size=10_000).astype(dtype) def func(seed): x[::2] = np.random.randint(2) From 1b73bdddb3b7ca31702f79502b7212e7467162c9 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 20 Feb 2025 16:12:36 +0100 Subject: [PATCH 09/49] split test --- numpy/_core/tests/test_multithreading.py | 56 ++++++++++++++++++------ 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index e49996ab887f..013690c8d101 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -272,20 +272,48 @@ def closure(b): # bug. Better to skip on some platforms than add a useless test. pytest.skip("Couldn't spawn enough threads to run the test") +def test_nonzero_bool(): + # np.nonzero uses np.count_nonzero to determine the size of the output array + # In a second pass the indices of the non-zero elements are determined, but they can have changed + x = np.random.randint(4, size=10_000).astype(bool) + + def func(seed): + x[::2] = np.random.randint(2) + try: + r = np.nonzero(x) + assert r[0].min() >= 0 + except RuntimeError as ex: + assert 'number of non-zero array elements changed during function execution' in str(ex) -def test_nonzero(): + run_threaded(func, max_workers=10, pass_count=True, outer_iterations=10) + +def test_nonzero_int(): # np.nonzero uses np.count_nonzero to determine the size of the output array # In a second pass the indices of the non-zero elements are determined, but they can have changed - - for dtype in [bool, int, float]: - x = np.random.randint(4, size=10_000).astype(dtype) - - def func(seed): - x[::2] = np.random.randint(2) - try: - r = np.nonzero(x) - assert r[0].min() >= 0 - except RuntimeError as ex: - assert 'number of non-zero array elements changed during function execution' in str(ex) - - run_threaded(func, max_workers=10, pass_count=True, outer_iterations=10) + x = np.random.randint(4, size=10_000).astype(int) + + def func(seed): + x[::2] = np.random.randint(2) + try: + r = np.nonzero(x) + assert r[0].min() >= 0 + except RuntimeError as ex: + assert 'number of non-zero array elements changed during function execution' in str(ex) + + run_threaded(func, max_workers=10, pass_count=True, outer_iterations=10) + +def test_nonzero_float(): + # np.nonzero uses np.count_nonzero to determine the size of the output array + # In a second pass the indices of the non-zero elements are determined, but they can have changed + x = np.random.randint(4, size=10_000).astype(float) + + def func(seed): + x[::2] = np.random.randint(2) + try: + r = np.nonzero(x) + assert r[0].min() >= 0 + except RuntimeError as ex: + assert 'number of non-zero array elements changed during function execution' in str(ex) + + run_threaded(func, max_workers=10, pass_count=True, outer_iterations=10) + From ec14eff2c4b817db323c4275be71489b1db4bb35 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 20 Feb 2025 17:06:20 +0100 Subject: [PATCH 10/49] fix buffer overflow --- numpy/_core/src/multiarray/item_selection.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/numpy/_core/src/multiarray/item_selection.c b/numpy/_core/src/multiarray/item_selection.c index fbcc0f7b162c..f71af3b0ed03 100644 --- a/numpy/_core/src/multiarray/item_selection.c +++ b/numpy/_core/src/multiarray/item_selection.c @@ -2896,7 +2896,8 @@ PyArray_Nonzero(PyArrayObject *self) if (((double)nonzero_count / count) <= 0.1) { npy_intp subsize; npy_intp j = 0; - while (1) { + npy_intp * multi_index_end = multi_index + nonzero_count; + while (multi_index < multi_index_end) { npy_memchr(data + j * stride, 0, stride, count - j, &subsize, 1); j += subsize; From df22d5dced832e8893d1dd200ef0dedf761d89b9 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 20 Feb 2025 19:35:21 +0100 Subject: [PATCH 11/49] fix one more buffer overflow --- numpy/_core/src/multiarray/item_selection.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/numpy/_core/src/multiarray/item_selection.c b/numpy/_core/src/multiarray/item_selection.c index f71af3b0ed03..fcd78a28b365 100644 --- a/numpy/_core/src/multiarray/item_selection.c +++ b/numpy/_core/src/multiarray/item_selection.c @@ -2916,7 +2916,7 @@ PyArray_Nonzero(PyArrayObject *self) npy_intp j = 0; /* Manually unroll for GCC and maybe other compilers */ - while (multi_index + 4 < multi_index_end) { + while (multi_index + 4 < multi_index_end && (j < count - 4) ) { *multi_index = j; multi_index += data[0] != 0; *multi_index = j + 1; @@ -2929,7 +2929,7 @@ PyArray_Nonzero(PyArrayObject *self) j += 4; } - while (multi_index < multi_index_end) { + while (multi_index < multi_index_end && (j < count) ) { *multi_index = j; multi_index += *data != 0; data += stride; From de288cb24e1f266a7f08c81b832117e49e70b81c Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 20 Feb 2025 19:51:16 +0100 Subject: [PATCH 12/49] fix test --- numpy/_core/tests/test_multithreading.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index 013690c8d101..57f940804c70 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -281,7 +281,6 @@ def func(seed): x[::2] = np.random.randint(2) try: r = np.nonzero(x) - assert r[0].min() >= 0 except RuntimeError as ex: assert 'number of non-zero array elements changed during function execution' in str(ex) @@ -296,7 +295,6 @@ def func(seed): x[::2] = np.random.randint(2) try: r = np.nonzero(x) - assert r[0].min() >= 0 except RuntimeError as ex: assert 'number of non-zero array elements changed during function execution' in str(ex) @@ -311,7 +309,6 @@ def func(seed): x[::2] = np.random.randint(2) try: r = np.nonzero(x) - assert r[0].min() >= 0 except RuntimeError as ex: assert 'number of non-zero array elements changed during function execution' in str(ex) From 96cc64d997c0da365fe3960c149c8592fd815a0c Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Thu, 20 Feb 2025 20:32:13 +0100 Subject: [PATCH 13/49] parameterize tests --- numpy/_core/tests/test_multithreading.py | 37 ++++-------------------- 1 file changed, 5 insertions(+), 32 deletions(-) diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index 57f940804c70..ee8e3f86eaec 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -272,45 +272,18 @@ def closure(b): # bug. Better to skip on some platforms than add a useless test. pytest.skip("Couldn't spawn enough threads to run the test") -def test_nonzero_bool(): +@pytest.mark.parametrize("dtype", [bool, int, float]) +def test_nonzero_bool(dtype): # np.nonzero uses np.count_nonzero to determine the size of the output array # In a second pass the indices of the non-zero elements are determined, but they can have changed - x = np.random.randint(4, size=10_000).astype(bool) + x = np.random.randint(4, size=10_000).astype(dtype) def func(seed): x[::2] = np.random.randint(2) try: - r = np.nonzero(x) + _ = np.nonzero(x) except RuntimeError as ex: assert 'number of non-zero array elements changed during function execution' in str(ex) - run_threaded(func, max_workers=10, pass_count=True, outer_iterations=10) - -def test_nonzero_int(): - # np.nonzero uses np.count_nonzero to determine the size of the output array - # In a second pass the indices of the non-zero elements are determined, but they can have changed - x = np.random.randint(4, size=10_000).astype(int) - - def func(seed): - x[::2] = np.random.randint(2) - try: - r = np.nonzero(x) - except RuntimeError as ex: - assert 'number of non-zero array elements changed during function execution' in str(ex) - - run_threaded(func, max_workers=10, pass_count=True, outer_iterations=10) - -def test_nonzero_float(): - # np.nonzero uses np.count_nonzero to determine the size of the output array - # In a second pass the indices of the non-zero elements are determined, but they can have changed - x = np.random.randint(4, size=10_000).astype(float) - - def func(seed): - x[::2] = np.random.randint(2) - try: - r = np.nonzero(x) - except RuntimeError as ex: - assert 'number of non-zero array elements changed during function execution' in str(ex) - - run_threaded(func, max_workers=10, pass_count=True, outer_iterations=10) + run_threaded(func, max_workers=10, pass_count=True, outer_iterations=50) From fa697ad64717749bcaad2df4edc133d19dfd1df5 Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Fri, 21 Feb 2025 10:18:26 -0700 Subject: [PATCH 14/49] MAINT: turn off halt_on_error on sanitizer CI --- .github/workflows/compiler_sanitizers.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/compiler_sanitizers.yml b/.github/workflows/compiler_sanitizers.yml index 9477e0be1bd1..78a923ff98e5 100644 --- a/.github/workflows/compiler_sanitizers.yml +++ b/.github/workflows/compiler_sanitizers.yml @@ -68,7 +68,7 @@ jobs: - name: Test run: | # pass -s to pytest to see ASAN errors and warnings, otherwise pytest captures them - ASAN_OPTIONS=detect_leaks=0:symbolize=1:strict_init_order=true:allocator_may_return_null=1:halt_on_error=1 \ + ASAN_OPTIONS=detect_leaks=0:symbolize=1:strict_init_order=true:allocator_may_return_null=1 \ python -m spin test -- -v -s --timeout=600 --durations=10 clang_TSAN: @@ -121,7 +121,7 @@ jobs: - name: Test run: | # These tests are slow, so only run tests in files that do "import threading" to make them count - TSAN_OPTIONS=allocator_may_return_null=1:halt_on_error=1 \ + TSAN_OPTIONS=allocator_may_return_null=1 \ python -m spin test \ `find numpy -name "test*.py" | xargs grep -l "import threading" | tr '\n' ' '` \ -- -v -s --timeout=600 --durations=10 From 4c9da471fb3da558d7f51d5e8a43c4c5da191ff8 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 21 Feb 2025 21:05:37 +0100 Subject: [PATCH 15/49] attampt to add TSAN suppressions --- .github/workflows/compiler_sanitizers.yml | 2 +- tools/ci/tsan_suppressions.txt | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tools/ci/tsan_suppressions.txt diff --git a/.github/workflows/compiler_sanitizers.yml b/.github/workflows/compiler_sanitizers.yml index 78a923ff98e5..7a5745228b7d 100644 --- a/.github/workflows/compiler_sanitizers.yml +++ b/.github/workflows/compiler_sanitizers.yml @@ -121,7 +121,7 @@ jobs: - name: Test run: | # These tests are slow, so only run tests in files that do "import threading" to make them count - TSAN_OPTIONS=allocator_may_return_null=1 \ + TSAN_OPTIONS="allocator_may_return_null=1:halt_on_error=1:suppressions=tools\ci\tsan_suppressions.txt" \ python -m spin test \ `find numpy -name "test*.py" | xargs grep -l "import threading" | tr '\n' ' '` \ -- -v -s --timeout=600 --durations=10 diff --git a/tools/ci/tsan_suppressions.txt b/tools/ci/tsan_suppressions.txt new file mode 100644 index 000000000000..3dc9168f6b5d --- /dev/null +++ b/tools/ci/tsan_suppressions.txt @@ -0,0 +1,9 @@ +# This file contains suppressions for the TSAN tool +# +# Reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions + + +# These warnings trigger directly in a CPython function. + +# For np.nonzero, see gh-28361 +race:lowlevel_strided_loops.c.src From 2c9afc69d50a01d1d8ab8829601aedea68b54232 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Fri, 21 Feb 2025 22:37:31 +0100 Subject: [PATCH 16/49] try to suppress --- tools/ci/tsan_suppressions.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/ci/tsan_suppressions.txt b/tools/ci/tsan_suppressions.txt index 3dc9168f6b5d..b74671231e27 100644 --- a/tools/ci/tsan_suppressions.txt +++ b/tools/ci/tsan_suppressions.txt @@ -7,3 +7,8 @@ # For np.nonzero, see gh-28361 race:lowlevel_strided_loops.c.src +race:count_nonzero_int +race:count_nonzero_bool +race:count_nonzero_float + + From ef3c7d08a64fd5be559859818f319739c2144c52 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 22 Feb 2025 14:07:49 +0100 Subject: [PATCH 17/49] review comments --- .github/workflows/compiler_sanitizers.yml | 2 +- numpy/_core/src/multiarray/item_selection.c | 3 +-- numpy/_core/tests/test_multithreading.py | 9 +++++++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/compiler_sanitizers.yml b/.github/workflows/compiler_sanitizers.yml index 7a5745228b7d..cc9659696b2d 100644 --- a/.github/workflows/compiler_sanitizers.yml +++ b/.github/workflows/compiler_sanitizers.yml @@ -121,7 +121,7 @@ jobs: - name: Test run: | # These tests are slow, so only run tests in files that do "import threading" to make them count - TSAN_OPTIONS="allocator_may_return_null=1:halt_on_error=1:suppressions=tools\ci\tsan_suppressions.txt" \ + TSAN_OPTIONS="allocator_may_return_null=1:suppressions=/Users/runner/work/numpy/numpy/tools/ci/tsan_suppressions.txt" \ python -m spin test \ `find numpy -name "test*.py" | xargs grep -l "import threading" | tr '\n' ' '` \ -- -v -s --timeout=600 --durations=10 diff --git a/numpy/_core/src/multiarray/item_selection.c b/numpy/_core/src/multiarray/item_selection.c index fcd78a28b365..6e9b03a41343 100644 --- a/numpy/_core/src/multiarray/item_selection.c +++ b/numpy/_core/src/multiarray/item_selection.c @@ -2893,10 +2893,10 @@ PyArray_Nonzero(PyArrayObject *self) * the fast bool count is followed by this sparse path is faster * than combining the two loops, even for larger arrays */ + npy_intp * multi_index_end = multi_index + nonzero_count; if (((double)nonzero_count / count) <= 0.1) { npy_intp subsize; npy_intp j = 0; - npy_intp * multi_index_end = multi_index + nonzero_count; while (multi_index < multi_index_end) { npy_memchr(data + j * stride, 0, stride, count - j, &subsize, 1); @@ -2912,7 +2912,6 @@ PyArray_Nonzero(PyArrayObject *self) * stalls that are very expensive on most modern processors. */ else { - npy_intp *multi_index_end = multi_index + nonzero_count; npy_intp j = 0; /* Manually unroll for GCC and maybe other compilers */ diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index ee8e3f86eaec..677245ff902a 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -274,16 +274,21 @@ def closure(b): @pytest.mark.parametrize("dtype", [bool, int, float]) def test_nonzero_bool(dtype): + # See: gh-28361 + # # np.nonzero uses np.count_nonzero to determine the size of the output array # In a second pass the indices of the non-zero elements are determined, but they can have changed + # + # This test triggers a data race which is suppressed in the TSAN CI. The test is to ensure + # np.nonzero does not generate a segmentation fault x = np.random.randint(4, size=10_000).astype(dtype) - def func(seed): + def func(): x[::2] = np.random.randint(2) try: _ = np.nonzero(x) except RuntimeError as ex: assert 'number of non-zero array elements changed during function execution' in str(ex) - run_threaded(func, max_workers=10, pass_count=True, outer_iterations=50) + run_threaded(func, max_workers=10, pass_count=False, outer_iterations=50) From 336a2c3012d6fb8497f522339edb99dc5051a279 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 22 Feb 2025 15:06:04 +0100 Subject: [PATCH 18/49] more suppressions --- tools/ci/tsan_suppressions.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/ci/tsan_suppressions.txt b/tools/ci/tsan_suppressions.txt index b74671231e27..6360cf716d00 100644 --- a/tools/ci/tsan_suppressions.txt +++ b/tools/ci/tsan_suppressions.txt @@ -6,9 +6,10 @@ # These warnings trigger directly in a CPython function. # For np.nonzero, see gh-28361 -race:lowlevel_strided_loops.c.src +#race:lowlevel_strided_loops.c.src +race:PyArray_Nonzero race:count_nonzero_int race:count_nonzero_bool race:count_nonzero_float - +race:DOUBLE_nonzero From 823b3f01bbc29e70304e80c69f3db86700533d6c Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 22 Feb 2025 21:10:51 +0100 Subject: [PATCH 19/49] avoid race in writing --- .github/workflows/compiler_sanitizers.yml | 3 +++ numpy/_core/tests/test_multithreading.py | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/.github/workflows/compiler_sanitizers.yml b/.github/workflows/compiler_sanitizers.yml index cc9659696b2d..7f5329da89a2 100644 --- a/.github/workflows/compiler_sanitizers.yml +++ b/.github/workflows/compiler_sanitizers.yml @@ -120,6 +120,9 @@ jobs: python -m spin build -j2 -- -Db_sanitize=thread - name: Test run: | + # try to figure out root folder for source + echo $HOME + set # These tests are slow, so only run tests in files that do "import threading" to make them count TSAN_OPTIONS="allocator_may_return_null=1:suppressions=/Users/runner/work/numpy/numpy/tools/ci/tsan_suppressions.txt" \ python -m spin test \ diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index 677245ff902a..b3bbbe403577 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -273,7 +273,7 @@ def closure(b): pytest.skip("Couldn't spawn enough threads to run the test") @pytest.mark.parametrize("dtype", [bool, int, float]) -def test_nonzero_bool(dtype): +def test_nonzero(dtype): # See: gh-28361 # # np.nonzero uses np.count_nonzero to determine the size of the output array @@ -283,12 +283,15 @@ def test_nonzero_bool(dtype): # np.nonzero does not generate a segmentation fault x = np.random.randint(4, size=10_000).astype(dtype) - def func(): - x[::2] = np.random.randint(2) - try: - _ = np.nonzero(x) - except RuntimeError as ex: - assert 'number of non-zero array elements changed during function execution' in str(ex) + def func(index): + for _ in range(10): + if index == 0: + x[::2] = np.random.randint(2) + else: + try: + _ = np.nonzero(x) + except RuntimeError as ex: + assert 'number of non-zero array elements changed during function execution' in str(ex) - run_threaded(func, max_workers=10, pass_count=False, outer_iterations=50) + run_threaded(func, max_workers=10, pass_count=True, outer_iterations=50) From 448da118337a6af507dd66a0e423b48f2327ab82 Mon Sep 17 00:00:00 2001 From: Pieter Eendebak Date: Sat, 22 Feb 2025 21:35:00 +0100 Subject: [PATCH 20/49] cleanup --- .github/workflows/compiler_sanitizers.yml | 5 +---- tools/ci/tsan_suppressions.txt | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/compiler_sanitizers.yml b/.github/workflows/compiler_sanitizers.yml index 7f5329da89a2..09e71051556d 100644 --- a/.github/workflows/compiler_sanitizers.yml +++ b/.github/workflows/compiler_sanitizers.yml @@ -120,11 +120,8 @@ jobs: python -m spin build -j2 -- -Db_sanitize=thread - name: Test run: | - # try to figure out root folder for source - echo $HOME - set # These tests are slow, so only run tests in files that do "import threading" to make them count - TSAN_OPTIONS="allocator_may_return_null=1:suppressions=/Users/runner/work/numpy/numpy/tools/ci/tsan_suppressions.txt" \ + TSAN_OPTIONS="allocator_may_return_null=1:suppressions=$GITHUB_WORKSPACE/tools/ci/tsan_suppressions.txt" \ python -m spin test \ `find numpy -name "test*.py" | xargs grep -l "import threading" | tr '\n' ' '` \ -- -v -s --timeout=600 --durations=10 diff --git a/tools/ci/tsan_suppressions.txt b/tools/ci/tsan_suppressions.txt index 6360cf716d00..0745debd8e5f 100644 --- a/tools/ci/tsan_suppressions.txt +++ b/tools/ci/tsan_suppressions.txt @@ -2,11 +2,7 @@ # # Reference: https://github.com/google/sanitizers/wiki/ThreadSanitizerSuppressions - -# These warnings trigger directly in a CPython function. - # For np.nonzero, see gh-28361 -#race:lowlevel_strided_loops.c.src race:PyArray_Nonzero race:count_nonzero_int race:count_nonzero_bool From 1efec00b4db1ff49b6ae16f99abd5c237d1c200b Mon Sep 17 00:00:00 2001 From: Tyler Reddy Date: Mon, 3 Mar 2025 07:31:30 -0700 Subject: [PATCH 21/49] BUG: safer bincount casting (#28355) * BUG: safer bincount casting * Fixes #28354 * Force usage of `npy_intp` type in `np.bincount()` and avoid unsafe casting errors with i.e., `npy_uint64`. This is similar to our behavior with indexing. * MAINT, BUG: PR 28355 revisions * `arr_bincount()` now only uses unsafe casting for integer input types, and the number of casting operations has been reduced for the code path used in above PR. * a test for non-crashing behavior with non-contiguous `bincount()` input has been added. * MAINT, BUG: PR 28355 revisions * Based on reviewer feedback, narrow the scope of the `flags` variable in `arr_bincount()`. * Based on reviewer feedback, add an array-like test for the `uint64` casting issue, which indeed fails before and passes after adding a similar shim to the array-like code path in `arr_bincount()`. * Based on reviewer feedback, switch the patching from `PyArray_Size` to `PyArray_Check` in a few places. * Update numpy/_core/src/multiarray/compiled_base.c --------- Co-authored-by: Sebastian Berg --- numpy/_core/src/multiarray/compiled_base.c | 16 +++++++++++++--- numpy/lib/tests/test_function_base.py | 21 +++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/numpy/_core/src/multiarray/compiled_base.c b/numpy/_core/src/multiarray/compiled_base.c index 48524aff4dac..fca733597a2d 100644 --- a/numpy/_core/src/multiarray/compiled_base.c +++ b/numpy/_core/src/multiarray/compiled_base.c @@ -150,8 +150,12 @@ arr_bincount(PyObject *NPY_UNUSED(self), PyObject *const *args, } if (PyArray_SIZE(tmp1) > 0) { /* The input is not empty, so convert it to NPY_INTP. */ - lst = (PyArrayObject *)PyArray_ContiguousFromAny((PyObject *)tmp1, - NPY_INTP, 1, 1); + int flags = NPY_ARRAY_WRITEABLE | NPY_ARRAY_ALIGNED | NPY_ARRAY_C_CONTIGUOUS; + if (PyArray_ISINTEGER(tmp1)) { + flags = flags | NPY_ARRAY_FORCECAST; + } + PyArray_Descr* local_dtype = PyArray_DescrFromType(NPY_INTP); + lst = (PyArrayObject *)PyArray_FromAny((PyObject *)tmp1, local_dtype, 1, 1, flags, NULL); Py_DECREF(tmp1); if (lst == NULL) { /* Failed converting to NPY_INTP. */ @@ -177,7 +181,13 @@ arr_bincount(PyObject *NPY_UNUSED(self), PyObject *const *args, } if (lst == NULL) { - lst = (PyArrayObject *)PyArray_ContiguousFromAny(list, NPY_INTP, 1, 1); + int flags = NPY_ARRAY_WRITEABLE | NPY_ARRAY_ALIGNED | NPY_ARRAY_C_CONTIGUOUS; + if (PyArray_Check((PyObject *)list) && + PyArray_ISINTEGER((PyArrayObject *)list)) { + flags = flags | NPY_ARRAY_FORCECAST; + } + PyArray_Descr* local_dtype = PyArray_DescrFromType(NPY_INTP); + lst = (PyArrayObject *)PyArray_FromAny(list, local_dtype, 1, 1, flags, NULL); if (lst == NULL) { goto fail; } diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index ed59a4a86181..c97ef92a7889 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -2925,6 +2925,27 @@ def test_error_not_1d(self, vals): with assert_raises(ValueError): np.bincount(vals) + @pytest.mark.parametrize("dt", np.typecodes["AllInteger"]) + def test_gh_28354(self, dt): + a = np.array([0, 1, 1, 3, 2, 1, 7], dtype=dt) + actual = np.bincount(a) + expected = [1, 3, 1, 1, 0, 0, 0, 1] + assert_array_equal(actual, expected) + + def test_contiguous_handling(self): + # check for absence of hard crash + np.bincount(np.arange(10000)[::2]) + + def test_gh_28354_array_like(self): + class A: + def __array__(self): + return np.array([0, 1, 1, 3, 2, 1, 7], dtype=np.uint64) + + a = A() + actual = np.bincount(a) + expected = [1, 3, 1, 1, 0, 0, 0, 1] + assert_array_equal(actual, expected) + class TestInterp: From ac7e1a11f0be590753f1c36077bd142d5f74c496 Mon Sep 17 00:00:00 2001 From: Jonathan Albrecht Date: Fri, 14 Feb 2025 15:50:16 -0500 Subject: [PATCH 22/49] BUG: Fix building on s390x with clang clang on s390x did not have implementations of vector logical operators such as vec_and, vec_or and vec_xor in vecintrin.h until __VEC__ == 10305 which caused compile errors. Add implementations to allow the build to complete. Currently, clang >= 19 is required for all tests to pass because that is the minimum version supported by highway on s390x with clang. --- numpy/_core/src/common/simd/vec/operators.h | 25 ++++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/numpy/_core/src/common/simd/vec/operators.h b/numpy/_core/src/common/simd/vec/operators.h index 50dac20f6d7d..3a402689d02f 100644 --- a/numpy/_core/src/common/simd/vec/operators.h +++ b/numpy/_core/src/common/simd/vec/operators.h @@ -44,6 +44,10 @@ /*************************** * Logical ***************************/ +#define NPYV_IMPL_VEC_BIN_WRAP(INTRIN, SFX) \ + NPY_FINLINE npyv_##SFX npyv_##INTRIN##_##SFX(npyv_##SFX a, npyv_##SFX b) \ + { return vec_##INTRIN(a, b); } + #define NPYV_IMPL_VEC_BIN_CAST(INTRIN, SFX, CAST) \ NPY_FINLINE npyv_##SFX npyv_##INTRIN##_##SFX(npyv_##SFX a, npyv_##SFX b) \ { return (npyv_##SFX)vec_##INTRIN((CAST)a, (CAST)b); } @@ -54,6 +58,15 @@ #else #define NPYV_IMPL_VEC_BIN_B64(INTRIN) NPYV_IMPL_VEC_BIN_CAST(INTRIN, b64, npyv_b64) #endif + +// Up to clang __VEC__ 10305 logical intrinsics do not support f32 or f64 +#if defined(NPY_HAVE_VX) && defined(__clang__) && __VEC__ < 10305 + #define NPYV_IMPL_VEC_BIN_F32(INTRIN) NPYV_IMPL_VEC_BIN_CAST(INTRIN, f32, npyv_u32) + #define NPYV_IMPL_VEC_BIN_F64(INTRIN) NPYV_IMPL_VEC_BIN_CAST(INTRIN, f64, npyv_u64) +#else + #define NPYV_IMPL_VEC_BIN_F32(INTRIN) NPYV_IMPL_VEC_BIN_WRAP(INTRIN, f32) + #define NPYV_IMPL_VEC_BIN_F64(INTRIN) NPYV_IMPL_VEC_BIN_WRAP(INTRIN, f64) +#endif // AND #define npyv_and_u8 vec_and #define npyv_and_s8 vec_and @@ -64,9 +77,9 @@ #define npyv_and_u64 vec_and #define npyv_and_s64 vec_and #if NPY_SIMD_F32 - #define npyv_and_f32 vec_and + NPYV_IMPL_VEC_BIN_F32(and) #endif -#define npyv_and_f64 vec_and +NPYV_IMPL_VEC_BIN_F64(and) #define npyv_and_b8 vec_and #define npyv_and_b16 vec_and #define npyv_and_b32 vec_and @@ -82,9 +95,9 @@ NPYV_IMPL_VEC_BIN_B64(and) #define npyv_or_u64 vec_or #define npyv_or_s64 vec_or #if NPY_SIMD_F32 - #define npyv_or_f32 vec_or + NPYV_IMPL_VEC_BIN_F32(or) #endif -#define npyv_or_f64 vec_or +NPYV_IMPL_VEC_BIN_F64(or) #define npyv_or_b8 vec_or #define npyv_or_b16 vec_or #define npyv_or_b32 vec_or @@ -100,9 +113,9 @@ NPYV_IMPL_VEC_BIN_B64(or) #define npyv_xor_u64 vec_xor #define npyv_xor_s64 vec_xor #if NPY_SIMD_F32 - #define npyv_xor_f32 vec_xor + NPYV_IMPL_VEC_BIN_F32(xor) #endif -#define npyv_xor_f64 vec_xor +NPYV_IMPL_VEC_BIN_F64(xor) #define npyv_xor_b8 vec_xor #define npyv_xor_b16 vec_xor #define npyv_xor_b32 vec_xor From 8f78bcfe7d8c0e3253ffc7291acd1c6295c44320 Mon Sep 17 00:00:00 2001 From: mayeut Date: Sun, 2 Mar 2025 08:20:36 +0100 Subject: [PATCH 23/49] CI: use QEMU 9.2.2 for Linux Qemu tests Following a kernel update on GHA runners, QEMU tests are failing randomly. Update the version of QEMU used in order to fix the random segfauls. --- .github/workflows/linux_qemu.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/linux_qemu.yml b/.github/workflows/linux_qemu.yml index d773152bb1bb..5ac7cea857a7 100644 --- a/.github/workflows/linux_qemu.yml +++ b/.github/workflows/linux_qemu.yml @@ -14,6 +14,7 @@ on: branches: - main - maintenance/** + workflow_dispatch: defaults: run: @@ -28,8 +29,9 @@ permissions: jobs: linux_qemu: - # To enable this workflow on a fork, comment out: - if: github.repository == 'numpy/numpy' + # Only workflow_dispatch is enabled on forks. + # To enable this job and subsequent jobs on a fork for other events, comment out: + if: github.repository == 'numpy/numpy' || github.event_name == 'workflow_dispatch' runs-on: ubuntu-22.04 continue-on-error: true strategy: @@ -107,7 +109,7 @@ jobs: - name: Initialize binfmt_misc for qemu-user-static run: | - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes + docker run --rm --privileged tonistiigi/binfmt:qemu-v9.2.2-52 --install all - name: Install GCC cross-compilers run: | @@ -176,4 +178,3 @@ jobs: cd /numpy && spin test -- -k \"${RUNTIME_TEST_FILTER}\" '" - From a73839bdfa953c013e6d2781153ad0dfcdd0202f Mon Sep 17 00:00:00 2001 From: Andrew Nelson Date: Sun, 2 Mar 2025 19:48:44 +1100 Subject: [PATCH 24/49] Update .github/workflows/linux_qemu.yml --- .github/workflows/linux_qemu.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/linux_qemu.yml b/.github/workflows/linux_qemu.yml index 5ac7cea857a7..dd0b5778c2c7 100644 --- a/.github/workflows/linux_qemu.yml +++ b/.github/workflows/linux_qemu.yml @@ -109,6 +109,7 @@ jobs: - name: Initialize binfmt_misc for qemu-user-static run: | + # see https://hub.docker.com/r/tonistiigi/binfmt for available versions docker run --rm --privileged tonistiigi/binfmt:qemu-v9.2.2-52 --install all - name: Install GCC cross-compilers From eb85757c5005fff1fca883e52252623d1a364509 Mon Sep 17 00:00:00 2001 From: Andrew Nelson Date: Sun, 2 Mar 2025 19:49:15 +1100 Subject: [PATCH 25/49] Update linux_qemu.yml --- .github/workflows/linux_qemu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux_qemu.yml b/.github/workflows/linux_qemu.yml index dd0b5778c2c7..15681f4c476f 100644 --- a/.github/workflows/linux_qemu.yml +++ b/.github/workflows/linux_qemu.yml @@ -109,7 +109,7 @@ jobs: - name: Initialize binfmt_misc for qemu-user-static run: | - # see https://hub.docker.com/r/tonistiigi/binfmt for available versions + # see https://hub.docker.com/r/tonistiigi/binfmt for available versions docker run --rm --privileged tonistiigi/binfmt:qemu-v9.2.2-52 --install all - name: Install GCC cross-compilers From 975443d05bb98d46267ae63d53e95e4bbb0e006b Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Mon, 3 Mar 2025 11:53:31 -0700 Subject: [PATCH 26/49] TST: add new IS_64BIT constant for testing --- numpy/_core/tests/test_array_coercion.py | 6 +++--- numpy/_core/tests/test_multiarray.py | 4 ++-- numpy/_core/tests/test_regression.py | 5 +++-- numpy/f2py/tests/test_return_real.py | 5 ++--- numpy/f2py/tests/test_semicolon_split.py | 9 ++++----- numpy/lib/tests/test_format.py | 5 ++--- numpy/testing/_private/utils.py | 3 ++- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/numpy/_core/tests/test_array_coercion.py b/numpy/_core/tests/test_array_coercion.py index c7ceb92650c9..55c5005149c1 100644 --- a/numpy/_core/tests/test_array_coercion.py +++ b/numpy/_core/tests/test_array_coercion.py @@ -14,7 +14,8 @@ from numpy._core._rational_tests import rational from numpy.testing import ( - assert_array_equal, assert_warns, IS_PYPY) + assert_array_equal, assert_warns, IS_PYPY, IS_64BIT +) def arraylikes(): @@ -716,8 +717,7 @@ def __array__(self, dtype=None, copy=None): arr = np.array([ArrayLike]) assert arr[0] is ArrayLike - @pytest.mark.skipif( - np.dtype(np.intp).itemsize < 8, reason="Needs 64bit platform") + @pytest.mark.skipif(not IS_64BIT, reason="Needs 64bit platform") def test_too_large_array_error_paths(self): """Test the error paths, including for memory leaks""" arr = np.array(0, dtype="uint8") diff --git a/numpy/_core/tests/test_multiarray.py b/numpy/_core/tests/test_multiarray.py index 7ac22869495f..cc9fc9b19ac8 100644 --- a/numpy/_core/tests/test_multiarray.py +++ b/numpy/_core/tests/test_multiarray.py @@ -30,7 +30,7 @@ assert_array_equal, assert_raises_regex, assert_array_almost_equal, assert_allclose, IS_PYPY, IS_WASM, IS_PYSTON, HAS_REFCOUNT, assert_array_less, runstring, temppath, suppress_warnings, break_cycles, - check_support_sve, assert_array_compare, + check_support_sve, assert_array_compare, IS_64BIT ) from numpy.testing._private.utils import requires_memory, _no_tracing from numpy._core.tests._locales import CommaDecimalPointLocale @@ -983,7 +983,7 @@ def test_too_big_error(self): assert_raises(ValueError, np.zeros, shape, dtype=np.int8) assert_raises(ValueError, np.ones, shape, dtype=np.int8) - @pytest.mark.skipif(np.dtype(np.intp).itemsize != 8, + @pytest.mark.skipif(not IS_64BIT, reason="malloc may not fail on 32 bit systems") def test_malloc_fails(self): # This test is guaranteed to fail due to a too large allocation diff --git a/numpy/_core/tests/test_regression.py b/numpy/_core/tests/test_regression.py index c4a0a55227a0..13b4ad16d592 100644 --- a/numpy/_core/tests/test_regression.py +++ b/numpy/_core/tests/test_regression.py @@ -14,7 +14,8 @@ assert_, assert_equal, IS_PYPY, assert_almost_equal, assert_array_equal, assert_array_almost_equal, assert_raises, assert_raises_regex, assert_warns, suppress_warnings, - _assert_valid_refcount, HAS_REFCOUNT, IS_PYSTON, IS_WASM + _assert_valid_refcount, HAS_REFCOUNT, IS_PYSTON, IS_WASM, + IS_64BIT, ) from numpy.testing._private.utils import _no_tracing, requires_memory from numpy._utils import asbytes, asunicode @@ -2265,7 +2266,7 @@ def test_void_compare_segfault(self): def test_reshape_size_overflow(self): # gh-7455 a = np.ones(20)[::2] - if np.dtype(np.intp).itemsize == 8: + if IS_64BIT: # 64 bit. The following are the prime factors of 2**63 + 5, # plus a leading 2, so when multiplied together as int64, # the result overflows to a total size of 10. diff --git a/numpy/f2py/tests/test_return_real.py b/numpy/f2py/tests/test_return_real.py index d9b316dcc45d..25b638890a96 100644 --- a/numpy/f2py/tests/test_return_real.py +++ b/numpy/f2py/tests/test_return_real.py @@ -1,8 +1,8 @@ import platform import pytest -import numpy as np from numpy import array +from numpy.testing import IS_64BIT from . import util @@ -53,8 +53,7 @@ def check_function(self, t, tname): "but not when run in isolation", ) @pytest.mark.skipif( - np.dtype(np.intp).itemsize < 8, - reason="32-bit builds are buggy" + not IS_64BIT, reason="32-bit builds are buggy" ) class TestCReturnReal(TestReturnReal): suffix = ".pyf" diff --git a/numpy/f2py/tests/test_semicolon_split.py b/numpy/f2py/tests/test_semicolon_split.py index ab9c093dbb82..8a9eb8743501 100644 --- a/numpy/f2py/tests/test_semicolon_split.py +++ b/numpy/f2py/tests/test_semicolon_split.py @@ -1,6 +1,7 @@ import platform import pytest -import numpy as np + +from numpy.testing import IS_64BIT from . import util @@ -11,8 +12,7 @@ "but not when run in isolation", ) @pytest.mark.skipif( - np.dtype(np.intp).itemsize < 8, - reason="32-bit builds are buggy" + not IS_64BIT, reason="32-bit builds are buggy" ) class TestMultiline(util.F2PyTest): suffix = ".pyf" @@ -44,8 +44,7 @@ def test_multiline(self): "but not when run in isolation", ) @pytest.mark.skipif( - np.dtype(np.intp).itemsize < 8, - reason="32-bit builds are buggy" + not IS_64BIT, reason="32-bit builds are buggy" ) @pytest.mark.slow class TestCallstatement(util.F2PyTest): diff --git a/numpy/lib/tests/test_format.py b/numpy/lib/tests/test_format.py index f237dffbc244..0cac8819f5fd 100644 --- a/numpy/lib/tests/test_format.py +++ b/numpy/lib/tests/test_format.py @@ -283,7 +283,7 @@ import numpy as np from numpy.testing import ( assert_, assert_array_equal, assert_raises, assert_raises_regex, - assert_warns, IS_PYPY, IS_WASM + assert_warns, IS_PYPY, IS_WASM, IS_64BIT ) from numpy.testing._private.utils import requires_memory from numpy.lib import format @@ -927,8 +927,7 @@ def test_large_file_support(tmpdir): @pytest.mark.skipif(IS_PYPY, reason="flaky on PyPy") -@pytest.mark.skipif(np.dtype(np.intp).itemsize < 8, - reason="test requires 64-bit system") +@pytest.mark.skipif(not IS_64BIT, reason="test requires 64-bit system") @pytest.mark.slow @requires_memory(free_bytes=2 * 2**30) def test_large_archive(tmpdir): diff --git a/numpy/testing/_private/utils.py b/numpy/testing/_private/utils.py index 01fe6327713c..42e43e21f37b 100644 --- a/numpy/testing/_private/utils.py +++ b/numpy/testing/_private/utils.py @@ -46,7 +46,7 @@ 'HAS_REFCOUNT', "IS_WASM", 'suppress_warnings', 'assert_array_compare', 'assert_no_gc_cycles', 'break_cycles', 'HAS_LAPACK64', 'IS_PYSTON', 'IS_MUSL', 'check_support_sve', 'NOGIL_BUILD', - 'IS_EDITABLE', 'IS_INSTALLED', 'NUMPY_ROOT', 'run_threaded', + 'IS_EDITABLE', 'IS_INSTALLED', 'NUMPY_ROOT', 'run_threaded', 'IS_64BIT', ] @@ -105,6 +105,7 @@ class KnownFailureException(Exception): IS_MUSL = True NOGIL_BUILD = bool(sysconfig.get_config_var("Py_GIL_DISABLED")) +IS_64BIT = np.dtype(np.intp).itemsize == 8 def assert_(val, msg=''): """ From 3b6288cc9f364078606c85a4ccbc008b9e35273e Mon Sep 17 00:00:00 2001 From: Nathan Goldbaum Date: Mon, 3 Mar 2025 11:53:54 -0700 Subject: [PATCH 27/49] BUG: skip legacy dtype multithreaded test on 32 bit runners [wheel build] --- numpy/_core/tests/test_multithreading.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/numpy/_core/tests/test_multithreading.py b/numpy/_core/tests/test_multithreading.py index b3bbbe403577..0f7e01aef033 100644 --- a/numpy/_core/tests/test_multithreading.py +++ b/numpy/_core/tests/test_multithreading.py @@ -5,7 +5,7 @@ import numpy as np import pytest -from numpy.testing import IS_WASM +from numpy.testing import IS_WASM, IS_64BIT from numpy.testing._private.utils import run_threaded from numpy._core import _rational_tests @@ -257,20 +257,16 @@ def func(arr): f.result() +@pytest.mark.skipif( + not IS_64BIT, + reason="Sometimes causes failures or crashes due to OOM on 32 bit runners" +) def test_legacy_usertype_cast_init_thread_safety(): def closure(b): b.wait() np.full((10, 10), 1, _rational_tests.rational) - try: - run_threaded(closure, 250, pass_barrier=True) - except RuntimeError: - # The 32 bit linux runner will trigger this with 250 threads. I can - # trigger it on my Linux laptop with 500 threads but the CI runner is - # more resource-constrained. - # Reducing the number of threads means the test doesn't trigger the - # bug. Better to skip on some platforms than add a useless test. - pytest.skip("Couldn't spawn enough threads to run the test") + run_threaded(closure, 250, pass_barrier=True) @pytest.mark.parametrize("dtype", [bool, int, float]) def test_nonzero(dtype): From 2966a6d745b63e25222dcf55d3e1ffda89f1bd15 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Wed, 5 Mar 2025 21:48:44 +0100 Subject: [PATCH 28/49] BUG: Fix searchsorted and CheckFromAny byte-swapping logic * BUG: Fix searchsorted and CheckFromAny byte-swapping logic This closes gh-28190 and fixes another issue in the initial code that triggered the regression. Note that we may still want to avoid this, since this does lead to constructing (view compatible) structured dtypes unnecessarily here. It would also compactify the dtype. For building unnecessary dtypes, the better solution may be to just introduce a "canonical" flag to the dtypes (now that we have the space). * STY: Adopt code comment suggestions --- numpy/_core/src/multiarray/ctors.c | 18 ++++++++---------- numpy/_core/src/multiarray/item_selection.c | 18 +++++++----------- numpy/_core/tests/test_regression.py | 6 ++++++ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/numpy/_core/src/multiarray/ctors.c b/numpy/_core/src/multiarray/ctors.c index b6a935e419a6..0eeb8e61f153 100644 --- a/numpy/_core/src/multiarray/ctors.c +++ b/numpy/_core/src/multiarray/ctors.c @@ -1821,32 +1821,30 @@ PyArray_CheckFromAny(PyObject *op, PyArray_Descr *descr, int min_depth, * Internal version of PyArray_CheckFromAny that accepts a dtypemeta. Borrows * references to the descriptor and dtype. */ - NPY_NO_EXPORT PyObject * PyArray_CheckFromAny_int(PyObject *op, PyArray_Descr *in_descr, PyArray_DTypeMeta *in_DType, int min_depth, int max_depth, int requires, PyObject *context) { PyObject *obj; + Py_XINCREF(in_descr); /* take ownership as we may replace it */ if (requires & NPY_ARRAY_NOTSWAPPED) { - if (!in_descr && PyArray_Check(op) && - PyArray_ISBYTESWAPPED((PyArrayObject* )op)) { - in_descr = PyArray_DescrNew(PyArray_DESCR((PyArrayObject *)op)); + if (!in_descr && PyArray_Check(op)) { + in_descr = PyArray_DESCR((PyArrayObject *)op); + Py_INCREF(in_descr); + } + if (in_descr) { + PyArray_DESCR_REPLACE_CANONICAL(in_descr); if (in_descr == NULL) { return NULL; } } - else if (in_descr && !PyArray_ISNBO(in_descr->byteorder)) { - PyArray_DESCR_REPLACE(in_descr); - } - if (in_descr && in_descr->byteorder != NPY_IGNORE && in_descr->byteorder != NPY_NATIVE) { - in_descr->byteorder = NPY_NATIVE; - } } int was_scalar; obj = PyArray_FromAny_int(op, in_descr, in_DType, min_depth, max_depth, requires, context, &was_scalar); + Py_XDECREF(in_descr); if (obj == NULL) { return NULL; } diff --git a/numpy/_core/src/multiarray/item_selection.c b/numpy/_core/src/multiarray/item_selection.c index 6e9b03a41343..4549f107d76e 100644 --- a/numpy/_core/src/multiarray/item_selection.c +++ b/numpy/_core/src/multiarray/item_selection.c @@ -2111,7 +2111,6 @@ PyArray_SearchSorted(PyArrayObject *op1, PyObject *op2, if (dtype == NULL) { return NULL; } - /* refs to dtype we own = 1 */ /* Look for binary search function */ if (perm) { @@ -2122,26 +2121,23 @@ PyArray_SearchSorted(PyArrayObject *op1, PyObject *op2, } if (binsearch == NULL && argbinsearch == NULL) { PyErr_SetString(PyExc_TypeError, "compare not supported for type"); - /* refs to dtype we own = 1 */ Py_DECREF(dtype); - /* refs to dtype we own = 0 */ return NULL; } - /* need ap2 as contiguous array and of right type */ - /* refs to dtype we own = 1 */ - Py_INCREF(dtype); - /* refs to dtype we own = 2 */ + /* need ap2 as contiguous array and of right dtype (note: steals dtype reference) */ ap2 = (PyArrayObject *)PyArray_CheckFromAny(op2, dtype, 0, 0, NPY_ARRAY_CARRAY_RO | NPY_ARRAY_NOTSWAPPED, NULL); - /* refs to dtype we own = 1, array creation steals one even on failure */ if (ap2 == NULL) { - Py_DECREF(dtype); - /* refs to dtype we own = 0 */ return NULL; } + /* + * The dtype reference we had was used for creating ap2, which may have + * replaced it with another. So here we copy the dtype of ap2 and use it for `ap1`. + */ + dtype = (PyArray_Descr *)Py_NewRef(PyArray_DESCR(ap2)); /* * If the needle (ap2) is larger than the haystack (op1) we copy the @@ -2150,9 +2146,9 @@ PyArray_SearchSorted(PyArrayObject *op1, PyObject *op2, if (PyArray_SIZE(ap2) > PyArray_SIZE(op1)) { ap1_flags |= NPY_ARRAY_CARRAY_RO; } + /* dtype is stolen, after this we have no reference */ ap1 = (PyArrayObject *)PyArray_CheckFromAny((PyObject *)op1, dtype, 1, 1, ap1_flags, NULL); - /* refs to dtype we own = 0, array creation steals one even on failure */ if (ap1 == NULL) { goto fail; } diff --git a/numpy/_core/tests/test_regression.py b/numpy/_core/tests/test_regression.py index 13b4ad16d592..851ce324d76c 100644 --- a/numpy/_core/tests/test_regression.py +++ b/numpy/_core/tests/test_regression.py @@ -2655,3 +2655,9 @@ def test_sort_overlap(self): inp = np.linspace(0, size, num=size, dtype=np.intc) out = np.sort(inp) assert_equal(inp, out) + + def test_searchsorted_structured(self): + # gh-28190 + x = np.array([(0, 1.)], dtype=[('time', ' Date: Thu, 6 Mar 2025 02:21:30 -0500 Subject: [PATCH 29/49] BUG: sanity check ``__array_interface__`` number of dimensions (#28407) ``__array_interface__`` should typically not have more dimensions than NumPy supports, but unlike other malformed interfaces, this should fail gracefully if someone were to pass more. --- numpy/_core/src/multiarray/ctors.c | 8 +++++++- numpy/_core/tests/test_multiarray.py | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/numpy/_core/src/multiarray/ctors.c b/numpy/_core/src/multiarray/ctors.c index 0eeb8e61f153..f4f66142101c 100644 --- a/numpy/_core/src/multiarray/ctors.c +++ b/numpy/_core/src/multiarray/ctors.c @@ -2153,7 +2153,7 @@ PyArray_FromInterface(PyObject *origin) PyArray_Descr *dtype = NULL; char *data = NULL; Py_buffer view; - int i, n; + Py_ssize_t i, n; npy_intp dims[NPY_MAXDIMS], strides[NPY_MAXDIMS]; int dataflags = NPY_ARRAY_BEHAVED; @@ -2269,6 +2269,12 @@ PyArray_FromInterface(PyObject *origin) /* Get dimensions from shape tuple */ else { n = PyTuple_GET_SIZE(attr); + if (n > NPY_MAXDIMS) { + PyErr_Format(PyExc_ValueError, + "number of dimensions must be within [0, %d], got %d", + NPY_MAXDIMS, n); + goto fail; + } for (i = 0; i < n; i++) { PyObject *tmp = PyTuple_GET_ITEM(attr, i); dims[i] = PyArray_PyIntAsIntp(tmp); diff --git a/numpy/_core/tests/test_multiarray.py b/numpy/_core/tests/test_multiarray.py index cc9fc9b19ac8..87508732d85c 100644 --- a/numpy/_core/tests/test_multiarray.py +++ b/numpy/_core/tests/test_multiarray.py @@ -10358,3 +10358,24 @@ def test_to_device(self): r"The stream argument in to_device\(\) is not supported" ): arr.to_device("cpu", stream=1) + +def test_array_interface_excess_dimensions_raises(): + """Regression test for gh-27949: ensure too many dims raises ValueError instead of segfault.""" + + # Dummy object to hold a custom __array_interface__ + class DummyArray: + def __init__(self, interface): + # Attach the array interface dict to mimic an array + self.__array_interface__ = interface + + # Create a base array (scalar) and copy its interface + base = np.array(42) # base can be any scalar or array + interface = dict(base.__array_interface__) + + # Modify the shape to exceed NumPy's dimension limit (NPY_MAXDIMS, typically 64) + interface['shape'] = tuple([1] * 136) # match the original bug report + + dummy = DummyArray(interface) + # Now, using np.asanyarray on this dummy should trigger a ValueError (not segfault) + with pytest.raises(ValueError, match="dimensions must be within"): + np.asanyarray(dummy) \ No newline at end of file From 85f2711e17211c7003a8ea9e30dc2e9796d39c44 Mon Sep 17 00:00:00 2001 From: Mark Harfouche Date: Tue, 21 Jan 2025 14:17:17 -0500 Subject: [PATCH 30/49] MAINT: Hide decorator from pytest traceback Currently it points the user to the internal numpy function ``` kwargs = {'strict': False}, old_name = 'y', new_name = 'desired' @functools.wraps(fun) def wrapper(*args, **kwargs): for old_name, new_name in zip(old_names, new_names): if old_name in kwargs: if dep_version: end_version = dep_version.split('.') end_version[1] = str(int(end_version[1]) + 2) end_version = '.'.join(end_version) msg = (f"Use of keyword argument `{old_name}` is " f"deprecated and replaced by `{new_name}`. " f"Support for `{old_name}` will be removed " f"in NumPy {end_version}.") warnings.warn(msg, DeprecationWarning, stacklevel=2) if new_name in kwargs: msg = (f"{fun.__name__}() got multiple values for " f"argument now known as `{new_name}`") raise TypeError(msg) kwargs[new_name] = kwargs.pop(old_name) > return fun(*args, **kwargs) E AssertionError: E Arrays are not equal E E (shapes (2, 2, 200, 200), (2, 2, 800, 800) mismatch) E ACTUAL: array([[[[0, 0, 0, ..., 0, 0, 0], E [0, 0, 0, ..., 0, 0, 0], E [0, 0, 0, ..., 0, 0, 0],... E DESIRED: array([[[[0, 0, 0, ..., 0, 0, 0], E [0, 0, 0, ..., 0, 0, 0], E [0, 0, 0, ..., 0, 0, 0],... ../../miniforge3/envs/dev/lib/python3.10/site-packages/numpy/_utils/__init__.py:85: AssertionError ``` --- numpy/_utils/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/numpy/_utils/__init__.py b/numpy/_utils/__init__.py index 9794c4e0c4a1..ca3aacd84d5b 100644 --- a/numpy/_utils/__init__.py +++ b/numpy/_utils/__init__.py @@ -66,6 +66,7 @@ def _rename_parameter(old_names, new_names, dep_version=None): def decorator(fun): @functools.wraps(fun) def wrapper(*args, **kwargs): + __tracebackhide__ = True # Hide traceback for py.test for old_name, new_name in zip(old_names, new_names): if old_name in kwargs: if dep_version: From 7d567aa5b3781be5c8cd343f52b833f28fd6a277 Mon Sep 17 00:00:00 2001 From: "Guan Ming(Wesley) Chiu" <105915352+guan404ming@users.noreply.github.com> Date: Sat, 8 Mar 2025 16:37:23 +0800 Subject: [PATCH 31/49] TYP: stub ``random._pickle`` (#28452) * Add random._pickle.pyi * Add new file in meson.build * Align style in numpy/random/meson.build --- numpy/random/_pickle.pyi | 43 ++++++++++++++++++++++++++++++++++++++++ numpy/random/meson.build | 1 + 2 files changed, 44 insertions(+) create mode 100644 numpy/random/_pickle.pyi diff --git a/numpy/random/_pickle.pyi b/numpy/random/_pickle.pyi new file mode 100644 index 000000000000..d4c6e8155ae9 --- /dev/null +++ b/numpy/random/_pickle.pyi @@ -0,0 +1,43 @@ +from collections.abc import Callable +from typing import Final, Literal, TypeVar, TypedDict, overload, type_check_only + +from numpy.random._generator import Generator +from numpy.random._mt19937 import MT19937 +from numpy.random._pcg64 import PCG64, PCG64DXSM +from numpy.random._philox import Philox +from numpy.random._sfc64 import SFC64 +from numpy.random.bit_generator import BitGenerator +from numpy.random.mtrand import RandomState + +_T = TypeVar("_T", bound=BitGenerator) + +@type_check_only +class _BitGenerators(TypedDict): + MT19937: type[MT19937] + PCG64: type[PCG64] + PCG64DXSM: type[PCG64DXSM] + Philox: type[Philox] + SFC64: type[SFC64] + +BitGenerators: Final[_BitGenerators] = ... + +@overload +def __bit_generator_ctor(bit_generator: Literal["MT19937"] = "MT19937") -> MT19937: ... +@overload +def __bit_generator_ctor(bit_generator: Literal["PCG64"]) -> PCG64: ... +@overload +def __bit_generator_ctor(bit_generator: Literal["PCG64DXSM"]) -> PCG64DXSM: ... +@overload +def __bit_generator_ctor(bit_generator: Literal["Philox"]) -> Philox: ... +@overload +def __bit_generator_ctor(bit_generator: Literal["SFC64"]) -> SFC64: ... +@overload +def __bit_generator_ctor(bit_generator: type[_T]) -> _T: ... +def __generator_ctor( + bit_generator_name: str | type[BitGenerator] | BitGenerator = "MT19937", + bit_generator_ctor: Callable[[str | type[BitGenerator]], BitGenerator] = ..., +) -> Generator: ... +def __randomstate_ctor( + bit_generator_name: str | type[BitGenerator] | BitGenerator = "MT19937", + bit_generator_ctor: Callable[[str | type[BitGenerator]], BitGenerator] = ..., +) -> RandomState: ... diff --git a/numpy/random/meson.build b/numpy/random/meson.build index f2f2e0ac755c..be342c443b32 100644 --- a/numpy/random/meson.build +++ b/numpy/random/meson.build @@ -104,6 +104,7 @@ py.install_sources( '_mt19937.pyi', '_pcg64.pyi', '_pickle.py', + '_pickle.pyi', '_philox.pyi', '_sfc64.pyi', 'bit_generator.pxd', From 5f957463d06c1ba15be3de5b71bd54d3b202cb30 Mon Sep 17 00:00:00 2001 From: jorenham Date: Thu, 13 Mar 2025 03:08:32 +0100 Subject: [PATCH 32/49] TYP: fix typing typing errors in `_core.shape_base` Backport of numpy/numtype#108 --- numpy/_core/shape_base.pyi | 76 +++++++++++++------ .../tests/data/reveal/array_constructors.pyi | 20 +++-- 2 files changed, 64 insertions(+), 32 deletions(-) diff --git a/numpy/_core/shape_base.pyi b/numpy/_core/shape_base.pyi index 0dadded9423a..decb7be48f9e 100644 --- a/numpy/_core/shape_base.pyi +++ b/numpy/_core/shape_base.pyi @@ -1,14 +1,8 @@ from collections.abc import Sequence -from typing import TypeVar, overload, Any, SupportsIndex +from typing import Any, SupportsIndex, TypeVar, overload -from numpy import generic, _CastingKind -from numpy._typing import ( - NDArray, - ArrayLike, - DTypeLike, - _ArrayLike, - _DTypeLike, -) +from numpy import _CastingKind, generic +from numpy._typing import ArrayLike, DTypeLike, NDArray, _ArrayLike, _DTypeLike __all__ = [ "atleast_1d", @@ -22,29 +16,54 @@ __all__ = [ ] _SCT = TypeVar("_SCT", bound=generic) -_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) +_SCT1 = TypeVar("_SCT1", bound=generic) +_SCT2 = TypeVar("_SCT2", bound=generic) +_ArrayT = TypeVar("_ArrayT", bound=NDArray[Any]) +### + +@overload +def atleast_1d(a0: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... +@overload +def atleast_1d(a0: _ArrayLike[_SCT1], a1: _ArrayLike[_SCT2], /) -> tuple[NDArray[_SCT1], NDArray[_SCT2]]: ... @overload -def atleast_1d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... +def atleast_1d(a0: _ArrayLike[_SCT], a1: _ArrayLike[_SCT], /, *arys: _ArrayLike[_SCT]) -> tuple[NDArray[_SCT], ...]: ... @overload -def atleast_1d(arys: ArrayLike, /) -> NDArray[Any]: ... +def atleast_1d(a0: ArrayLike, /) -> NDArray[Any]: ... @overload -def atleast_1d(*arys: ArrayLike) -> tuple[NDArray[Any], ...]: ... +def atleast_1d(a0: ArrayLike, a1: ArrayLike, /) -> tuple[NDArray[Any], NDArray[Any]]: ... +@overload +def atleast_1d(a0: ArrayLike, a1: ArrayLike, /, *ai: ArrayLike) -> tuple[NDArray[Any], ...]: ... +# +@overload +def atleast_2d(a0: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... @overload -def atleast_2d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... +def atleast_2d(a0: _ArrayLike[_SCT1], a1: _ArrayLike[_SCT2], /) -> tuple[NDArray[_SCT1], NDArray[_SCT2]]: ... @overload -def atleast_2d(arys: ArrayLike, /) -> NDArray[Any]: ... +def atleast_2d(a0: _ArrayLike[_SCT], a1: _ArrayLike[_SCT], /, *arys: _ArrayLike[_SCT]) -> tuple[NDArray[_SCT], ...]: ... @overload -def atleast_2d(*arys: ArrayLike) -> tuple[NDArray[Any], ...]: ... +def atleast_2d(a0: ArrayLike, /) -> NDArray[Any]: ... +@overload +def atleast_2d(a0: ArrayLike, a1: ArrayLike, /) -> tuple[NDArray[Any], NDArray[Any]]: ... +@overload +def atleast_2d(a0: ArrayLike, a1: ArrayLike, /, *ai: ArrayLike) -> tuple[NDArray[Any], ...]: ... +# +@overload +def atleast_3d(a0: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... +@overload +def atleast_3d(a0: _ArrayLike[_SCT1], a1: _ArrayLike[_SCT2], /) -> tuple[NDArray[_SCT1], NDArray[_SCT2]]: ... @overload -def atleast_3d(arys: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... +def atleast_3d(a0: _ArrayLike[_SCT], a1: _ArrayLike[_SCT], /, *arys: _ArrayLike[_SCT]) -> tuple[NDArray[_SCT], ...]: ... @overload -def atleast_3d(arys: ArrayLike, /) -> NDArray[Any]: ... +def atleast_3d(a0: ArrayLike, /) -> NDArray[Any]: ... @overload -def atleast_3d(*arys: ArrayLike) -> tuple[NDArray[Any], ...]: ... +def atleast_3d(a0: ArrayLike, a1: ArrayLike, /) -> tuple[NDArray[Any], NDArray[Any]]: ... +@overload +def atleast_3d(a0: ArrayLike, a1: ArrayLike, /, *ai: ArrayLike) -> tuple[NDArray[Any], ...]: ... +# @overload def vstack( tup: Sequence[_ArrayLike[_SCT]], @@ -119,12 +138,21 @@ def stack( @overload def stack( arrays: Sequence[ArrayLike], - axis: SupportsIndex = ..., - out: _ArrayType = ..., + axis: SupportsIndex, + out: _ArrayT, *, - dtype: DTypeLike = ..., - casting: _CastingKind = ... -) -> _ArrayType: ... + dtype: DTypeLike | None = None, + casting: _CastingKind = "same_kind", +) -> _ArrayT: ... +@overload +def stack( + arrays: Sequence[ArrayLike], + axis: SupportsIndex = 0, + *, + out: _ArrayT, + dtype: DTypeLike | None = None, + casting: _CastingKind = "same_kind", +) -> _ArrayT: ... @overload def unstack( diff --git a/numpy/typing/tests/data/reveal/array_constructors.pyi b/numpy/typing/tests/data/reveal/array_constructors.pyi index c6d56ab0de2d..35861cc0e942 100644 --- a/numpy/typing/tests/data/reveal/array_constructors.pyi +++ b/numpy/typing/tests/data/reveal/array_constructors.pyi @@ -203,26 +203,30 @@ assert_type(np.identity(10, dtype=int), npt.NDArray[Any]) assert_type(np.atleast_1d(A), npt.NDArray[np.float64]) assert_type(np.atleast_1d(C), npt.NDArray[Any]) -assert_type(np.atleast_1d(A, A), tuple[npt.NDArray[Any], ...]) -assert_type(np.atleast_1d(A, C), tuple[npt.NDArray[Any], ...]) -assert_type(np.atleast_1d(C, C), tuple[npt.NDArray[Any], ...]) +assert_type(np.atleast_1d(A, A), tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]) +assert_type(np.atleast_1d(A, C), tuple[npt.NDArray[Any], npt.NDArray[Any]]) +assert_type(np.atleast_1d(C, C), tuple[npt.NDArray[Any], npt.NDArray[Any]]) +assert_type(np.atleast_1d(A, A, A), tuple[npt.NDArray[np.float64], ...]) +assert_type(np.atleast_1d(C, C, C), tuple[npt.NDArray[Any], ...]) assert_type(np.atleast_2d(A), npt.NDArray[np.float64]) -assert_type(np.atleast_2d(A, A), tuple[npt.NDArray[Any], ...]) +assert_type(np.atleast_2d(A, A), tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]) +assert_type(np.atleast_2d(A, A, A), tuple[npt.NDArray[np.float64], ...]) assert_type(np.atleast_3d(A), npt.NDArray[np.float64]) -assert_type(np.atleast_3d(A, A), tuple[npt.NDArray[Any], ...]) +assert_type(np.atleast_3d(A, A), tuple[npt.NDArray[np.float64], npt.NDArray[np.float64]]) +assert_type(np.atleast_3d(A, A, A), tuple[npt.NDArray[np.float64], ...]) assert_type(np.vstack([A, A]), npt.NDArray[np.float64]) -assert_type(np.vstack([A, A], dtype=np.float64), npt.NDArray[np.float64]) +assert_type(np.vstack([A, A], dtype=np.float32), npt.NDArray[np.float32]) assert_type(np.vstack([A, C]), npt.NDArray[Any]) assert_type(np.vstack([C, C]), npt.NDArray[Any]) assert_type(np.hstack([A, A]), npt.NDArray[np.float64]) -assert_type(np.hstack([A, A], dtype=np.float64), npt.NDArray[np.float64]) +assert_type(np.hstack([A, A], dtype=np.float32), npt.NDArray[np.float32]) assert_type(np.stack([A, A]), npt.NDArray[np.float64]) -assert_type(np.stack([A, A], dtype=np.float64), npt.NDArray[np.float64]) +assert_type(np.stack([A, A], dtype=np.float32), npt.NDArray[np.float32]) assert_type(np.stack([A, C]), npt.NDArray[Any]) assert_type(np.stack([C, C]), npt.NDArray[Any]) assert_type(np.stack([A, A], axis=0), npt.NDArray[np.float64]) From 6c5a7853b20f1a0039a60f0de4800511deb9bcc6 Mon Sep 17 00:00:00 2001 From: jorenham Date: Thu, 13 Mar 2025 16:35:41 +0100 Subject: [PATCH 33/49] TYP: fix typing errors in `_core.records` backport of numpy/numtype#116 --- numpy/_core/records.pyi | 365 +++++++++++-------------- numpy/typing/tests/data/reveal/rec.pyi | 2 +- 2 files changed, 163 insertions(+), 204 deletions(-) diff --git a/numpy/_core/records.pyi b/numpy/_core/records.pyi index ef60803ffeb4..308f96b7407b 100644 --- a/numpy/_core/records.pyi +++ b/numpy/_core/records.pyi @@ -1,48 +1,24 @@ -from _typeshed import StrOrBytesPath -from collections.abc import Sequence, Iterable +# ruff: noqa: ANN401 +# pyright: reportSelfClsParameterName=false +from collections.abc import Iterable, Sequence from types import EllipsisType -from typing import ( - Any, - TypeAlias, - TypeVar, - overload, - Protocol, - SupportsIndex, - Literal, - type_check_only -) +from typing import Any, Literal, Protocol, SupportsIndex, TypeAlias, TypeVar, overload, type_check_only -from numpy import ( - ndarray, - dtype, - generic, - void, - _ByteOrder, - _SupportsBuffer, - _OrderKACF, -) +from _typeshed import StrOrBytesPath -from numpy._typing import ( - ArrayLike, - DTypeLike, - NDArray, - _Shape, - _ShapeLike, - _ArrayLikeInt_co, - _ArrayLikeVoid_co, - _NestedSequence, -) +from numpy import _ByteOrder, _OrderKACF, _SupportsBuffer, dtype, generic, ndarray, void +from numpy._typing import ArrayLike, DTypeLike, NDArray, _ArrayLikeVoid_co, _NestedSequence, _ShapeLike __all__ = [ - "record", - "recarray", + "array", + "find_duplicate", "format_parser", "fromarrays", + "fromfile", "fromrecords", "fromstring", - "fromfile", - "array", - "find_duplicate", + "recarray", + "record", ] _T = TypeVar("_T") @@ -58,6 +34,9 @@ class _SupportsReadInto(Protocol): def tell(self, /) -> int: ... def readinto(self, buffer: memoryview, /) -> int: ... +### + +# exported in `numpy.rec` class record(void): def __getattribute__(self, attr: str) -> Any: ... def __setattr__(self, attr: str, val: ArrayLike) -> None: ... @@ -67,6 +46,7 @@ class record(void): @overload def __getitem__(self, key: list[str]) -> record: ... +# exported in `numpy.rec` class recarray(ndarray[_ShapeT_co, _DType_co]): # NOTE: While not strictly mandatory, we're demanding here that arguments # for the `format_parser`- and `dtype`-based dtype constructors are @@ -75,273 +55,252 @@ class recarray(ndarray[_ShapeT_co, _DType_co]): def __new__( subtype, shape: _ShapeLike, - dtype: None = ..., - buf: None | _SupportsBuffer = ..., - offset: SupportsIndex = ..., - strides: None | _ShapeLike = ..., + dtype: None = None, + buf: _SupportsBuffer | None = None, + offset: SupportsIndex = 0, + strides: _ShapeLike | None = None, *, formats: DTypeLike, - names: None | str | Sequence[str] = ..., - titles: None | str | Sequence[str] = ..., - byteorder: None | _ByteOrder = ..., - aligned: bool = ..., - order: _OrderKACF = ..., + names: str | Sequence[str] | None = None, + titles: str | Sequence[str] | None = None, + byteorder: _ByteOrder | None = None, + aligned: bool = False, + order: _OrderKACF = "C", ) -> recarray[Any, dtype[record]]: ... @overload def __new__( subtype, shape: _ShapeLike, dtype: DTypeLike, - buf: None | _SupportsBuffer = ..., - offset: SupportsIndex = ..., - strides: None | _ShapeLike = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - byteorder: None = ..., - aligned: Literal[False] = ..., - order: _OrderKACF = ..., + buf: _SupportsBuffer | None = None, + offset: SupportsIndex = 0, + strides: _ShapeLike | None = None, + formats: None = None, + names: None = None, + titles: None = None, + byteorder: None = None, + aligned: Literal[False] = False, + order: _OrderKACF = "C", ) -> recarray[Any, dtype[Any]]: ... - def __array_finalize__(self, obj: object) -> None: ... - def __getattribute__(self, attr: str) -> Any: ... - def __setattr__(self, attr: str, val: ArrayLike) -> None: ... - @overload - def __getitem__(self, indx: ( - SupportsIndex - | _ArrayLikeInt_co - | tuple[SupportsIndex | _ArrayLikeInt_co, ...] - )) -> Any: ... + def __array_finalize__(self, /, obj: object) -> None: ... + def __getattribute__(self, attr: str, /) -> Any: ... + def __setattr__(self, attr: str, val: ArrayLike, /) -> None: ... @overload - def __getitem__(self: recarray[Any, dtype[void]], indx: ( - None - | slice - | EllipsisType - | SupportsIndex - | _ArrayLikeInt_co - | tuple[None | slice | EllipsisType | _ArrayLikeInt_co | SupportsIndex, ...] - )) -> recarray[_Shape, _DType_co]: ... + def field(self, /, attr: int | str, val: None = None) -> Any: ... @overload - def __getitem__(self, indx: ( - None - | slice - | EllipsisType - | SupportsIndex - | _ArrayLikeInt_co - | tuple[None | slice | EllipsisType | _ArrayLikeInt_co | SupportsIndex, ...] - )) -> ndarray[_Shape, _DType_co]: ... - @overload - def __getitem__(self, indx: str) -> NDArray[Any]: ... - @overload - def __getitem__(self, indx: list[str]) -> recarray[_ShapeT_co, dtype[record]]: ... - @overload - def field(self, attr: int | str, val: None = ...) -> Any: ... - @overload - def field(self, attr: int | str, val: ArrayLike) -> None: ... + def field(self, /, attr: int | str, val: ArrayLike) -> None: ... +# exported in `numpy.rec` class format_parser: dtype: dtype[void] def __init__( self, + /, formats: DTypeLike, - names: None | str | Sequence[str], - titles: None | str | Sequence[str], - aligned: bool = ..., - byteorder: None | _ByteOrder = ..., + names: str | Sequence[str] | None, + titles: str | Sequence[str] | None, + aligned: bool = False, + byteorder: _ByteOrder | None = None, ) -> None: ... +# exported in `numpy.rec` @overload def fromarrays( arrayList: Iterable[ArrayLike], - dtype: DTypeLike = ..., - shape: None | _ShapeLike = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - aligned: bool = ..., - byteorder: None = ..., + dtype: DTypeLike | None = None, + shape: _ShapeLike | None = None, + formats: None = None, + names: None = None, + titles: None = None, + aligned: bool = False, + byteorder: None = None, ) -> _RecArray[Any]: ... @overload def fromarrays( arrayList: Iterable[ArrayLike], - dtype: None = ..., - shape: None | _ShapeLike = ..., + dtype: None = None, + shape: _ShapeLike | None = None, *, formats: DTypeLike, - names: None | str | Sequence[str] = ..., - titles: None | str | Sequence[str] = ..., - aligned: bool = ..., - byteorder: None | _ByteOrder = ..., + names: str | Sequence[str] | None = None, + titles: str | Sequence[str] | None = None, + aligned: bool = False, + byteorder: _ByteOrder | None = None, ) -> _RecArray[record]: ... @overload def fromrecords( - recList: _ArrayLikeVoid_co | tuple[Any, ...] | _NestedSequence[tuple[Any, ...]], - dtype: DTypeLike = ..., - shape: None | _ShapeLike = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - aligned: bool = ..., - byteorder: None = ..., + recList: _ArrayLikeVoid_co | tuple[object, ...] | _NestedSequence[tuple[object, ...]], + dtype: DTypeLike | None = None, + shape: _ShapeLike | None = None, + formats: None = None, + names: None = None, + titles: None = None, + aligned: bool = False, + byteorder: None = None, ) -> _RecArray[record]: ... @overload def fromrecords( - recList: _ArrayLikeVoid_co | tuple[Any, ...] | _NestedSequence[tuple[Any, ...]], - dtype: None = ..., - shape: None | _ShapeLike = ..., + recList: _ArrayLikeVoid_co | tuple[object, ...] | _NestedSequence[tuple[object, ...]], + dtype: None = None, + shape: _ShapeLike | None = None, *, - formats: DTypeLike = ..., - names: None | str | Sequence[str] = ..., - titles: None | str | Sequence[str] = ..., - aligned: bool = ..., - byteorder: None | _ByteOrder = ..., + formats: DTypeLike, + names: str | Sequence[str] | None = None, + titles: str | Sequence[str] | None = None, + aligned: bool = False, + byteorder: _ByteOrder | None = None, ) -> _RecArray[record]: ... +# exported in `numpy.rec` @overload def fromstring( datastring: _SupportsBuffer, dtype: DTypeLike, - shape: None | _ShapeLike = ..., - offset: int = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - aligned: bool = ..., - byteorder: None = ..., + shape: _ShapeLike | None = None, + offset: int = 0, + formats: None = None, + names: None = None, + titles: None = None, + aligned: bool = False, + byteorder: None = None, ) -> _RecArray[record]: ... @overload def fromstring( datastring: _SupportsBuffer, - dtype: None = ..., - shape: None | _ShapeLike = ..., - offset: int = ..., + dtype: None = None, + shape: _ShapeLike | None = None, + offset: int = 0, *, formats: DTypeLike, - names: None | str | Sequence[str] = ..., - titles: None | str | Sequence[str] = ..., - aligned: bool = ..., - byteorder: None | _ByteOrder = ..., + names: str | Sequence[str] | None = None, + titles: str | Sequence[str] | None = None, + aligned: bool = False, + byteorder: _ByteOrder | None = None, ) -> _RecArray[record]: ... +# exported in `numpy.rec` @overload def fromfile( fd: StrOrBytesPath | _SupportsReadInto, dtype: DTypeLike, - shape: None | _ShapeLike = ..., - offset: int = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - aligned: bool = ..., - byteorder: None = ..., + shape: _ShapeLike | None = None, + offset: int = 0, + formats: None = None, + names: None = None, + titles: None = None, + aligned: bool = False, + byteorder: None = None, ) -> _RecArray[Any]: ... @overload def fromfile( fd: StrOrBytesPath | _SupportsReadInto, - dtype: None = ..., - shape: None | _ShapeLike = ..., - offset: int = ..., + dtype: None = None, + shape: _ShapeLike | None = None, + offset: int = 0, *, formats: DTypeLike, - names: None | str | Sequence[str] = ..., - titles: None | str | Sequence[str] = ..., - aligned: bool = ..., - byteorder: None | _ByteOrder = ..., + names: str | Sequence[str] | None = None, + titles: str | Sequence[str] | None = None, + aligned: bool = False, + byteorder: _ByteOrder | None = None, ) -> _RecArray[record]: ... +# exported in `numpy.rec` @overload def array( obj: _SCT | NDArray[_SCT], - dtype: None = ..., - shape: None | _ShapeLike = ..., - offset: int = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - aligned: bool = ..., - byteorder: None = ..., - copy: bool = ..., + dtype: None = None, + shape: _ShapeLike | None = None, + offset: int = 0, + formats: None = None, + names: None = None, + titles: None = None, + aligned: bool = False, + byteorder: None = None, + copy: bool = True, ) -> _RecArray[_SCT]: ... @overload def array( obj: ArrayLike, dtype: DTypeLike, - shape: None | _ShapeLike = ..., - offset: int = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - aligned: bool = ..., - byteorder: None = ..., - copy: bool = ..., + shape: _ShapeLike | None = None, + offset: int = 0, + formats: None = None, + names: None = None, + titles: None = None, + aligned: bool = False, + byteorder: None = None, + copy: bool = True, ) -> _RecArray[Any]: ... @overload def array( obj: ArrayLike, - dtype: None = ..., - shape: None | _ShapeLike = ..., - offset: int = ..., + dtype: None = None, + shape: _ShapeLike | None = None, + offset: int = 0, *, formats: DTypeLike, - names: None | str | Sequence[str] = ..., - titles: None | str | Sequence[str] = ..., - aligned: bool = ..., - byteorder: None | _ByteOrder = ..., - copy: bool = ..., + names: str | Sequence[str] | None = None, + titles: str | Sequence[str] | None = None, + aligned: bool = False, + byteorder: _ByteOrder | None = None, + copy: bool = True, ) -> _RecArray[record]: ... @overload def array( obj: None, dtype: DTypeLike, shape: _ShapeLike, - offset: int = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - aligned: bool = ..., - byteorder: None = ..., - copy: bool = ..., + offset: int = 0, + formats: None = None, + names: None = None, + titles: None = None, + aligned: bool = False, + byteorder: None = None, + copy: bool = True, ) -> _RecArray[Any]: ... @overload def array( obj: None, - dtype: None = ..., + dtype: None = None, *, shape: _ShapeLike, - offset: int = ..., + offset: int = 0, formats: DTypeLike, - names: None | str | Sequence[str] = ..., - titles: None | str | Sequence[str] = ..., - aligned: bool = ..., - byteorder: None | _ByteOrder = ..., - copy: bool = ..., + names: str | Sequence[str] | None = None, + titles: str | Sequence[str] | None = None, + aligned: bool = False, + byteorder: _ByteOrder | None = None, + copy: bool = True, ) -> _RecArray[record]: ... @overload def array( obj: _SupportsReadInto, dtype: DTypeLike, - shape: None | _ShapeLike = ..., - offset: int = ..., - formats: None = ..., - names: None = ..., - titles: None = ..., - aligned: bool = ..., - byteorder: None = ..., - copy: bool = ..., + shape: _ShapeLike | None = None, + offset: int = 0, + formats: None = None, + names: None = None, + titles: None = None, + aligned: bool = False, + byteorder: None = None, + copy: bool = True, ) -> _RecArray[Any]: ... @overload def array( obj: _SupportsReadInto, - dtype: None = ..., - shape: None | _ShapeLike = ..., - offset: int = ..., + dtype: None = None, + shape: _ShapeLike | None = None, + offset: int = 0, *, formats: DTypeLike, - names: None | str | Sequence[str] = ..., - titles: None | str | Sequence[str] = ..., - aligned: bool = ..., - byteorder: None | _ByteOrder = ..., - copy: bool = ..., + names: str | Sequence[str] | None = None, + titles: str | Sequence[str] | None = None, + aligned: bool = False, + byteorder: _ByteOrder | None = None, + copy: bool = True, ) -> _RecArray[record]: ... +# exported in `numpy.rec` def find_duplicate(list: Iterable[_T]) -> list[_T]: ... diff --git a/numpy/typing/tests/data/reveal/rec.pyi b/numpy/typing/tests/data/reveal/rec.pyi index 13db0a969773..1b88f6b46316 100644 --- a/numpy/typing/tests/data/reveal/rec.pyi +++ b/numpy/typing/tests/data/reveal/rec.pyi @@ -7,7 +7,7 @@ import numpy.typing as npt from typing_extensions import assert_type AR_i8: npt.NDArray[np.int64] -REC_AR_V: np.recarray[Any, np.dtype[np.record]] +REC_AR_V: np.recarray[tuple[int, ...], np.dtype[np.record]] AR_LIST: list[npt.NDArray[np.int64]] record: np.record From 8c6007f5f7d2ea43210184f2f0e1c30e8157e367 Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Fri, 14 Mar 2025 20:23:15 -0600 Subject: [PATCH 34/49] TYP: Backport typing fixes from #28505, #28506, #28508, and #28511 (#28521) * TYP: stub ``numpy._core.umath`` Backport of numpy/numtype#123 * TYP: fix typing errors in ``numpy.lib._arrayterator_impl`` Ported from numpy/numtype#165 * TYP: fix signatures of ``ndarray.put`` and ``ndarray.view`` partial port of numpy/numtype#200 * TYP: fix typing errors in ``_core.fromnumeric`` Partial port of numpy/numtype#221 * TYP: fix typing errors in ``_core.function_base`` Partial port of numpy/numtype#221 * TYP: add missing implicit re-exports in ``_core.numeric`` Partial port of numpy/numtype#221 * STY: fix `E203` flake8 error --------- Co-authored-by: jorenham --- numpy/__init__.pyi | 44 +- numpy/_core/fromnumeric.pyi | 767 ++++++++++++++++++++----------- numpy/_core/function_base.pyi | 91 ++-- numpy/_core/meson.build | 1 + numpy/_core/numeric.pyi | 7 + numpy/_core/umath.pyi | 197 ++++++++ numpy/lib/_arrayterator_impl.pyi | 63 ++- 7 files changed, 805 insertions(+), 365 deletions(-) create mode 100644 numpy/_core/umath.pyi diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 1a2d6a08bbb1..7f6bd9db55a4 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -1097,8 +1097,12 @@ class _HasShape(Protocol[_ShapeT_co]): def shape(self, /) -> _ShapeT_co: ... @type_check_only -class _HasShapeAndSupportsItem(_HasShape[_ShapeT_co], _SupportsItem[_T_co], Protocol[_ShapeT_co, _T_co]): - pass +class _HasDType(Protocol[_T_co]): + @property + def dtype(self, /) -> _T_co: ... + +@type_check_only +class _HasShapeAndSupportsItem(_HasShape[_ShapeT_co], _SupportsItem[_T_co], Protocol[_ShapeT_co, _T_co]): ... # matches any `x` on `x.type.item() -> _T_co`, e.g. `dtype[np.int8]` gives `_T_co: int` @type_check_only @@ -2345,12 +2349,7 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DType_co]): # `put` is technically available to `generic`, # but is pointless as `generic`s are immutable - def put( - self, - ind: _ArrayLikeInt_co, - v: ArrayLike, - mode: _ModeKind = ..., - ) -> None: ... + def put(self, /, indices: _ArrayLikeInt_co, values: ArrayLike, mode: _ModeKind = "raise") -> None: ... @overload def searchsorted( # type: ignore[misc] @@ -2537,20 +2536,21 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DType_co]): copy: builtins.bool | _CopyMode = ..., ) -> ndarray[_ShapeT_co, dtype[Any]]: ... - @overload - def view(self) -> Self: ... - @overload - def view(self, type: type[_ArrayT]) -> _ArrayT: ... - @overload - def view(self, dtype: _DTypeLike[_SCT]) -> NDArray[_SCT]: ... - @overload - def view(self, dtype: DTypeLike) -> NDArray[Any]: ... - @overload - def view( - self, - dtype: DTypeLike, - type: type[_ArrayT], - ) -> _ArrayT: ... + # + @overload # () + def view(self, /) -> Self: ... + @overload # (dtype: T) + def view(self, /, dtype: _DType | _HasDType[_DType]) -> ndarray[_ShapeT_co, _DType]: ... + @overload # (dtype: dtype[T]) + def view(self, /, dtype: _DTypeLike[_SCT]) -> NDArray[_SCT]: ... + @overload # (type: T) + def view(self, /, *, type: type[_ArrayT]) -> _ArrayT: ... + @overload # (_: T) + def view(self, /, dtype: type[_ArrayT]) -> _ArrayT: ... + @overload # (dtype: ?) + def view(self, /, dtype: DTypeLike) -> ndarray[_ShapeT_co, dtype[Any]]: ... + @overload # (dtype: ?, type: type[T]) + def view(self, /, dtype: DTypeLike, type: type[_ArrayT]) -> _ArrayT: ... @overload def getfield( diff --git a/numpy/_core/fromnumeric.pyi b/numpy/_core/fromnumeric.pyi index 0465cc5aaa54..f7207db17b0c 100644 --- a/numpy/_core/fromnumeric.pyi +++ b/numpy/_core/fromnumeric.pyi @@ -1,3 +1,4 @@ +# ruff: noqa: ANN401 from collections.abc import Sequence from typing import ( Any, @@ -34,6 +35,7 @@ from numpy import ( _SortSide, _CastingKind, ) +from numpy._globals import _NoValueType from numpy._typing import ( DTypeLike, _DTypeLike, @@ -105,7 +107,7 @@ __all__ = [ _SCT = TypeVar("_SCT", bound=generic) _SCT_uifcO = TypeVar("_SCT_uifcO", bound=number[Any] | object_) -_ArrayType = TypeVar("_ArrayType", bound=np.ndarray[Any, Any]) +_ArrayT = TypeVar("_ArrayT", bound=np.ndarray[Any, Any]) _SizeType = TypeVar("_SizeType", bound=int) _ShapeType = TypeVar("_ShapeType", bound=tuple[int, ...]) _ShapeType_co = TypeVar("_ShapeType_co", bound=tuple[int, ...], covariant=True) @@ -120,7 +122,7 @@ class _SupportsShape(Protocol[_ShapeType_co]): _T = TypeVar("_T") _PyArray: TypeAlias = list[_T] | tuple[_T, ...] # `int` also covers `bool` -_PyScalar: TypeAlias = int | float | complex | bytes | str +_PyScalar: TypeAlias = float | complex | bytes | str @overload def take( @@ -134,7 +136,7 @@ def take( def take( a: ArrayLike, indices: _IntLike_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., out: None = ..., mode: _ModeKind = ..., ) -> Any: ... @@ -142,7 +144,7 @@ def take( def take( a: _ArrayLike[_SCT], indices: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., out: None = ..., mode: _ModeKind = ..., ) -> NDArray[_SCT]: ... @@ -150,7 +152,7 @@ def take( def take( a: ArrayLike, indices: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., out: None = ..., mode: _ModeKind = ..., ) -> NDArray[Any]: ... @@ -158,10 +160,19 @@ def take( def take( a: ArrayLike, indices: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., - out: _ArrayType = ..., + axis: SupportsIndex | None, + out: _ArrayT, mode: _ModeKind = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... +@overload +def take( + a: ArrayLike, + indices: _ArrayLikeInt_co, + axis: SupportsIndex | None = ..., + *, + out: _ArrayT, + mode: _ModeKind = ..., +) -> _ArrayT: ... @overload def reshape( # shape: index @@ -258,21 +269,21 @@ def choose( def choose( a: _ArrayLikeInt_co, choices: ArrayLike, - out: _ArrayType = ..., + out: _ArrayT, mode: _ModeKind = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... @overload def repeat( a: _ArrayLike[_SCT], repeats: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., ) -> NDArray[_SCT]: ... @overload def repeat( a: ArrayLike, repeats: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., ) -> NDArray[Any]: ... def put( @@ -298,70 +309,70 @@ def swapaxes( @overload def transpose( a: _ArrayLike[_SCT], - axes: None | _ShapeLike = ... + axes: _ShapeLike | None = ... ) -> NDArray[_SCT]: ... @overload def transpose( a: ArrayLike, - axes: None | _ShapeLike = ... + axes: _ShapeLike | None = ... ) -> NDArray[Any]: ... @overload -def matrix_transpose(x: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... +def matrix_transpose(x: _ArrayLike[_SCT], /) -> NDArray[_SCT]: ... @overload -def matrix_transpose(x: ArrayLike) -> NDArray[Any]: ... +def matrix_transpose(x: ArrayLike, /) -> NDArray[Any]: ... @overload def partition( a: _ArrayLike[_SCT], kth: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., kind: _PartitionKind = ..., - order: None | str | Sequence[str] = ..., + order: str | Sequence[str] | None = ..., ) -> NDArray[_SCT]: ... @overload def partition( a: ArrayLike, kth: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., kind: _PartitionKind = ..., - order: None | str | Sequence[str] = ..., + order: str | Sequence[str] | None = ..., ) -> NDArray[Any]: ... def argpartition( a: ArrayLike, kth: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = -1, kind: _PartitionKind = ..., - order: None | str | Sequence[str] = ..., + order: str | Sequence[str] | None = ..., ) -> NDArray[intp]: ... @overload def sort( a: _ArrayLike[_SCT], - axis: None | SupportsIndex = ..., - kind: None | _SortKind = ..., - order: None | str | Sequence[str] = ..., + axis: SupportsIndex | None = ..., + kind: _SortKind | None = ..., + order: str | Sequence[str] | None = ..., *, - stable: None | bool = ..., + stable: bool | None = ..., ) -> NDArray[_SCT]: ... @overload def sort( a: ArrayLike, - axis: None | SupportsIndex = ..., - kind: None | _SortKind = ..., - order: None | str | Sequence[str] = ..., + axis: SupportsIndex | None = ..., + kind: _SortKind | None = ..., + order: str | Sequence[str] | None = ..., *, - stable: None | bool = ..., + stable: bool | None = ..., ) -> NDArray[Any]: ... def argsort( a: ArrayLike, - axis: None | SupportsIndex = ..., - kind: None | _SortKind = ..., - order: None | str | Sequence[str] = ..., + axis: SupportsIndex | None = ..., + kind: _SortKind | None = ..., + order: str | Sequence[str] | None = ..., *, - stable: None | bool = ..., + stable: bool | None = ..., ) -> NDArray[intp]: ... @overload @@ -375,7 +386,7 @@ def argmax( @overload def argmax( a: ArrayLike, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., out: None = ..., *, keepdims: bool = ..., @@ -383,11 +394,19 @@ def argmax( @overload def argmax( a: ArrayLike, - axis: None | SupportsIndex = ..., - out: _ArrayType = ..., + axis: SupportsIndex | None, + out: _ArrayT, *, keepdims: bool = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... +@overload +def argmax( + a: ArrayLike, + axis: SupportsIndex | None = ..., + *, + out: _ArrayT, + keepdims: bool = ..., +) -> _ArrayT: ... @overload def argmin( @@ -400,7 +419,7 @@ def argmin( @overload def argmin( a: ArrayLike, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., out: None = ..., *, keepdims: bool = ..., @@ -408,25 +427,33 @@ def argmin( @overload def argmin( a: ArrayLike, - axis: None | SupportsIndex = ..., - out: _ArrayType = ..., + axis: SupportsIndex | None, + out: _ArrayT, *, keepdims: bool = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... +@overload +def argmin( + a: ArrayLike, + axis: SupportsIndex | None = ..., + *, + out: _ArrayT, + keepdims: bool = ..., +) -> _ArrayT: ... @overload def searchsorted( a: ArrayLike, v: _ScalarLike_co, side: _SortSide = ..., - sorter: None | _ArrayLikeInt_co = ..., # 1D int array + sorter: _ArrayLikeInt_co | None = ..., # 1D int array ) -> intp: ... @overload def searchsorted( a: ArrayLike, v: ArrayLike, side: _SortSide = ..., - sorter: None | _ArrayLikeInt_co = ..., # 1D int array + sorter: _ArrayLikeInt_co | None = ..., # 1D int array ) -> NDArray[intp]: ... # unlike `reshape`, `resize` only accepts positive integers, so literal ints can be used @@ -450,17 +477,17 @@ def resize(a: ArrayLike, new_shape: Sequence[SupportsIndex]) -> NDArray[Any]: .. @overload def squeeze( a: _SCT, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., ) -> _SCT: ... @overload def squeeze( a: _ArrayLike[_SCT], - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., ) -> NDArray[_SCT]: ... @overload def squeeze( a: ArrayLike, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., ) -> NDArray[Any]: ... @overload @@ -488,14 +515,24 @@ def trace( out: None = ..., ) -> Any: ... @overload +def trace( + a: ArrayLike, # >= 2D array + offset: SupportsIndex, + axis1: SupportsIndex, + axis2: SupportsIndex, + dtype: DTypeLike, + out: _ArrayT, +) -> _ArrayT: ... +@overload def trace( a: ArrayLike, # >= 2D array offset: SupportsIndex = ..., axis1: SupportsIndex = ..., axis2: SupportsIndex = ..., dtype: DTypeLike = ..., - out: _ArrayType = ..., -) -> _ArrayType: ... + *, + out: _ArrayT, +) -> _ArrayT: ... _Array1D: TypeAlias = np.ndarray[tuple[int], np.dtype[_SCT]] @@ -547,120 +584,128 @@ def shape(a: ArrayLike) -> tuple[int, ...]: ... def compress( condition: _ArrayLikeBool_co, # 1D bool array a: _ArrayLike[_SCT], - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., out: None = ..., ) -> NDArray[_SCT]: ... @overload def compress( condition: _ArrayLikeBool_co, # 1D bool array a: ArrayLike, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., out: None = ..., ) -> NDArray[Any]: ... @overload def compress( condition: _ArrayLikeBool_co, # 1D bool array a: ArrayLike, - axis: None | SupportsIndex = ..., - out: _ArrayType = ..., -) -> _ArrayType: ... + axis: SupportsIndex | None, + out: _ArrayT, +) -> _ArrayT: ... +@overload +def compress( + condition: _ArrayLikeBool_co, # 1D bool array + a: ArrayLike, + axis: SupportsIndex | None = ..., + *, + out: _ArrayT, +) -> _ArrayT: ... @overload def clip( a: _SCT, - a_min: None | ArrayLike, - a_max: None | ArrayLike, + a_min: ArrayLike | None, + a_max: ArrayLike | None, out: None = ..., *, - min: None | ArrayLike = ..., - max: None | ArrayLike = ..., + min: ArrayLike | None = ..., + max: ArrayLike | None = ..., dtype: None = ..., - where: None | _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | None = ..., order: _OrderKACF = ..., subok: bool = ..., - signature: str | tuple[None | str, ...] = ..., + signature: str | tuple[str | None, ...] = ..., casting: _CastingKind = ..., ) -> _SCT: ... @overload def clip( a: _ScalarLike_co, - a_min: None | ArrayLike, - a_max: None | ArrayLike, + a_min: ArrayLike | None, + a_max: ArrayLike | None, out: None = ..., *, - min: None | ArrayLike = ..., - max: None | ArrayLike = ..., + min: ArrayLike | None = ..., + max: ArrayLike | None = ..., dtype: None = ..., - where: None | _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | None = ..., order: _OrderKACF = ..., subok: bool = ..., - signature: str | tuple[None | str, ...] = ..., + signature: str | tuple[str | None, ...] = ..., casting: _CastingKind = ..., ) -> Any: ... @overload def clip( a: _ArrayLike[_SCT], - a_min: None | ArrayLike, - a_max: None | ArrayLike, + a_min: ArrayLike | None, + a_max: ArrayLike | None, out: None = ..., *, - min: None | ArrayLike = ..., - max: None | ArrayLike = ..., + min: ArrayLike | None = ..., + max: ArrayLike | None = ..., dtype: None = ..., - where: None | _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | None = ..., order: _OrderKACF = ..., subok: bool = ..., - signature: str | tuple[None | str, ...] = ..., + signature: str | tuple[str | None, ...] = ..., casting: _CastingKind = ..., ) -> NDArray[_SCT]: ... @overload def clip( a: ArrayLike, - a_min: None | ArrayLike, - a_max: None | ArrayLike, + a_min: ArrayLike | None, + a_max: ArrayLike | None, out: None = ..., *, - min: None | ArrayLike = ..., - max: None | ArrayLike = ..., + min: ArrayLike | None = ..., + max: ArrayLike | None = ..., dtype: None = ..., - where: None | _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | None = ..., order: _OrderKACF = ..., subok: bool = ..., - signature: str | tuple[None | str, ...] = ..., + signature: str | tuple[str | None, ...] = ..., casting: _CastingKind = ..., ) -> NDArray[Any]: ... @overload def clip( a: ArrayLike, - a_min: None | ArrayLike, - a_max: None | ArrayLike, - out: _ArrayType = ..., + a_min: ArrayLike | None, + a_max: ArrayLike | None, + out: _ArrayT, *, - min: None | ArrayLike = ..., - max: None | ArrayLike = ..., - dtype: DTypeLike, - where: None | _ArrayLikeBool_co = ..., + min: ArrayLike | None = ..., + max: ArrayLike | None = ..., + dtype: DTypeLike = ..., + where: _ArrayLikeBool_co | None = ..., order: _OrderKACF = ..., subok: bool = ..., - signature: str | tuple[None | str, ...] = ..., + signature: str | tuple[str | None, ...] = ..., casting: _CastingKind = ..., -) -> Any: ... +) -> _ArrayT: ... @overload def clip( a: ArrayLike, - a_min: None | ArrayLike, - a_max: None | ArrayLike, - out: _ArrayType, + a_min: ArrayLike | None, + a_max: ArrayLike | None, + out: ArrayLike = ..., *, - min: None | ArrayLike = ..., - max: None | ArrayLike = ..., - dtype: DTypeLike = ..., - where: None | _ArrayLikeBool_co = ..., + min: ArrayLike | None = ..., + max: ArrayLike | None = ..., + dtype: DTypeLike, + where: _ArrayLikeBool_co | None = ..., order: _OrderKACF = ..., subok: bool = ..., - signature: str | tuple[None | str, ...] = ..., + signature: str | tuple[str | None, ...] = ..., casting: _CastingKind = ..., -) -> _ArrayType: ... +) -> Any: ... @overload def sum( @@ -706,7 +751,7 @@ def sum( @overload def sum( a: ArrayLike, - axis: None | _ShapeLike, + axis: _ShapeLike | None, dtype: _DTypeLike[_SCT], out: None = ..., keepdims: bool = ..., @@ -716,7 +761,7 @@ def sum( @overload def sum( a: ArrayLike, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., *, dtype: _DTypeLike[_SCT], out: None = ..., @@ -727,7 +772,7 @@ def sum( @overload def sum( a: ArrayLike, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., dtype: DTypeLike = ..., out: None = ..., keepdims: bool = ..., @@ -737,130 +782,157 @@ def sum( @overload def sum( a: ArrayLike, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None, + dtype: DTypeLike, + out: _ArrayT, + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> _ArrayT: ... +@overload +def sum( + a: ArrayLike, + axis: _ShapeLike | None = ..., dtype: DTypeLike = ..., - out: _ArrayType = ..., + *, + out: _ArrayT, keepdims: bool = ..., initial: _NumberLike_co = ..., where: _ArrayLikeBool_co = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... @overload def all( a: ArrayLike, axis: None = None, out: None = None, - keepdims: Literal[False, 0] = False, + keepdims: Literal[False, 0] | _NoValueType = ..., *, - where: _ArrayLikeBool_co = True, + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> np.bool: ... @overload def all( a: ArrayLike, - axis: None | int | tuple[int, ...] = None, + axis: int | tuple[int, ...] | None = None, out: None = None, - keepdims: SupportsIndex = False, + keepdims: _BoolLike_co | _NoValueType = ..., *, - where: _ArrayLikeBool_co = True, + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> np.bool | NDArray[np.bool]: ... @overload def all( a: ArrayLike, - axis: None | int | tuple[int, ...], - out: _ArrayType, - keepdims: SupportsIndex = False, + axis: int | tuple[int, ...] | None, + out: _ArrayT, + keepdims: _BoolLike_co | _NoValueType = ..., *, - where: _ArrayLikeBool_co = True, -) -> _ArrayType: ... + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _ArrayT: ... @overload def all( a: ArrayLike, - axis: None | int | tuple[int, ...] = None, + axis: int | tuple[int, ...] | None = None, *, - out: _ArrayType, - keepdims: SupportsIndex = False, - where: _ArrayLikeBool_co = True, -) -> _ArrayType: ... + out: _ArrayT, + keepdims: _BoolLike_co | _NoValueType = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _ArrayT: ... @overload def any( a: ArrayLike, axis: None = None, out: None = None, - keepdims: Literal[False, 0] = False, + keepdims: Literal[False, 0] | _NoValueType = ..., *, - where: _ArrayLikeBool_co = True, + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> np.bool: ... @overload def any( a: ArrayLike, - axis: None | int | tuple[int, ...] = None, + axis: int | tuple[int, ...] | None = None, out: None = None, - keepdims: SupportsIndex = False, + keepdims: _BoolLike_co | _NoValueType = ..., *, - where: _ArrayLikeBool_co = True, + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> np.bool | NDArray[np.bool]: ... @overload def any( a: ArrayLike, - axis: None | int | tuple[int, ...], - out: _ArrayType, - keepdims: SupportsIndex = False, + axis: int | tuple[int, ...] | None, + out: _ArrayT, + keepdims: _BoolLike_co | _NoValueType = ..., *, - where: _ArrayLikeBool_co = True, -) -> _ArrayType: ... + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _ArrayT: ... @overload def any( a: ArrayLike, - axis: None | int | tuple[int, ...] = None, + axis: int | tuple[int, ...] | None = None, *, - out: _ArrayType, - keepdims: SupportsIndex = False, - where: _ArrayLikeBool_co = True, -) -> _ArrayType: ... + out: _ArrayT, + keepdims: _BoolLike_co | _NoValueType = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _ArrayT: ... @overload def cumsum( a: _ArrayLike[_SCT], - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., ) -> NDArray[_SCT]: ... @overload def cumsum( a: ArrayLike, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., ) -> NDArray[Any]: ... @overload def cumsum( a: ArrayLike, - axis: None | SupportsIndex = ..., - dtype: _DTypeLike[_SCT] = ..., + axis: SupportsIndex | None, + dtype: _DTypeLike[_SCT], + out: None = ..., +) -> NDArray[_SCT]: ... +@overload +def cumsum( + a: ArrayLike, + axis: SupportsIndex | None = ..., + *, + dtype: _DTypeLike[_SCT], out: None = ..., ) -> NDArray[_SCT]: ... @overload def cumsum( a: ArrayLike, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: DTypeLike = ..., out: None = ..., ) -> NDArray[Any]: ... @overload def cumsum( a: ArrayLike, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None, + dtype: DTypeLike, + out: _ArrayT, +) -> _ArrayT: ... +@overload +def cumsum( + a: ArrayLike, + axis: SupportsIndex | None = ..., dtype: DTypeLike = ..., - out: _ArrayType = ..., -) -> _ArrayType: ... + *, + out: _ArrayT, +) -> _ArrayT: ... @overload def cumulative_sum( x: _ArrayLike[_SCT], /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., include_initial: bool = ..., @@ -870,7 +942,7 @@ def cumulative_sum( x: ArrayLike, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., include_initial: bool = ..., @@ -880,8 +952,8 @@ def cumulative_sum( x: ArrayLike, /, *, - axis: None | SupportsIndex = ..., - dtype: _DTypeLike[_SCT] = ..., + axis: SupportsIndex | None = ..., + dtype: _DTypeLike[_SCT], out: None = ..., include_initial: bool = ..., ) -> NDArray[_SCT]: ... @@ -890,7 +962,7 @@ def cumulative_sum( x: ArrayLike, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: DTypeLike = ..., out: None = ..., include_initial: bool = ..., @@ -900,11 +972,11 @@ def cumulative_sum( x: ArrayLike, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: DTypeLike = ..., - out: _ArrayType = ..., + out: _ArrayT, include_initial: bool = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... @overload def ptp( @@ -916,17 +988,25 @@ def ptp( @overload def ptp( a: ArrayLike, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., out: None = ..., keepdims: bool = ..., ) -> Any: ... @overload def ptp( a: ArrayLike, - axis: None | _ShapeLike = ..., - out: _ArrayType = ..., + axis: _ShapeLike | None, + out: _ArrayT, keepdims: bool = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... +@overload +def ptp( + a: ArrayLike, + axis: _ShapeLike | None = ..., + *, + out: _ArrayT, + keepdims: bool = ..., +) -> _ArrayT: ... @overload def amax( @@ -940,7 +1020,7 @@ def amax( @overload def amax( a: ArrayLike, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., out: None = ..., keepdims: bool = ..., initial: _NumberLike_co = ..., @@ -949,12 +1029,22 @@ def amax( @overload def amax( a: ArrayLike, - axis: None | _ShapeLike = ..., - out: _ArrayType = ..., + axis: _ShapeLike | None, + out: _ArrayT, keepdims: bool = ..., initial: _NumberLike_co = ..., where: _ArrayLikeBool_co = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... +@overload +def amax( + a: ArrayLike, + axis: _ShapeLike | None = ..., + *, + out: _ArrayT, + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> _ArrayT: ... @overload def amin( @@ -968,7 +1058,7 @@ def amin( @overload def amin( a: ArrayLike, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., out: None = ..., keepdims: bool = ..., initial: _NumberLike_co = ..., @@ -977,12 +1067,22 @@ def amin( @overload def amin( a: ArrayLike, - axis: None | _ShapeLike = ..., - out: _ArrayType = ..., + axis: _ShapeLike | None, + out: _ArrayT, keepdims: bool = ..., initial: _NumberLike_co = ..., where: _ArrayLikeBool_co = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... +@overload +def amin( + a: ArrayLike, + axis: _ShapeLike | None = ..., + *, + out: _ArrayT, + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> _ArrayT: ... # TODO: `np.prod()``: For object arrays `initial` does not necessarily # have to be a numerical scalar. @@ -1044,7 +1144,7 @@ def prod( @overload def prod( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., dtype: None = ..., out: None = ..., keepdims: bool = ..., @@ -1052,10 +1152,21 @@ def prod( where: _ArrayLikeBool_co = ..., ) -> Any: ... @overload +def prod( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: None, + dtype: _DTypeLike[_SCT], + out: None = ..., + keepdims: Literal[False] = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> _SCT: ... +@overload def prod( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, axis: None = ..., - dtype: _DTypeLike[_SCT] = ..., + *, + dtype: _DTypeLike[_SCT], out: None = ..., keepdims: Literal[False] = ..., initial: _NumberLike_co = ..., @@ -1064,8 +1175,8 @@ def prod( @overload def prod( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., - dtype: None | DTypeLike = ..., + axis: _ShapeLike | None = ..., + dtype: DTypeLike | None = ..., out: None = ..., keepdims: bool = ..., initial: _NumberLike_co = ..., @@ -1074,84 +1185,111 @@ def prod( @overload def prod( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., - dtype: None | DTypeLike = ..., - out: _ArrayType = ..., + axis: _ShapeLike | None, + dtype: DTypeLike | None, + out: _ArrayT, + keepdims: bool = ..., + initial: _NumberLike_co = ..., + where: _ArrayLikeBool_co = ..., +) -> _ArrayT: ... +@overload +def prod( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: _ShapeLike | None = ..., + dtype: DTypeLike | None = ..., + *, + out: _ArrayT, keepdims: bool = ..., initial: _NumberLike_co = ..., where: _ArrayLikeBool_co = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... @overload def cumprod( a: _ArrayLikeBool_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., ) -> NDArray[int_]: ... @overload def cumprod( a: _ArrayLikeUInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., ) -> NDArray[uint64]: ... @overload def cumprod( a: _ArrayLikeInt_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., ) -> NDArray[int64]: ... @overload def cumprod( a: _ArrayLikeFloat_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., ) -> NDArray[floating[Any]]: ... @overload def cumprod( a: _ArrayLikeComplex_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., ) -> NDArray[complexfloating[Any, Any]]: ... @overload def cumprod( a: _ArrayLikeObject_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., ) -> NDArray[object_]: ... @overload def cumprod( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | SupportsIndex = ..., - dtype: _DTypeLike[_SCT] = ..., + axis: SupportsIndex | None, + dtype: _DTypeLike[_SCT], + out: None = ..., +) -> NDArray[_SCT]: ... +@overload +def cumprod( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: SupportsIndex | None = ..., + *, + dtype: _DTypeLike[_SCT], out: None = ..., ) -> NDArray[_SCT]: ... @overload def cumprod( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: DTypeLike = ..., out: None = ..., ) -> NDArray[Any]: ... @overload def cumprod( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None, + dtype: DTypeLike, + out: _ArrayT, +) -> _ArrayT: ... +@overload +def cumprod( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: SupportsIndex | None = ..., dtype: DTypeLike = ..., - out: _ArrayType = ..., -) -> _ArrayType: ... + *, + out: _ArrayT, +) -> _ArrayT: ... @overload def cumulative_prod( x: _ArrayLikeBool_co, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., include_initial: bool = ..., @@ -1161,7 +1299,7 @@ def cumulative_prod( x: _ArrayLikeUInt_co, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., include_initial: bool = ..., @@ -1171,7 +1309,7 @@ def cumulative_prod( x: _ArrayLikeInt_co, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., include_initial: bool = ..., @@ -1181,7 +1319,7 @@ def cumulative_prod( x: _ArrayLikeFloat_co, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., include_initial: bool = ..., @@ -1191,7 +1329,7 @@ def cumulative_prod( x: _ArrayLikeComplex_co, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., include_initial: bool = ..., @@ -1201,7 +1339,7 @@ def cumulative_prod( x: _ArrayLikeObject_co, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: None = ..., out: None = ..., include_initial: bool = ..., @@ -1211,8 +1349,8 @@ def cumulative_prod( x: _ArrayLikeComplex_co | _ArrayLikeObject_co, /, *, - axis: None | SupportsIndex = ..., - dtype: _DTypeLike[_SCT] = ..., + axis: SupportsIndex | None = ..., + dtype: _DTypeLike[_SCT], out: None = ..., include_initial: bool = ..., ) -> NDArray[_SCT]: ... @@ -1221,7 +1359,7 @@ def cumulative_prod( x: _ArrayLikeComplex_co | _ArrayLikeObject_co, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: DTypeLike = ..., out: None = ..., include_initial: bool = ..., @@ -1231,15 +1369,15 @@ def cumulative_prod( x: _ArrayLikeComplex_co | _ArrayLikeObject_co, /, *, - axis: None | SupportsIndex = ..., + axis: SupportsIndex | None = ..., dtype: DTypeLike = ..., - out: _ArrayType = ..., + out: _ArrayT, include_initial: bool = ..., -) -> _ArrayType: ... +) -> _ArrayT: ... def ndim(a: ArrayLike) -> int: ... -def size(a: ArrayLike, axis: None | int = ...) -> int: ... +def size(a: ArrayLike, axis: int | None = ...) -> int: ... @overload def around( @@ -1278,11 +1416,18 @@ def around( out: None = ..., ) -> NDArray[Any]: ... @overload +def around( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + decimals: SupportsIndex, + out: _ArrayT, +) -> _ArrayT: ... +@overload def around( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, decimals: SupportsIndex = ..., - out: _ArrayType = ..., -) -> _ArrayType: ... + *, + out: _ArrayT, +) -> _ArrayT: ... @overload def mean( @@ -1290,9 +1435,9 @@ def mean( axis: None = ..., dtype: None = ..., out: None = ..., - keepdims: Literal[False] = ..., + keepdims: Literal[False] | _NoValueType = ..., *, - where: _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> floating[Any]: ... @overload def mean( @@ -1300,9 +1445,9 @@ def mean( axis: None = ..., dtype: None = ..., out: None = ..., - keepdims: Literal[False] = ..., + keepdims: Literal[False] | _NoValueType = ..., *, - where: _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> complexfloating[Any, Any]: ... @overload def mean( @@ -1310,40 +1455,40 @@ def mean( axis: None = ..., dtype: None = ..., out: None = ..., - keepdims: Literal[False] = ..., + keepdims: Literal[False] | _NoValueType = ..., *, - where: _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> timedelta64: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., dtype: None = ..., out: None = ..., - keepdims: bool = ..., + keepdims: bool | _NoValueType = ..., *, - where: _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> Any: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None = ..., - dtype: _DTypeLike[_SCT] = ..., + axis: None, + dtype: _DTypeLike[_SCT], out: None = ..., - keepdims: Literal[False] = ..., + keepdims: bool | _NoValueType = ..., *, - where: _ArrayLikeBool_co = ..., -) -> _SCT: ... + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _SCT | NDArray[_SCT]: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None, + axis: None = ..., + *, dtype: _DTypeLike[_SCT], out: None = ..., - keepdims: bool = ..., - *, - where: _ArrayLikeBool_co = ..., -) -> _SCT | NDArray[_SCT]: ... + keepdims: Literal[False] | _NoValueType = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _SCT: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, @@ -1351,29 +1496,39 @@ def mean( *, dtype: _DTypeLike[_SCT], out: None = ..., - keepdims: bool = ..., - where: _ArrayLikeBool_co = ..., + keepdims: bool | _NoValueType = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> _SCT | NDArray[_SCT]: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., dtype: DTypeLike = ..., out: None = ..., - keepdims: bool = ..., + keepdims: bool | _NoValueType = ..., *, - where: _ArrayLikeBool_co = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., ) -> Any: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None, + dtype: DTypeLike, + out: _ArrayT, + keepdims: bool | _NoValueType = ..., + *, + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _ArrayT: ... +@overload +def mean( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: _ShapeLike | None = ..., dtype: DTypeLike = ..., - out: _ArrayType = ..., - keepdims: bool = ..., *, - where: _ArrayLikeBool_co = ..., -) -> _ArrayType: ... + out: _ArrayT, + keepdims: bool | _NoValueType = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _ArrayT: ... @overload def std( @@ -1381,65 +1536,91 @@ def std( axis: None = ..., dtype: None = ..., out: None = ..., - ddof: int | float = ..., + ddof: float = ..., keepdims: Literal[False] = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co = ..., - correction: int | float = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _NoValueType = ..., + correction: float | _NoValueType = ..., ) -> floating[Any]: ... @overload def std( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., dtype: None = ..., out: None = ..., - ddof: int | float = ..., + ddof: float = ..., keepdims: bool = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., - correction: int | float = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., ) -> Any: ... @overload def std( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None = ..., - dtype: _DTypeLike[_SCT] = ..., + axis: None, + dtype: _DTypeLike[_SCT], out: None = ..., - ddof: int | float = ..., + ddof: float = ..., keepdims: Literal[False] = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., - correction: int | float = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., ) -> _SCT: ... @overload def std( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., + axis: None = ..., + *, + dtype: _DTypeLike[_SCT], + out: None = ..., + ddof: float = ..., + keepdims: Literal[False] = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., +) -> _SCT: ... +@overload +def std( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: _ShapeLike | None = ..., dtype: DTypeLike = ..., out: None = ..., - ddof: int | float = ..., + ddof: float = ..., keepdims: bool = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., - correction: int | float = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., ) -> Any: ... @overload def std( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., - dtype: DTypeLike = ..., - out: _ArrayType = ..., - ddof: int | float = ..., + axis: _ShapeLike | None, + dtype: DTypeLike, + out: _ArrayT, + ddof: float = ..., keepdims: bool = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., - correction: int | float = ..., -) -> _ArrayType: ... + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., +) -> _ArrayT: ... +@overload +def std( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: _ShapeLike | None = ..., + dtype: DTypeLike = ..., + *, + out: _ArrayT, + ddof: float = ..., + keepdims: bool = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., +) -> _ArrayT: ... @overload def var( @@ -1447,65 +1628,91 @@ def var( axis: None = ..., dtype: None = ..., out: None = ..., - ddof: int | float = ..., + ddof: float = ..., keepdims: Literal[False] = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co = ..., - correction: int | float = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _NoValueType = ..., + correction: float | _NoValueType = ..., ) -> floating[Any]: ... @overload def var( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., dtype: None = ..., out: None = ..., - ddof: int | float = ..., + ddof: float = ..., keepdims: bool = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., - correction: int | float = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., ) -> Any: ... @overload def var( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None = ..., - dtype: _DTypeLike[_SCT] = ..., + axis: None, + dtype: _DTypeLike[_SCT], out: None = ..., - ddof: int | float = ..., + ddof: float = ..., keepdims: Literal[False] = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., - correction: int | float = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., +) -> _SCT: ... +@overload +def var( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: None = ..., + *, + dtype: _DTypeLike[_SCT], + out: None = ..., + ddof: float = ..., + keepdims: Literal[False] = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., ) -> _SCT: ... @overload def var( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., + axis: _ShapeLike | None = ..., dtype: DTypeLike = ..., out: None = ..., - ddof: int | float = ..., + ddof: float = ..., keepdims: bool = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., - correction: int | float = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., ) -> Any: ... @overload def var( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., - dtype: DTypeLike = ..., - out: _ArrayType = ..., - ddof: int | float = ..., + axis: _ShapeLike | None, + dtype: DTypeLike, + out: _ArrayT, + ddof: float = ..., keepdims: bool = ..., *, - where: _ArrayLikeBool_co = ..., - mean: _ArrayLikeComplex_co | _ArrayLikeObject_co = ..., - correction: int | float = ..., -) -> _ArrayType: ... + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., +) -> _ArrayT: ... +@overload +def var( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: _ShapeLike | None = ..., + dtype: DTypeLike = ..., + *, + out: _ArrayT, + ddof: float = ..., + keepdims: bool = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., + mean: _ArrayLikeComplex_co | _ArrayLikeObject_co | _NoValueType = ..., + correction: float | _NoValueType = ..., +) -> _ArrayT: ... max = amax min = amin diff --git a/numpy/_core/function_base.pyi b/numpy/_core/function_base.pyi index 1d7ea3a2792e..12fdf677d0f5 100644 --- a/numpy/_core/function_base.pyi +++ b/numpy/_core/function_base.pyi @@ -29,8 +29,8 @@ def linspace( dtype: None = ..., axis: SupportsIndex = ..., *, - device: None | L["cpu"] = ..., -) -> NDArray[floating[Any]]: ... + device: L["cpu"] | None = ..., +) -> NDArray[floating]: ... @overload def linspace( start: _ArrayLikeComplex_co, @@ -41,8 +41,20 @@ def linspace( dtype: None = ..., axis: SupportsIndex = ..., *, - device: None | L["cpu"] = ..., -) -> NDArray[complexfloating[Any, Any]]: ... + device: L["cpu"] | None = ..., +) -> NDArray[complexfloating]: ... +@overload +def linspace( + start: _ArrayLikeComplex_co, + stop: _ArrayLikeComplex_co, + num: SupportsIndex, + endpoint: bool, + retstep: L[False], + dtype: _DTypeLike[_SCT], + axis: SupportsIndex = ..., + *, + device: L["cpu"] | None = ..., +) -> NDArray[_SCT]: ... @overload def linspace( start: _ArrayLikeComplex_co, @@ -50,10 +62,10 @@ def linspace( num: SupportsIndex = ..., endpoint: bool = ..., retstep: L[False] = ..., - dtype: _DTypeLike[_SCT] = ..., - axis: SupportsIndex = ..., *, - device: None | L["cpu"] = ..., + dtype: _DTypeLike[_SCT], + axis: SupportsIndex = ..., + device: L["cpu"] | None = ..., ) -> NDArray[_SCT]: ... @overload def linspace( @@ -65,7 +77,7 @@ def linspace( dtype: DTypeLike = ..., axis: SupportsIndex = ..., *, - device: None | L["cpu"] = ..., + device: L["cpu"] | None = ..., ) -> NDArray[Any]: ... @overload def linspace( @@ -73,35 +85,35 @@ def linspace( stop: _ArrayLikeFloat_co, num: SupportsIndex = ..., endpoint: bool = ..., - retstep: L[True] = ..., + *, + retstep: L[True], dtype: None = ..., axis: SupportsIndex = ..., - *, - device: None | L["cpu"] = ..., -) -> tuple[NDArray[floating[Any]], floating[Any]]: ... + device: L["cpu"] | None = ..., +) -> tuple[NDArray[floating], floating]: ... @overload def linspace( start: _ArrayLikeComplex_co, stop: _ArrayLikeComplex_co, num: SupportsIndex = ..., endpoint: bool = ..., - retstep: L[True] = ..., + *, + retstep: L[True], dtype: None = ..., axis: SupportsIndex = ..., - *, - device: None | L["cpu"] = ..., -) -> tuple[NDArray[complexfloating[Any, Any]], complexfloating[Any, Any]]: ... + device: L["cpu"] | None = ..., +) -> tuple[NDArray[complexfloating], complexfloating]: ... @overload def linspace( start: _ArrayLikeComplex_co, stop: _ArrayLikeComplex_co, num: SupportsIndex = ..., endpoint: bool = ..., - retstep: L[True] = ..., - dtype: _DTypeLike[_SCT] = ..., - axis: SupportsIndex = ..., *, - device: None | L["cpu"] = ..., + retstep: L[True], + dtype: _DTypeLike[_SCT], + axis: SupportsIndex = ..., + device: L["cpu"] | None = ..., ) -> tuple[NDArray[_SCT], _SCT]: ... @overload def linspace( @@ -109,11 +121,11 @@ def linspace( stop: _ArrayLikeComplex_co, num: SupportsIndex = ..., endpoint: bool = ..., - retstep: L[True] = ..., + *, + retstep: L[True], dtype: DTypeLike = ..., axis: SupportsIndex = ..., - *, - device: None | L["cpu"] = ..., + device: L["cpu"] | None = ..., ) -> tuple[NDArray[Any], Any]: ... @overload @@ -125,7 +137,7 @@ def logspace( base: _ArrayLikeFloat_co = ..., dtype: None = ..., axis: SupportsIndex = ..., -) -> NDArray[floating[Any]]: ... +) -> NDArray[floating]: ... @overload def logspace( start: _ArrayLikeComplex_co, @@ -135,7 +147,17 @@ def logspace( base: _ArrayLikeComplex_co = ..., dtype: None = ..., axis: SupportsIndex = ..., -) -> NDArray[complexfloating[Any, Any]]: ... +) -> NDArray[complexfloating]: ... +@overload +def logspace( + start: _ArrayLikeComplex_co, + stop: _ArrayLikeComplex_co, + num: SupportsIndex, + endpoint: bool, + base: _ArrayLikeComplex_co, + dtype: _DTypeLike[_SCT], + axis: SupportsIndex = ..., +) -> NDArray[_SCT]: ... @overload def logspace( start: _ArrayLikeComplex_co, @@ -143,7 +165,8 @@ def logspace( num: SupportsIndex = ..., endpoint: bool = ..., base: _ArrayLikeComplex_co = ..., - dtype: _DTypeLike[_SCT] = ..., + *, + dtype: _DTypeLike[_SCT], axis: SupportsIndex = ..., ) -> NDArray[_SCT]: ... @overload @@ -165,7 +188,7 @@ def geomspace( endpoint: bool = ..., dtype: None = ..., axis: SupportsIndex = ..., -) -> NDArray[floating[Any]]: ... +) -> NDArray[floating]: ... @overload def geomspace( start: _ArrayLikeComplex_co, @@ -174,14 +197,24 @@ def geomspace( endpoint: bool = ..., dtype: None = ..., axis: SupportsIndex = ..., -) -> NDArray[complexfloating[Any, Any]]: ... +) -> NDArray[complexfloating]: ... +@overload +def geomspace( + start: _ArrayLikeComplex_co, + stop: _ArrayLikeComplex_co, + num: SupportsIndex, + endpoint: bool, + dtype: _DTypeLike[_SCT], + axis: SupportsIndex = ..., +) -> NDArray[_SCT]: ... @overload def geomspace( start: _ArrayLikeComplex_co, stop: _ArrayLikeComplex_co, num: SupportsIndex = ..., endpoint: bool = ..., - dtype: _DTypeLike[_SCT] = ..., + *, + dtype: _DTypeLike[_SCT], axis: SupportsIndex = ..., ) -> NDArray[_SCT]: ... @overload diff --git a/numpy/_core/meson.build b/numpy/_core/meson.build index d32d71adc5dd..ec40c290f59a 100644 --- a/numpy/_core/meson.build +++ b/numpy/_core/meson.build @@ -1335,6 +1335,7 @@ python_sources = [ 'strings.py', 'strings.pyi', 'umath.py', + 'umath.pyi', ] py.install_sources( diff --git a/numpy/_core/numeric.pyi b/numpy/_core/numeric.pyi index d97ee6e4f649..7966d9ac118b 100644 --- a/numpy/_core/numeric.pyi +++ b/numpy/_core/numeric.pyi @@ -47,6 +47,13 @@ from numpy import ( _OrderKACF, _OrderCF, ) +from .fromnumeric import ( + all as all, + any as any, + argpartition as argpartition, + matrix_transpose as matrix_transpose, + mean as mean, +) from .multiarray import ( # re-exports arange, diff --git a/numpy/_core/umath.pyi b/numpy/_core/umath.pyi new file mode 100644 index 000000000000..d9f0d384cf6d --- /dev/null +++ b/numpy/_core/umath.pyi @@ -0,0 +1,197 @@ +from numpy import ( + absolute, + add, + arccos, + arccosh, + arcsin, + arcsinh, + arctan, + arctan2, + arctanh, + bitwise_and, + bitwise_count, + bitwise_or, + bitwise_xor, + cbrt, + ceil, + conj, + conjugate, + copysign, + cos, + cosh, + deg2rad, + degrees, + divide, + divmod, + e, + equal, + euler_gamma, + exp, + exp2, + expm1, + fabs, + float_power, + floor, + floor_divide, + fmax, + fmin, + fmod, + frexp, + frompyfunc, + gcd, + greater, + greater_equal, + heaviside, + hypot, + invert, + isfinite, + isinf, + isnan, + isnat, + lcm, + ldexp, + left_shift, + less, + less_equal, + log, + log1p, + log2, + log10, + logaddexp, + logaddexp2, + logical_and, + logical_not, + logical_or, + logical_xor, + matvec, + maximum, + minimum, + mod, + modf, + multiply, + negative, + nextafter, + not_equal, + pi, + positive, + power, + rad2deg, + radians, + reciprocal, + remainder, + right_shift, + rint, + sign, + signbit, + sin, + sinh, + spacing, + sqrt, + square, + subtract, + tan, + tanh, + true_divide, + trunc, + vecdot, + vecmat, +) + +__all__ = [ + "absolute", + "add", + "arccos", + "arccosh", + "arcsin", + "arcsinh", + "arctan", + "arctan2", + "arctanh", + "bitwise_and", + "bitwise_count", + "bitwise_or", + "bitwise_xor", + "cbrt", + "ceil", + "conj", + "conjugate", + "copysign", + "cos", + "cosh", + "deg2rad", + "degrees", + "divide", + "divmod", + "e", + "equal", + "euler_gamma", + "exp", + "exp2", + "expm1", + "fabs", + "float_power", + "floor", + "floor_divide", + "fmax", + "fmin", + "fmod", + "frexp", + "frompyfunc", + "gcd", + "greater", + "greater_equal", + "heaviside", + "hypot", + "invert", + "isfinite", + "isinf", + "isnan", + "isnat", + "lcm", + "ldexp", + "left_shift", + "less", + "less_equal", + "log", + "log1p", + "log2", + "log10", + "logaddexp", + "logaddexp2", + "logical_and", + "logical_not", + "logical_or", + "logical_xor", + "matvec", + "maximum", + "minimum", + "mod", + "modf", + "multiply", + "negative", + "nextafter", + "not_equal", + "pi", + "positive", + "power", + "rad2deg", + "radians", + "reciprocal", + "remainder", + "right_shift", + "rint", + "sign", + "signbit", + "sin", + "sinh", + "spacing", + "sqrt", + "square", + "subtract", + "tan", + "tanh", + "true_divide", + "trunc", + "vecdot", + "vecmat", +] diff --git a/numpy/lib/_arrayterator_impl.pyi b/numpy/lib/_arrayterator_impl.pyi index 58875b3c9301..c24fe56ac8a9 100644 --- a/numpy/lib/_arrayterator_impl.pyi +++ b/numpy/lib/_arrayterator_impl.pyi @@ -1,51 +1,46 @@ +# pyright: reportIncompatibleMethodOverride=false + from collections.abc import Generator from types import EllipsisType -from typing import ( - Any, - TypeAlias, - TypeVar, - overload, -) +from typing import Any, Final, TypeAlias, overload + +from typing_extensions import TypeVar -from numpy import ndarray, dtype, generic -from numpy._typing import DTypeLike, NDArray, _Shape as _AnyShape +import numpy as np __all__ = ["Arrayterator"] -# TODO: Rename to ``_ShapeType`` -_Shape = TypeVar("_Shape", bound=_AnyShape) -_DType = TypeVar("_DType", bound=dtype[Any]) -_ScalarType = TypeVar("_ScalarType", bound=generic) +_ShapeT_co = TypeVar("_ShapeT_co", bound=tuple[int, ...], covariant=True) +_DTypeT = TypeVar("_DTypeT", bound=np.dtype[Any]) +_DTypeT_co = TypeVar("_DTypeT_co", bound=np.dtype[Any], covariant=True) +_ScalarT = TypeVar("_ScalarT", bound=np.generic) -_Index: TypeAlias = ( - EllipsisType - | int - | slice - | tuple[EllipsisType | int | slice, ...] -) +_AnyIndex: TypeAlias = EllipsisType | int | slice | tuple[EllipsisType | int | slice, ...] # NOTE: In reality `Arrayterator` does not actually inherit from `ndarray`, # but its ``__getattr__` method does wrap around the former and thus has # access to all its methods -class Arrayterator(ndarray[_Shape, _DType]): - var: ndarray[_Shape, _DType] # type: ignore[assignment] - buf_size: None | int - start: list[int] - stop: list[int] - step: list[int] +class Arrayterator(np.ndarray[_ShapeT_co, _DTypeT_co]): + var: np.ndarray[_ShapeT_co, _DTypeT_co] # type: ignore[assignment] + buf_size: Final[int | None] + start: Final[list[int]] + stop: Final[list[int]] + step: Final[list[int]] @property # type: ignore[misc] - def shape(self) -> tuple[int, ...]: ... + def shape(self) -> _ShapeT_co: ... @property - def flat(self: NDArray[_ScalarType]) -> Generator[_ScalarType, None, None]: ... - def __init__( - self, var: ndarray[_Shape, _DType], buf_size: None | int = ... - ) -> None: ... - @overload - def __array__(self, dtype: None = ..., copy: None | bool = ...) -> ndarray[_AnyShape, _DType]: ... + def flat(self: Arrayterator[Any, np.dtype[_ScalarT]]) -> Generator[_ScalarT]: ... # type: ignore[override] + + # + def __init__(self, /, var: np.ndarray[_ShapeT_co, _DTypeT_co], buf_size: int | None = None) -> None: ... + def __getitem__(self, index: _AnyIndex, /) -> Arrayterator[tuple[int, ...], _DTypeT_co]: ... # type: ignore[override] + def __iter__(self) -> Generator[np.ndarray[tuple[int, ...], _DTypeT_co]]: ... + + # + @overload # type: ignore[override] + def __array__(self, /, dtype: None = None, copy: bool | None = None) -> np.ndarray[_ShapeT_co, _DTypeT_co]: ... @overload - def __array__(self, dtype: DTypeLike, copy: None | bool = ...) -> NDArray[Any]: ... - def __getitem__(self, index: _Index) -> Arrayterator[_AnyShape, _DType]: ... - def __iter__(self) -> Generator[ndarray[_AnyShape, _DType], None, None]: ... + def __array__(self, /, dtype: _DTypeT, copy: bool | None = None) -> np.ndarray[_ShapeT_co, _DTypeT]: ... From c694399cda60883d1d088b74e72475b72508c349 Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Sat, 15 Mar 2025 12:52:07 -0600 Subject: [PATCH 35/49] TYP: Backport typing fixes from main (2) (#28533) * TYP: don't use literals in shape-types Partial backport of numpy/numtype#122 and numpy/numtype#152 * TYP: annotate the missing ``ufunc.resolve_dtypes`` method Ported from numpy/numtype#218 * TYP: stub ``numpy._core.overrides`` Ported from numpy/numtype#226 * TYP: stub ``numpy._utils`` Ported from numpy/numtype#225 * TYP: stub ``numpy._core._dtype[_ctypes]`` Ported from numpy/numtype#227 * TYP: stub the remaining ``numpy._core.*`` modules Ported from numpy/numtype#241 * TYP: stub the missing submodules of ``numpy.linalg`` Ported from numpy/numtype#248 * TYP: stub ``numpy._pyinstaller`` Ported from numpy/numtype#264 * TYP: stub ``numpy.fft.helper`` (deprecated) Ported from numpy/numtype#261 * TYP: annotate the missing deprecated ``row_stack`` function Ported from numpy/numtype#223 --------- Co-authored-by: jorenham --- numpy/__init__.pyi | 15 +- numpy/_core/_add_newdocs.pyi | 3 + numpy/_core/_add_newdocs_scalars.pyi | 16 ++ numpy/_core/_dtype.pyi | 58 +++++++ numpy/_core/_dtype_ctypes.pyi | 83 +++++++++++ numpy/_core/_exceptions.pyi | 73 +++++++++ numpy/_core/_machar.pyi | 73 +++++++++ numpy/_core/_methods.pyi | 24 +++ numpy/_core/_simd.pyi | 25 ++++ numpy/_core/_string_helpers.pyi | 12 ++ numpy/_core/fromnumeric.pyi | 19 +-- numpy/_core/meson.build | 11 ++ numpy/_core/multiarray.pyi | 88 ++++++----- numpy/_core/numeric.pyi | 34 ++--- numpy/_core/overrides.pyi | 50 +++++++ numpy/_core/printoptions.pyi | 28 ++++ numpy/_pyinstaller/__init__.pyi | 0 numpy/_pyinstaller/hook-numpy.pyi | 13 ++ numpy/_utils/__init__.pyi | 31 ++++ numpy/_utils/_convertions.pyi | 4 + numpy/_utils/_inspect.pyi | 71 +++++++++ numpy/_utils/_pep440.pyi | 121 +++++++++++++++ numpy/core/_dtype.pyi | 0 numpy/core/_dtype_ctypes.pyi | 0 numpy/core/overrides.pyi | 7 + numpy/fft/_helper.pyi | 57 +++---- numpy/fft/helper.pyi | 22 +++ numpy/fft/meson.build | 1 + numpy/lib/_shape_base_impl.pyi | 27 ++-- numpy/linalg/_linalg.pyi | 7 +- numpy/linalg/_umath_linalg.pyi | 61 ++++++++ numpy/linalg/lapack_lite.pyi | 141 ++++++++++++++++++ numpy/linalg/linalg.pyi | 69 +++++++++ numpy/linalg/meson.build | 3 + numpy/matlib.pyi | 2 +- .../typing/tests/data/reveal/fromnumeric.pyi | 10 +- 36 files changed, 1131 insertions(+), 128 deletions(-) create mode 100644 numpy/_core/_add_newdocs.pyi create mode 100644 numpy/_core/_add_newdocs_scalars.pyi create mode 100644 numpy/_core/_dtype.pyi create mode 100644 numpy/_core/_dtype_ctypes.pyi create mode 100644 numpy/_core/_exceptions.pyi create mode 100644 numpy/_core/_machar.pyi create mode 100644 numpy/_core/_methods.pyi create mode 100644 numpy/_core/_simd.pyi create mode 100644 numpy/_core/_string_helpers.pyi create mode 100644 numpy/_core/overrides.pyi create mode 100644 numpy/_core/printoptions.pyi create mode 100644 numpy/_pyinstaller/__init__.pyi create mode 100644 numpy/_pyinstaller/hook-numpy.pyi create mode 100644 numpy/_utils/__init__.pyi create mode 100644 numpy/_utils/_convertions.pyi create mode 100644 numpy/_utils/_inspect.pyi create mode 100644 numpy/_utils/_pep440.pyi create mode 100644 numpy/core/_dtype.pyi create mode 100644 numpy/core/_dtype_ctypes.pyi create mode 100644 numpy/core/overrides.pyi create mode 100644 numpy/fft/helper.pyi create mode 100644 numpy/linalg/_umath_linalg.pyi create mode 100644 numpy/linalg/lapack_lite.pyi create mode 100644 numpy/linalg/linalg.pyi diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 7f6bd9db55a4..527f71d897ee 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -574,6 +574,7 @@ from numpy.lib._polynomial_impl import ( from numpy.lib._shape_base_impl import ( column_stack, + row_stack, dstack, array_split, split, @@ -730,10 +731,9 @@ __all__ = [ # noqa: RUF022 "histogram2d", "mask_indices", "tril_indices", "tril_indices_from", "triu_indices", "triu_indices_from", # lib._shape_base_impl.__all__ - # NOTE: `row_stack` is omitted because it is deprecated "column_stack", "dstack", "array_split", "split", "hsplit", "vsplit", "dsplit", "apply_over_axes", "expand_dims", "apply_along_axis", "kron", "tile", - "take_along_axis", "put_along_axis", + "take_along_axis", "put_along_axis", "row_stack", # lib._type_check_impl.__all__ "iscomplexobj", "isrealobj", "imag", "iscomplex", "isreal", "nan_to_num", "real", "real_if_close", "typename", "mintypecode", "common_type", @@ -4803,6 +4803,17 @@ class ufunc: # outputs, so we can't type it very precisely. def at(self, /, *args: Any, **kwargs: Any) -> None: ... + # + def resolve_dtypes( + self, + /, + dtypes: tuple[dtype[Any] | type | None, ...], + *, + signature: tuple[dtype[Any] | None, ...] | None = None, + casting: _CastingKind | None = None, + reduction: builtins.bool = False, + ) -> tuple[dtype[Any], ...]: ... + # Parameters: `__name__`, `ntypes` and `identity` absolute: _UFunc_Nin1_Nout1[L['absolute'], L[20], None] add: _UFunc_Nin2_Nout1[L['add'], L[22], L[0]] diff --git a/numpy/_core/_add_newdocs.pyi b/numpy/_core/_add_newdocs.pyi new file mode 100644 index 000000000000..b23c3b1adedd --- /dev/null +++ b/numpy/_core/_add_newdocs.pyi @@ -0,0 +1,3 @@ +from .overrides import get_array_function_like_doc as get_array_function_like_doc + +def refer_to_array_attribute(attr: str, method: bool = True) -> tuple[str, str]: ... diff --git a/numpy/_core/_add_newdocs_scalars.pyi b/numpy/_core/_add_newdocs_scalars.pyi new file mode 100644 index 000000000000..4a06c9b07d74 --- /dev/null +++ b/numpy/_core/_add_newdocs_scalars.pyi @@ -0,0 +1,16 @@ +from collections.abc import Iterable +from typing import Final + +import numpy as np + +possible_aliases: Final[list[tuple[type[np.number], str, str]]] = ... +_system: Final[str] = ... +_machine: Final[str] = ... +_doc_alias_string: Final[str] = ... +_bool_docstring: Final[str] = ... +int_name: str = ... +float_name: str = ... + +def numeric_type_aliases(aliases: list[tuple[str, str]]) -> list[tuple[type[np.number], str, str]]: ... +def add_newdoc_for_scalar_type(obj: str, fixed_aliases: Iterable[str], doc: str) -> None: ... +def _get_platform_and_machine() -> tuple[str, str]: ... diff --git a/numpy/_core/_dtype.pyi b/numpy/_core/_dtype.pyi new file mode 100644 index 000000000000..c3e966e3f517 --- /dev/null +++ b/numpy/_core/_dtype.pyi @@ -0,0 +1,58 @@ +from typing import Any, Final, TypeAlias, TypedDict, overload, type_check_only +from typing import Literal as L + +from typing_extensions import ReadOnly, TypeVar + +import numpy as np + +### + +_T = TypeVar("_T") + +_Name: TypeAlias = L["uint", "int", "complex", "float", "bool", "void", "object", "datetime", "timedelta", "bytes", "str"] + +@type_check_only +class _KindToStemType(TypedDict): + u: ReadOnly[L["uint"]] + i: ReadOnly[L["int"]] + c: ReadOnly[L["complex"]] + f: ReadOnly[L["float"]] + b: ReadOnly[L["bool"]] + V: ReadOnly[L["void"]] + O: ReadOnly[L["object"]] + M: ReadOnly[L["datetime"]] + m: ReadOnly[L["timedelta"]] + S: ReadOnly[L["bytes"]] + U: ReadOnly[L["str"]] + +### + +_kind_to_stem: Final[_KindToStemType] = ... + +# +def _kind_name(dtype: np.dtype[Any]) -> _Name: ... +def __str__(dtype: np.dtype[Any]) -> str: ... +def __repr__(dtype: np.dtype[Any]) -> str: ... + +# +def _isunsized(dtype: np.dtype[Any]) -> bool: ... +def _is_packed(dtype: np.dtype[Any]) -> bool: ... +def _name_includes_bit_suffix(dtype: np.dtype[Any]) -> bool: ... + +# +def _construction_repr(dtype: np.dtype[Any], include_align: bool = False, short: bool = False) -> str: ... +def _scalar_str(dtype: np.dtype[Any], short: bool) -> str: ... +def _byte_order_str(dtype: np.dtype[Any]) -> str: ... +def _datetime_metadata_str(dtype: np.dtype[Any]) -> str: ... +def _struct_dict_str(dtype: np.dtype[Any], includealignedflag: bool) -> str: ... +def _struct_list_str(dtype: np.dtype[Any]) -> str: ... +def _struct_str(dtype: np.dtype[Any], include_align: bool) -> str: ... +def _subarray_str(dtype: np.dtype[Any]) -> str: ... +def _name_get(dtype: np.dtype[Any]) -> str: ... + +# +@overload +def _unpack_field(dtype: np.dtype[Any], offset: int, title: _T) -> tuple[np.dtype[Any], int, _T]: ... +@overload +def _unpack_field(dtype: np.dtype[Any], offset: int, title: None = None) -> tuple[np.dtype[Any], int, None]: ... +def _aligned_offset(offset: int, alignment: int) -> int: ... diff --git a/numpy/_core/_dtype_ctypes.pyi b/numpy/_core/_dtype_ctypes.pyi new file mode 100644 index 000000000000..69438a2c1b4c --- /dev/null +++ b/numpy/_core/_dtype_ctypes.pyi @@ -0,0 +1,83 @@ +import _ctypes +import ctypes as ct +from typing import Any, overload + +import numpy as np + +# +@overload +def dtype_from_ctypes_type(t: type[_ctypes.Array[Any] | _ctypes.Structure]) -> np.dtype[np.void]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_bool]) -> np.dtype[np.bool]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_int8 | ct.c_byte]) -> np.dtype[np.int8]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_uint8 | ct.c_ubyte]) -> np.dtype[np.uint8]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_int16 | ct.c_short]) -> np.dtype[np.int16]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_uint16 | ct.c_ushort]) -> np.dtype[np.uint16]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_int32 | ct.c_int]) -> np.dtype[np.int32]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_uint32 | ct.c_uint]) -> np.dtype[np.uint32]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_ssize_t | ct.c_long]) -> np.dtype[np.int32 | np.int64]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_size_t | ct.c_ulong]) -> np.dtype[np.uint32 | np.uint64]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_int64 | ct.c_longlong]) -> np.dtype[np.int64]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_uint64 | ct.c_ulonglong]) -> np.dtype[np.uint64]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_float]) -> np.dtype[np.float32]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_double]) -> np.dtype[np.float64]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_longdouble]) -> np.dtype[np.longdouble]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.c_char]) -> np.dtype[np.bytes_]: ... +@overload +def dtype_from_ctypes_type(t: type[ct.py_object[Any]]) -> np.dtype[np.object_]: ... + +# NOTE: the complex ctypes on python>=3.14 are not yet supported at runtim, see +# https://github.com/numpy/numpy/issues/28360 + +# +def _from_ctypes_array(t: type[_ctypes.Array[Any]]) -> np.dtype[np.void]: ... +def _from_ctypes_structure(t: type[_ctypes.Structure]) -> np.dtype[np.void]: ... +def _from_ctypes_union(t: type[_ctypes.Union]) -> np.dtype[np.void]: ... + +# keep in sync with `dtype_from_ctypes_type` (minus the first overload) +@overload +def _from_ctypes_scalar(t: type[ct.c_bool]) -> np.dtype[np.bool]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_int8 | ct.c_byte]) -> np.dtype[np.int8]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_uint8 | ct.c_ubyte]) -> np.dtype[np.uint8]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_int16 | ct.c_short]) -> np.dtype[np.int16]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_uint16 | ct.c_ushort]) -> np.dtype[np.uint16]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_int32 | ct.c_int]) -> np.dtype[np.int32]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_uint32 | ct.c_uint]) -> np.dtype[np.uint32]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_ssize_t | ct.c_long]) -> np.dtype[np.int32 | np.int64]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_size_t | ct.c_ulong]) -> np.dtype[np.uint32 | np.uint64]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_int64 | ct.c_longlong]) -> np.dtype[np.int64]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_uint64 | ct.c_ulonglong]) -> np.dtype[np.uint64]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_float]) -> np.dtype[np.float32]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_double]) -> np.dtype[np.float64]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_longdouble]) -> np.dtype[np.longdouble]: ... +@overload +def _from_ctypes_scalar(t: type[ct.c_char]) -> np.dtype[np.bytes_]: ... +@overload +def _from_ctypes_scalar(t: type[ct.py_object[Any]]) -> np.dtype[np.object_]: ... diff --git a/numpy/_core/_exceptions.pyi b/numpy/_core/_exceptions.pyi new file mode 100644 index 000000000000..5abfc779c212 --- /dev/null +++ b/numpy/_core/_exceptions.pyi @@ -0,0 +1,73 @@ +from collections.abc import Iterable +from typing import Any, Final, overload + +from typing_extensions import TypeVar, Unpack + +import numpy as np +from numpy import _CastingKind +from numpy._utils import set_module as set_module + +### + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=tuple[()] | tuple[Any, Any, Unpack[tuple[Any, ...]]]) +_ExceptionT = TypeVar("_ExceptionT", bound=Exception) + +### + +class UFuncTypeError(TypeError): + ufunc: Final[np.ufunc] + def __init__(self, /, ufunc: np.ufunc) -> None: ... + +class _UFuncNoLoopError(UFuncTypeError): + dtypes: tuple[np.dtype[Any], ...] + def __init__(self, /, ufunc: np.ufunc, dtypes: Iterable[np.dtype[Any]]) -> None: ... + +class _UFuncBinaryResolutionError(_UFuncNoLoopError): + dtypes: tuple[np.dtype[Any], np.dtype[Any]] + def __init__(self, /, ufunc: np.ufunc, dtypes: Iterable[np.dtype[Any]]) -> None: ... + +class _UFuncCastingError(UFuncTypeError): + casting: Final[_CastingKind] + from_: Final[np.dtype[Any]] + to: Final[np.dtype[Any]] + def __init__(self, /, ufunc: np.ufunc, casting: _CastingKind, from_: np.dtype[Any], to: np.dtype[Any]) -> None: ... + +class _UFuncInputCastingError(_UFuncCastingError): + in_i: Final[int] + def __init__( + self, + /, + ufunc: np.ufunc, + casting: _CastingKind, + from_: np.dtype[Any], + to: np.dtype[Any], + i: int, + ) -> None: ... + +class _UFuncOutputCastingError(_UFuncCastingError): + out_i: Final[int] + def __init__( + self, + /, + ufunc: np.ufunc, + casting: _CastingKind, + from_: np.dtype[Any], + to: np.dtype[Any], + i: int, + ) -> None: ... + +class _ArrayMemoryError(MemoryError): + shape: tuple[int, ...] + dtype: np.dtype[Any] + def __init__(self, /, shape: tuple[int, ...], dtype: np.dtype[Any]) -> None: ... + @property + def _total_size(self) -> int: ... + @staticmethod + def _size_to_string(num_bytes: int) -> str: ... + +@overload +def _unpack_tuple(tup: tuple[_T]) -> _T: ... +@overload +def _unpack_tuple(tup: _TupleT) -> _TupleT: ... +def _display_as_base(cls: type[_ExceptionT]) -> type[_ExceptionT]: ... diff --git a/numpy/_core/_machar.pyi b/numpy/_core/_machar.pyi new file mode 100644 index 000000000000..5abfc779c212 --- /dev/null +++ b/numpy/_core/_machar.pyi @@ -0,0 +1,73 @@ +from collections.abc import Iterable +from typing import Any, Final, overload + +from typing_extensions import TypeVar, Unpack + +import numpy as np +from numpy import _CastingKind +from numpy._utils import set_module as set_module + +### + +_T = TypeVar("_T") +_TupleT = TypeVar("_TupleT", bound=tuple[()] | tuple[Any, Any, Unpack[tuple[Any, ...]]]) +_ExceptionT = TypeVar("_ExceptionT", bound=Exception) + +### + +class UFuncTypeError(TypeError): + ufunc: Final[np.ufunc] + def __init__(self, /, ufunc: np.ufunc) -> None: ... + +class _UFuncNoLoopError(UFuncTypeError): + dtypes: tuple[np.dtype[Any], ...] + def __init__(self, /, ufunc: np.ufunc, dtypes: Iterable[np.dtype[Any]]) -> None: ... + +class _UFuncBinaryResolutionError(_UFuncNoLoopError): + dtypes: tuple[np.dtype[Any], np.dtype[Any]] + def __init__(self, /, ufunc: np.ufunc, dtypes: Iterable[np.dtype[Any]]) -> None: ... + +class _UFuncCastingError(UFuncTypeError): + casting: Final[_CastingKind] + from_: Final[np.dtype[Any]] + to: Final[np.dtype[Any]] + def __init__(self, /, ufunc: np.ufunc, casting: _CastingKind, from_: np.dtype[Any], to: np.dtype[Any]) -> None: ... + +class _UFuncInputCastingError(_UFuncCastingError): + in_i: Final[int] + def __init__( + self, + /, + ufunc: np.ufunc, + casting: _CastingKind, + from_: np.dtype[Any], + to: np.dtype[Any], + i: int, + ) -> None: ... + +class _UFuncOutputCastingError(_UFuncCastingError): + out_i: Final[int] + def __init__( + self, + /, + ufunc: np.ufunc, + casting: _CastingKind, + from_: np.dtype[Any], + to: np.dtype[Any], + i: int, + ) -> None: ... + +class _ArrayMemoryError(MemoryError): + shape: tuple[int, ...] + dtype: np.dtype[Any] + def __init__(self, /, shape: tuple[int, ...], dtype: np.dtype[Any]) -> None: ... + @property + def _total_size(self) -> int: ... + @staticmethod + def _size_to_string(num_bytes: int) -> str: ... + +@overload +def _unpack_tuple(tup: tuple[_T]) -> _T: ... +@overload +def _unpack_tuple(tup: _TupleT) -> _TupleT: ... +def _display_as_base(cls: type[_ExceptionT]) -> type[_ExceptionT]: ... diff --git a/numpy/_core/_methods.pyi b/numpy/_core/_methods.pyi new file mode 100644 index 000000000000..45e2b8b9f761 --- /dev/null +++ b/numpy/_core/_methods.pyi @@ -0,0 +1,24 @@ +from collections.abc import Callable +from typing import Any, TypeAlias + +from typing_extensions import Concatenate + +import numpy as np + +from . import _exceptions as _exceptions + +### + +_Reduce2: TypeAlias = Callable[Concatenate[object, ...], Any] + +### + +bool_dt: np.dtype[np.bool] = ... +umr_maximum: _Reduce2 = ... +umr_minimum: _Reduce2 = ... +umr_sum: _Reduce2 = ... +umr_prod: _Reduce2 = ... +umr_bitwise_count = np.bitwise_count +umr_any: _Reduce2 = ... +umr_all: _Reduce2 = ... +_complex_to_float: dict[np.dtype[np.complexfloating], np.dtype[np.floating]] = ... diff --git a/numpy/_core/_simd.pyi b/numpy/_core/_simd.pyi new file mode 100644 index 000000000000..70bb7077797e --- /dev/null +++ b/numpy/_core/_simd.pyi @@ -0,0 +1,25 @@ +from types import ModuleType +from typing import TypedDict, type_check_only + +# NOTE: these 5 are only defined on systems with an intel processor +SSE42: ModuleType | None = ... +FMA3: ModuleType | None = ... +AVX2: ModuleType | None = ... +AVX512F: ModuleType | None = ... +AVX512_SKX: ModuleType | None = ... + +baseline: ModuleType | None = ... + +@type_check_only +class SimdTargets(TypedDict): + SSE42: ModuleType | None + AVX2: ModuleType | None + FMA3: ModuleType | None + AVX512F: ModuleType | None + AVX512_SKX: ModuleType | None + baseline: ModuleType | None + +targets: SimdTargets = ... + +def clear_floatstatus() -> None: ... +def get_floatstatus() -> int: ... diff --git a/numpy/_core/_string_helpers.pyi b/numpy/_core/_string_helpers.pyi new file mode 100644 index 000000000000..6a85832b7a93 --- /dev/null +++ b/numpy/_core/_string_helpers.pyi @@ -0,0 +1,12 @@ +from typing import Final + +_all_chars: Final[tuple[str, ...]] = ... +_ascii_upper: Final[tuple[str, ...]] = ... +_ascii_lower: Final[tuple[str, ...]] = ... + +LOWER_TABLE: Final[tuple[str, ...]] = ... +UPPER_TABLE: Final[tuple[str, ...]] = ... + +def english_lower(s: str) -> str: ... +def english_upper(s: str) -> str: ... +def english_capitalize(s: str) -> str: ... diff --git a/numpy/_core/fromnumeric.pyi b/numpy/_core/fromnumeric.pyi index f7207db17b0c..3de05f3db362 100644 --- a/numpy/_core/fromnumeric.pyi +++ b/numpy/_core/fromnumeric.pyi @@ -108,7 +108,6 @@ __all__ = [ _SCT = TypeVar("_SCT", bound=generic) _SCT_uifcO = TypeVar("_SCT_uifcO", bound=number[Any] | object_) _ArrayT = TypeVar("_ArrayT", bound=np.ndarray[Any, Any]) -_SizeType = TypeVar("_SizeType", bound=int) _ShapeType = TypeVar("_ShapeType", bound=tuple[int, ...]) _ShapeType_co = TypeVar("_ShapeType_co", bound=tuple[int, ...], covariant=True) @@ -456,23 +455,19 @@ def searchsorted( sorter: _ArrayLikeInt_co | None = ..., # 1D int array ) -> NDArray[intp]: ... -# unlike `reshape`, `resize` only accepts positive integers, so literal ints can be used +# @overload -def resize(a: _ArrayLike[_SCT], new_shape: _SizeType) -> np.ndarray[tuple[_SizeType], np.dtype[_SCT]]: ... +def resize(a: _ArrayLike[_SCT], new_shape: SupportsIndex | tuple[SupportsIndex]) -> np.ndarray[tuple[int], np.dtype[_SCT]]: ... @overload -def resize(a: _ArrayLike[_SCT], new_shape: SupportsIndex) -> np.ndarray[tuple[int], np.dtype[_SCT]]: ... +def resize(a: _ArrayLike[_SCT], new_shape: _AnyShapeType) -> np.ndarray[_AnyShapeType, np.dtype[_SCT]]: ... @overload -def resize(a: _ArrayLike[_SCT], new_shape: _ShapeType) -> np.ndarray[_ShapeType, np.dtype[_SCT]]: ... +def resize(a: _ArrayLike[_SCT], new_shape: _ShapeLike) -> NDArray[_SCT]: ... @overload -def resize(a: _ArrayLike[_SCT], new_shape: Sequence[SupportsIndex]) -> NDArray[_SCT]: ... +def resize(a: ArrayLike, new_shape: SupportsIndex | tuple[SupportsIndex]) -> np.ndarray[tuple[int], np.dtype[Any]]: ... @overload -def resize(a: ArrayLike, new_shape: _SizeType) -> np.ndarray[tuple[_SizeType], np.dtype[Any]]: ... +def resize(a: ArrayLike, new_shape: _AnyShapeType) -> np.ndarray[_AnyShapeType, np.dtype[Any]]: ... @overload -def resize(a: ArrayLike, new_shape: SupportsIndex) -> np.ndarray[tuple[int], np.dtype[Any]]: ... -@overload -def resize(a: ArrayLike, new_shape: _ShapeType) -> np.ndarray[_ShapeType, np.dtype[Any]]: ... -@overload -def resize(a: ArrayLike, new_shape: Sequence[SupportsIndex]) -> NDArray[Any]: ... +def resize(a: ArrayLike, new_shape: _ShapeLike) -> NDArray[Any]: ... @overload def squeeze( diff --git a/numpy/_core/meson.build b/numpy/_core/meson.build index ec40c290f59a..6b6fbd3490ae 100644 --- a/numpy/_core/meson.build +++ b/numpy/_core/meson.build @@ -1290,17 +1290,26 @@ python_sources = [ '__init__.py', '__init__.pyi', '_add_newdocs.py', + '_add_newdocs.pyi', '_add_newdocs_scalars.py', + '_add_newdocs_scalars.pyi', '_asarray.py', '_asarray.pyi', '_dtype.py', + '_dtype.pyi', '_dtype_ctypes.py', + '_dtype_ctypes.pyi', '_exceptions.py', + '_exceptions.pyi', '_internal.py', '_internal.pyi', '_machar.py', + '_machar.pyi', '_methods.py', + '_methods.pyi', + '_simd.pyi', '_string_helpers.py', + '_string_helpers.pyi', '_type_aliases.py', '_type_aliases.pyi', '_ufunc_config.py', @@ -1327,7 +1336,9 @@ python_sources = [ 'numerictypes.py', 'numerictypes.pyi', 'overrides.py', + 'overrides.pyi', 'printoptions.py', + 'printoptions.pyi', 'records.py', 'records.pyi', 'shape_base.py', diff --git a/numpy/_core/multiarray.pyi b/numpy/_core/multiarray.pyi index 28cf5411645f..b656472dfec7 100644 --- a/numpy/_core/multiarray.pyi +++ b/numpy/_core/multiarray.pyi @@ -49,6 +49,7 @@ from numpy import ( # type: ignore[attr-defined] signedinteger, floating, complexfloating, + _AnyShapeType, _OrderKACF, _OrderCF, _CastingKind, @@ -191,8 +192,6 @@ __all__ = [ "zeros", ] -_T_co = TypeVar("_T_co", covariant=True) -_T_contra = TypeVar("_T_contra", contravariant=True) _SCT = TypeVar("_SCT", bound=generic) _DType = TypeVar("_DType", bound=np.dtype[Any]) _ArrayType = TypeVar("_ArrayType", bound=ndarray[Any, Any]) @@ -206,10 +205,9 @@ _IDType = TypeVar("_IDType") _Nin = TypeVar("_Nin", bound=int) _Nout = TypeVar("_Nout", bound=int) -_SizeType = TypeVar("_SizeType", bound=int) -_ShapeType = TypeVar("_ShapeType", bound=tuple[int, ...]) -_1DArray: TypeAlias = ndarray[tuple[_SizeType], dtype[_SCT]] -_Array: TypeAlias = ndarray[_ShapeType, dtype[_SCT]] +_ShapeT = TypeVar("_ShapeT", bound=tuple[int, ...]) +_Array: TypeAlias = ndarray[_ShapeT, dtype[_SCT]] +_Array1D: TypeAlias = ndarray[tuple[int], dtype[_SCT]] # Valid time units _UnitKind: TypeAlias = L[ @@ -250,70 +248,78 @@ class _ConstructorEmpty(Protocol): # 1-D shape @overload def __call__( - self, /, - shape: _SizeType, + self, + /, + shape: SupportsIndex, dtype: None = ..., order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], - ) -> _Array[tuple[_SizeType], float64]: ... + ) -> _Array1D[float64]: ... @overload def __call__( - self, /, - shape: _SizeType, + self, + /, + shape: SupportsIndex, dtype: _DType | _SupportsDType[_DType], order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], - ) -> ndarray[tuple[_SizeType], _DType]: ... + ) -> ndarray[tuple[int], _DType]: ... @overload def __call__( - self, /, - shape: _SizeType, + self, + /, + shape: SupportsIndex, dtype: type[_SCT], order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], - ) -> _Array[tuple[_SizeType], _SCT]: ... + ) -> _Array1D[_SCT]: ... @overload def __call__( - self, /, - shape: _SizeType, + self, + /, + shape: SupportsIndex, dtype: DTypeLike, order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], - ) -> _Array[tuple[_SizeType], Any]: ... + ) -> _Array1D[Any]: ... # known shape @overload def __call__( - self, /, - shape: _ShapeType, + self, + /, + shape: _AnyShapeType, dtype: None = ..., order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], - ) -> _Array[_ShapeType, float64]: ... + ) -> _Array[_AnyShapeType, float64]: ... @overload def __call__( - self, /, - shape: _ShapeType, + self, + /, + shape: _AnyShapeType, dtype: _DType | _SupportsDType[_DType], order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], - ) -> ndarray[_ShapeType, _DType]: ... + ) -> ndarray[_AnyShapeType, _DType]: ... @overload def __call__( - self, /, - shape: _ShapeType, + self, + /, + shape: _AnyShapeType, dtype: type[_SCT], order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], - ) -> _Array[_ShapeType, _SCT]: ... + ) -> _Array[_AnyShapeType, _SCT]: ... @overload def __call__( - self, /, - shape: _ShapeType, + self, + /, + shape: _AnyShapeType, dtype: DTypeLike, order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], - ) -> _Array[_ShapeType, Any]: ... + ) -> _Array[_AnyShapeType, Any]: ... # unknown shape @overload @@ -1036,7 +1042,7 @@ def arange( # type: ignore[misc] dtype: None = ..., device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, signedinteger[Any]]: ... +) -> _Array1D[signedinteger]: ... @overload def arange( # type: ignore[misc] start: _IntLike_co, @@ -1046,7 +1052,7 @@ def arange( # type: ignore[misc] *, device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, signedinteger[Any]]: ... +) -> _Array1D[signedinteger]: ... @overload def arange( # type: ignore[misc] stop: _FloatLike_co, @@ -1054,7 +1060,7 @@ def arange( # type: ignore[misc] dtype: None = ..., device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, floating[Any]]: ... +) -> _Array1D[floating]: ... @overload def arange( # type: ignore[misc] start: _FloatLike_co, @@ -1064,7 +1070,7 @@ def arange( # type: ignore[misc] *, device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, floating[Any]]: ... +) -> _Array1D[floating]: ... @overload def arange( stop: _TD64Like_co, @@ -1072,7 +1078,7 @@ def arange( dtype: None = ..., device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, timedelta64]: ... +) -> _Array1D[timedelta64]: ... @overload def arange( start: _TD64Like_co, @@ -1082,7 +1088,7 @@ def arange( *, device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, timedelta64]: ... +) -> _Array1D[timedelta64]: ... @overload def arange( # both start and stop must always be specified for datetime64 start: datetime64, @@ -1092,7 +1098,7 @@ def arange( # both start and stop must always be specified for datetime64 *, device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, datetime64]: ... +) -> _Array1D[datetime64]: ... @overload def arange( stop: Any, @@ -1100,7 +1106,7 @@ def arange( dtype: _DTypeLike[_SCT], device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, _SCT]: ... +) -> _Array1D[_SCT]: ... @overload def arange( start: Any, @@ -1110,7 +1116,7 @@ def arange( *, device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, _SCT]: ... +) -> _Array1D[_SCT]: ... @overload def arange( stop: Any, /, @@ -1118,7 +1124,7 @@ def arange( dtype: DTypeLike, device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, Any]: ... +) -> _Array1D[Any]: ... @overload def arange( start: Any, @@ -1128,7 +1134,7 @@ def arange( *, device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., -) -> _1DArray[int, Any]: ... +) -> _Array1D[Any]: ... def datetime_data( dtype: str | _DTypeLike[datetime64] | _DTypeLike[timedelta64], /, diff --git a/numpy/_core/numeric.pyi b/numpy/_core/numeric.pyi index 7966d9ac118b..a5af4d372968 100644 --- a/numpy/_core/numeric.pyi +++ b/numpy/_core/numeric.pyi @@ -44,6 +44,7 @@ from numpy import ( float64, timedelta64, object_, + _AnyShapeType, _OrderKACF, _OrderCF, ) @@ -190,7 +191,6 @@ _T = TypeVar("_T") _SCT = TypeVar("_SCT", bound=generic) _DType = TypeVar("_DType", bound=np.dtype[Any]) _ArrayType = TypeVar("_ArrayType", bound=np.ndarray[Any, Any]) -_SizeType = TypeVar("_SizeType", bound=int) _ShapeType = TypeVar("_ShapeType", bound=tuple[int, ...]) _CorrelateMode: TypeAlias = L["valid", "same", "full"] @@ -303,69 +303,69 @@ def ones_like( # 1-D shape @overload def full( - shape: _SizeType, + shape: SupportsIndex, fill_value: _SCT, dtype: None = ..., order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], -) -> _Array[tuple[_SizeType], _SCT]: ... +) -> _Array[tuple[int], _SCT]: ... @overload def full( - shape: _SizeType, + shape: SupportsIndex, fill_value: Any, dtype: _DType | _SupportsDType[_DType], order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], -) -> np.ndarray[tuple[_SizeType], _DType]: ... +) -> np.ndarray[tuple[int], _DType]: ... @overload def full( - shape: _SizeType, + shape: SupportsIndex, fill_value: Any, dtype: type[_SCT], order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], -) -> _Array[tuple[_SizeType], _SCT]: ... +) -> _Array[tuple[int], _SCT]: ... @overload def full( - shape: _SizeType, + shape: SupportsIndex, fill_value: Any, dtype: None | DTypeLike = ..., order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], -) -> _Array[tuple[_SizeType], Any]: ... +) -> _Array[tuple[int], Any]: ... # known shape @overload def full( - shape: _ShapeType, + shape: _AnyShapeType, fill_value: _SCT, dtype: None = ..., order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], -) -> _Array[_ShapeType, _SCT]: ... +) -> _Array[_AnyShapeType, _SCT]: ... @overload def full( - shape: _ShapeType, + shape: _AnyShapeType, fill_value: Any, dtype: _DType | _SupportsDType[_DType], order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], -) -> np.ndarray[_ShapeType, _DType]: ... +) -> np.ndarray[_AnyShapeType, _DType]: ... @overload def full( - shape: _ShapeType, + shape: _AnyShapeType, fill_value: Any, dtype: type[_SCT], order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], -) -> _Array[_ShapeType, _SCT]: ... +) -> _Array[_AnyShapeType, _SCT]: ... @overload def full( - shape: _ShapeType, + shape: _AnyShapeType, fill_value: Any, dtype: None | DTypeLike = ..., order: _OrderCF = ..., **kwargs: Unpack[_KwargsEmpty], -) -> _Array[_ShapeType, Any]: ... +) -> _Array[_AnyShapeType, Any]: ... # unknown shape @overload def full( diff --git a/numpy/_core/overrides.pyi b/numpy/_core/overrides.pyi new file mode 100644 index 000000000000..9babbcc26a0b --- /dev/null +++ b/numpy/_core/overrides.pyi @@ -0,0 +1,50 @@ +from collections.abc import Callable, Iterable +from typing import Any, Final, NamedTuple + +from typing_extensions import ParamSpec, TypeVar + +from numpy._typing import _SupportsArrayFunc + +_T = TypeVar("_T") +_Tss = ParamSpec("_Tss") +_FuncT = TypeVar("_FuncT", bound=Callable[..., object]) + +### + +ARRAY_FUNCTIONS: set[Callable[..., Any]] = ... +array_function_like_doc: Final[str] = ... + +class ArgSpec(NamedTuple): + args: list[str] + varargs: str | None + keywords: str | None + defaults: tuple[Any, ...] + +def get_array_function_like_doc(public_api: Callable[..., Any], docstring_template: str = "") -> str: ... +def finalize_array_function_like(public_api: _FuncT) -> _FuncT: ... + +# +def verify_matching_signatures( + implementation: Callable[_Tss, object], + dispatcher: Callable[_Tss, Iterable[_SupportsArrayFunc]], +) -> None: ... + +# NOTE: This actually returns a `_ArrayFunctionDispatcher` callable wrapper object, with +# the original wrapped callable stored in the `._implementation` attribute. It checks +# for any `__array_function__` of the values of specific arguments that the dispatcher +# specifies. Since the dispatcher only returns an iterable of passed array-like args, +# this overridable behaviour is impossible to annotate. +def array_function_dispatch( + dispatcher: Callable[_Tss, Iterable[_SupportsArrayFunc]] | None = None, + module: str | None = None, + verify: bool = True, + docs_from_dispatcher: bool = False, +) -> Callable[[_FuncT], _FuncT]: ... + +# +def array_function_from_dispatcher( + implementation: Callable[_Tss, _T], + module: str | None = None, + verify: bool = True, + docs_from_dispatcher: bool = True, +) -> Callable[[Callable[_Tss, Iterable[_SupportsArrayFunc]]], Callable[_Tss, _T]]: ... diff --git a/numpy/_core/printoptions.pyi b/numpy/_core/printoptions.pyi new file mode 100644 index 000000000000..bd7c7b40692d --- /dev/null +++ b/numpy/_core/printoptions.pyi @@ -0,0 +1,28 @@ +from collections.abc import Callable +from contextvars import ContextVar +from typing import Any, Final, TypedDict + +from .arrayprint import _FormatDict + +__all__ = ["format_options"] + +### + +class _FormatOptionsDict(TypedDict): + edgeitems: int + threshold: int + floatmode: str + precision: int + suppress: bool + linewidth: int + nanstr: str + infstr: str + sign: str + formatter: _FormatDict | None + legacy: int + override_repr: Callable[[Any], str] | None + +### + +default_format_options_dict: Final[_FormatOptionsDict] = ... +format_options: ContextVar[_FormatOptionsDict] diff --git a/numpy/_pyinstaller/__init__.pyi b/numpy/_pyinstaller/__init__.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/numpy/_pyinstaller/hook-numpy.pyi b/numpy/_pyinstaller/hook-numpy.pyi new file mode 100644 index 000000000000..2642996dad7e --- /dev/null +++ b/numpy/_pyinstaller/hook-numpy.pyi @@ -0,0 +1,13 @@ +from typing import Final + +# from `PyInstaller.compat` +is_conda: Final[bool] +is_pure_conda: Final[bool] + +# from `PyInstaller.utils.hooks` +def is_module_satisfies(requirements: str, version: None = None, version_attr: None = None) -> bool: ... + +binaries: Final[list[tuple[str, str]]] + +hiddenimports: Final[list[str]] +excludedimports: Final[list[str]] diff --git a/numpy/_utils/__init__.pyi b/numpy/_utils/__init__.pyi new file mode 100644 index 000000000000..ced45bfdeb44 --- /dev/null +++ b/numpy/_utils/__init__.pyi @@ -0,0 +1,31 @@ +from collections.abc import Callable, Iterable +from typing import Protocol, overload, type_check_only + +from _typeshed import IdentityFunction +from typing_extensions import TypeVar + +from ._convertions import asbytes as asbytes +from ._convertions import asunicode as asunicode + +### + +_T = TypeVar("_T") +_HasModuleT = TypeVar("_HasModuleT", bound=_HasModule) + +@type_check_only +class _HasModule(Protocol): + __module__: str + +### + +@overload +def set_module(module: None) -> IdentityFunction: ... +@overload +def set_module(module: _HasModuleT) -> _HasModuleT: ... + +# +def _rename_parameter( + old_names: Iterable[str], + new_names: Iterable[str], + dep_version: str | None = None, +) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ... diff --git a/numpy/_utils/_convertions.pyi b/numpy/_utils/_convertions.pyi new file mode 100644 index 000000000000..6cc599acc94f --- /dev/null +++ b/numpy/_utils/_convertions.pyi @@ -0,0 +1,4 @@ +__all__ = ["asbytes", "asunicode"] + +def asunicode(s: bytes | str) -> str: ... +def asbytes(s: bytes | str) -> str: ... diff --git a/numpy/_utils/_inspect.pyi b/numpy/_utils/_inspect.pyi new file mode 100644 index 000000000000..ba0260d3a593 --- /dev/null +++ b/numpy/_utils/_inspect.pyi @@ -0,0 +1,71 @@ +import types +from collections.abc import Callable, Mapping +from typing import Any, Final, TypeAlias, overload + +from _typeshed import SupportsLenAndGetItem +from typing_extensions import TypeIs, TypeVar + +__all__ = ["formatargspec", "getargspec"] + +### + +_T = TypeVar("_T") +_RT = TypeVar("_RT") + +_StrSeq: TypeAlias = SupportsLenAndGetItem[str] +_NestedSeq: TypeAlias = list[_T | _NestedSeq[_T]] | tuple[_T | _NestedSeq[_T], ...] + +_JoinFunc: TypeAlias = Callable[[list[_T]], _T] +_FormatFunc: TypeAlias = Callable[[_T], str] + +### + +CO_OPTIMIZED: Final = 1 +CO_NEWLOCALS: Final = 2 +CO_VARARGS: Final = 4 +CO_VARKEYWORDS: Final = 8 + +### + +def ismethod(object: object) -> TypeIs[types.MethodType]: ... +def isfunction(object: object) -> TypeIs[types.FunctionType]: ... +def iscode(object: object) -> TypeIs[types.CodeType]: ... + +### + +def getargs(co: types.CodeType) -> tuple[list[str], str | None, str | None]: ... +def getargspec(func: types.MethodType | types.FunctionType) -> tuple[list[str], str | None, str | None, tuple[Any, ...]]: ... +def getargvalues(frame: types.FrameType) -> tuple[list[str], str | None, str | None, dict[str, Any]]: ... + +# +def joinseq(seq: _StrSeq) -> str: ... + +# +@overload +def strseq(object: _NestedSeq[str], convert: Callable[[Any], Any], join: _JoinFunc[str] = ...) -> str: ... +@overload +def strseq(object: _NestedSeq[_T], convert: Callable[[_T], _RT], join: _JoinFunc[_RT]) -> _RT: ... + +# +def formatargspec( + args: _StrSeq, + varargs: str | None = None, + varkw: str | None = None, + defaults: SupportsLenAndGetItem[object] | None = None, + formatarg: _FormatFunc[str] = ..., # str + formatvarargs: _FormatFunc[str] = ..., # "*{}".format + formatvarkw: _FormatFunc[str] = ..., # "**{}".format + formatvalue: _FormatFunc[object] = ..., # "={!r}".format + join: _JoinFunc[str] = ..., # joinseq +) -> str: ... +def formatargvalues( + args: _StrSeq, + varargs: str | None, + varkw: str | None, + locals: Mapping[str, object] | None, + formatarg: _FormatFunc[str] = ..., # str + formatvarargs: _FormatFunc[str] = ..., # "*{}".format + formatvarkw: _FormatFunc[str] = ..., # "**{}".format + formatvalue: _FormatFunc[object] = ..., # "={!r}".format + join: _JoinFunc[str] = ..., # joinseq +) -> str: ... diff --git a/numpy/_utils/_pep440.pyi b/numpy/_utils/_pep440.pyi new file mode 100644 index 000000000000..29dd4c912aa9 --- /dev/null +++ b/numpy/_utils/_pep440.pyi @@ -0,0 +1,121 @@ +import re +from collections.abc import Callable +from typing import ( + Any, + ClassVar, + Final, + Generic, + NamedTuple, + TypeVar, + final, + type_check_only, +) +from typing import ( + Literal as L, +) + +from typing_extensions import TypeIs + +__all__ = ["VERSION_PATTERN", "InvalidVersion", "LegacyVersion", "Version", "parse"] + +### + +_CmpKeyT = TypeVar("_CmpKeyT", bound=tuple[object, ...]) +_CmpKeyT_co = TypeVar("_CmpKeyT_co", bound=tuple[object, ...], default=tuple[Any, ...], covariant=True) + +### + +VERSION_PATTERN: Final[str] = ... + +class InvalidVersion(ValueError): ... + +@type_check_only +@final +class _InfinityType: + def __hash__(self) -> int: ... + def __eq__(self, other: object, /) -> TypeIs[_InfinityType]: ... + def __ne__(self, other: object, /) -> bool: ... + def __lt__(self, other: object, /) -> L[False]: ... + def __le__(self, other: object, /) -> L[False]: ... + def __gt__(self, other: object, /) -> L[True]: ... + def __ge__(self, other: object, /) -> L[True]: ... + def __neg__(self) -> _NegativeInfinityType: ... + +Infinity: Final[_InfinityType] = ... + +@type_check_only +@final +class _NegativeInfinityType: + def __hash__(self) -> int: ... + def __eq__(self, other: object, /) -> TypeIs[_NegativeInfinityType]: ... + def __ne__(self, other: object, /) -> bool: ... + def __lt__(self, other: object, /) -> L[True]: ... + def __le__(self, other: object, /) -> L[True]: ... + def __gt__(self, other: object, /) -> L[False]: ... + def __ge__(self, other: object, /) -> L[False]: ... + def __neg__(self) -> _InfinityType: ... + +NegativeInfinity: Final[_NegativeInfinityType] = ... + +class _Version(NamedTuple): + epoch: int + release: tuple[int, ...] + dev: tuple[str, int] | None + pre: tuple[str, int] | None + post: tuple[str, int] | None + local: tuple[str | int, ...] | None + +class _BaseVersion(Generic[_CmpKeyT_co]): + _key: _CmpKeyT_co + def __hash__(self) -> int: ... + def __eq__(self, other: _BaseVersion, /) -> bool: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __ne__(self, other: _BaseVersion, /) -> bool: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __lt__(self, other: _BaseVersion, /) -> bool: ... + def __le__(self, other: _BaseVersion, /) -> bool: ... + def __ge__(self, other: _BaseVersion, /) -> bool: ... + def __gt__(self, other: _BaseVersion, /) -> bool: ... + def _compare(self, /, other: _BaseVersion[_CmpKeyT], method: Callable[[_CmpKeyT_co, _CmpKeyT], bool]) -> bool: ... + +class LegacyVersion(_BaseVersion[tuple[L[-1], tuple[str, ...]]]): + _version: Final[str] + def __init__(self, /, version: str) -> None: ... + @property + def public(self) -> str: ... + @property + def base_version(self) -> str: ... + @property + def local(self) -> None: ... + @property + def is_prerelease(self) -> L[False]: ... + @property + def is_postrelease(self) -> L[False]: ... + +class Version( + _BaseVersion[ + tuple[ + int, # epoch + tuple[int, ...], # release + tuple[str, int] | _InfinityType | _NegativeInfinityType, # pre + tuple[str, int] | _NegativeInfinityType, # post + tuple[str, int] | _InfinityType, # dev + tuple[tuple[int, L[""]] | tuple[_NegativeInfinityType, str], ...] | _NegativeInfinityType, # local + ], + ], +): + _regex: ClassVar[re.Pattern[str]] = ... + _version: Final[str] + + def __init__(self, /, version: str) -> None: ... + @property + def public(self) -> str: ... + @property + def base_version(self) -> str: ... + @property + def local(self) -> str | None: ... + @property + def is_prerelease(self) -> bool: ... + @property + def is_postrelease(self) -> bool: ... + +# +def parse(version: str) -> Version | LegacyVersion: ... diff --git a/numpy/core/_dtype.pyi b/numpy/core/_dtype.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/numpy/core/_dtype_ctypes.pyi b/numpy/core/_dtype_ctypes.pyi new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/numpy/core/overrides.pyi b/numpy/core/overrides.pyi new file mode 100644 index 000000000000..fab3512626f8 --- /dev/null +++ b/numpy/core/overrides.pyi @@ -0,0 +1,7 @@ +# NOTE: At runtime, this submodule dynamically re-exports any `numpy._core.overrides` +# member, and issues a `DeprecationWarning` when accessed. But since there is no +# `__dir__` or `__all__` present, these annotations would be unverifiable. Because +# this module is also deprecated in favor of `numpy._core`, and therefore not part of +# the public API, we omit the "re-exports", which in practice would require literal +# duplication of the stubs in order for the `@deprecated` decorator to be understood +# by type-checkers. diff --git a/numpy/fft/_helper.pyi b/numpy/fft/_helper.pyi index 5cb28db2239e..7673c1800a92 100644 --- a/numpy/fft/_helper.pyi +++ b/numpy/fft/_helper.pyi @@ -1,51 +1,38 @@ -from typing import Any, TypeVar, overload, Literal as L +from typing import Any, Final, TypeVar, overload +from typing import Literal as L -from numpy import generic, integer, floating, complexfloating -from numpy._typing import ( - NDArray, - ArrayLike, - _ShapeLike, - _ArrayLike, - _ArrayLikeFloat_co, - _ArrayLikeComplex_co, -) +from numpy import complexfloating, floating, generic, integer +from numpy._typing import ArrayLike, NDArray, _ArrayLike, _ArrayLikeComplex_co, _ArrayLikeFloat_co, _ShapeLike -__all__ = ["fftshift", "ifftshift", "fftfreq", "rfftfreq"] +__all__ = ["fftfreq", "fftshift", "ifftshift", "rfftfreq"] _SCT = TypeVar("_SCT", bound=generic) +### + +integer_types: Final[tuple[type[int], type[integer]]] = ... + +### + @overload -def fftshift(x: _ArrayLike[_SCT], axes: None | _ShapeLike = ...) -> NDArray[_SCT]: ... +def fftshift(x: _ArrayLike[_SCT], axes: _ShapeLike | None = None) -> NDArray[_SCT]: ... @overload -def fftshift(x: ArrayLike, axes: None | _ShapeLike = ...) -> NDArray[Any]: ... +def fftshift(x: ArrayLike, axes: _ShapeLike | None = None) -> NDArray[Any]: ... +# @overload -def ifftshift(x: _ArrayLike[_SCT], axes: None | _ShapeLike = ...) -> NDArray[_SCT]: ... +def ifftshift(x: _ArrayLike[_SCT], axes: _ShapeLike | None = None) -> NDArray[_SCT]: ... @overload -def ifftshift(x: ArrayLike, axes: None | _ShapeLike = ...) -> NDArray[Any]: ... +def ifftshift(x: ArrayLike, axes: _ShapeLike | None = None) -> NDArray[Any]: ... +# @overload -def fftfreq( - n: int | integer[Any], - d: _ArrayLikeFloat_co = ..., - device: None | L["cpu"] = ..., -) -> NDArray[floating[Any]]: ... +def fftfreq(n: int | integer, d: _ArrayLikeFloat_co = 1.0, device: L["cpu"] | None = None) -> NDArray[floating]: ... @overload -def fftfreq( - n: int | integer[Any], - d: _ArrayLikeComplex_co = ..., - device: None | L["cpu"] = ..., -) -> NDArray[complexfloating[Any, Any]]: ... +def fftfreq(n: int | integer, d: _ArrayLikeComplex_co = 1.0, device: L["cpu"] | None = None) -> NDArray[complexfloating]: ... +# @overload -def rfftfreq( - n: int | integer[Any], - d: _ArrayLikeFloat_co = ..., - device: None | L["cpu"] = ..., -) -> NDArray[floating[Any]]: ... +def rfftfreq(n: int | integer, d: _ArrayLikeFloat_co = 1.0, device: L["cpu"] | None = None) -> NDArray[floating]: ... @overload -def rfftfreq( - n: int | integer[Any], - d: _ArrayLikeComplex_co = ..., - device: None | L["cpu"] = ..., -) -> NDArray[complexfloating[Any, Any]]: ... +def rfftfreq(n: int | integer, d: _ArrayLikeComplex_co = 1.0, device: L["cpu"] | None = None) -> NDArray[complexfloating]: ... diff --git a/numpy/fft/helper.pyi b/numpy/fft/helper.pyi new file mode 100644 index 000000000000..887cbe7e27c9 --- /dev/null +++ b/numpy/fft/helper.pyi @@ -0,0 +1,22 @@ +from typing import Any +from typing import Literal as L + +from typing_extensions import deprecated + +import numpy as np +from numpy._typing import ArrayLike, NDArray, _ShapeLike + +from ._helper import integer_types as integer_types + +__all__ = ["fftfreq", "fftshift", "ifftshift", "rfftfreq"] + +### + +@deprecated("Please use `numpy.fft.fftshift` instead.") +def fftshift(x: ArrayLike, axes: _ShapeLike | None = None) -> NDArray[Any]: ... +@deprecated("Please use `numpy.fft.ifftshift` instead.") +def ifftshift(x: ArrayLike, axes: _ShapeLike | None = None) -> NDArray[Any]: ... +@deprecated("Please use `numpy.fft.fftfreq` instead.") +def fftfreq(n: int | np.integer, d: ArrayLike = 1.0, device: L["cpu"] | None = None) -> NDArray[Any]: ... +@deprecated("Please use `numpy.fft.rfftfreq` instead.") +def rfftfreq(n: int | np.integer, d: ArrayLike = 1.0, device: L["cpu"] | None = None) -> NDArray[Any]: ... diff --git a/numpy/fft/meson.build b/numpy/fft/meson.build index 751b5dc74d30..e18949af5e31 100644 --- a/numpy/fft/meson.build +++ b/numpy/fft/meson.build @@ -24,6 +24,7 @@ py.install_sources( '_helper.py', '_helper.pyi', 'helper.py', + 'helper.pyi', ], subdir: 'numpy/fft' ) diff --git a/numpy/lib/_shape_base_impl.pyi b/numpy/lib/_shape_base_impl.pyi index 5439c533edff..77e5d2de9cb9 100644 --- a/numpy/lib/_shape_base_impl.pyi +++ b/numpy/lib/_shape_base_impl.pyi @@ -10,20 +10,13 @@ from typing import ( type_check_only, ) +from typing_extensions import deprecated + import numpy as np -from numpy import ( - generic, - integer, - ufunc, - unsignedinteger, - signedinteger, - floating, - complexfloating, - object_, -) -from numpy._core.shape_base import vstack as row_stack +from numpy import _CastingKind, generic, integer, ufunc, unsignedinteger, signedinteger, floating, complexfloating, object_ from numpy._typing import ( ArrayLike, + DTypeLike, NDArray, _ShapeLike, _ArrayLike, @@ -72,6 +65,8 @@ class _SupportsArrayWrap(Protocol): @property def __array_wrap__(self) -> _ArrayWrap: ... +### + def take_along_axis( arr: _SCT | NDArray[_SCT], indices: NDArray[integer[Any]], @@ -119,6 +114,16 @@ def expand_dims( axis: _ShapeLike, ) -> NDArray[Any]: ... +# Deprecated in NumPy 2.0, 2023-08-18 +@deprecated("`row_stack` alias is deprecated. Use `np.vstack` directly.") +def row_stack( + tup: Sequence[ArrayLike], + *, + dtype: DTypeLike | None = None, + casting: _CastingKind = "same_kind", +) -> NDArray[Any]: ... + +# @overload def column_stack(tup: Sequence[_ArrayLike[_SCT]]) -> NDArray[_SCT]: ... @overload diff --git a/numpy/linalg/_linalg.pyi b/numpy/linalg/_linalg.pyi index 8ac75a47f5f8..9f646ec94037 100644 --- a/numpy/linalg/_linalg.pyi +++ b/numpy/linalg/_linalg.pyi @@ -16,7 +16,6 @@ from numpy import ( vecdot, # other - generic, floating, complexfloating, signedinteger, @@ -79,13 +78,13 @@ __all__ = [ "vecdot", ] -_T = TypeVar("_T") _ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) -_SCT2 = TypeVar("_SCT2", bound=generic, covariant=True) -_2Tuple: TypeAlias = tuple[_T, _T] _ModeKind: TypeAlias = L["reduced", "complete", "r", "raw"] +### + +fortran_int = np.intc class EigResult(NamedTuple): eigenvalues: NDArray[Any] diff --git a/numpy/linalg/_umath_linalg.pyi b/numpy/linalg/_umath_linalg.pyi new file mode 100644 index 000000000000..cd07acdb1f9e --- /dev/null +++ b/numpy/linalg/_umath_linalg.pyi @@ -0,0 +1,61 @@ +from typing import Final +from typing import Literal as L + +import numpy as np +from numpy._typing._ufunc import _GUFunc_Nin2_Nout1 + +__version__: Final[str] = ... +_ilp64: Final[bool] = ... + +### +# 1 -> 1 + +# (m,m) -> () +det: Final[np.ufunc] = ... +# (m,m) -> (m) +cholesky_lo: Final[np.ufunc] = ... +cholesky_up: Final[np.ufunc] = ... +eigvals: Final[np.ufunc] = ... +eigvalsh_lo: Final[np.ufunc] = ... +eigvalsh_up: Final[np.ufunc] = ... +# (m,m) -> (m,m) +inv: Final[np.ufunc] = ... +# (m,n) -> (p) +qr_r_raw: Final[np.ufunc] = ... +svd: Final[np.ufunc] = ... + +### +# 1 -> 2 + +# (m,m) -> (), () +slogdet: Final[np.ufunc] = ... +# (m,m) -> (m), (m,m) +eig: Final[np.ufunc] = ... +eigh_lo: Final[np.ufunc] = ... +eigh_up: Final[np.ufunc] = ... + +### +# 2 -> 1 + +# (m,n), (n) -> (m,m) +qr_complete: Final[_GUFunc_Nin2_Nout1[L["qr_complete"], L[2], None, L["(m,n),(n)->(m,m)"]]] = ... +# (m,n), (k) -> (m,k) +qr_reduced: Final[_GUFunc_Nin2_Nout1[L["qr_reduced"], L[2], None, L["(m,n),(k)->(m,k)"]]] = ... +# (m,m), (m,n) -> (m,n) +solve: Final[_GUFunc_Nin2_Nout1[L["solve"], L[4], None, L["(m,m),(m,n)->(m,n)"]]] = ... +# (m,m), (m) -> (m) +solve1: Final[_GUFunc_Nin2_Nout1[L["solve1"], L[4], None, L["(m,m),(m)->(m)"]]] = ... + +### +# 1 -> 3 + +# (m,n) -> (m,m), (p), (n,n) +svd_f: Final[np.ufunc] = ... +# (m,n) -> (m,p), (p), (p,n) +svd_s: Final[np.ufunc] = ... + +### +# 3 -> 4 + +# (m,n), (m,k), () -> (n,k), (k), (), (p) +lstsq: Final[np.ufunc] = ... diff --git a/numpy/linalg/lapack_lite.pyi b/numpy/linalg/lapack_lite.pyi new file mode 100644 index 000000000000..0f6bfa3a022b --- /dev/null +++ b/numpy/linalg/lapack_lite.pyi @@ -0,0 +1,141 @@ +from typing import Any, Final, TypedDict, type_check_only + +import numpy as np +from numpy._typing import NDArray + +from ._linalg import fortran_int + +### + +@type_check_only +class _GELSD(TypedDict): + m: int + n: int + nrhs: int + lda: int + ldb: int + rank: int + lwork: int + info: int + +@type_check_only +class _DGELSD(_GELSD): + dgelsd_: int + rcond: float + +@type_check_only +class _ZGELSD(_GELSD): + zgelsd_: int + +@type_check_only +class _GEQRF(TypedDict): + m: int + n: int + lda: int + lwork: int + info: int + +@type_check_only +class _DGEQRF(_GEQRF): + dgeqrf_: int + +@type_check_only +class _ZGEQRF(_GEQRF): + zgeqrf_: int + +@type_check_only +class _DORGQR(TypedDict): + dorgqr_: int + info: int + +@type_check_only +class _ZUNGQR(TypedDict): + zungqr_: int + info: int + +### + +_ilp64: Final[bool] = ... + +def dgelsd( + m: int, + n: int, + nrhs: int, + a: NDArray[np.float64], + lda: int, + b: NDArray[np.float64], + ldb: int, + s: NDArray[np.float64], + rcond: float, + rank: int, + work: NDArray[np.float64], + lwork: int, + iwork: NDArray[fortran_int], + info: int, +) -> _DGELSD: ... +def zgelsd( + m: int, + n: int, + nrhs: int, + a: NDArray[np.complex128], + lda: int, + b: NDArray[np.complex128], + ldb: int, + s: NDArray[np.float64], + rcond: float, + rank: int, + work: NDArray[np.complex128], + lwork: int, + rwork: NDArray[np.float64], + iwork: NDArray[fortran_int], + info: int, +) -> _ZGELSD: ... + +# +def dgeqrf( + m: int, + n: int, + a: NDArray[np.float64], # in/out, shape: (lda, n) + lda: int, + tau: NDArray[np.float64], # out, shape: (min(m, n),) + work: NDArray[np.float64], # out, shape: (max(1, lwork),) + lwork: int, + info: int, # out +) -> _DGEQRF: ... +def zgeqrf( + m: int, + n: int, + a: NDArray[np.complex128], # in/out, shape: (lda, n) + lda: int, + tau: NDArray[np.complex128], # out, shape: (min(m, n),) + work: NDArray[np.complex128], # out, shape: (max(1, lwork),) + lwork: int, + info: int, # out +) -> _ZGEQRF: ... + +# +def dorgqr( + m: int, # >=0 + n: int, # m >= n >= 0 + k: int, # n >= k >= 0 + a: NDArray[np.float64], # in/out, shape: (lda, n) + lda: int, # >= max(1, m) + tau: NDArray[np.float64], # in, shape: (k,) + work: NDArray[np.float64], # out, shape: (max(1, lwork),) + lwork: int, + info: int, # out +) -> _DORGQR: ... +def zungqr( + m: int, + n: int, + k: int, + a: NDArray[np.complex128], + lda: int, + tau: NDArray[np.complex128], + work: NDArray[np.complex128], + lwork: int, + info: int, +) -> _ZUNGQR: ... + +# +def xerbla(srname: object, info: int) -> None: ... diff --git a/numpy/linalg/linalg.pyi b/numpy/linalg/linalg.pyi new file mode 100644 index 000000000000..dbe9becfb8d5 --- /dev/null +++ b/numpy/linalg/linalg.pyi @@ -0,0 +1,69 @@ +from ._linalg import ( + LinAlgError, + cholesky, + cond, + cross, + det, + diagonal, + eig, + eigh, + eigvals, + eigvalsh, + inv, + lstsq, + matmul, + matrix_norm, + matrix_power, + matrix_rank, + matrix_transpose, + multi_dot, + norm, + outer, + pinv, + qr, + slogdet, + solve, + svd, + svdvals, + tensordot, + tensorinv, + tensorsolve, + trace, + vecdot, + vector_norm, +) + +__all__ = [ + "LinAlgError", + "cholesky", + "cond", + "cross", + "det", + "diagonal", + "eig", + "eigh", + "eigvals", + "eigvalsh", + "inv", + "lstsq", + "matmul", + "matrix_norm", + "matrix_power", + "matrix_rank", + "matrix_transpose", + "multi_dot", + "norm", + "outer", + "pinv", + "qr", + "slogdet", + "solve", + "svd", + "svdvals", + "tensordot", + "tensorinv", + "tensorsolve", + "trace", + "vecdot", + "vector_norm", +] diff --git a/numpy/linalg/meson.build b/numpy/linalg/meson.build index 740c9f56c6fa..e2f8136208d6 100644 --- a/numpy/linalg/meson.build +++ b/numpy/linalg/meson.build @@ -45,7 +45,10 @@ py.install_sources( '__init__.pyi', '_linalg.py', '_linalg.pyi', + '_umath_linalg.pyi', + 'lapack_lite.pyi', 'linalg.py', + 'linalg.pyi', ], subdir: 'numpy/linalg' ) diff --git a/numpy/matlib.pyi b/numpy/matlib.pyi index 67b753a87c32..cce0bf5b0541 100644 --- a/numpy/matlib.pyi +++ b/numpy/matlib.pyi @@ -394,7 +394,7 @@ from numpy import ( roots, rot90, round, - # row_stack, + row_stack, s_, save, savetxt, diff --git a/numpy/typing/tests/data/reveal/fromnumeric.pyi b/numpy/typing/tests/data/reveal/fromnumeric.pyi index 40bb578d0d46..366e34d8af99 100644 --- a/numpy/typing/tests/data/reveal/fromnumeric.pyi +++ b/numpy/typing/tests/data/reveal/fromnumeric.pyi @@ -102,11 +102,11 @@ assert_type(np.searchsorted(AR_f4[0], 0), np.intp) assert_type(np.searchsorted(AR_b[0], [0]), npt.NDArray[np.intp]) assert_type(np.searchsorted(AR_f4[0], [0]), npt.NDArray[np.intp]) -assert_type(np.resize(b, (5, 5)), np.ndarray[tuple[L[5], L[5]], np.dtype[np.bool]]) -assert_type(np.resize(f4, (5, 5)), np.ndarray[tuple[L[5], L[5]], np.dtype[np.float32]]) -assert_type(np.resize(f, (5, 5)), np.ndarray[tuple[L[5], L[5]], np.dtype[Any]]) -assert_type(np.resize(AR_b, (5, 5)), np.ndarray[tuple[L[5], L[5]], np.dtype[np.bool]]) -assert_type(np.resize(AR_f4, (5, 5)), np.ndarray[tuple[L[5], L[5]], np.dtype[np.float32]]) +assert_type(np.resize(b, (5, 5)), np.ndarray[tuple[int, int], np.dtype[np.bool]]) +assert_type(np.resize(f4, (5, 5)), np.ndarray[tuple[int, int], np.dtype[np.float32]]) +assert_type(np.resize(f, (5, 5)), np.ndarray[tuple[int, int], np.dtype[Any]]) +assert_type(np.resize(AR_b, (5, 5)), np.ndarray[tuple[int, int], np.dtype[np.bool]]) +assert_type(np.resize(AR_f4, (5, 5)), np.ndarray[tuple[int, int], np.dtype[np.float32]]) assert_type(np.squeeze(b), np.bool) assert_type(np.squeeze(f4), np.float32) From 29929338af26f8d3ed444364fdbf520a9954f2c9 Mon Sep 17 00:00:00 2001 From: jorenham Date: Fri, 14 Mar 2025 23:00:42 +0100 Subject: [PATCH 36/49] TYP: fix stubtest errors in ``numpy.dtype`` and ``numpy.dtypes.*`` Ported from numpy/numtype#229 --- numpy/__init__.pyi | 15 ++++++++++++++- numpy/dtypes.pyi | 45 +++++++++++++++++++++++---------------------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 527f71d897ee..f8c499410993 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -1196,8 +1196,21 @@ __future_scalars__: Final[set[L["bytes", "str", "object"]]] = ... __array_api_version__: Final[L["2023.12"]] = "2023.12" test: Final[PytestTester] = ... +@type_check_only +class _DTypeMeta(type): + @property + def type(cls, /) -> type[generic] | None: ... + @property + def _abstract(cls, /) -> bool: ... + @property + def _is_numeric(cls, /) -> bool: ... + @property + def _parametric(cls, /) -> bool: ... + @property + def _legacy(cls, /) -> bool: ... + @final -class dtype(Generic[_SCT_co]): +class dtype(Generic[_SCT_co], metaclass=_DTypeMeta): names: None | tuple[builtins.str, ...] def __hash__(self) -> int: ... diff --git a/numpy/dtypes.pyi b/numpy/dtypes.pyi index 5cb345035f2c..11e5611653fa 100644 --- a/numpy/dtypes.pyi +++ b/numpy/dtypes.pyi @@ -1,18 +1,13 @@ -from typing import ( - Any, - Final, - Generic, - Literal as L, - NoReturn, - TypeAlias, - final, - type_check_only, -) +# ruff: noqa: ANN401 +from types import MemberDescriptorType +from typing import Any, ClassVar, Generic, NoReturn, TypeAlias, final, type_check_only +from typing import Literal as L + from typing_extensions import LiteralString, Self, TypeVar import numpy as np -__all__ = [ +__all__ = [ # noqa: RUF022 'BoolDType', 'Int8DType', 'ByteDType', @@ -53,7 +48,7 @@ __all__ = [ _SCT_co = TypeVar("_SCT_co", bound=np.generic, covariant=True) @type_check_only -class _SimpleDType(Generic[_SCT_co], np.dtype[_SCT_co]): # type: ignore[misc] +class _SimpleDType(np.dtype[_SCT_co], Generic[_SCT_co]): # type: ignore[misc] # pyright: ignore[reportGeneralTypeIssues] names: None # pyright: ignore[reportIncompatibleVariableOverride] def __new__(cls, /) -> Self: ... def __getitem__(self, key: Any, /) -> NoReturn: ... @@ -73,7 +68,7 @@ class _SimpleDType(Generic[_SCT_co], np.dtype[_SCT_co]): # type: ignore[misc] def subdtype(self) -> None: ... @type_check_only -class _LiteralDType(Generic[_SCT_co], _SimpleDType[_SCT_co]): # type: ignore[misc] +class _LiteralDType(_SimpleDType[_SCT_co], Generic[_SCT_co]): # type: ignore[misc] @property def flags(self) -> L[0]: ... @property @@ -234,10 +229,11 @@ class UInt64DType( # type: ignore[misc] def str(self) -> L["u8"]: ... # Standard C-named version/alias: -ByteDType: Final = Int8DType -UByteDType: Final = UInt8DType -ShortDType: Final = Int16DType -UShortDType: Final = UInt16DType +# NOTE: Don't make these `Final`: it will break stubtest +ByteDType = Int8DType +UByteDType = UInt8DType +ShortDType = Int16DType +UShortDType = UInt16DType @final class IntDType( # type: ignore[misc] @@ -419,11 +415,11 @@ class ObjectDType( # type: ignore[misc] @final class BytesDType( # type: ignore[misc] - Generic[_ItemSize_co], _TypeCodes[L["S"], L["S"], L[18]], _NoOrder, _NBit[L[1],_ItemSize_co], _SimpleDType[np.bytes_], + Generic[_ItemSize_co], ): def __new__(cls, size: _ItemSize_co, /) -> BytesDType[_ItemSize_co]: ... @property @@ -435,11 +431,11 @@ class BytesDType( # type: ignore[misc] @final class StrDType( # type: ignore[misc] - Generic[_ItemSize_co], _TypeCodes[L["U"], L["U"], L[19]], _NativeOrder, _NBit[L[4],_ItemSize_co], _SimpleDType[np.str_], + Generic[_ItemSize_co], ): def __new__(cls, size: _ItemSize_co, /) -> StrDType[_ItemSize_co]: ... @property @@ -451,11 +447,11 @@ class StrDType( # type: ignore[misc] @final class VoidDType( # type: ignore[misc] - Generic[_ItemSize_co], _TypeCodes[L["V"], L["V"], L[20]], _NoOrder, _NBit[L[1], _ItemSize_co], - np.dtype[np.void], + np.dtype[np.void], # pyright: ignore[reportGeneralTypeIssues] + Generic[_ItemSize_co], ): # NOTE: `VoidDType(...)` raises a `TypeError` at the moment def __new__(cls, length: _ItemSize_co, /) -> NoReturn: ... @@ -578,8 +574,13 @@ class StringDType( # type: ignore[misc] _NativeOrder, _NBit[L[8], L[16]], # TODO: Replace the (invalid) `str` with the scalar type, once implemented - np.dtype[str], # type: ignore[type-var] + np.dtype[str], # type: ignore[type-var] # pyright: ignore[reportGeneralTypeIssues,reportInvalidTypeArguments] ): + @property + def coerce(self) -> L[True]: ... + na_object: ClassVar[MemberDescriptorType] # does not get instantiated + + # def __new__(cls, /) -> StringDType: ... def __getitem__(self, key: Any, /) -> NoReturn: ... @property From 79b8c26a0ae7b2b1eda1a8b23ded33f16b07d19c Mon Sep 17 00:00:00 2001 From: jorenham Date: Fri, 14 Mar 2025 23:08:23 +0100 Subject: [PATCH 37/49] TYP: fix stubtest errors in ``timedelta64`` and ``object_`` Ported from numpy/numtype#228 --- the following methods were missing according to stubtest: - `numpy.object_.__call__` - `numpy.timedelta64.__class_getitem__` --- numpy/__init__.pyi | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index f8c499410993..7806724d707b 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -3990,7 +3990,7 @@ bool_ = bool # NOTE: Because mypy has some long-standing bugs related to `__new__`, `object_` can't # be made generic. @final -class object_(_RealMixin, generic): +class object_(_RealMixin, generic[Any]): @overload def __new__(cls, nothing_to_see_here: None = None, /) -> None: ... # type: ignore[misc] @overload @@ -4004,6 +4004,8 @@ class object_(_RealMixin, generic): @overload # catch-all def __new__(cls, value: Any = ..., /) -> object | NDArray[Self]: ... # type: ignore[misc] def __init__(self, value: object = ..., /) -> None: ... + def __hash__(self, /) -> int: ... + def __call__(self, /, *args: object, **kwargs: object) -> Any: ... if sys.version_info >= (3, 12): def __release_buffer__(self, buffer: memoryview, /) -> None: ... @@ -4466,6 +4468,9 @@ class timedelta64(_IntegralMixin, generic[_TD64ItemT_co], Generic[_TD64ItemT_co] @overload def __init__(self, value: _ConvertibleToTD64, format: _TimeUnitSpec = ..., /) -> None: ... + # inherited at runtime from `signedinteger` + def __class_getitem__(cls, type_arg: type | object, /) -> GenericAlias: ... + # NOTE: Only a limited number of units support conversion # to builtin scalar types: `Y`, `M`, `ns`, `ps`, `fs`, `as` def __int__(self: timedelta64[int], /) -> int: ... From 4a20c5154b7cca9a627df298f8ab6aefbdea0acd Mon Sep 17 00:00:00 2001 From: jorenham Date: Fri, 14 Mar 2025 23:41:42 +0100 Subject: [PATCH 38/49] TYP: fix stubtest errors in ``numpy.lib._function_base_impl`` Ported from numpy/numtype#233 --- This fixes the signatures of the following public `numpy` members: - `average` - `ma.average` - `median` - `corrcoef` --- numpy/lib/_function_base_impl.pyi | 185 +++++++++++++++--------------- numpy/ma/extras.pyi | 3 +- 2 files changed, 95 insertions(+), 93 deletions(-) diff --git a/numpy/lib/_function_base_impl.pyi b/numpy/lib/_function_base_impl.pyi index 214ad1f04f4b..e98dcbb7e741 100644 --- a/numpy/lib/_function_base_impl.pyi +++ b/numpy/lib/_function_base_impl.pyi @@ -1,57 +1,60 @@ -from collections.abc import Sequence, Callable, Iterable +# ruff: noqa: ANN401 +from collections.abc import Callable, Iterable, Sequence from typing import ( - Concatenate, - Literal as L, Any, + Concatenate, ParamSpec, - TypeAlias, - TypeVar, - overload, Protocol, SupportsIndex, SupportsInt, - TypeGuard, - type_check_only + TypeAlias, + TypeVar, + overload, + type_check_only, ) -from typing_extensions import deprecated +from typing import Literal as L + +from _typeshed import Incomplete +from typing_extensions import TypeIs, deprecated import numpy as np from numpy import ( - vectorize, + _OrderKACF, + bool_, + complex128, + complexfloating, + datetime64, + float64, + floating, generic, integer, - floating, - complexfloating, intp, - float64, - complex128, - timedelta64, - datetime64, object_, - bool_, - _OrderKACF, + timedelta64, + vectorize, ) from numpy._core.multiarray import bincount +from numpy._globals import _NoValueType from numpy._typing import ( - NDArray, ArrayLike, DTypeLike, + NDArray, _ArrayLike, - _DTypeLike, - _ShapeLike, _ArrayLikeBool_co, - _ArrayLikeInt_co, - _ArrayLikeFloat_co, _ArrayLikeComplex_co, - _ArrayLikeNumber_co, - _ArrayLikeTD64_co, _ArrayLikeDT64_co, + _ArrayLikeFloat_co, + _ArrayLikeInt_co, + _ArrayLikeNumber_co, _ArrayLikeObject_co, - _FloatLike_co, + _ArrayLikeTD64_co, _ComplexLike_co, + _DTypeLike, + _FloatLike_co, + _NestedSequence, _NumberLike_co, _ScalarLike_co, - _NestedSequence + _ShapeLike, ) __all__ = [ @@ -106,12 +109,14 @@ _2Tuple: TypeAlias = tuple[_T, _T] @type_check_only class _TrimZerosSequence(Protocol[_T_co]): - def __len__(self) -> int: ... + def __len__(self, /) -> int: ... @overload def __getitem__(self, key: int, /) -> object: ... @overload def __getitem__(self, key: slice, /) -> _T_co: ... +### + @overload def rot90( m: _ArrayLike[_SCT], @@ -134,72 +139,62 @@ def flip(m: _ArrayLike[_SCT], axis: None | _ShapeLike = ...) -> NDArray[_SCT]: . @overload def flip(m: ArrayLike, axis: None | _ShapeLike = ...) -> NDArray[Any]: ... -def iterable(y: object) -> TypeGuard[Iterable[Any]]: ... +def iterable(y: object) -> TypeIs[Iterable[Any]]: ... @overload def average( a: _ArrayLikeFloat_co, - axis: None = ..., - weights: None | _ArrayLikeFloat_co= ..., - returned: L[False] = ..., - keepdims: L[False] = ..., -) -> floating[Any]: ... -@overload -def average( - a: _ArrayLikeComplex_co, - axis: None = ..., - weights: None | _ArrayLikeComplex_co = ..., - returned: L[False] = ..., - keepdims: L[False] = ..., -) -> complexfloating[Any, Any]: ... -@overload -def average( - a: _ArrayLikeObject_co, - axis: None = ..., - weights: None | Any = ..., - returned: L[False] = ..., - keepdims: L[False] = ..., -) -> Any: ... + axis: None = None, + weights: _ArrayLikeFloat_co | None = None, + returned: L[False] = False, + *, + keepdims: L[False] | _NoValueType = ..., +) -> floating: ... @overload def average( a: _ArrayLikeFloat_co, - axis: None = ..., - weights: None | _ArrayLikeFloat_co= ..., - returned: L[True] = ..., - keepdims: L[False] = ..., -) -> _2Tuple[floating[Any]]: ... + axis: None = None, + weights: _ArrayLikeFloat_co | None = None, + *, + returned: L[True], + keepdims: L[False] | _NoValueType = ..., +) -> _2Tuple[floating]: ... @overload def average( a: _ArrayLikeComplex_co, - axis: None = ..., - weights: None | _ArrayLikeComplex_co = ..., - returned: L[True] = ..., - keepdims: L[False] = ..., -) -> _2Tuple[complexfloating[Any, Any]]: ... + axis: None = None, + weights: _ArrayLikeComplex_co | None = None, + returned: L[False] = False, + *, + keepdims: L[False] | _NoValueType = ..., +) -> complexfloating: ... @overload def average( - a: _ArrayLikeObject_co, - axis: None = ..., - weights: None | Any = ..., - returned: L[True] = ..., - keepdims: L[False] = ..., -) -> _2Tuple[Any]: ... + a: _ArrayLikeComplex_co, + axis: None = None, + weights: _ArrayLikeComplex_co | None = None, + *, + returned: L[True], + keepdims: L[False] | _NoValueType = ..., +) -> _2Tuple[complexfloating]: ... @overload def average( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., - weights: None | Any = ..., - returned: L[False] = ..., - keepdims: bool = ..., -) -> Any: ... + axis: _ShapeLike | None = None, + weights: object | None = None, + *, + returned: L[True], + keepdims: bool | bool_ | _NoValueType = ..., +) -> _2Tuple[Incomplete]: ... @overload def average( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None | _ShapeLike = ..., - weights: None | Any = ..., - returned: L[True] = ..., - keepdims: bool = ..., -) -> _2Tuple[Any]: ... + axis: _ShapeLike | None = None, + weights: object | None = None, + returned: bool | bool_ = False, + *, + keepdims: bool | bool_ | _NoValueType = ..., +) -> Incomplete: ... @overload def asarray_chkfinite( @@ -478,38 +473,46 @@ def cov( dtype: DTypeLike, ) -> NDArray[Any]: ... -# NOTE `bias` and `ddof` have been deprecated +# NOTE `bias` and `ddof` are deprecated and ignored @overload def corrcoef( m: _ArrayLikeFloat_co, - y: None | _ArrayLikeFloat_co = ..., - rowvar: bool = ..., + y: _ArrayLikeFloat_co | None = None, + rowvar: bool = True, + bias: _NoValueType = ..., + ddof: _NoValueType = ..., *, - dtype: None = ..., -) -> NDArray[floating[Any]]: ... + dtype: None = None, +) -> NDArray[floating]: ... @overload def corrcoef( m: _ArrayLikeComplex_co, - y: None | _ArrayLikeComplex_co = ..., - rowvar: bool = ..., + y: _ArrayLikeComplex_co | None = None, + rowvar: bool = True, + bias: _NoValueType = ..., + ddof: _NoValueType = ..., *, - dtype: None = ..., -) -> NDArray[complexfloating[Any, Any]]: ... + dtype: None = None, +) -> NDArray[complexfloating]: ... @overload def corrcoef( m: _ArrayLikeComplex_co, - y: None | _ArrayLikeComplex_co = ..., - rowvar: bool = ..., + y: _ArrayLikeComplex_co | None = None, + rowvar: bool = True, + bias: _NoValueType = ..., + ddof: _NoValueType = ..., *, dtype: _DTypeLike[_SCT], ) -> NDArray[_SCT]: ... @overload def corrcoef( m: _ArrayLikeComplex_co, - y: None | _ArrayLikeComplex_co = ..., - rowvar: bool = ..., + y: _ArrayLikeComplex_co | None = None, + rowvar: bool = True, + bias: _NoValueType = ..., + ddof: _NoValueType = ..., *, - dtype: DTypeLike, + dtype: DTypeLike | None = None, ) -> NDArray[Any]: ... def blackman(M: _FloatLike_co) -> NDArray[floating[Any]]: ... @@ -581,7 +584,6 @@ def median( a: _ArrayLikeFloat_co | _ArrayLikeComplex_co | _ArrayLikeTD64_co | _ArrayLikeObject_co, axis: None | _ShapeLike, out: _ArrayType, - /, overwrite_input: bool = ..., keepdims: bool = ..., ) -> _ArrayType: ... @@ -749,7 +751,6 @@ def percentile( q: _ArrayLikeFloat_co, axis: None | _ShapeLike, out: _ArrayType, - /, overwrite_input: bool = ..., method: _MethodKind = ..., keepdims: bool = ..., diff --git a/numpy/ma/extras.pyi b/numpy/ma/extras.pyi index df69cd5d3465..580309cc679d 100644 --- a/numpy/ma/extras.pyi +++ b/numpy/ma/extras.pyi @@ -1,6 +1,8 @@ from typing import Any +from numpy.lib._function_base_impl import average from numpy.lib._index_tricks_impl import AxisConcatenator + from .core import dot, mask_rowcols __all__ = [ @@ -88,7 +90,6 @@ diagflat: _fromnxfunction_single def apply_along_axis(func1d, axis, arr, *args, **kwargs): ... def apply_over_axes(func, a, axes): ... -def average(a, axis=..., weights=..., returned=..., keepdims=...): ... def median(a, axis=..., out=..., overwrite_input=..., keepdims=...): ... def compress_nd(x, axis=...): ... def compress_rowcols(x, axis=...): ... From b6de917f8bc88b9d6240d68764429b3c9d108f16 Mon Sep 17 00:00:00 2001 From: jorenham Date: Sat, 15 Mar 2025 01:10:10 +0100 Subject: [PATCH 39/49] TYP: fix stubtest errors in ``numpy.lib._index_tricks_impl`` Ported from numpy/numtype#235 --- - move `ndenumerate` and `ndindex` definitions to `lib._index_tricks_impl` - add deprecated `ndenumerate.ndincr` property - removed non-existent `ndenumerate.iter` property - remove incorrect "pass" and "reveal" type-tests for `ndenumerate.iter` - fix incorrect `ndenumerate` constructor fallback return type - fix `AxisConcatenator.makemat` signature --- numpy/__init__.pyi | 46 +-- numpy/lib/_index_tricks_impl.pyi | 261 ++++++++++-------- numpy/typing/tests/data/pass/index_tricks.py | 4 - .../typing/tests/data/reveal/index_tricks.pyi | 12 +- 4 files changed, 149 insertions(+), 174 deletions(-) diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 7806724d707b..539c5fa53c24 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -515,6 +515,8 @@ from numpy.lib._histograms_impl import ( ) from numpy.lib._index_tricks_impl import ( + ndenumerate, + ndindex, ravel_multi_index, unravel_index, mgrid, @@ -4964,50 +4966,6 @@ class errstate: ) -> None: ... def __call__(self, func: _CallableT) -> _CallableT: ... -class ndenumerate(Generic[_SCT_co]): - @property - def iter(self) -> flatiter[NDArray[_SCT_co]]: ... - - @overload - def __new__( - cls, arr: _FiniteNestedSequence[_SupportsArray[dtype[_SCT]]], - ) -> ndenumerate[_SCT]: ... - @overload - def __new__(cls, arr: str | _NestedSequence[str]) -> ndenumerate[str_]: ... - @overload - def __new__(cls, arr: bytes | _NestedSequence[bytes]) -> ndenumerate[bytes_]: ... - @overload - def __new__(cls, arr: builtins.bool | _NestedSequence[builtins.bool]) -> ndenumerate[np.bool]: ... - @overload - def __new__(cls, arr: int | _NestedSequence[int]) -> ndenumerate[int_]: ... - @overload - def __new__(cls, arr: float | _NestedSequence[float]) -> ndenumerate[float64]: ... - @overload - def __new__(cls, arr: complex | _NestedSequence[complex]) -> ndenumerate[complex128]: ... - @overload - def __new__(cls, arr: object) -> ndenumerate[object_]: ... - - # The first overload is a (semi-)workaround for a mypy bug (tested with v1.10 and v1.11) - @overload - def __next__( - self: ndenumerate[np.bool | datetime64 | timedelta64 | number[Any] | flexible], - /, - ) -> tuple[_Shape, _SCT_co]: ... - @overload - def __next__(self: ndenumerate[object_], /) -> tuple[_Shape, Any]: ... - @overload - def __next__(self, /) -> tuple[_Shape, _SCT_co]: ... - - def __iter__(self) -> Self: ... - -class ndindex: - @overload - def __init__(self, shape: tuple[SupportsIndex, ...], /) -> None: ... - @overload - def __init__(self, *shape: SupportsIndex) -> None: ... - def __iter__(self) -> Self: ... - def __next__(self) -> _Shape: ... - # TODO: The type of each `__next__` and `iters` return-type depends # on the length and dtype of `args`; we can't describe this behavior yet # as we lack variadics (PEP 646). diff --git a/numpy/lib/_index_tricks_impl.pyi b/numpy/lib/_index_tricks_impl.pyi index bd508a8b5905..4a1426fd4d6c 100644 --- a/numpy/lib/_index_tricks_impl.pyi +++ b/numpy/lib/_index_tricks_impl.pyi @@ -1,45 +1,23 @@ from collections.abc import Sequence -from typing import ( - Any, - TypeVar, - Generic, - overload, - Literal, - SupportsIndex, -) +from typing import Any, ClassVar, Final, Generic, SupportsIndex, final, overload +from typing import Literal as L + +from _typeshed import Incomplete +from typing_extensions import Self, TypeVar, deprecated import numpy as np -from numpy import ( - # Circumvent a naming conflict with `AxisConcatenator.matrix` - matrix as _Matrix, - ndenumerate, - ndindex, - ndarray, - dtype, - str_, - bytes_, - int_, - float64, - complex128, -) +from numpy._core.multiarray import ravel_multi_index, unravel_index from numpy._typing import ( - # Arrays ArrayLike, - _NestedSequence, - _FiniteNestedSequence, NDArray, - - # DTypes - DTypeLike, - _SupportsDType, - - # Shapes + _FiniteNestedSequence, + _NestedSequence, _Shape, + _SupportsArray, + _SupportsDType, ) -from numpy._core.multiarray import unravel_index, ravel_multi_index - -__all__ = [ +__all__ = [ # noqa: RUF022 "ravel_multi_index", "unravel_index", "mgrid", @@ -56,114 +34,163 @@ __all__ = [ "diag_indices_from", ] +### + _T = TypeVar("_T") -_DType = TypeVar("_DType", bound=dtype[Any]) -_BoolType = TypeVar("_BoolType", Literal[True], Literal[False]) -_TupType = TypeVar("_TupType", bound=tuple[Any, ...]) -_ArrayType = TypeVar("_ArrayType", bound=NDArray[Any]) +_TupleT = TypeVar("_TupleT", bound=tuple[Any, ...]) +_ArrayT = TypeVar("_ArrayT", bound=NDArray[Any]) +_DTypeT = TypeVar("_DTypeT", bound=np.dtype[Any]) +_ScalarT = TypeVar("_ScalarT", bound=np.generic) +_ScalarT_co = TypeVar("_ScalarT_co", bound=np.generic, covariant=True) +_BoolT_co = TypeVar("_BoolT_co", bound=bool, default=bool, covariant=True) -@overload -def ix_(*args: _FiniteNestedSequence[_SupportsDType[_DType]]) -> tuple[ndarray[_Shape, _DType], ...]: ... -@overload -def ix_(*args: str | _NestedSequence[str]) -> tuple[NDArray[str_], ...]: ... -@overload -def ix_(*args: bytes | _NestedSequence[bytes]) -> tuple[NDArray[bytes_], ...]: ... -@overload -def ix_(*args: bool | _NestedSequence[bool]) -> tuple[NDArray[np.bool], ...]: ... -@overload -def ix_(*args: int | _NestedSequence[int]) -> tuple[NDArray[int_], ...]: ... -@overload -def ix_(*args: float | _NestedSequence[float]) -> tuple[NDArray[float64], ...]: ... -@overload -def ix_(*args: complex | _NestedSequence[complex]) -> tuple[NDArray[complex128], ...]: ... +_AxisT_co = TypeVar("_AxisT_co", bound=int, default=L[0], covariant=True) +_MatrixT_co = TypeVar("_MatrixT_co", bound=bool, default=L[False], covariant=True) +_NDMinT_co = TypeVar("_NDMinT_co", bound=int, default=L[1], covariant=True) +_Trans1DT_co = TypeVar("_Trans1DT_co", bound=int, default=L[-1], covariant=True) + +### -class nd_grid(Generic[_BoolType]): - sparse: _BoolType - def __init__(self, sparse: _BoolType = ...) -> None: ... +class ndenumerate(Generic[_ScalarT_co]): + @overload + def __new__(cls, arr: _FiniteNestedSequence[_SupportsArray[np.dtype[_ScalarT]]]) -> ndenumerate[_ScalarT]: ... + @overload + def __new__(cls, arr: str | _NestedSequence[str]) -> ndenumerate[np.str_]: ... @overload - def __getitem__( - self: nd_grid[Literal[False]], - key: slice | Sequence[slice], - ) -> NDArray[Any]: ... + def __new__(cls, arr: bytes | _NestedSequence[bytes]) -> ndenumerate[np.bytes_]: ... @overload - def __getitem__( - self: nd_grid[Literal[True]], - key: slice | Sequence[slice], - ) -> tuple[NDArray[Any], ...]: ... + def __new__(cls, arr: bool | _NestedSequence[bool]) -> ndenumerate[np.bool]: ... + @overload + def __new__(cls, arr: int | _NestedSequence[int]) -> ndenumerate[np.intp]: ... + @overload + def __new__(cls, arr: float | _NestedSequence[float]) -> ndenumerate[np.float64]: ... + @overload + def __new__(cls, arr: complex | _NestedSequence[complex]) -> ndenumerate[np.complex128]: ... + @overload + def __new__(cls, arr: object) -> ndenumerate[Any]: ... -class MGridClass(nd_grid[Literal[False]]): - def __init__(self) -> None: ... + # The first overload is a (semi-)workaround for a mypy bug (tested with v1.10 and v1.11) + @overload + def __next__( + self: ndenumerate[np.bool | np.number | np.flexible | np.datetime64 | np.timedelta64], + /, + ) -> tuple[tuple[int, ...], _ScalarT_co]: ... + @overload + def __next__(self: ndenumerate[np.object_], /) -> tuple[tuple[int, ...], Any]: ... + @overload + def __next__(self, /) -> tuple[tuple[int, ...], _ScalarT_co]: ... + + # + def __iter__(self) -> Self: ... + +class ndindex: + @overload + def __init__(self, shape: tuple[SupportsIndex, ...], /) -> None: ... + @overload + def __init__(self, /, *shape: SupportsIndex) -> None: ... + + # + def __iter__(self) -> Self: ... + def __next__(self) -> tuple[int, ...]: ... -mgrid: MGridClass + # + @deprecated("Deprecated since 1.20.0.") + def ndincr(self, /) -> None: ... -class OGridClass(nd_grid[Literal[True]]): +class nd_grid(Generic[_BoolT_co]): + sparse: _BoolT_co + def __init__(self, sparse: _BoolT_co = ...) -> None: ... + @overload + def __getitem__(self: nd_grid[L[False]], key: slice | Sequence[slice]) -> NDArray[Any]: ... + @overload + def __getitem__(self: nd_grid[L[True]], key: slice | Sequence[slice]) -> tuple[NDArray[Any], ...]: ... + +@final +class MGridClass(nd_grid[L[False]]): + def __init__(self) -> None: ... + +@final +class OGridClass(nd_grid[L[True]]): def __init__(self) -> None: ... -ogrid: OGridClass +class AxisConcatenator(Generic[_AxisT_co, _MatrixT_co, _NDMinT_co, _Trans1DT_co]): + __slots__ = "axis", "matrix", "ndmin", "trans1d" + + makemat: ClassVar[type[np.matrix[tuple[int, int], np.dtype[Any]]]] -class AxisConcatenator: - axis: int - matrix: bool - ndmin: int - trans1d: int + axis: _AxisT_co + matrix: _MatrixT_co + ndmin: _NDMinT_co + trans1d: _Trans1DT_co + + # def __init__( self, - axis: int = ..., - matrix: bool = ..., - ndmin: int = ..., - trans1d: int = ..., + /, + axis: _AxisT_co = ..., + matrix: _MatrixT_co = ..., + ndmin: _NDMinT_co = ..., + trans1d: _Trans1DT_co = ..., ) -> None: ... + + # TODO(jorenham): annotate this + def __getitem__(self, key: Incomplete, /) -> Incomplete: ... + def __len__(self, /) -> L[0]: ... + + # @staticmethod @overload - def concatenate( # type: ignore[misc] - *a: ArrayLike, axis: SupportsIndex = ..., out: None = ... - ) -> NDArray[Any]: ... + def concatenate(*a: ArrayLike, axis: SupportsIndex | None = 0, out: _ArrayT) -> _ArrayT: ... @staticmethod @overload - def concatenate( - *a: ArrayLike, axis: SupportsIndex = ..., out: _ArrayType = ... - ) -> _ArrayType: ... - @staticmethod - def makemat( - data: ArrayLike, dtype: DTypeLike = ..., copy: bool = ... - ) -> _Matrix[Any, Any]: ... - - # TODO: Sort out this `__getitem__` method - def __getitem__(self, key: Any) -> Any: ... - -class RClass(AxisConcatenator): - axis: Literal[0] - matrix: Literal[False] - ndmin: Literal[1] - trans1d: Literal[-1] - def __init__(self) -> None: ... + def concatenate(*a: ArrayLike, axis: SupportsIndex | None = 0, out: None = None) -> NDArray[Any]: ... -r_: RClass - -class CClass(AxisConcatenator): - axis: Literal[-1] - matrix: Literal[False] - ndmin: Literal[2] - trans1d: Literal[0] - def __init__(self) -> None: ... +@final +class RClass(AxisConcatenator[L[0], L[False], L[1], L[-1]]): + def __init__(self, /) -> None: ... -c_: CClass +@final +class CClass(AxisConcatenator[L[-1], L[False], L[2], L[0]]): + def __init__(self, /) -> None: ... -class IndexExpression(Generic[_BoolType]): - maketuple: _BoolType - def __init__(self, maketuple: _BoolType) -> None: ... +class IndexExpression(Generic[_BoolT_co]): + maketuple: _BoolT_co + def __init__(self, maketuple: _BoolT_co) -> None: ... @overload - def __getitem__(self, item: _TupType) -> _TupType: ... # type: ignore[misc] + def __getitem__(self, item: _TupleT) -> _TupleT: ... @overload - def __getitem__(self: IndexExpression[Literal[True]], item: _T) -> tuple[_T]: ... + def __getitem__(self: IndexExpression[L[True]], item: _T) -> tuple[_T]: ... @overload - def __getitem__(self: IndexExpression[Literal[False]], item: _T) -> _T: ... + def __getitem__(self: IndexExpression[L[False]], item: _T) -> _T: ... + +@overload +def ix_(*args: _FiniteNestedSequence[_SupportsDType[_DTypeT]]) -> tuple[np.ndarray[_Shape, _DTypeT], ...]: ... +@overload +def ix_(*args: str | _NestedSequence[str]) -> tuple[NDArray[np.str_], ...]: ... +@overload +def ix_(*args: bytes | _NestedSequence[bytes]) -> tuple[NDArray[np.bytes_], ...]: ... +@overload +def ix_(*args: bool | _NestedSequence[bool]) -> tuple[NDArray[np.bool], ...]: ... +@overload +def ix_(*args: int | _NestedSequence[int]) -> tuple[NDArray[np.intp], ...]: ... +@overload +def ix_(*args: float | _NestedSequence[float]) -> tuple[NDArray[np.float64], ...]: ... +@overload +def ix_(*args: complex | _NestedSequence[complex]) -> tuple[NDArray[np.complex128], ...]: ... + +# +def fill_diagonal(a: NDArray[Any], val: object, wrap: bool = ...) -> None: ... + +# +def diag_indices(n: int, ndim: int = ...) -> tuple[NDArray[np.intp], ...]: ... +def diag_indices_from(arr: ArrayLike) -> tuple[NDArray[np.intp], ...]: ... -index_exp: IndexExpression[Literal[True]] -s_: IndexExpression[Literal[False]] +# +mgrid: Final[MGridClass] = ... +ogrid: Final[OGridClass] = ... -def fill_diagonal(a: NDArray[Any], val: Any, wrap: bool = ...) -> None: ... -def diag_indices(n: int, ndim: int = ...) -> tuple[NDArray[int_], ...]: ... -def diag_indices_from(arr: ArrayLike) -> tuple[NDArray[int_], ...]: ... +r_: Final[RClass] = ... +c_: Final[CClass] = ... -# NOTE: see `numpy/__init__.pyi` for `ndenumerate` and `ndindex` +index_exp: Final[IndexExpression[L[True]]] = ... +s_: Final[IndexExpression[L[False]]] = ... diff --git a/numpy/typing/tests/data/pass/index_tricks.py b/numpy/typing/tests/data/pass/index_tricks.py index 4c4c1195990a..dfc4ff2f314a 100644 --- a/numpy/typing/tests/data/pass/index_tricks.py +++ b/numpy/typing/tests/data/pass/index_tricks.py @@ -13,10 +13,6 @@ np.ndenumerate(AR_LIKE_f) np.ndenumerate(AR_LIKE_U) -np.ndenumerate(AR_i8).iter -np.ndenumerate(AR_LIKE_f).iter -np.ndenumerate(AR_LIKE_U).iter - next(np.ndenumerate(AR_i8)) next(np.ndenumerate(AR_LIKE_f)) next(np.ndenumerate(AR_LIKE_U)) diff --git a/numpy/typing/tests/data/reveal/index_tricks.pyi b/numpy/typing/tests/data/reveal/index_tricks.pyi index 1db10928d2f5..06071feddd79 100644 --- a/numpy/typing/tests/data/reveal/index_tricks.pyi +++ b/numpy/typing/tests/data/reveal/index_tricks.pyi @@ -18,23 +18,17 @@ AR_O: npt.NDArray[np.object_] assert_type(np.ndenumerate(AR_i8), np.ndenumerate[np.int64]) assert_type(np.ndenumerate(AR_LIKE_f), np.ndenumerate[np.float64]) assert_type(np.ndenumerate(AR_LIKE_U), np.ndenumerate[np.str_]) -assert_type(np.ndenumerate(AR_LIKE_O), np.ndenumerate[np.object_]) - -assert_type(np.ndenumerate(AR_i8).iter, np.flatiter[npt.NDArray[np.int64]]) -assert_type(np.ndenumerate(AR_LIKE_f).iter, np.flatiter[npt.NDArray[np.float64]]) -assert_type(np.ndenumerate(AR_LIKE_U).iter, np.flatiter[npt.NDArray[np.str_]]) -assert_type(np.ndenumerate(AR_LIKE_O).iter, np.flatiter[npt.NDArray[np.object_]]) +assert_type(np.ndenumerate(AR_LIKE_O), np.ndenumerate[Any]) assert_type(next(np.ndenumerate(AR_i8)), tuple[tuple[int, ...], np.int64]) assert_type(next(np.ndenumerate(AR_LIKE_f)), tuple[tuple[int, ...], np.float64]) assert_type(next(np.ndenumerate(AR_LIKE_U)), tuple[tuple[int, ...], np.str_]) -# this fails due to an unknown mypy bug -# assert_type(next(np.ndenumerate(AR_LIKE_O)), tuple[tuple[int, ...], Any]) +assert_type(next(np.ndenumerate(AR_LIKE_O)), tuple[tuple[int, ...], Any]) assert_type(iter(np.ndenumerate(AR_i8)), np.ndenumerate[np.int64]) assert_type(iter(np.ndenumerate(AR_LIKE_f)), np.ndenumerate[np.float64]) assert_type(iter(np.ndenumerate(AR_LIKE_U)), np.ndenumerate[np.str_]) -assert_type(iter(np.ndenumerate(AR_LIKE_O)), np.ndenumerate[np.object_]) +assert_type(iter(np.ndenumerate(AR_LIKE_O)), np.ndenumerate[Any]) assert_type(np.ndindex(1, 2, 3), np.ndindex) assert_type(np.ndindex((1, 2, 3)), np.ndindex) From 710d3b1e9c4253579a199d9828537fabbab051b1 Mon Sep 17 00:00:00 2001 From: jorenham Date: Sat, 15 Mar 2025 03:23:05 +0100 Subject: [PATCH 40/49] TYP: work around a quantum-entangled mypy issue, somehow --- numpy/_core/fromnumeric.pyi | 72 ++++++++++--------- .../typing/tests/data/reveal/fromnumeric.pyi | 5 +- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/numpy/_core/fromnumeric.pyi b/numpy/_core/fromnumeric.pyi index 3de05f3db362..52f48efa9345 100644 --- a/numpy/_core/fromnumeric.pyi +++ b/numpy/_core/fromnumeric.pyi @@ -3,7 +3,6 @@ from collections.abc import Sequence from typing import ( Any, Literal, - NoReturn, Protocol, SupportsIndex, TypeAlias, @@ -11,6 +10,8 @@ from typing import ( overload, type_check_only, ) + +from _typeshed import Incomplete from typing_extensions import Never, deprecated import numpy as np @@ -551,9 +552,6 @@ def ravel( @overload def ravel(a: ArrayLike, order: _OrderKACF = "C") -> np.ndarray[tuple[int], np.dtype[Any]]: ... -@overload -def nonzero(a: np.generic | np.ndarray[tuple[()], Any]) -> NoReturn: ... -@overload def nonzero(a: _ArrayLike[Any]) -> tuple[NDArray[intp], ...]: ... # this prevents `Any` from being returned with Pyright @@ -813,7 +811,7 @@ def all( keepdims: _BoolLike_co | _NoValueType = ..., *, where: _ArrayLikeBool_co | _NoValueType = ..., -) -> np.bool | NDArray[np.bool]: ... +) -> Incomplete: ... @overload def all( a: ArrayLike, @@ -850,7 +848,7 @@ def any( keepdims: _BoolLike_co | _NoValueType = ..., *, where: _ArrayLikeBool_co | _NoValueType = ..., -) -> np.bool | NDArray[np.bool]: ... +) -> Incomplete: ... @overload def any( a: ArrayLike, @@ -1443,10 +1441,10 @@ def mean( keepdims: Literal[False] | _NoValueType = ..., *, where: _ArrayLikeBool_co | _NoValueType = ..., -) -> complexfloating[Any, Any]: ... +) -> complexfloating[Any]: ... @overload def mean( - a: _ArrayLikeTD64_co, + a: _ArrayLike[np.timedelta64], axis: None = ..., dtype: None = ..., out: None = ..., @@ -1457,23 +1455,33 @@ def mean( @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: _ShapeLike | None = ..., - dtype: None = ..., - out: None = ..., + axis: _ShapeLike | None, + dtype: DTypeLike, + out: _ArrayT, keepdims: bool | _NoValueType = ..., *, where: _ArrayLikeBool_co | _NoValueType = ..., -) -> Any: ... +) -> _ArrayT: ... +@overload +def mean( + a: _ArrayLikeComplex_co | _ArrayLikeObject_co, + axis: _ShapeLike | None = ..., + dtype: DTypeLike | None = ..., + *, + out: _ArrayT, + keepdims: bool | _NoValueType = ..., + where: _ArrayLikeBool_co | _NoValueType = ..., +) -> _ArrayT: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, axis: None, dtype: _DTypeLike[_SCT], out: None = ..., - keepdims: bool | _NoValueType = ..., + keepdims: Literal[False] | _NoValueType = ..., *, where: _ArrayLikeBool_co | _NoValueType = ..., -) -> _SCT | NDArray[_SCT]: ... +) -> _SCT: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, @@ -1487,43 +1495,43 @@ def mean( @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: None = ..., - *, + axis: _ShapeLike | None, dtype: _DTypeLike[_SCT], - out: None = ..., - keepdims: bool | _NoValueType = ..., + out: None, + keepdims: Literal[True, 1], + *, where: _ArrayLikeBool_co | _NoValueType = ..., -) -> _SCT | NDArray[_SCT]: ... +) -> NDArray[_SCT]: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: _ShapeLike | None = ..., - dtype: DTypeLike = ..., + axis: _ShapeLike | None, + dtype: _DTypeLike[_SCT], out: None = ..., - keepdims: bool | _NoValueType = ..., *, + keepdims: bool | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., -) -> Any: ... +) -> _SCT | NDArray[_SCT]: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, - axis: _ShapeLike | None, - dtype: DTypeLike, - out: _ArrayT, - keepdims: bool | _NoValueType = ..., + axis: _ShapeLike | None = ..., *, + dtype: _DTypeLike[_SCT], + out: None = ..., + keepdims: bool | _NoValueType = ..., where: _ArrayLikeBool_co | _NoValueType = ..., -) -> _ArrayT: ... +) -> _SCT | NDArray[_SCT]: ... @overload def mean( a: _ArrayLikeComplex_co | _ArrayLikeObject_co, axis: _ShapeLike | None = ..., - dtype: DTypeLike = ..., - *, - out: _ArrayT, + dtype: DTypeLike | None = ..., + out: None = ..., keepdims: bool | _NoValueType = ..., + *, where: _ArrayLikeBool_co | _NoValueType = ..., -) -> _ArrayT: ... +) -> Incomplete: ... @overload def std( diff --git a/numpy/typing/tests/data/reveal/fromnumeric.pyi b/numpy/typing/tests/data/reveal/fromnumeric.pyi index 366e34d8af99..7e778dc58410 100644 --- a/numpy/typing/tests/data/reveal/fromnumeric.pyi +++ b/numpy/typing/tests/data/reveal/fromnumeric.pyi @@ -1,6 +1,6 @@ """Tests for :mod:`_core.fromnumeric`.""" -from typing import Any, Literal as L, NoReturn +from typing import Any, Literal as L import numpy as np import numpy.typing as npt @@ -127,11 +127,8 @@ assert_type(np.ravel(f), np.ndarray[tuple[int], np.dtype[np.float64 | np.int_ | assert_type(np.ravel(AR_b), np.ndarray[tuple[int], np.dtype[np.bool]]) assert_type(np.ravel(AR_f4), np.ndarray[tuple[int], np.dtype[np.float32]]) -assert_type(np.nonzero(b), NoReturn) -assert_type(np.nonzero(f4), NoReturn) assert_type(np.nonzero(AR_b), tuple[npt.NDArray[np.intp], ...]) assert_type(np.nonzero(AR_f4), tuple[npt.NDArray[np.intp], ...]) -assert_type(np.nonzero(AR_0d), NoReturn) assert_type(np.nonzero(AR_1d), tuple[npt.NDArray[np.intp], ...]) assert_type(np.nonzero(AR_nd), tuple[npt.NDArray[np.intp], ...]) From c80a57ffae3279936b76c4d56432709987d2922e Mon Sep 17 00:00:00 2001 From: jorenham Date: Sat, 15 Mar 2025 02:13:11 +0100 Subject: [PATCH 41/49] TYP: fix stubtest errors in ``numpy.lib._twodim_base_impl`` Ported from numpy/numtype#245 --- This fixes incorrect parameter names of `tril` and `triu`, and resolves a typing error in the signatures of `eye` and `tri`. --- numpy/lib/_twodim_base_impl.pyi | 90 ++++++++++++++++----------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/numpy/lib/_twodim_base_impl.pyi b/numpy/lib/_twodim_base_impl.pyi index e748e91fb908..5d3ea54511b8 100644 --- a/numpy/lib/_twodim_base_impl.pyi +++ b/numpy/lib/_twodim_base_impl.pyi @@ -10,7 +10,6 @@ from typing import ( import numpy as np from numpy import ( generic, - number, timedelta64, datetime64, int_, @@ -56,14 +55,28 @@ __all__ = [ "triu_indices_from", ] +### + _T = TypeVar("_T") _SCT = TypeVar("_SCT", bound=generic) +_SCT_complex = TypeVar("_SCT_complex", bound=np.complexfloating) +_SCT_inexact = TypeVar("_SCT_inexact", bound=np.inexact) +_SCT_number_co = TypeVar("_SCT_number_co", bound=_Number_co) # The returned arrays dtype must be compatible with `np.equal` -_MaskFunc: TypeAlias = Callable[ - [NDArray[int_], _T], - NDArray[number[Any] | np.bool | timedelta64 | datetime64 | object_], -] +_MaskFunc: TypeAlias = Callable[[NDArray[int_], _T], NDArray[_Number_co | timedelta64 | datetime64 | object_]] + +_Int_co: TypeAlias = np.integer | np.bool +_Float_co: TypeAlias = np.floating | _Int_co +_Number_co: TypeAlias = np.number | np.bool + +_ArrayLike1D: TypeAlias = _SupportsArray[np.dtype[_SCT]] | Sequence[_SCT] +_ArrayLike1DInt_co: TypeAlias = _SupportsArray[np.dtype[_Int_co]] | Sequence[int | _Int_co] +_ArrayLike1DFloat_co: TypeAlias = _SupportsArray[np.dtype[_Float_co]] | Sequence[float | _Float_co] +_ArrayLike2DFloat_co: TypeAlias = _SupportsArray[np.dtype[_Float_co]] | Sequence[_ArrayLike1DFloat_co] +_ArrayLike1DNumber_co: TypeAlias = _SupportsArray[np.dtype[_Number_co]] | Sequence[complex | _Number_co] + +### @overload def fliplr(m: _ArrayLike[_SCT]) -> NDArray[_SCT]: ... @@ -87,13 +100,24 @@ def eye( like: None | _SupportsArrayFunc = ..., ) -> NDArray[float64]: ... @overload +def eye( + N: int, + M: None | int, + k: int, + dtype: _DTypeLike[_SCT], + order: _OrderCF = ..., + *, + device: None | L["cpu"] = ..., + like: None | _SupportsArrayFunc = ..., +) -> NDArray[_SCT]: ... +@overload def eye( N: int, M: None | int = ..., k: int = ..., - dtype: _DTypeLike[_SCT] = ..., - order: _OrderCF = ..., *, + dtype: _DTypeLike[_SCT], + order: _OrderCF = ..., device: None | L["cpu"] = ..., like: None | _SupportsArrayFunc = ..., ) -> NDArray[_SCT]: ... @@ -129,12 +153,21 @@ def tri( like: None | _SupportsArrayFunc = ... ) -> NDArray[float64]: ... @overload +def tri( + N: int, + M: None | int, + k: int, + dtype: _DTypeLike[_SCT], + *, + like: None | _SupportsArrayFunc = ... +) -> NDArray[_SCT]: ... +@overload def tri( N: int, M: None | int = ..., k: int = ..., - dtype: _DTypeLike[_SCT] = ..., *, + dtype: _DTypeLike[_SCT], like: None | _SupportsArrayFunc = ... ) -> NDArray[_SCT]: ... @overload @@ -148,14 +181,14 @@ def tri( ) -> NDArray[Any]: ... @overload -def tril(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +def tril(m: _ArrayLike[_SCT], k: int = 0) -> NDArray[_SCT]: ... @overload -def tril(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... +def tril(m: ArrayLike, k: int = 0) -> NDArray[Any]: ... @overload -def triu(v: _ArrayLike[_SCT], k: int = ...) -> NDArray[_SCT]: ... +def triu(m: _ArrayLike[_SCT], k: int = 0) -> NDArray[_SCT]: ... @overload -def triu(v: ArrayLike, k: int = ...) -> NDArray[Any]: ... +def triu(m: ArrayLike, k: int = 0) -> NDArray[Any]: ... @overload def vander( # type: ignore[misc] @@ -182,38 +215,6 @@ def vander( increasing: bool = ..., ) -> NDArray[object_]: ... - -_Int_co: TypeAlias = np.integer[Any] | np.bool -_Float_co: TypeAlias = np.floating[Any] | _Int_co -_Number_co: TypeAlias = np.number[Any] | np.bool - -_ArrayLike1D: TypeAlias = _SupportsArray[np.dtype[_SCT]] | Sequence[_SCT] -_ArrayLike2D: TypeAlias = ( - _SupportsArray[np.dtype[_SCT]] - | Sequence[_ArrayLike1D[_SCT]] -) - -_ArrayLike1DInt_co: TypeAlias = ( - _SupportsArray[np.dtype[_Int_co]] - | Sequence[int | _Int_co] -) -_ArrayLike1DFloat_co: TypeAlias = ( - _SupportsArray[np.dtype[_Float_co]] - | Sequence[float | int | _Float_co] -) -_ArrayLike2DFloat_co: TypeAlias = ( - _SupportsArray[np.dtype[_Float_co]] - | Sequence[_ArrayLike1DFloat_co] -) -_ArrayLike1DNumber_co: TypeAlias = ( - _SupportsArray[np.dtype[_Number_co]] - | Sequence[int | float | complex | _Number_co] -) - -_SCT_complex = TypeVar("_SCT_complex", bound=np.complexfloating[Any, Any]) -_SCT_inexact = TypeVar("_SCT_inexact", bound=np.inexact[Any]) -_SCT_number_co = TypeVar("_SCT_number_co", bound=_Number_co) - @overload def histogram2d( x: _ArrayLike1D[_SCT_complex], @@ -344,7 +345,6 @@ def histogram2d( NDArray[_SCT_number_co | complex128 | float64], NDArray[_SCT_number_co | complex128 | float64] , ]: ... - @overload def histogram2d( x: _ArrayLike1DNumber_co, From 8f561dbad1779ec9f78eaeeef30723d73e8f5b3c Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 16 Mar 2025 00:08:49 +0100 Subject: [PATCH 42/49] TYP: fix stubtest errors in ``numpy._core`` (#28535) * TYP: fix stubtest errors in ``numpy._core._internal`` Ported from numpy/numtype#238 * TYP: fix stubtest errors in ``numpy._core.einsumfunc`` Ported from numpy/numtype#239 * TYP: fix stubtest errors in ``numpy._core.arrayprint``` Ported from numpy/numtype#240 * TYP: fix remaining stubtest errors in ``numpy._core`` Ported from numpy/numtype#246 * TYP: fix incorrect warning ignore in "pass" type-tests --- numpy/_core/_internal.pyi | 64 +++++++++++++++---- numpy/_core/arrayprint.pyi | 4 +- numpy/_core/einsumfunc.pyi | 3 +- numpy/_core/multiarray.pyi | 3 +- numpy/_core/records.pyi | 41 +++++++----- numpy/typing/tests/data/fail/ndarray_misc.pyi | 5 -- numpy/typing/tests/data/pass/ndarray_misc.py | 13 ++++ 7 files changed, 98 insertions(+), 35 deletions(-) diff --git a/numpy/_core/_internal.pyi b/numpy/_core/_internal.pyi index 690554f66f94..15726fe3064e 100644 --- a/numpy/_core/_internal.pyi +++ b/numpy/_core/_internal.pyi @@ -1,23 +1,41 @@ -from typing import Any, TypeVar, overload, Generic import ctypes as ct +import re +from collections.abc import Callable, Iterable +from typing import Any, Final, Generic, overload -from numpy.typing import NDArray +from typing_extensions import Self, TypeVar, deprecated + +import numpy as np +import numpy.typing as npt from numpy.ctypeslib import c_intp -_CastT = TypeVar("_CastT", bound=ct._CanCastTo) # Copied from `ctypes.cast` +_CastT = TypeVar("_CastT", bound=ct._CanCastTo) +_T_co = TypeVar("_T_co", covariant=True) _CT = TypeVar("_CT", bound=ct._CData) -_PT = TypeVar("_PT", bound=int) +_PT_co = TypeVar("_PT_co", bound=int | None, default=None, covariant=True) + +### + +IS_PYPY: Final[bool] = ... + +format_re: Final[re.Pattern[str]] = ... +sep_re: Final[re.Pattern[str]] = ... +space_re: Final[re.Pattern[str]] = ... + +### # TODO: Let the likes of `shape_as` and `strides_as` return `None` # for 0D arrays once we've got shape-support -class _ctypes(Generic[_PT]): +class _ctypes(Generic[_PT_co]): @overload - def __new__(cls, array: NDArray[Any], ptr: None = ...) -> _ctypes[None]: ... + def __init__(self: _ctypes[None], /, array: npt.NDArray[Any], ptr: None = None) -> None: ... @overload - def __new__(cls, array: NDArray[Any], ptr: _PT) -> _ctypes[_PT]: ... + def __init__(self, /, array: npt.NDArray[Any], ptr: _PT_co) -> None: ... + + # @property - def data(self) -> _PT: ... + def data(self) -> _PT_co: ... @property def shape(self) -> ct.Array[c_intp]: ... @property @@ -25,6 +43,30 @@ class _ctypes(Generic[_PT]): @property def _as_parameter_(self) -> ct.c_void_p: ... - def data_as(self, obj: type[_CastT]) -> _CastT: ... - def shape_as(self, obj: type[_CT]) -> ct.Array[_CT]: ... - def strides_as(self, obj: type[_CT]) -> ct.Array[_CT]: ... + # + def data_as(self, /, obj: type[_CastT]) -> _CastT: ... + def shape_as(self, /, obj: type[_CT]) -> ct.Array[_CT]: ... + def strides_as(self, /, obj: type[_CT]) -> ct.Array[_CT]: ... + + # + @deprecated('"get_data" is deprecated. Use "data" instead') + def get_data(self, /) -> _PT_co: ... + @deprecated('"get_shape" is deprecated. Use "shape" instead') + def get_shape(self, /) -> ct.Array[c_intp]: ... + @deprecated('"get_strides" is deprecated. Use "strides" instead') + def get_strides(self, /) -> ct.Array[c_intp]: ... + @deprecated('"get_as_parameter" is deprecated. Use "_as_parameter_" instead') + def get_as_parameter(self, /) -> ct.c_void_p: ... + +class dummy_ctype(Generic[_T_co]): + _cls: type[_T_co] + + def __init__(self, /, cls: type[_T_co]) -> None: ... + def __eq__(self, other: Self, /) -> bool: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __ne__(self, other: Self, /) -> bool: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def __mul__(self, other: object, /) -> Self: ... + def __call__(self, /, *other: object) -> _T_co: ... + +def array_ufunc_errmsg_formatter(dummy: object, ufunc: np.ufunc, method: str, *inputs: object, **kwargs: object) -> str: ... +def array_function_errmsg_formatter(public_api: Callable[..., object], types: Iterable[str]) -> str: ... +def npy_ctypes_check(cls: type) -> bool: ... diff --git a/numpy/_core/arrayprint.pyi b/numpy/_core/arrayprint.pyi index 661d58a22fe3..1f8be64d5e7b 100644 --- a/numpy/_core/arrayprint.pyi +++ b/numpy/_core/arrayprint.pyi @@ -8,6 +8,7 @@ from typing import Any, Final, Literal, SupportsIndex, TypeAlias, TypedDict, ove from typing_extensions import deprecated import numpy as np +from numpy._globals import _NoValueType from numpy._typing import NDArray, _CharLike_co, _FloatLike_co __all__ = [ @@ -93,13 +94,14 @@ def array2string( suppress_small: bool | None = None, separator: str = " ", prefix: str = "", - *, + style: _NoValueType = ..., formatter: _FormatDict | None = None, threshold: int | None = None, edgeitems: int | None = None, sign: _Sign | None = None, floatmode: _FloatMode | None = None, suffix: str = "", + *, legacy: _Legacy | None = None, ) -> str: ... @overload # style= (positional), legacy="1.13" diff --git a/numpy/_core/einsumfunc.pyi b/numpy/_core/einsumfunc.pyi index d7de9c02e16e..00629a478c25 100644 --- a/numpy/_core/einsumfunc.pyi +++ b/numpy/_core/einsumfunc.pyi @@ -180,5 +180,6 @@ def einsum_path( subscripts: str | _ArrayLikeInt_co, /, *operands: _ArrayLikeComplex_co | _DTypeLikeObject, - optimize: _OptimizeKind = ..., + optimize: _OptimizeKind = "greedy", + einsum_call: Literal[False] = False, ) -> tuple[list[Any], str]: ... diff --git a/numpy/_core/multiarray.pyi b/numpy/_core/multiarray.pyi index b656472dfec7..ea304c0789ab 100644 --- a/numpy/_core/multiarray.pyi +++ b/numpy/_core/multiarray.pyi @@ -355,7 +355,8 @@ class _ConstructorEmpty(Protocol): **kwargs: Unpack[_KwargsEmpty], ) -> NDArray[Any]: ... -error: Final = Exception +# using `Final` or `TypeAlias` will break stubtest +error = Exception # from ._multiarray_umath ITEM_HASOBJECT: Final[L[1]] diff --git a/numpy/_core/records.pyi b/numpy/_core/records.pyi index 308f96b7407b..b4ca5ff0e3bf 100644 --- a/numpy/_core/records.pyi +++ b/numpy/_core/records.pyi @@ -1,12 +1,13 @@ # ruff: noqa: ANN401 # pyright: reportSelfClsParameterName=false from collections.abc import Iterable, Sequence -from types import EllipsisType -from typing import Any, Literal, Protocol, SupportsIndex, TypeAlias, TypeVar, overload, type_check_only +from typing import Any, ClassVar, Literal, Protocol, SupportsIndex, TypeAlias, overload, type_check_only from _typeshed import StrOrBytesPath +from typing_extensions import TypeVar -from numpy import _ByteOrder, _OrderKACF, _SupportsBuffer, dtype, generic, ndarray, void +import numpy as np +from numpy import _ByteOrder, _OrderKACF, _SupportsBuffer from numpy._typing import ArrayLike, DTypeLike, NDArray, _ArrayLikeVoid_co, _NestedSequence, _ShapeLike __all__ = [ @@ -22,11 +23,11 @@ __all__ = [ ] _T = TypeVar("_T") -_SCT = TypeVar("_SCT", bound=generic) -_DType_co = TypeVar("_DType_co", bound=dtype[Any], covariant=True) +_SCT = TypeVar("_SCT", bound=np.generic) +_DTypeT_co = TypeVar("_DTypeT_co", bound=np.dtype[Any], covariant=True) _ShapeT_co = TypeVar("_ShapeT_co", bound=tuple[int, ...], covariant=True) -_RecArray: TypeAlias = recarray[Any, dtype[_SCT]] +_RecArray: TypeAlias = recarray[Any, np.dtype[_SCT]] @type_check_only class _SupportsReadInto(Protocol): @@ -37,7 +38,7 @@ class _SupportsReadInto(Protocol): ### # exported in `numpy.rec` -class record(void): +class record(np.void): def __getattribute__(self, attr: str) -> Any: ... def __setattr__(self, attr: str, val: ArrayLike) -> None: ... def pprint(self) -> str: ... @@ -47,10 +48,9 @@ class record(void): def __getitem__(self, key: list[str]) -> record: ... # exported in `numpy.rec` -class recarray(ndarray[_ShapeT_co, _DType_co]): - # NOTE: While not strictly mandatory, we're demanding here that arguments - # for the `format_parser`- and `dtype`-based dtype constructors are - # mutually exclusive +class recarray(np.ndarray[_ShapeT_co, _DTypeT_co]): + __name__: ClassVar[Literal["record"]] = "record" + __module__: Literal["numpy"] = "numpy" @overload def __new__( subtype, @@ -66,7 +66,7 @@ class recarray(ndarray[_ShapeT_co, _DType_co]): byteorder: _ByteOrder | None = None, aligned: bool = False, order: _OrderKACF = "C", - ) -> recarray[Any, dtype[record]]: ... + ) -> _RecArray[record]: ... @overload def __new__( subtype, @@ -81,18 +81,20 @@ class recarray(ndarray[_ShapeT_co, _DType_co]): byteorder: None = None, aligned: Literal[False] = False, order: _OrderKACF = "C", - ) -> recarray[Any, dtype[Any]]: ... + ) -> _RecArray[Any]: ... def __array_finalize__(self, /, obj: object) -> None: ... def __getattribute__(self, attr: str, /) -> Any: ... def __setattr__(self, attr: str, val: ArrayLike, /) -> None: ... - @overload - def field(self, /, attr: int | str, val: None = None) -> Any: ... + + # @overload def field(self, /, attr: int | str, val: ArrayLike) -> None: ... + @overload + def field(self, /, attr: int | str, val: None = None) -> Any: ... # exported in `numpy.rec` class format_parser: - dtype: dtype[void] + dtype: np.dtype[np.void] def __init__( self, /, @@ -213,6 +215,7 @@ def array( dtype: None = None, shape: _ShapeLike | None = None, offset: int = 0, + strides: tuple[int, ...] | None = None, formats: None = None, names: None = None, titles: None = None, @@ -226,6 +229,7 @@ def array( dtype: DTypeLike, shape: _ShapeLike | None = None, offset: int = 0, + strides: tuple[int, ...] | None = None, formats: None = None, names: None = None, titles: None = None, @@ -239,6 +243,7 @@ def array( dtype: None = None, shape: _ShapeLike | None = None, offset: int = 0, + strides: tuple[int, ...] | None = None, *, formats: DTypeLike, names: str | Sequence[str] | None = None, @@ -253,6 +258,7 @@ def array( dtype: DTypeLike, shape: _ShapeLike, offset: int = 0, + strides: tuple[int, ...] | None = None, formats: None = None, names: None = None, titles: None = None, @@ -267,6 +273,7 @@ def array( *, shape: _ShapeLike, offset: int = 0, + strides: tuple[int, ...] | None = None, formats: DTypeLike, names: str | Sequence[str] | None = None, titles: str | Sequence[str] | None = None, @@ -280,6 +287,7 @@ def array( dtype: DTypeLike, shape: _ShapeLike | None = None, offset: int = 0, + strides: tuple[int, ...] | None = None, formats: None = None, names: None = None, titles: None = None, @@ -293,6 +301,7 @@ def array( dtype: None = None, shape: _ShapeLike | None = None, offset: int = 0, + strides: tuple[int, ...] | None = None, *, formats: DTypeLike, names: str | Sequence[str] | None = None, diff --git a/numpy/typing/tests/data/fail/ndarray_misc.pyi b/numpy/typing/tests/data/fail/ndarray_misc.pyi index 674b378829a0..38729557b43e 100644 --- a/numpy/typing/tests/data/fail/ndarray_misc.pyi +++ b/numpy/typing/tests/data/fail/ndarray_misc.pyi @@ -16,11 +16,6 @@ AR_b: npt.NDArray[np.bool] ctypes_obj = AR_f8.ctypes -reveal_type(ctypes_obj.get_data()) # E: has no attribute -reveal_type(ctypes_obj.get_shape()) # E: has no attribute -reveal_type(ctypes_obj.get_strides()) # E: has no attribute -reveal_type(ctypes_obj.get_as_parameter()) # E: has no attribute - f8.argpartition(0) # E: has no attribute f8.diagonal() # E: has no attribute f8.dot(1) # E: has no attribute diff --git a/numpy/typing/tests/data/pass/ndarray_misc.py b/numpy/typing/tests/data/pass/ndarray_misc.py index fef9d519b78b..758626e18dd6 100644 --- a/numpy/typing/tests/data/pass/ndarray_misc.py +++ b/numpy/typing/tests/data/pass/ndarray_misc.py @@ -24,6 +24,8 @@ class SubClass(npt.NDArray[np.float64]): ... C: np.ndarray[Any, np.dtype[np.int32]] = np.array([0, 1, 2], dtype=np.int32) D = np.ones(3).view(SubClass) +ctypes_obj = A.ctypes + i4.all() A.all() A.all(axis=0) @@ -181,3 +183,14 @@ class SubClass(npt.NDArray[np.float64]): ... A_void: npt.NDArray[np.void] = np.empty(3, [("yop", float), ("yap", float)]) A_void["yop"] = A_float[:, 0] A_void["yap"] = A_float[:, 1] + +# deprecated + +with np.testing.assert_warns(DeprecationWarning): + ctypes_obj.get_data() # pyright: ignore[reportDeprecated] +with np.testing.assert_warns(DeprecationWarning): + ctypes_obj.get_shape() # pyright: ignore[reportDeprecated] +with np.testing.assert_warns(DeprecationWarning): + ctypes_obj.get_strides() # pyright: ignore[reportDeprecated] +with np.testing.assert_warns(DeprecationWarning): + ctypes_obj.get_as_parameter() # pyright: ignore[reportDeprecated] From 5bdbd6d1c0a5e934e1f38631ac2b84b4d3857cd5 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 16 Mar 2025 00:10:32 +0100 Subject: [PATCH 43/49] TYP: fix stubtest errors in ``numpy._globals`` (#28536) Ported from numpy/numtype#249 --- numpy/_globals.pyi | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/numpy/_globals.pyi b/numpy/_globals.pyi index c6b17d68d6b2..b2231a9636b0 100644 --- a/numpy/_globals.pyi +++ b/numpy/_globals.pyi @@ -6,8 +6,10 @@ from typing import Final, final @final class _CopyMode(enum.Enum): ALWAYS = True - IF_NEEDED = False - NEVER = 2 + NEVER = False + IF_NEEDED = 2 + + def __bool__(self, /) -> bool: ... @final class _NoValueType: ... From ecf97ae7d381aeb917e4517721c8f981a77a153d Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 16 Mar 2025 00:12:05 +0100 Subject: [PATCH 44/49] TYP: fix stubtest errors in ``numpy.mat[rix]lib`` (#28537) Ported from numpy/numtype#247 --- numpy/matlib.pyi | 8 ++++++++ numpy/matrixlib/defmatrix.pyi | 10 +++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/numpy/matlib.pyi b/numpy/matlib.pyi index cce0bf5b0541..c6a10c6327ef 100644 --- a/numpy/matlib.pyi +++ b/numpy/matlib.pyi @@ -70,6 +70,7 @@ from numpy import ( bitwise_invert, bitwise_left_shift, bitwise_not, + bitwise_or, bitwise_right_shift, bitwise_xor, blackman, @@ -89,6 +90,7 @@ from numpy import ( c_, can_cast, cbrt, + cdouble, ceil, char, character, @@ -160,6 +162,7 @@ from numpy import ( expand_dims, expm1, extract, + f2py, fabs, fft, fill_diagonal, @@ -209,6 +212,7 @@ from numpy import ( greater_equal, half, hamming, + hanning, heaviside, histogram, histogram2d, @@ -220,6 +224,7 @@ from numpy import ( i0, iinfo, imag, + in1d, index_exp, indices, inexact, @@ -425,9 +430,11 @@ from numpy import ( sort, sort_complex, spacing, + split, sqrt, square, squeeze, + stack, std, str_, strings, @@ -459,6 +466,7 @@ from numpy import ( trunc, typecodes, typename, + typing, ubyte, ufunc, uint, diff --git a/numpy/matrixlib/defmatrix.pyi b/numpy/matrixlib/defmatrix.pyi index 03476555e59e..a6095cc1155a 100644 --- a/numpy/matrixlib/defmatrix.pyi +++ b/numpy/matrixlib/defmatrix.pyi @@ -1,10 +1,10 @@ -from collections.abc import Sequence, Mapping +from collections.abc import Mapping, Sequence from typing import Any from numpy import matrix from numpy._typing import ArrayLike, DTypeLike, NDArray -__all__ = ["matrix", "bmat", "asmatrix"] +__all__ = ["asmatrix", "bmat", "matrix"] def bmat( obj: str | Sequence[ArrayLike] | NDArray[Any], @@ -12,6 +12,6 @@ def bmat( gdict: None | Mapping[str, Any] = ..., ) -> matrix[tuple[int, int], Any]: ... -def asmatrix(data: ArrayLike, dtype: DTypeLike = ...) -> matrix[tuple[int, int], Any]: ... - -mat = asmatrix +def asmatrix( + data: ArrayLike, dtype: DTypeLike = ... +) -> matrix[tuple[int, int], Any]: ... From 2ff0b8870639ae2d81282159928030382c01acef Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 16 Mar 2025 00:13:17 +0100 Subject: [PATCH 45/49] TYP: fix stubtest errors in ``numpy.random`` (#28538) Ported from numpy/numtype#251 --- numpy/random/bit_generator.pyi | 159 ++++++++++++++------------------- 1 file changed, 69 insertions(+), 90 deletions(-) diff --git a/numpy/random/bit_generator.pyi b/numpy/random/bit_generator.pyi index 8dfbcd9909dd..78fb769683d3 100644 --- a/numpy/random/bit_generator.pyi +++ b/numpy/random/bit_generator.pyi @@ -1,128 +1,107 @@ import abc -from threading import Lock from collections.abc import Callable, Mapping, Sequence -from typing import ( - Any, - NamedTuple, - TypeAlias, - TypedDict, - TypeVar, - overload, - Literal, - type_check_only, -) - -from numpy import dtype, uint32, uint64 -from numpy._typing import ( - NDArray, - _ArrayLikeInt_co, - _ShapeLike, - _SupportsDType, - _UInt32Codes, - _UInt64Codes, -) - -_T = TypeVar("_T") - -_DTypeLikeUint32: TypeAlias = ( - dtype[uint32] - | _SupportsDType[dtype[uint32]] - | type[uint32] - | _UInt32Codes -) -_DTypeLikeUint64: TypeAlias = ( - dtype[uint64] - | _SupportsDType[dtype[uint64]] - | type[uint64] - | _UInt64Codes -) +from threading import Lock +from typing import Any, ClassVar, Literal, NamedTuple, TypeAlias, TypedDict, overload, type_check_only + +from _typeshed import Incomplete +from typing_extensions import CapsuleType, Self + +import numpy as np +from numpy._typing import NDArray, _ArrayLikeInt_co, _DTypeLike, _ShapeLike, _UInt32Codes, _UInt64Codes + +__all__ = ["BitGenerator", "SeedSequence"] + +### + +_DTypeLikeUint_: TypeAlias = _DTypeLike[np.uint32 | np.uint64] | _UInt32Codes | _UInt64Codes @type_check_only class _SeedSeqState(TypedDict): - entropy: None | int | Sequence[int] + entropy: int | Sequence[int] | None spawn_key: tuple[int, ...] pool_size: int n_children_spawned: int @type_check_only class _Interface(NamedTuple): - state_address: Any - state: Any - next_uint64: Any - next_uint32: Any - next_double: Any - bit_generator: Any + state_address: Incomplete + state: Incomplete + next_uint64: Incomplete + next_uint32: Incomplete + next_double: Incomplete + bit_generator: Incomplete + +@type_check_only +class _CythonMixin: + def __setstate_cython__(self, pyx_state: object, /) -> None: ... + def __reduce_cython__(self) -> Any: ... # noqa: ANN401 + +@type_check_only +class _GenerateStateMixin(_CythonMixin): + def generate_state(self, /, n_words: int, dtype: _DTypeLikeUint_ = ...) -> NDArray[np.uint32 | np.uint64]: ... + +### class ISeedSequence(abc.ABC): @abc.abstractmethod - def generate_state( - self, n_words: int, dtype: _DTypeLikeUint32 | _DTypeLikeUint64 = ... - ) -> NDArray[uint32 | uint64]: ... + def generate_state(self, /, n_words: int, dtype: _DTypeLikeUint_ = ...) -> NDArray[np.uint32 | np.uint64]: ... -class ISpawnableSeedSequence(ISeedSequence): +class ISpawnableSeedSequence(ISeedSequence, abc.ABC): @abc.abstractmethod - def spawn(self: _T, n_children: int) -> list[_T]: ... + def spawn(self, /, n_children: int) -> list[Self]: ... + +class SeedlessSeedSequence(_GenerateStateMixin, ISpawnableSeedSequence): + def spawn(self, /, n_children: int) -> list[Self]: ... -class SeedlessSeedSequence(ISpawnableSeedSequence): - def generate_state( - self, n_words: int, dtype: _DTypeLikeUint32 | _DTypeLikeUint64 = ... - ) -> NDArray[uint32 | uint64]: ... - def spawn(self: _T, n_children: int) -> list[_T]: ... +class SeedSequence(_GenerateStateMixin, ISpawnableSeedSequence): + __pyx_vtable__: ClassVar[CapsuleType] = ... -class SeedSequence(ISpawnableSeedSequence): - entropy: None | int | Sequence[int] + entropy: int | Sequence[int] | None spawn_key: tuple[int, ...] pool_size: int n_children_spawned: int - pool: NDArray[uint32] + pool: NDArray[np.uint32] + def __init__( self, - entropy: None | int | Sequence[int] | _ArrayLikeInt_co = ..., + /, + entropy: _ArrayLikeInt_co | None = None, *, - spawn_key: Sequence[int] = ..., - pool_size: int = ..., + spawn_key: Sequence[int] = (), + pool_size: int = 4, n_children_spawned: int = ..., ) -> None: ... - def __repr__(self) -> str: ... + def spawn(self, /, n_children: int) -> list[Self]: ... @property - def state( - self, - ) -> _SeedSeqState: ... - def generate_state( - self, n_words: int, dtype: _DTypeLikeUint32 | _DTypeLikeUint64 = ... - ) -> NDArray[uint32 | uint64]: ... - def spawn(self, n_children: int) -> list[SeedSequence]: ... + def state(self) -> _SeedSeqState: ... -class BitGenerator(abc.ABC): +class BitGenerator(_CythonMixin, abc.ABC): lock: Lock - def __init__(self, seed: None | _ArrayLikeInt_co | SeedSequence = ...) -> None: ... - def __getstate__(self) -> tuple[dict[str, Any], ISeedSequence]: ... - def __setstate__( - self, state_seed_seq: dict[str, Any] | tuple[dict[str, Any], ISeedSequence] - ) -> None: ... - def __reduce__( - self, - ) -> tuple[ - Callable[[str], BitGenerator], - tuple[str], - tuple[dict[str, Any], ISeedSequence] - ]: ... - @abc.abstractmethod @property def state(self) -> Mapping[str, Any]: ... @state.setter - def state(self, value: Mapping[str, Any]) -> None: ... + def state(self, value: Mapping[str, Any], /) -> None: ... @property def seed_seq(self) -> ISeedSequence: ... - def spawn(self, n_children: int) -> list[BitGenerator]: ... - @overload - def random_raw(self, size: None = ..., output: Literal[True] = ...) -> int: ... # type: ignore[misc] - @overload - def random_raw(self, size: _ShapeLike = ..., output: Literal[True] = ...) -> NDArray[uint64]: ... # type: ignore[misc] - @overload - def random_raw(self, size: None | _ShapeLike = ..., output: Literal[False] = ...) -> None: ... # type: ignore[misc] - def _benchmark(self, cnt: int, method: str = ...) -> None: ... @property def ctypes(self) -> _Interface: ... @property def cffi(self) -> _Interface: ... + @property + def capsule(self) -> CapsuleType: ... + + # + def __init__(self, /, seed: _ArrayLikeInt_co | SeedSequence | None = None) -> None: ... + def __reduce__(self) -> tuple[Callable[[str], Self], tuple[str], tuple[Mapping[str, Any], ISeedSequence]]: ... + def spawn(self, /, n_children: int) -> list[Self]: ... + def _benchmark(self, /, cnt: int, method: str = "uint64") -> None: ... + + # + @overload + def random_raw(self, /, size: None = None, output: Literal[True] = True) -> int: ... + @overload + def random_raw(self, /, size: _ShapeLike, output: Literal[True] = True) -> NDArray[np.uint64]: ... + @overload + def random_raw(self, /, size: _ShapeLike | None, output: Literal[False]) -> None: ... + @overload + def random_raw(self, /, size: _ShapeLike | None = None, *, output: Literal[False]) -> None: ... From bed0064322e5f5c19fa108f7fe8a16cc9e141f2c Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 16 Mar 2025 00:20:20 +0100 Subject: [PATCH 46/49] TYP: fix stubtest errors in ``numpy.testing`` (#28539) Ported from numpy/numtype#252 --- numpy/testing/__init__.pyi | 128 ++--- numpy/testing/_private/utils.pyi | 541 +++++++++++---------- numpy/typing/tests/data/fail/testing.pyi | 8 +- numpy/typing/tests/data/reveal/testing.pyi | 6 +- 4 files changed, 360 insertions(+), 323 deletions(-) diff --git a/numpy/testing/__init__.pyi b/numpy/testing/__init__.pyi index e47b8f9546c6..ba3c9a2b7a44 100644 --- a/numpy/testing/__init__.pyi +++ b/numpy/testing/__init__.pyi @@ -2,97 +2,101 @@ from unittest import TestCase from . import overrides from ._private.utils import ( - NOGIL_BUILD, - IS_WASM, + HAS_LAPACK64, + HAS_REFCOUNT, + IS_EDITABLE, + IS_INSTALLED, + IS_MUSL, IS_PYPY, IS_PYSTON, - IS_MUSL, - IS_EDITABLE, - HAS_REFCOUNT, - HAS_LAPACK64, - assert_equal, + IS_WASM, + NOGIL_BUILD, + NUMPY_ROOT, + IgnoreException, + KnownFailureException, + SkipTest, + assert_, + assert_allclose, assert_almost_equal, assert_approx_equal, + assert_array_almost_equal, + assert_array_almost_equal_nulp, + assert_array_compare, assert_array_equal, assert_array_less, - assert_string_equal, - assert_array_almost_equal, + assert_array_max_ulp, + assert_equal, + assert_no_gc_cycles, + assert_no_warnings, assert_raises, + assert_raises_regex, + assert_string_equal, + assert_warns, + break_cycles, build_err_msg, + check_support_sve, + clear_and_catch_warnings, decorate_methods, jiffies, + measure, memusage, print_assert_equal, + run_threaded, rundocs, runstring, - verbose, - measure, - assert_, - assert_array_almost_equal_nulp, - assert_raises_regex, - assert_array_max_ulp, - assert_warns, - assert_no_warnings, - assert_allclose, - IgnoreException, - clear_and_catch_warnings, - SkipTest, - KnownFailureException, - temppath, - tempdir, suppress_warnings, - assert_array_compare, - assert_no_gc_cycles, - break_cycles, - check_support_sve, - run_threaded, + tempdir, + temppath, + verbose, ) __all__ = [ - "assert_equal", + "HAS_LAPACK64", + "HAS_REFCOUNT", + "IS_EDITABLE", + "IS_INSTALLED", + "IS_MUSL", + "IS_PYPY", + "IS_PYSTON", + "IS_WASM", + "NOGIL_BUILD", + "NUMPY_ROOT", + "IgnoreException", + "KnownFailureException", + "SkipTest", + "TestCase", + "assert_", + "assert_allclose", "assert_almost_equal", "assert_approx_equal", + "assert_array_almost_equal", + "assert_array_almost_equal_nulp", + "assert_array_compare", "assert_array_equal", "assert_array_less", - "assert_string_equal", - "assert_array_almost_equal", + "assert_array_max_ulp", + "assert_equal", + "assert_no_gc_cycles", + "assert_no_warnings", "assert_raises", + "assert_raises_regex", + "assert_string_equal", + "assert_warns", + "break_cycles", "build_err_msg", + "check_support_sve", + "clear_and_catch_warnings", "decorate_methods", "jiffies", + "measure", "memusage", + "overrides", "print_assert_equal", + "run_threaded", "rundocs", "runstring", - "verbose", - "measure", - "assert_", - "assert_array_almost_equal_nulp", - "assert_raises_regex", - "assert_array_max_ulp", - "assert_warns", - "assert_no_warnings", - "assert_allclose", - "IgnoreException", - "clear_and_catch_warnings", - "SkipTest", - "KnownFailureException", - "temppath", - "tempdir", - "IS_PYPY", - "HAS_REFCOUNT", - "IS_WASM", "suppress_warnings", - "assert_array_compare", - "assert_no_gc_cycles", - "break_cycles", - "HAS_LAPACK64", - "IS_PYSTON", - "IS_MUSL", - "check_support_sve", - "NOGIL_BUILD", - "IS_EDITABLE", - "run_threaded", - "TestCase", - "overrides", + "tempdir", + "temppath", + "verbose", ] diff --git a/numpy/testing/_private/utils.pyi b/numpy/testing/_private/utils.pyi index b2f4045c7703..75ea45d3a721 100644 --- a/numpy/testing/_private/utils.pyi +++ b/numpy/testing/_private/utils.pyi @@ -1,42 +1,42 @@ -import sys import ast +import sys import types -import warnings import unittest -from _typeshed import GenericPath, StrOrBytesPath, StrPath +import warnings from collections.abc import Callable, Iterable, Sequence from contextlib import _GeneratorContextManager +from pathlib import Path from re import Pattern from typing import ( - Literal as L, Any, AnyStr, ClassVar, + Final, + Generic, NoReturn, + SupportsIndex, TypeAlias, overload, type_check_only, - TypeVar, - Final, - SupportsIndex, - ParamSpec ) +from typing import Literal as L +from unittest.case import SkipTest + +from _typeshed import ConvertibleToFloat, GenericPath, StrOrBytesPath, StrPath +from typing_extensions import ParamSpec, Self, TypeVar, TypeVarTuple, Unpack import numpy as np -from numpy import number, object_, _ConvertibleToFloat from numpy._typing import ( - NDArray, ArrayLike, DTypeLike, + NDArray, + _ArrayLikeDT64_co, _ArrayLikeNumber_co, _ArrayLikeObject_co, _ArrayLikeTD64_co, - _ArrayLikeDT64_co, ) -from unittest.case import SkipTest - -__all__ = [ +__all__ = [ # noqa: RUF022 "IS_EDITABLE", "IS_MUSL", "IS_PYPY", @@ -83,58 +83,33 @@ __all__ = [ "run_threaded", ] -_P = ParamSpec("_P") +### + _T = TypeVar("_T") -_ET = TypeVar("_ET", bound=BaseException) +_Ts = TypeVarTuple("_Ts") +_Tss = ParamSpec("_Tss") +_ET = TypeVar("_ET", bound=BaseException, default=BaseException) _FT = TypeVar("_FT", bound=Callable[..., Any]) +_W_co = TypeVar("_W_co", bound=_WarnLog | None, default=_WarnLog | None, covariant=True) +_T_or_bool = TypeVar("_T_or_bool", default=bool) + +_StrLike: TypeAlias = str | bytes +_RegexLike: TypeAlias = _StrLike | Pattern[Any] +_NumericArrayLike: TypeAlias = _ArrayLikeNumber_co | _ArrayLikeObject_co -# Must return a bool or an ndarray/generic type -# that is supported by `np.logical_and.reduce` +_ExceptionSpec: TypeAlias = type[_ET] | tuple[type[_ET], ...] +_WarningSpec: TypeAlias = type[Warning] +_WarnLog: TypeAlias = list[warnings.WarningMessage] +_ToModules: TypeAlias = Iterable[types.ModuleType] + +# Must return a bool or an ndarray/generic type that is supported by `np.logical_and.reduce` _ComparisonFunc: TypeAlias = Callable[ [NDArray[Any], NDArray[Any]], - ( - bool - | np.bool - | number[Any] - | NDArray[np.bool | number[Any] | object_] - ) + bool | np.bool | np.number | NDArray[np.bool | np.number | np.object_], ] -class KnownFailureException(Exception): ... -class IgnoreException(Exception): ... - -class clear_and_catch_warnings(warnings.catch_warnings[list[warnings.WarningMessage]]): - class_modules: ClassVar[tuple[types.ModuleType, ...]] - modules: set[types.ModuleType] - @overload - def __new__( - cls, - record: L[False] = ..., - modules: Iterable[types.ModuleType] = ..., - ) -> _clear_and_catch_warnings_without_records: ... - @overload - def __new__( - cls, - record: L[True], - modules: Iterable[types.ModuleType] = ..., - ) -> _clear_and_catch_warnings_with_records: ... - @overload - def __new__( - cls, - record: bool, - modules: Iterable[types.ModuleType] = ..., - ) -> clear_and_catch_warnings: ... - def __enter__(self) -> None | list[warnings.WarningMessage]: ... - def __exit__( - self, - __exc_type: None | type[BaseException] = ..., - __exc_val: None | BaseException = ..., - __exc_tb: None | types.TracebackType = ..., - ) -> None: ... - # Type-check only `clear_and_catch_warnings` subclasses for both values of the # `record` parameter. Copied from the stdlib `warnings` stubs. - @type_check_only class _clear_and_catch_warnings_with_records(clear_and_catch_warnings): def __enter__(self) -> list[warnings.WarningMessage]: ... @@ -143,321 +118,379 @@ class _clear_and_catch_warnings_with_records(clear_and_catch_warnings): class _clear_and_catch_warnings_without_records(clear_and_catch_warnings): def __enter__(self) -> None: ... +### + +verbose: int = 0 +NUMPY_ROOT: Final[Path] = ... +IS_INSTALLED: Final[bool] = ... +IS_EDITABLE: Final[bool] = ... +IS_MUSL: Final[bool] = ... +IS_PYPY: Final[bool] = ... +IS_PYSTON: Final[bool] = ... +IS_WASM: Final[bool] = ... +HAS_REFCOUNT: Final[bool] = ... +HAS_LAPACK64: Final[bool] = ... +NOGIL_BUILD: Final[bool] = ... + +class KnownFailureException(Exception): ... +class IgnoreException(Exception): ... + +# NOTE: `warnings.catch_warnings` is incorrectly defined as invariant in typeshed +class clear_and_catch_warnings(warnings.catch_warnings[_W_co], Generic[_W_co]): # type: ignore[type-var] # pyright: ignore[reportInvalidTypeArguments] + class_modules: ClassVar[tuple[types.ModuleType, ...]] = () + modules: Final[set[types.ModuleType]] + @overload # record: True + def __init__(self: clear_and_catch_warnings[_WarnLog], /, record: L[True], modules: _ToModules = ()) -> None: ... + @overload # record: False (default) + def __init__(self: clear_and_catch_warnings[None], /, record: L[False] = False, modules: _ToModules = ()) -> None: ... + @overload # record; bool + def __init__(self, /, record: bool, modules: _ToModules = ()) -> None: ... + class suppress_warnings: - log: list[warnings.WarningMessage] - def __init__( - self, - forwarding_rule: L["always", "module", "once", "location"] = ..., - ) -> None: ... - def filter( - self, - category: type[Warning] = ..., - message: str = ..., - module: None | types.ModuleType = ..., - ) -> None: ... - def record( - self, - category: type[Warning] = ..., - message: str = ..., - module: None | types.ModuleType = ..., - ) -> list[warnings.WarningMessage]: ... - def __enter__(self: _T) -> _T: ... - def __exit__( - self, - __exc_type: None | type[BaseException] = ..., - __exc_val: None | BaseException = ..., - __exc_tb: None | types.TracebackType = ..., - ) -> None: ... - def __call__(self, func: _FT) -> _FT: ... - -verbose: int -IS_EDITABLE: Final[bool] -IS_MUSL: Final[bool] -IS_PYPY: Final[bool] -IS_PYSTON: Final[bool] -IS_WASM: Final[bool] -HAS_REFCOUNT: Final[bool] -HAS_LAPACK64: Final[bool] -NOGIL_BUILD: Final[bool] - -def assert_(val: object, msg: str | Callable[[], str] = ...) -> None: ... + log: Final[_WarnLog] + def __init__(self, /, forwarding_rule: L["always", "module", "once", "location"] = "always") -> None: ... + def __enter__(self) -> Self: ... + def __exit__(self, cls: type[BaseException] | None, exc: BaseException | None, tb: types.TracebackType | None, /) -> None: ... + def __call__(self, /, func: _FT) -> _FT: ... + + # + def filter(self, /, category: type[Warning] = ..., message: str = "", module: types.ModuleType | None = None) -> None: ... + def record(self, /, category: type[Warning] = ..., message: str = "", module: types.ModuleType | None = None) -> _WarnLog: ... # Contrary to runtime we can't do `os.name` checks while type checking, # only `sys.platform` checks if sys.platform == "win32" or sys.platform == "cygwin": def memusage(processName: str = ..., instance: int = ...) -> int: ... elif sys.platform == "linux": - def memusage(_proc_pid_stat: StrOrBytesPath = ...) -> None | int: ... + def memusage(_proc_pid_stat: StrOrBytesPath = ...) -> int | None: ... else: def memusage() -> NoReturn: ... if sys.platform == "linux": - def jiffies( - _proc_pid_stat: StrOrBytesPath = ..., - _load_time: list[float] = ..., - ) -> int: ... + def jiffies(_proc_pid_stat: StrOrBytesPath = ..., _load_time: list[float] = []) -> int: ... else: - def jiffies(_load_time: list[float] = ...) -> int: ... + def jiffies(_load_time: list[float] = []) -> int: ... +# def build_err_msg( arrays: Iterable[object], - err_msg: str, + err_msg: object, header: str = ..., verbose: bool = ..., names: Sequence[str] = ..., - precision: None | SupportsIndex = ..., + precision: SupportsIndex | None = ..., ) -> str: ... +# +def print_assert_equal(test_string: str, actual: object, desired: object) -> None: ... + +# +def assert_(val: object, msg: str | Callable[[], str] = "") -> None: ... + +# def assert_equal( actual: object, desired: object, - err_msg: object = ..., - verbose: bool = ..., + err_msg: object = "", + verbose: bool = True, *, - strict: bool = ... -) -> None: ... - -def print_assert_equal( - test_string: str, - actual: object, - desired: object, + strict: bool = False, ) -> None: ... def assert_almost_equal( - actual: _ArrayLikeNumber_co | _ArrayLikeObject_co, - desired: _ArrayLikeNumber_co | _ArrayLikeObject_co, - decimal: int = ..., - err_msg: object = ..., - verbose: bool = ..., + actual: _NumericArrayLike, + desired: _NumericArrayLike, + decimal: int = 7, + err_msg: object = "", + verbose: bool = True, ) -> None: ... -# Anything that can be coerced into `builtins.float` +# def assert_approx_equal( - actual: _ConvertibleToFloat, - desired: _ConvertibleToFloat, - significant: int = ..., - err_msg: object = ..., - verbose: bool = ..., + actual: ConvertibleToFloat, + desired: ConvertibleToFloat, + significant: int = 7, + err_msg: object = "", + verbose: bool = True, ) -> None: ... +# def assert_array_compare( comparison: _ComparisonFunc, x: ArrayLike, y: ArrayLike, - err_msg: object = ..., - verbose: bool = ..., - header: str = ..., - precision: SupportsIndex = ..., - equal_nan: bool = ..., - equal_inf: bool = ..., + err_msg: object = "", + verbose: bool = True, + header: str = "", + precision: SupportsIndex = 6, + equal_nan: bool = True, + equal_inf: bool = True, *, - strict: bool = ... + strict: bool = False, + names: tuple[str, str] = ("ACTUAL", "DESIRED"), ) -> None: ... +# def assert_array_equal( - x: ArrayLike, - y: ArrayLike, - /, - err_msg: object = ..., - verbose: bool = ..., + actual: object, + desired: object, + err_msg: object = "", + verbose: bool = True, *, - strict: bool = ... + strict: bool = False, ) -> None: ... +# def assert_array_almost_equal( - x: _ArrayLikeNumber_co | _ArrayLikeObject_co, - y: _ArrayLikeNumber_co | _ArrayLikeObject_co, - /, - decimal: float = ..., - err_msg: object = ..., - verbose: bool = ..., + actual: _NumericArrayLike, + desired: _NumericArrayLike, + decimal: float = 6, + err_msg: object = "", + verbose: bool = True, ) -> None: ... @overload def assert_array_less( - x: _ArrayLikeNumber_co | _ArrayLikeObject_co, - y: _ArrayLikeNumber_co | _ArrayLikeObject_co, - err_msg: object = ..., - verbose: bool = ..., + x: _ArrayLikeDT64_co, + y: _ArrayLikeDT64_co, + err_msg: object = "", + verbose: bool = True, *, - strict: bool = ... + strict: bool = False, ) -> None: ... @overload def assert_array_less( x: _ArrayLikeTD64_co, y: _ArrayLikeTD64_co, - err_msg: object = ..., - verbose: bool = ..., + err_msg: object = "", + verbose: bool = True, *, - strict: bool = ... + strict: bool = False, ) -> None: ... @overload def assert_array_less( - x: _ArrayLikeDT64_co, - y: _ArrayLikeDT64_co, - err_msg: object = ..., - verbose: bool = ..., + x: _NumericArrayLike, + y: _NumericArrayLike, + err_msg: object = "", + verbose: bool = True, *, - strict: bool = ... + strict: bool = False, ) -> None: ... -def runstring( - astr: str | bytes | types.CodeType, - dict: None | dict[str, Any], -) -> Any: ... - +# def assert_string_equal(actual: str, desired: str) -> None: ... -def rundocs( - filename: StrPath | None = ..., - raise_on_error: bool = ..., -) -> None: ... - -def check_support_sve(__cache: list[_T]) -> _T: ... - -def raises(*args: type[BaseException]) -> Callable[[_FT], _FT]: ... - -@overload -def assert_raises( # type: ignore - expected_exception: type[BaseException] | tuple[type[BaseException], ...], - callable: Callable[_P, Any], - /, - *args: _P.args, - **kwargs: _P.kwargs, -) -> None: ... +# @overload def assert_raises( - expected_exception: type[_ET] | tuple[type[_ET], ...], + exception_class: _ExceptionSpec[_ET], + /, *, - msg: None | str = ..., + msg: str | None = None, ) -> unittest.case._AssertRaisesContext[_ET]: ... - @overload -def assert_raises_regex( - expected_exception: type[BaseException] | tuple[type[BaseException], ...], - expected_regex: str | bytes | Pattern[Any], - callable: Callable[_P, Any], +def assert_raises( + exception_class: _ExceptionSpec, + callable: Callable[_Tss, Any], /, - *args: _P.args, - **kwargs: _P.kwargs, + *args: _Tss.args, + **kwargs: _Tss.kwargs, ) -> None: ... + +# @overload def assert_raises_regex( - expected_exception: type[_ET] | tuple[type[_ET], ...], - expected_regex: str | bytes | Pattern[Any], + exception_class: _ExceptionSpec[_ET], + expected_regexp: _RegexLike, *, - msg: None | str = ..., + msg: str | None = None, ) -> unittest.case._AssertRaisesContext[_ET]: ... - -def decorate_methods( - cls: type[Any], - decorator: Callable[[Callable[..., Any]], Any], - testmatch: None | str | bytes | Pattern[Any] = ..., +@overload +def assert_raises_regex( + exception_class: _ExceptionSpec, + expected_regexp: _RegexLike, + callable: Callable[_Tss, Any], + *args: _Tss.args, + **kwargs: _Tss.kwargs, ) -> None: ... -def measure( - code_str: str | bytes | ast.mod | ast.AST, - times: int = ..., - label: None | str = ..., -) -> float: ... - +# @overload def assert_allclose( - actual: _ArrayLikeNumber_co | _ArrayLikeObject_co, - desired: _ArrayLikeNumber_co | _ArrayLikeObject_co, - rtol: float = ..., - atol: float = ..., - equal_nan: bool = ..., - err_msg: object = ..., - verbose: bool = ..., + actual: _ArrayLikeTD64_co, + desired: _ArrayLikeTD64_co, + rtol: float = 1e-7, + atol: float = 0, + equal_nan: bool = True, + err_msg: object = "", + verbose: bool = True, *, - strict: bool = ... + strict: bool = False, ) -> None: ... @overload def assert_allclose( - actual: _ArrayLikeTD64_co, - desired: _ArrayLikeTD64_co, - rtol: float = ..., - atol: float = ..., - equal_nan: bool = ..., - err_msg: object = ..., - verbose: bool = ..., + actual: _NumericArrayLike, + desired: _NumericArrayLike, + rtol: float = 1e-7, + atol: float = 0, + equal_nan: bool = True, + err_msg: object = "", + verbose: bool = True, *, - strict: bool = ... + strict: bool = False, ) -> None: ... +# def assert_array_almost_equal_nulp( x: _ArrayLikeNumber_co, y: _ArrayLikeNumber_co, - nulp: float = ..., + nulp: float = 1, ) -> None: ... +# def assert_array_max_ulp( a: _ArrayLikeNumber_co, b: _ArrayLikeNumber_co, - maxulp: float = ..., - dtype: DTypeLike = ..., + maxulp: float = 1, + dtype: DTypeLike | None = None, ) -> NDArray[Any]: ... +# @overload -def assert_warns(warning_class: type[Warning]) -> _GeneratorContextManager[None]: ... +def assert_warns(warning_class: _WarningSpec) -> _GeneratorContextManager[None]: ... @overload -def assert_warns( - warning_class: type[Warning], - func: Callable[_P, _T], - /, - *args: _P.args, - **kwargs: _P.kwargs, -) -> _T: ... +def assert_warns(warning_class: _WarningSpec, func: Callable[_Tss, _T], *args: _Tss.args, **kwargs: _Tss.kwargs) -> _T: ... +# @overload def assert_no_warnings() -> _GeneratorContextManager[None]: ... @overload -def assert_no_warnings( - func: Callable[_P, _T], - /, - *args: _P.args, - **kwargs: _P.kwargs, -) -> _T: ... +def assert_no_warnings(func: Callable[_Tss, _T], /, *args: _Tss.args, **kwargs: _Tss.kwargs) -> _T: ... +# +@overload +def assert_no_gc_cycles() -> _GeneratorContextManager[None]: ... +@overload +def assert_no_gc_cycles(func: Callable[_Tss, Any], /, *args: _Tss.args, **kwargs: _Tss.kwargs) -> None: ... + +### + +# @overload def tempdir( - suffix: None = ..., - prefix: None = ..., - dir: None = ..., + suffix: None = None, + prefix: None = None, + dir: None = None, ) -> _GeneratorContextManager[str]: ... @overload def tempdir( - suffix: AnyStr | None = ..., - prefix: AnyStr | None = ..., - dir: GenericPath[AnyStr] | None = ..., + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + *, + dir: GenericPath[AnyStr], +) -> _GeneratorContextManager[AnyStr]: ... +@overload +def tempdir( + suffix: AnyStr | None = None, + *, + prefix: AnyStr, + dir: GenericPath[AnyStr] | None = None, +) -> _GeneratorContextManager[AnyStr]: ... +@overload +def tempdir( + suffix: AnyStr, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, ) -> _GeneratorContextManager[AnyStr]: ... +# @overload def temppath( - suffix: None = ..., - prefix: None = ..., - dir: None = ..., - text: bool = ..., + suffix: None = None, + prefix: None = None, + dir: None = None, + text: bool = False, ) -> _GeneratorContextManager[str]: ... @overload def temppath( - suffix: AnyStr | None = ..., - prefix: AnyStr | None = ..., - dir: GenericPath[AnyStr] | None = ..., - text: bool = ..., + suffix: AnyStr | None, + prefix: AnyStr | None, + dir: GenericPath[AnyStr], + text: bool = False, +) -> _GeneratorContextManager[AnyStr]: ... +@overload +def temppath( + suffix: AnyStr | None = None, + prefix: AnyStr | None = None, + *, + dir: GenericPath[AnyStr], + text: bool = False, +) -> _GeneratorContextManager[AnyStr]: ... +@overload +def temppath( + suffix: AnyStr | None, + prefix: AnyStr, + dir: GenericPath[AnyStr] | None = None, + text: bool = False, +) -> _GeneratorContextManager[AnyStr]: ... +@overload +def temppath( + suffix: AnyStr | None = None, + *, + prefix: AnyStr, + dir: GenericPath[AnyStr] | None = None, + text: bool = False, +) -> _GeneratorContextManager[AnyStr]: ... +@overload +def temppath( + suffix: AnyStr, + prefix: AnyStr | None = None, + dir: GenericPath[AnyStr] | None = None, + text: bool = False, ) -> _GeneratorContextManager[AnyStr]: ... +# +def check_support_sve(__cache: list[_T_or_bool] = []) -> _T_or_bool: ... # noqa: PYI063 + +# +def decorate_methods( + cls: type, + decorator: Callable[[Callable[..., Any]], Any], + testmatch: _RegexLike | None = None, +) -> None: ... + +# @overload -def assert_no_gc_cycles() -> _GeneratorContextManager[None]: ... +def run_threaded( + func: Callable[[], None], + max_workers: int = 8, + pass_count: bool = False, + pass_barrier: bool = False, + outer_iterations: int = 1, + prepare_args: None = None, +) -> None: ... @overload -def assert_no_gc_cycles( - func: Callable[_P, Any], - /, - *args: _P.args, - **kwargs: _P.kwargs, +def run_threaded( + func: Callable[[Unpack[_Ts]], None], + max_workers: int, + pass_count: bool, + pass_barrier: bool, + outer_iterations: int, + prepare_args: tuple[Unpack[_Ts]], +) -> None: ... +@overload +def run_threaded( + func: Callable[[Unpack[_Ts]], None], + max_workers: int = 8, + pass_count: bool = False, + pass_barrier: bool = False, + outer_iterations: int = 1, + *, + prepare_args: tuple[Unpack[_Ts]], ) -> None: ... +# +def runstring(astr: _StrLike | types.CodeType, dict: dict[str, Any] | None) -> Any: ... # noqa: ANN401 +def rundocs(filename: StrPath | None = None, raise_on_error: bool = True) -> None: ... +def measure(code_str: _StrLike | ast.AST, times: int = 1, label: str | None = None) -> float: ... def break_cycles() -> None: ... - -def run_threaded(func: Callable[[], None], iters: int, pass_count: bool = False) -> None: ... diff --git a/numpy/typing/tests/data/fail/testing.pyi b/numpy/typing/tests/data/fail/testing.pyi index 953670180203..f7eaa7d20836 100644 --- a/numpy/typing/tests/data/fail/testing.pyi +++ b/numpy/typing/tests/data/fail/testing.pyi @@ -3,7 +3,7 @@ import numpy.typing as npt AR_U: npt.NDArray[np.str_] -def func() -> bool: ... +def func(x: object) -> bool: ... np.testing.assert_(True, msg=1) # E: incompatible type np.testing.build_err_msg(1, "test") # E: incompatible type @@ -20,9 +20,9 @@ np.testing.assert_allclose(AR_U, AR_U) # E: incompatible type np.testing.assert_array_almost_equal_nulp(AR_U, AR_U) # E: incompatible type np.testing.assert_array_max_ulp(AR_U, AR_U) # E: incompatible type -np.testing.assert_warns(warning_class=RuntimeWarning, func=func) # E: No overload variant +np.testing.assert_warns(RuntimeWarning, func) # E: No overload variant np.testing.assert_no_warnings(func=func) # E: No overload variant -np.testing.assert_no_warnings(func, None) # E: Too many arguments -np.testing.assert_no_warnings(func, test=None) # E: No overload variant +np.testing.assert_no_warnings(func) # E: Too many arguments +np.testing.assert_no_warnings(func, y=None) # E: No overload variant np.testing.assert_no_gc_cycles(func=func) # E: No overload variant diff --git a/numpy/typing/tests/data/reveal/testing.pyi b/numpy/typing/tests/data/reveal/testing.pyi index 5301090a5f4b..741c71f62a5b 100644 --- a/numpy/typing/tests/data/reveal/testing.pyi +++ b/numpy/typing/tests/data/reveal/testing.pyi @@ -32,15 +32,15 @@ assert_type(np.testing.IgnoreException(), np.testing.IgnoreException) assert_type( np.testing.clear_and_catch_warnings(modules=[np.testing]), - np.testing._private.utils._clear_and_catch_warnings_without_records, + np.testing.clear_and_catch_warnings[None], ) assert_type( np.testing.clear_and_catch_warnings(True), - np.testing._private.utils._clear_and_catch_warnings_with_records, + np.testing.clear_and_catch_warnings[list[warnings.WarningMessage]], ) assert_type( np.testing.clear_and_catch_warnings(False), - np.testing._private.utils._clear_and_catch_warnings_without_records, + np.testing.clear_and_catch_warnings[None], ) assert_type( np.testing.clear_and_catch_warnings(bool_obj), From 110bb8e98d87ea291eb997a748f316bb286ad25b Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 16 Mar 2025 00:51:34 +0100 Subject: [PATCH 47/49] TYP: fix typing errors in ``numpy.ndarray`` (#28540) Ported from numpy/numtype#200 --- numpy/__init__.pyi | 36 +++++-------------- numpy/typing/tests/data/fail/ndarray_misc.pyi | 2 -- 2 files changed, 8 insertions(+), 30 deletions(-) diff --git a/numpy/__init__.pyi b/numpy/__init__.pyi index 539c5fa53c24..e5c440bc8a79 100644 --- a/numpy/__init__.pyi +++ b/numpy/__init__.pyi @@ -2231,11 +2231,9 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DType_co]): @overload def resize(self, new_shape: _ShapeLike, /, *, refcheck: builtins.bool = ...) -> None: ... @overload - def resize(self, *new_shape: SupportsIndex, refcheck: builtins.bool = ...) -> None: ... + def resize(self, /, *new_shape: SupportsIndex, refcheck: builtins.bool = ...) -> None: ... - def setflags( - self, write: builtins.bool = ..., align: builtins.bool = ..., uic: builtins.bool = ... - ) -> None: ... + def setflags(self, write: builtins.bool = ..., align: builtins.bool = ..., uic: builtins.bool = ...) -> None: ... def squeeze( self, @@ -2381,13 +2379,6 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DType_co]): sorter: None | _ArrayLikeInt_co = ..., ) -> NDArray[intp]: ... - def setfield( - self, - val: ArrayLike, - dtype: DTypeLike, - offset: SupportsIndex = ..., - ) -> None: ... - def sort( self, axis: SupportsIndex = ..., @@ -2567,23 +2558,14 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DType_co]): @overload # (dtype: ?, type: type[T]) def view(self, /, dtype: DTypeLike, type: type[_ArrayT]) -> _ArrayT: ... + def setfield(self, /, val: ArrayLike, dtype: DTypeLike, offset: CanIndex = 0) -> None: ... @overload - def getfield( - self, - dtype: _DTypeLike[_SCT], - offset: SupportsIndex = ... - ) -> NDArray[_SCT]: ... + def getfield(self, dtype: _DTypeLike[_SCT], offset: SupportsIndex = 0) -> NDArray[_SCT]: ... @overload - def getfield( - self, - dtype: DTypeLike, - offset: SupportsIndex = ... - ) -> NDArray[Any]: ... + def getfield(self, dtype: DTypeLike, offset: SupportsIndex = 0) -> NDArray[Any]: ... - def __index__(self: NDArray[np.integer[Any]], /) -> int: ... - def __int__(self: NDArray[number[Any] | np.timedelta64 | np.bool | object_], /) -> int: ... - def __float__(self: NDArray[number[Any] | np.timedelta64 | np.bool | object_], /) -> float: ... - def __complex__(self: NDArray[number[Any] | np.bool | object_], /) -> complex: ... + def __index__(self: NDArray[integer], /) -> int: ... + def __complex__(self: NDArray[number | np.bool | object_], /) -> complex: ... def __len__(self) -> int: ... def __contains__(self, value: object, /) -> builtins.bool: ... @@ -2659,9 +2641,7 @@ class ndarray(_ArrayOrScalarCommon, Generic[_ShapeT_co, _DType_co]): # @overload # def __abs__(self: ndarray[_ShapeType, dtype[complex128]], /) -> ndarray[_ShapeType, dtype[float64]]: ... @overload - def __abs__( - self: ndarray[_ShapeT, dtype[complexfloating[_AnyNBitInexact]]], / - ) -> ndarray[_ShapeT, dtype[floating[_AnyNBitInexact]]]: ... + def __abs__(self: ndarray[_ShapeT, dtype[complexfloating[_NBit]]], /) -> ndarray[_ShapeT, dtype[floating[_NBit]]]: ... @overload def __abs__(self: _RealArrayT, /) -> _RealArrayT: ... diff --git a/numpy/typing/tests/data/fail/ndarray_misc.pyi b/numpy/typing/tests/data/fail/ndarray_misc.pyi index 38729557b43e..489aefca7ffc 100644 --- a/numpy/typing/tests/data/fail/ndarray_misc.pyi +++ b/numpy/typing/tests/data/fail/ndarray_misc.pyi @@ -26,8 +26,6 @@ f8.setfield(2, np.float64) # E: has no attribute f8.sort() # E: has no attribute f8.trace() # E: has no attribute -AR_M.__int__() # E: Invalid self argument -AR_M.__float__() # E: Invalid self argument AR_M.__complex__() # E: Invalid self argument AR_b.__index__() # E: Invalid self argument From 18795c221083c69bd4950e570c8b849f45d7ce03 Mon Sep 17 00:00:00 2001 From: Joren Hammudoglu Date: Sun, 16 Mar 2025 00:33:51 +0100 Subject: [PATCH 48/49] TYP: fix stubtest error in ``numpy.ma`` (#28541) Ported from numpy/numtype#280 --- numpy/ma/core.pyi | 84 ++++++++++++++++++++++++--------------------- numpy/ma/extras.pyi | 28 ++++++++------- 2 files changed, 61 insertions(+), 51 deletions(-) diff --git a/numpy/ma/core.pyi b/numpy/ma/core.pyi index 57136fa9d31c..83c0636ce4a7 100644 --- a/numpy/ma/core.pyi +++ b/numpy/ma/core.pyi @@ -1,19 +1,13 @@ -from collections.abc import Callable -from typing import Any, TypeVar - -from numpy import ( - amax, - amin, - bool_, - expand_dims, - clip, - indices, - squeeze, - angle, - ndarray, - dtype, - float64, -) +# pyright: reportIncompatibleMethodOverride=false +# ruff: noqa: ANN001, ANN002, ANN003, ANN201, ANN202 ANN204 + +from typing import Any, SupportsIndex, TypeVar + +from _typeshed import Incomplete +from typing_extensions import deprecated + +from numpy import _OrderKACF, amax, amin, bool_, dtype, expand_dims, float64, ndarray +from numpy._typing import ArrayLike, _DTypeLikeBool __all__ = [ "MAError", @@ -111,8 +105,8 @@ __all__ = [ "less", "less_equal", "log", - "log10", "log2", + "log10", "logical_and", "logical_not", "logical_or", @@ -257,6 +251,7 @@ cosh: _MaskedUnaryOperation tanh: _MaskedUnaryOperation abs: _MaskedUnaryOperation absolute: _MaskedUnaryOperation +angle: _MaskedUnaryOperation fabs: _MaskedUnaryOperation negative: _MaskedUnaryOperation floor: _MaskedUnaryOperation @@ -284,20 +279,21 @@ greater_equal: _MaskedBinaryOperation less: _MaskedBinaryOperation greater: _MaskedBinaryOperation logical_and: _MaskedBinaryOperation -alltrue: _MaskedBinaryOperation +def alltrue(target: ArrayLike, axis: SupportsIndex | None = 0, dtype: _DTypeLikeBool | None = None) -> Incomplete: ... logical_or: _MaskedBinaryOperation -sometrue: Callable[..., Any] +def sometrue(target: ArrayLike, axis: SupportsIndex | None = 0, dtype: _DTypeLikeBool | None = None) -> Incomplete: ... logical_xor: _MaskedBinaryOperation bitwise_and: _MaskedBinaryOperation bitwise_or: _MaskedBinaryOperation bitwise_xor: _MaskedBinaryOperation hypot: _MaskedBinaryOperation -divide: _MaskedBinaryOperation -true_divide: _MaskedBinaryOperation -floor_divide: _MaskedBinaryOperation -remainder: _MaskedBinaryOperation -fmod: _MaskedBinaryOperation -mod: _MaskedBinaryOperation + +divide: _DomainedBinaryOperation +true_divide: _DomainedBinaryOperation +floor_divide: _DomainedBinaryOperation +remainder: _DomainedBinaryOperation +fmod: _DomainedBinaryOperation +mod: _DomainedBinaryOperation def make_mask_descr(ndtype): ... def getmask(a): ... @@ -448,10 +444,10 @@ class MaskedArray(ndarray[_ShapeType_co, _DType_co]): def var(self, axis=..., dtype=..., out=..., ddof=..., keepdims=...): ... def std(self, axis=..., dtype=..., out=..., ddof=..., keepdims=...): ... def round(self, decimals=..., out=...): ... - def argsort(self, axis=..., kind=..., order=..., endwith=..., fill_value=..., stable=...): ... + def argsort(self, axis=..., kind=..., order=..., endwith=..., fill_value=..., *, stable=...): ... def argmin(self, axis=..., fill_value=..., out=..., *, keepdims=...): ... def argmax(self, axis=..., fill_value=..., out=..., *, keepdims=...): ... - def sort(self, axis=..., kind=..., order=..., endwith=..., fill_value=..., stable=...): ... + def sort(self, axis=..., kind=..., order=..., endwith=..., fill_value=..., *, stable=...): ... def min(self, axis=..., out=..., fill_value=..., keepdims=...): ... # NOTE: deprecated # def tostring(self, fill_value=..., order=...): ... @@ -460,6 +456,7 @@ class MaskedArray(ndarray[_ShapeType_co, _DType_co]): def partition(self, *args, **kwargs): ... def argpartition(self, *args, **kwargs): ... def take(self, indices, axis=..., out=..., mode=...): ... + copy: Any diagonal: Any flatten: Any @@ -468,19 +465,26 @@ class MaskedArray(ndarray[_ShapeType_co, _DType_co]): swapaxes: Any T: Any transpose: Any + @property # type: ignore[misc] def mT(self): ... - def tolist(self, fill_value=...): ... - def tobytes(self, fill_value=..., order=...): ... - def tofile(self, fid, sep=..., format=...): ... - def toflex(self): ... - torecords: Any + + # + def toflex(self) -> Incomplete: ... + def torecords(self) -> Incomplete: ... + def tolist(self, fill_value: Incomplete | None = None) -> Incomplete: ... + @deprecated("tostring() is deprecated. Use tobytes() instead.") + def tostring(self, /, fill_value: Incomplete | None = None, order: _OrderKACF = "C") -> bytes: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def tobytes(self, /, fill_value: Incomplete | None = None, order: _OrderKACF = "C") -> bytes: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] + def tofile(self, /, fid: Incomplete, sep: str = "", format: str = "%s") -> Incomplete: ... + + # def __reduce__(self): ... def __deepcopy__(self, memo=...): ... class mvoid(MaskedArray[_ShapeType_co, _DType_co]): def __new__( - self, + self, # pyright: ignore[reportSelfClsParameterName] data, mask=..., dtype=..., @@ -593,8 +597,8 @@ maximum: _extrema_operation def take(a, indices, axis=..., out=..., mode=...): ... def power(a, b, third=...): ... -def argsort(a, axis=..., kind=..., order=..., endwith=..., fill_value=..., stable=...): ... -def sort(a, axis=..., kind=..., order=..., endwith=..., fill_value=..., stable=...): ... +def argsort(a, axis=..., kind=..., order=..., endwith=..., fill_value=..., *, stable=...): ... +def sort(a, axis=..., kind=..., order=..., endwith=..., fill_value=..., *, stable=...): ... def compressed(x): ... def concatenate(arrays, axis=...): ... def diag(v, k=...): ... @@ -629,19 +633,21 @@ def asanyarray(a, dtype=...): ... def fromflex(fxarray): ... class _convert2ma: - __doc__: Any - def __init__(self, funcname, params=...): ... - def getdoc(self): ... - def __call__(self, *args, **params): ... + def __init__(self, /, funcname: str, np_ret: str, np_ma_ret: str, params: dict[str, Any] | None = None) -> None: ... + def __call__(self, /, *args: object, **params: object) -> Any: ... # noqa: ANN401 + def getdoc(self, /, np_ret: str, np_ma_ret: str) -> str | None: ... arange: _convert2ma +clip: _convert2ma empty: _convert2ma empty_like: _convert2ma frombuffer: _convert2ma fromfunction: _convert2ma identity: _convert2ma +indices: _convert2ma ones: _convert2ma ones_like: _convert2ma +squeeze: _convert2ma zeros: _convert2ma zeros_like: _convert2ma diff --git a/numpy/ma/extras.pyi b/numpy/ma/extras.pyi index 580309cc679d..ba76f3517526 100644 --- a/numpy/ma/extras.pyi +++ b/numpy/ma/extras.pyi @@ -1,9 +1,10 @@ -from typing import Any +from _typeshed import Incomplete +import numpy as np from numpy.lib._function_base_impl import average from numpy.lib._index_tricks_impl import AxisConcatenator -from .core import dot, mask_rowcols +from .core import MaskedArray, dot __all__ = [ "apply_along_axis", @@ -19,8 +20,8 @@ __all__ = [ "compress_nd", "compress_rowcols", "compress_rows", - "count_masked", "corrcoef", + "count_masked", "cov", "diagflat", "dot", @@ -30,9 +31,9 @@ __all__ = [ "flatnotmasked_edges", "hsplit", "hstack", - "isin", "in1d", "intersect1d", + "isin", "mask_cols", "mask_rowcols", "mask_rows", @@ -48,8 +49,8 @@ __all__ = [ "setdiff1d", "setxor1d", "stack", - "unique", "union1d", + "unique", "vander", "vstack", ] @@ -59,9 +60,9 @@ def masked_all(shape, dtype = ...): ... def masked_all_like(arr): ... class _fromnxfunction: - __name__: Any - __doc__: Any - def __init__(self, funcname): ... + __name__: Incomplete + __doc__: Incomplete + def __init__(self, funcname) -> None: ... def getdoc(self): ... def __call__(self, *args, **params): ... @@ -109,13 +110,13 @@ def cov(x, y=..., rowvar=..., bias=..., allow_masked=..., ddof=...): ... def corrcoef(x, y=..., rowvar=..., bias = ..., allow_masked=..., ddof = ...): ... class MAxisConcatenator(AxisConcatenator): - concatenate: Any + @staticmethod + def concatenate(arrays: Incomplete, axis: int = 0) -> Incomplete: ... # type: ignore[override] # pyright: ignore[reportIncompatibleMethodOverride] @classmethod - def makemat(cls, arr): ... - def __getitem__(self, key): ... + def makemat(cls, arr: Incomplete) -> Incomplete: ... # type: ignore[override] # pyright: ignore[reportIncompatibleVariableOverride] class mr_class(MAxisConcatenator): - def __init__(self): ... + def __init__(self) -> None: ... mr_: mr_class @@ -128,3 +129,6 @@ def clump_unmasked(a): ... def clump_masked(a): ... def vander(x, n=...): ... def polyfit(x, y, deg, rcond=..., full=..., w=..., cov=...): ... + +# +def mask_rowcols(a: Incomplete, axis: Incomplete | None = None) -> MaskedArray[Incomplete, np.dtype[Incomplete]]: ... From 6f94b154ac44b1ee0d313cd510a8da6ce25941b1 Mon Sep 17 00:00:00 2001 From: Charles Harris Date: Sun, 16 Mar 2025 07:16:09 -0600 Subject: [PATCH 49/49] REL: Prepare for the NumPy 2.2.4 release [wheel build] - Create 2.2.4-changelog.rst - Update 2.2.4-notes.rst --- doc/changelog/2.2.4-changelog.rst | 45 +++++++++++++++++++++ doc/source/release/2.2.4-notes.rst | 64 ++++++++++++++++++++++++------ 2 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 doc/changelog/2.2.4-changelog.rst diff --git a/doc/changelog/2.2.4-changelog.rst b/doc/changelog/2.2.4-changelog.rst new file mode 100644 index 000000000000..1e2664ebde48 --- /dev/null +++ b/doc/changelog/2.2.4-changelog.rst @@ -0,0 +1,45 @@ + +Contributors +============ + +A total of 15 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Abhishek Kumar +* Andrej Zhilenkov +* Andrew Nelson +* Charles Harris +* Giovanni Del Monte +* Guan Ming(Wesley) Chiu + +* Jonathan Albrecht + +* Joren Hammudoglu +* Mark Harfouche +* Matthieu Darbois +* Nathan Goldbaum +* Pieter Eendebak +* Sebastian Berg +* Tyler Reddy +* lvllvl + + +Pull requests merged +==================== + +A total of 17 pull requests were merged for this release. + +* `#28333 `__: MAINT: Prepare 2.2.x for further development. +* `#28348 `__: TYP: fix positional- and keyword-only params in astype, cross... +* `#28377 `__: MAINT: Update FreeBSD version and fix test failure +* `#28379 `__: BUG: numpy.loadtxt reads only 50000 lines when skip_rows >= max_rows +* `#28385 `__: BUG: Make np.nonzero threading safe +* `#28420 `__: BUG: safer bincount casting (backport to 2.2.x) +* `#28422 `__: BUG: Fix building on s390x with clang +* `#28423 `__: CI: use QEMU 9.2.2 for Linux Qemu tests +* `#28424 `__: BUG: skip legacy dtype multithreaded test on 32 bit runners +* `#28435 `__: BUG: Fix searchsorted and CheckFromAny byte-swapping logic +* `#28449 `__: BUG: sanity check ``__array_interface__`` number of dimensions +* `#28510 `__: MAINT: Hide decorator from pytest traceback +* `#28512 `__: TYP: Typing fixes backported from #28452, #28491, #28494 +* `#28521 `__: TYP: Backport fixes from #28505, #28506, #28508, and #28511 +* `#28533 `__: TYP: Backport typing fixes from main (2) +* `#28534 `__: TYP: Backport typing fixes from main (3) +* `#28542 `__: TYP: Backport typing fixes from main (4) diff --git a/doc/source/release/2.2.4-notes.rst b/doc/source/release/2.2.4-notes.rst index 9cde851301f9..8542c98a8af9 100644 --- a/doc/source/release/2.2.4-notes.rst +++ b/doc/source/release/2.2.4-notes.rst @@ -5,16 +5,54 @@ NumPy 2.2.4 Release Notes ========================== NumPy 2.2.4 is a patch release that fixes bugs found after the 2.2.3 release. - -Highlights -========== - -*We'll choose highlights for this release near the end of the release cycle.* - - -.. if release snippets have been incorporated already, uncomment the follow - line (leave the `.. include:: directive) - -.. **Content from release note snippets in doc/release/upcoming_changes:** - -.. include:: notes-towncrier.rst +There are a large number of typing improvements, the rest of the changes are +the usual mix of bugfixes and platform maintenace. + +This release supports Python versions 3.10-3.13. + + +Contributors +============ + +A total of 15 people contributed to this release. People with a "+" by their +names contributed a patch for the first time. + +* Abhishek Kumar +* Andrej Zhilenkov +* Andrew Nelson +* Charles Harris +* Giovanni Del Monte +* Guan Ming(Wesley) Chiu + +* Jonathan Albrecht + +* Joren Hammudoglu +* Mark Harfouche +* Matthieu Darbois +* Nathan Goldbaum +* Pieter Eendebak +* Sebastian Berg +* Tyler Reddy +* lvllvl + + + +Pull requests merged +==================== + +A total of 17 pull requests were merged for this release. + +* `#28333 `__: MAINT: Prepare 2.2.x for further development. +* `#28348 `__: TYP: fix positional- and keyword-only params in astype, cross... +* `#28377 `__: MAINT: Update FreeBSD version and fix test failure +* `#28379 `__: BUG: numpy.loadtxt reads only 50000 lines when skip_rows >= max_rows +* `#28385 `__: BUG: Make np.nonzero threading safe +* `#28420 `__: BUG: safer bincount casting (backport to 2.2.x) +* `#28422 `__: BUG: Fix building on s390x with clang +* `#28423 `__: CI: use QEMU 9.2.2 for Linux Qemu tests +* `#28424 `__: BUG: skip legacy dtype multithreaded test on 32 bit runners +* `#28435 `__: BUG: Fix searchsorted and CheckFromAny byte-swapping logic +* `#28449 `__: BUG: sanity check ``__array_interface__`` number of dimensions +* `#28510 `__: MAINT: Hide decorator from pytest traceback +* `#28512 `__: TYP: Typing fixes backported from #28452, #28491, #28494 +* `#28521 `__: TYP: Backport fixes from #28505, #28506, #28508, and #28511 +* `#28533 `__: TYP: Backport typing fixes from main (2) +* `#28534 `__: TYP: Backport typing fixes from main (3) +* `#28542 `__: TYP: Backport typing fixes from main (4)