summaryrefslogtreecommitdiff
path: root/numpy/tests
diff options
context:
space:
mode:
authorJosh Wilson <person142@users.noreply.github.com>2020-06-06 15:31:33 -0700
committerJosh Wilson <person142@users.noreply.github.com>2020-06-06 15:31:33 -0700
commit11b95d15f10c2bc652ed19d5e27efa0384396cb8 (patch)
tree8d6f2020f6982fc9f2389796daca1429387f576e /numpy/tests
parenta5d021a1b6f439a19812926bc4d796ef5f346c44 (diff)
downloadnumpy-11b95d15f10c2bc652ed19d5e27efa0384396cb8.tar.gz
ENH: add type stubs from numpy-stubs
Add the type stubs and tests from numpy-stubs. Things this entails: - Copy over the stubs (numpy/__init__.pyi and numpy/core/_internal.pyi) - The only modification made was removing `ndarray.tostring` since it is deprecated - Update some setup.py files to include pyi files - Move the tests from numpy-stubs/tests into numpy/tests - Skip them if mypy is not installed (planning on setting up CI in a future PR) - Add a mypy.ini; use it to configure mypy in the tests - It tells mypy where to find NumPy in the test env - It ignores internal NumPy type errors (since we only want to consider errors from the tests cases) - Some small edits were made to fix test cases that were emitting deprecation warnings - Add numpy/py.typed so that the types are picked up in an installed version of NumPy
Diffstat (limited to 'numpy/tests')
-rw-r--r--numpy/tests/fail/array_like.py22
-rw-r--r--numpy/tests/fail/fromnumeric.py101
-rw-r--r--numpy/tests/fail/ndarray.py11
-rw-r--r--numpy/tests/fail/numerictypes.py13
-rw-r--r--numpy/tests/fail/scalars.py67
-rw-r--r--numpy/tests/fail/simple.py10
-rw-r--r--numpy/tests/fail/ufuncs.py5
-rw-r--r--numpy/tests/fail/warnings_and_errors.py7
-rw-r--r--numpy/tests/mypy.ini8
-rw-r--r--numpy/tests/pass/array_like.py44
-rw-r--r--numpy/tests/pass/fromnumeric.py116
-rw-r--r--numpy/tests/pass/ndarray_conversion.py94
-rw-r--r--numpy/tests/pass/ndarray_shape_manipulation.py47
-rw-r--r--numpy/tests/pass/numerictypes.py29
-rw-r--r--numpy/tests/pass/scalars.py88
-rw-r--r--numpy/tests/pass/simple.py175
-rw-r--r--numpy/tests/pass/simple_py3.py6
-rw-r--r--numpy/tests/pass/ufuncs.py11
-rw-r--r--numpy/tests/pass/warnings_and_errors.py7
-rw-r--r--numpy/tests/reveal/constants.py44
-rw-r--r--numpy/tests/reveal/fromnumeric.py135
-rw-r--r--numpy/tests/reveal/ndarray_conversion.py54
-rw-r--r--numpy/tests/reveal/ndarray_shape_manipulation.py35
-rw-r--r--numpy/tests/reveal/numerictypes.py18
-rw-r--r--numpy/tests/reveal/scalars.py30
-rw-r--r--numpy/tests/reveal/warnings_and_errors.py10
-rw-r--r--numpy/tests/setup.py13
-rw-r--r--numpy/tests/test_typing.py127
28 files changed, 1327 insertions, 0 deletions
diff --git a/numpy/tests/fail/array_like.py b/numpy/tests/fail/array_like.py
new file mode 100644
index 000000000..a5ef5795f
--- /dev/null
+++ b/numpy/tests/fail/array_like.py
@@ -0,0 +1,22 @@
+from typing import Any, TYPE_CHECKING
+
+import numpy as np
+
+if TYPE_CHECKING:
+ from numpy.typing import ArrayLike
+else:
+ ArrayLike = Any
+
+
+class A:
+ pass
+
+
+x1: ArrayLike = (i for i in range(10)) # E: Incompatible types in assignment
+x2: ArrayLike = A() # E: Incompatible types in assignment
+x3: ArrayLike = {1: "foo", 2: "bar"} # E: Incompatible types in assignment
+
+scalar = np.int64(1)
+scalar.__array__(dtype=np.float64) # E: Unexpected keyword argument
+array = np.array([1])
+array.__array__(dtype=np.float64) # E: Unexpected keyword argument
diff --git a/numpy/tests/fail/fromnumeric.py b/numpy/tests/fail/fromnumeric.py
new file mode 100644
index 000000000..f158a1071
--- /dev/null
+++ b/numpy/tests/fail/fromnumeric.py
@@ -0,0 +1,101 @@
+"""Tests for :mod:`numpy.core.fromnumeric`."""
+
+import numpy as np
+
+A = np.array(True, ndmin=2, dtype=bool)
+A.setflags(write=False)
+
+a = np.bool_(True)
+
+np.take(a, None) # E: No overload variant of "take" matches argument type
+np.take(a, axis=1.0) # E: No overload variant of "take" matches argument type
+np.take(A, out=1) # E: No overload variant of "take" matches argument type
+np.take(A, mode="bob") # E: No overload variant of "take" matches argument type
+
+np.reshape(a, None) # E: Argument 2 to "reshape" has incompatible type
+np.reshape(A, 1, order="bob") # E: Argument "order" to "reshape" has incompatible type
+
+np.choose(a, None) # E: No overload variant of "choose" matches argument type
+np.choose(a, out=1.0) # E: No overload variant of "choose" matches argument type
+np.choose(A, mode="bob") # E: No overload variant of "choose" matches argument type
+
+np.repeat(a, None) # E: Argument 2 to "repeat" has incompatible type
+np.repeat(A, 1, axis=1.0) # E: Argument "axis" to "repeat" has incompatible type
+
+np.swapaxes(a, 0, 0) # E: Argument 1 to "swapaxes" has incompatible type
+np.swapaxes(A, None, 1) # E: Argument 2 to "swapaxes" has incompatible type
+np.swapaxes(A, 1, [0]) # E: Argument 3 to "swapaxes" has incompatible type
+
+np.transpose(a, axes=1) # E: Argument "axes" to "transpose" has incompatible type
+np.transpose(A, axes=1.0) # E: Argument "axes" to "transpose" has incompatible type
+
+np.partition(a, None) # E: Argument 2 to "partition" has incompatible type
+np.partition(
+ a, 0, axis="bob" # E: Argument "axis" to "partition" has incompatible type
+)
+np.partition(
+ A, 0, kind="bob" # E: Argument "kind" to "partition" has incompatible type
+)
+np.partition(
+ A, 0, order=range(5) # E: Argument "order" to "partition" has incompatible type
+)
+
+np.argpartition( # E: No overload variant of "argpartition" matches argument type
+ a, None
+)
+np.argpartition( # E: No overload variant of "argpartition" matches argument type
+ a, 0, axis="bob"
+)
+np.argpartition( # E: No overload variant of "argpartition" matches argument type
+ A, 0, kind="bob"
+)
+np.argpartition(
+ A, 0, order=range(5) # E: Argument "order" to "argpartition" has incompatible type
+)
+
+np.sort(a) # E: Argument 1 to "sort" has incompatible type
+np.sort(A, axis="bob") # E: Argument "axis" to "sort" has incompatible type
+np.sort(A, kind="bob") # E: Argument "kind" to "sort" has incompatible type
+np.sort(A, order=range(5)) # E: Argument "order" to "sort" has incompatible type
+
+np.argsort(a) # E: Argument 1 to "argsort" has incompatible type
+np.argsort(A, axis="bob") # E: Argument "axis" to "argsort" has incompatible type
+np.argsort(A, kind="bob") # E: Argument "kind" to "argsort" has incompatible type
+np.argsort(A, order=range(5)) # E: Argument "order" to "argsort" has incompatible type
+
+np.argmax(a) # E: No overload variant of "argmax" matches argument type
+np.argmax(A, axis="bob") # E: No overload variant of "argmax" matches argument type
+np.argmax(A, kind="bob") # E: No overload variant of "argmax" matches argument type
+
+np.argmin(a) # E: No overload variant of "argmin" matches argument type
+np.argmin(A, axis="bob") # E: No overload variant of "argmin" matches argument type
+np.argmin(A, kind="bob") # E: No overload variant of "argmin" matches argument type
+
+np.searchsorted(a, 0) # E: No overload variant of "searchsorted" matches argument type
+np.searchsorted( # E: No overload variant of "searchsorted" matches argument type
+ A[0], 0, side="bob"
+)
+np.searchsorted( # E: No overload variant of "searchsorted" matches argument type
+ A[0], 0, sorter=1.0
+)
+
+np.resize(A, 1.0) # E: Argument 2 to "resize" has incompatible type
+
+np.squeeze(A, 1.0) # E: No overload variant of "squeeze" matches argument type
+
+np.diagonal(a) # E: Argument 1 to "diagonal" has incompatible type
+np.diagonal(A, offset=None) # E: Argument "offset" to "diagonal" has incompatible type
+np.diagonal(A, axis1="bob") # E: Argument "axis1" to "diagonal" has incompatible type
+np.diagonal(A, axis2=[]) # E: Argument "axis2" to "diagonal" has incompatible type
+
+np.trace(a) # E: Argument 1 to "trace" has incompatible type
+np.trace(A, offset=None) # E: Argument "offset" to "trace" has incompatible type
+np.trace(A, axis1="bob") # E: Argument "axis1" to "trace" has incompatible type
+np.trace(A, axis2=[]) # E: Argument "axis2" to "trace" has incompatible type
+
+np.ravel(a, order="bob") # E: Argument "order" to "ravel" has incompatible type
+
+np.compress(True, A) # E: Argument 1 to "compress" has incompatible type
+np.compress(
+ [True], A, axis=1.0 # E: Argument "axis" to "compress" has incompatible type
+)
diff --git a/numpy/tests/fail/ndarray.py b/numpy/tests/fail/ndarray.py
new file mode 100644
index 000000000..5a5130d40
--- /dev/null
+++ b/numpy/tests/fail/ndarray.py
@@ -0,0 +1,11 @@
+import numpy as np
+
+# Ban setting dtype since mutating the type of the array in place
+# makes having ndarray be generic over dtype impossible. Generally
+# users should use `ndarray.view` in this situation anyway. See
+#
+# https://github.com/numpy/numpy-stubs/issues/7
+#
+# for more context.
+float_array = np.array([1.0])
+float_array.dtype = np.bool_ # E: Property "dtype" defined in "ndarray" is read-only
diff --git a/numpy/tests/fail/numerictypes.py b/numpy/tests/fail/numerictypes.py
new file mode 100644
index 000000000..dd03eacc1
--- /dev/null
+++ b/numpy/tests/fail/numerictypes.py
@@ -0,0 +1,13 @@
+import numpy as np
+
+# Techincally this works, but probably shouldn't. See
+#
+# https://github.com/numpy/numpy/issues/16366
+#
+np.maximum_sctype(1) # E: incompatible type "int"
+
+np.issubsctype(1, np.int64) # E: incompatible type "int"
+
+np.issubdtype(1, np.int64) # E: incompatible type "int"
+
+np.find_common_type(np.int64, np.int64) # E: incompatible type "Type[int64]"
diff --git a/numpy/tests/fail/scalars.py b/numpy/tests/fail/scalars.py
new file mode 100644
index 000000000..0dfc55124
--- /dev/null
+++ b/numpy/tests/fail/scalars.py
@@ -0,0 +1,67 @@
+import numpy as np
+
+# Construction
+
+np.float32(3j) # E: incompatible type
+
+# Technically the following examples are valid NumPy code. But they
+# are not considered a best practice, and people who wish to use the
+# stubs should instead do
+#
+# np.array([1.0, 0.0, 0.0], dtype=np.float32)
+# np.array([], dtype=np.complex64)
+#
+# See e.g. the discussion on the mailing list
+#
+# https://mail.python.org/pipermail/numpy-discussion/2020-April/080566.html
+#
+# and the issue
+#
+# https://github.com/numpy/numpy-stubs/issues/41
+#
+# for more context.
+np.float32([1.0, 0.0, 0.0]) # E: incompatible type
+np.complex64([]) # E: incompatible type
+
+np.complex64(1, 2) # E: Too many arguments
+# TODO: protocols (can't check for non-existent protocols w/ __getattr__)
+
+np.datetime64(0) # E: non-matching overload
+
+dt_64 = np.datetime64(0, "D")
+td_64 = np.timedelta64(1, "h")
+
+dt_64 + dt_64 # E: Unsupported operand types
+
+td_64 - dt_64 # E: Unsupported operand types
+td_64 / dt_64 # E: No overload
+td_64 % 1 # E: Unsupported operand types
+td_64 % dt_64 # E: Unsupported operand types
+
+
+class A:
+ def __float__(self):
+ return 1.0
+
+
+np.int8(A()) # E: incompatible type
+np.int16(A()) # E: incompatible type
+np.int32(A()) # E: incompatible type
+np.int64(A()) # E: incompatible type
+np.uint8(A()) # E: incompatible type
+np.uint16(A()) # E: incompatible type
+np.uint32(A()) # E: incompatible type
+np.uint64(A()) # E: incompatible type
+
+np.void("test") # E: incompatible type
+
+np.generic(1) # E: Cannot instantiate abstract class
+np.number(1) # E: Cannot instantiate abstract class
+np.integer(1) # E: Cannot instantiate abstract class
+np.signedinteger(1) # E: Cannot instantiate abstract class
+np.unsignedinteger(1) # E: Cannot instantiate abstract class
+np.inexact(1) # E: Cannot instantiate abstract class
+np.floating(1) # E: Cannot instantiate abstract class
+np.complexfloating(1) # E: Cannot instantiate abstract class
+np.character("test") # E: Cannot instantiate abstract class
+np.flexible(b"test") # E: Cannot instantiate abstract class
diff --git a/numpy/tests/fail/simple.py b/numpy/tests/fail/simple.py
new file mode 100644
index 000000000..b5e9d1b13
--- /dev/null
+++ b/numpy/tests/fail/simple.py
@@ -0,0 +1,10 @@
+"""Simple expression that should fail with mypy."""
+
+import numpy as np
+
+# Array creation routines checks
+np.zeros("test") # E: incompatible type
+np.zeros() # E: Too few arguments
+
+np.ones("test") # E: incompatible type
+np.ones() # E: Too few arguments
diff --git a/numpy/tests/fail/ufuncs.py b/numpy/tests/fail/ufuncs.py
new file mode 100644
index 000000000..b178a4ea4
--- /dev/null
+++ b/numpy/tests/fail/ufuncs.py
@@ -0,0 +1,5 @@
+import numpy as np
+
+np.sin.nin + "foo" # E: Unsupported operand types
+np.sin(1, foo="bar") # E: Unexpected keyword argument
+np.sin(1, extobj=["foo", "foo", "foo"]) # E: incompatible type
diff --git a/numpy/tests/fail/warnings_and_errors.py b/numpy/tests/fail/warnings_and_errors.py
new file mode 100644
index 000000000..7390cc45f
--- /dev/null
+++ b/numpy/tests/fail/warnings_and_errors.py
@@ -0,0 +1,7 @@
+import numpy as np
+
+np.AxisError(1.0) # E: Argument 1 to "AxisError" has incompatible type
+np.AxisError(1, ndim=2.0) # E: Argument "ndim" to "AxisError" has incompatible type
+np.AxisError(
+ 2, msg_prefix=404 # E: Argument "msg_prefix" to "AxisError" has incompatible type
+)
diff --git a/numpy/tests/mypy.ini b/numpy/tests/mypy.ini
new file mode 100644
index 000000000..cad431a03
--- /dev/null
+++ b/numpy/tests/mypy.ini
@@ -0,0 +1,8 @@
+[mypy]
+mypy_path = ../..
+
+[mypy-numpy]
+ignore_errors = True
+
+[mypy-numpy.*]
+ignore_errors = True
diff --git a/numpy/tests/pass/array_like.py b/numpy/tests/pass/array_like.py
new file mode 100644
index 000000000..098149c4b
--- /dev/null
+++ b/numpy/tests/pass/array_like.py
@@ -0,0 +1,44 @@
+from typing import Any, List, Optional, TYPE_CHECKING
+
+import numpy as np
+
+if TYPE_CHECKING:
+ from numpy.typing import ArrayLike, DtypeLike, _SupportsArray
+else:
+ ArrayLike = Any
+ DtypeLike = Any
+ _SupportsArray = Any
+
+x1: ArrayLike = True
+x2: ArrayLike = 5
+x3: ArrayLike = 1.0
+x4: ArrayLike = 1 + 1j
+x5: ArrayLike = np.int8(1)
+x6: ArrayLike = np.float64(1)
+x7: ArrayLike = np.complex128(1)
+x8: ArrayLike = np.array([1, 2, 3])
+x9: ArrayLike = [1, 2, 3]
+x10: ArrayLike = (1, 2, 3)
+x11: ArrayLike = "foo"
+
+
+class A:
+ def __array__(self, dtype: DtypeLike = None) -> np.ndarray:
+ return np.array([1, 2, 3])
+
+
+x12: ArrayLike = A()
+
+scalar: _SupportsArray = np.int64(1)
+scalar.__array__(np.float64)
+array: _SupportsArray = np.array(1)
+array.__array__(np.float64)
+
+a: _SupportsArray = A()
+a.__array__(np.int64)
+a.__array__(dtype=np.int64)
+
+# Escape hatch for when you mean to make something like an object
+# array.
+object_array_scalar: Any = (i for i in range(10))
+np.array(object_array_scalar)
diff --git a/numpy/tests/pass/fromnumeric.py b/numpy/tests/pass/fromnumeric.py
new file mode 100644
index 000000000..0ce8ef970
--- /dev/null
+++ b/numpy/tests/pass/fromnumeric.py
@@ -0,0 +1,116 @@
+"""Tests for :mod:`numpy.core.fromnumeric`."""
+
+import numpy as np
+
+A = np.array(True, ndmin=2, dtype=bool)
+B = np.array(1.0, ndmin=2, dtype=np.float32)
+A.setflags(write=False)
+B.setflags(write=False)
+
+a = np.bool_(True)
+b = np.float32(1.0)
+c = 1.0
+
+np.take(a, 0)
+np.take(b, 0)
+np.take(c, 0)
+np.take(A, 0)
+np.take(B, 0)
+np.take(A, [0])
+np.take(B, [0])
+
+np.reshape(a, 1)
+np.reshape(b, 1)
+np.reshape(c, 1)
+np.reshape(A, 1)
+np.reshape(B, 1)
+
+np.choose(a, [True, True])
+np.choose(A, [1.0, 1.0])
+
+np.repeat(a, 1)
+np.repeat(b, 1)
+np.repeat(c, 1)
+np.repeat(A, 1)
+np.repeat(B, 1)
+
+np.swapaxes(A, 0, 0)
+np.swapaxes(B, 0, 0)
+
+np.transpose(a)
+np.transpose(b)
+np.transpose(c)
+np.transpose(A)
+np.transpose(B)
+
+np.partition(a, 0, axis=None)
+np.partition(b, 0, axis=None)
+np.partition(c, 0, axis=None)
+np.partition(A, 0)
+np.partition(B, 0)
+
+np.argpartition(a, 0)
+np.argpartition(b, 0)
+np.argpartition(c, 0)
+np.argpartition(A, 0)
+np.argpartition(B, 0)
+
+np.sort(A, 0)
+np.sort(B, 0)
+
+np.argsort(A, 0)
+np.argsort(B, 0)
+
+np.argmax(A)
+np.argmax(B)
+np.argmax(A, axis=0)
+np.argmax(B, axis=0)
+
+np.argmin(A)
+np.argmin(B)
+np.argmin(A, axis=0)
+np.argmin(B, axis=0)
+
+np.searchsorted(A[0], 0)
+np.searchsorted(B[0], 0)
+np.searchsorted(A[0], [0])
+np.searchsorted(B[0], [0])
+
+np.resize(a, (5, 5))
+np.resize(b, (5, 5))
+np.resize(c, (5, 5))
+np.resize(A, (5, 5))
+np.resize(B, (5, 5))
+
+np.squeeze(a)
+np.squeeze(b)
+np.squeeze(c)
+np.squeeze(A)
+np.squeeze(B)
+
+np.diagonal(A)
+np.diagonal(B)
+
+np.trace(A)
+np.trace(B)
+
+np.ravel(a)
+np.ravel(b)
+np.ravel(c)
+np.ravel(A)
+np.ravel(B)
+
+np.nonzero(A)
+np.nonzero(B)
+
+np.shape(a)
+np.shape(b)
+np.shape(c)
+np.shape(A)
+np.shape(B)
+
+np.compress([True], a)
+np.compress([True], b)
+np.compress([True], c)
+np.compress([True], A)
+np.compress([True], B)
diff --git a/numpy/tests/pass/ndarray_conversion.py b/numpy/tests/pass/ndarray_conversion.py
new file mode 100644
index 000000000..303cf53e4
--- /dev/null
+++ b/numpy/tests/pass/ndarray_conversion.py
@@ -0,0 +1,94 @@
+import os
+import tempfile
+
+import numpy as np
+
+nd = np.array([[1, 2], [3, 4]])
+scalar_array = np.array(1)
+
+# item
+scalar_array.item()
+nd.item(1)
+nd.item(0, 1)
+nd.item((0, 1))
+
+# tolist is pretty simple
+
+# itemset
+scalar_array.itemset(3)
+nd.itemset(3, 0)
+nd.itemset((0, 0), 3)
+
+# tobytes
+nd.tobytes()
+nd.tobytes("C")
+nd.tobytes(None)
+
+# tofile
+if os.name != "nt":
+ with tempfile.NamedTemporaryFile(suffix=".txt") as tmp:
+ nd.tofile(tmp.name)
+ nd.tofile(tmp.name, "")
+ nd.tofile(tmp.name, sep="")
+
+ nd.tofile(tmp.name, "", "%s")
+ nd.tofile(tmp.name, format="%s")
+
+ nd.tofile(tmp)
+
+# dump is pretty simple
+# dumps is pretty simple
+
+# astype
+nd.astype("float")
+nd.astype(float)
+
+nd.astype(float, "K")
+nd.astype(float, order="K")
+
+nd.astype(float, "K", "unsafe")
+nd.astype(float, casting="unsafe")
+
+nd.astype(float, "K", "unsafe", True)
+nd.astype(float, subok=True)
+
+nd.astype(float, "K", "unsafe", True, True)
+nd.astype(float, copy=True)
+
+# byteswap
+nd.byteswap()
+nd.byteswap(True)
+
+# copy
+nd.copy()
+nd.copy("C")
+
+# view
+nd.view()
+nd.view(np.int64)
+nd.view(dtype=np.int64)
+nd.view(np.int64, np.matrix)
+nd.view(type=np.matrix)
+
+# getfield
+complex_array = np.array([[1 + 1j, 0], [0, 1 - 1j]], dtype=np.complex128)
+
+complex_array.getfield("float")
+complex_array.getfield(float)
+
+complex_array.getfield("float", 8)
+complex_array.getfield(float, offset=8)
+
+# setflags
+nd.setflags()
+
+nd.setflags(True)
+nd.setflags(write=True)
+
+nd.setflags(True, True)
+nd.setflags(write=True, align=True)
+
+nd.setflags(True, True, False)
+nd.setflags(write=True, align=True, uic=False)
+
+# fill is pretty simple
diff --git a/numpy/tests/pass/ndarray_shape_manipulation.py b/numpy/tests/pass/ndarray_shape_manipulation.py
new file mode 100644
index 000000000..0ca3dff39
--- /dev/null
+++ b/numpy/tests/pass/ndarray_shape_manipulation.py
@@ -0,0 +1,47 @@
+import numpy as np
+
+nd1 = np.array([[1, 2], [3, 4]])
+
+# reshape
+nd1.reshape(4)
+nd1.reshape(2, 2)
+nd1.reshape((2, 2))
+
+nd1.reshape((2, 2), order="C")
+nd1.reshape(4, order="C")
+
+# resize
+nd1.resize()
+nd1.resize(4)
+nd1.resize(2, 2)
+nd1.resize((2, 2))
+
+nd1.resize((2, 2), refcheck=True)
+nd1.resize(4, refcheck=True)
+
+nd2 = np.array([[1, 2], [3, 4]])
+
+# transpose
+nd2.transpose()
+nd2.transpose(1, 0)
+nd2.transpose((1, 0))
+
+# swapaxes
+nd2.swapaxes(0, 1)
+
+# flatten
+nd2.flatten()
+nd2.flatten("C")
+
+# ravel
+nd2.ravel()
+nd2.ravel("C")
+
+# squeeze
+nd2.squeeze()
+
+nd3 = np.array([[1, 2]])
+nd3.squeeze(0)
+
+nd4 = np.array([[[1, 2]]])
+nd4.squeeze((0, 1))
diff --git a/numpy/tests/pass/numerictypes.py b/numpy/tests/pass/numerictypes.py
new file mode 100644
index 000000000..4f205cabc
--- /dev/null
+++ b/numpy/tests/pass/numerictypes.py
@@ -0,0 +1,29 @@
+import numpy as np
+
+np.maximum_sctype("S8")
+np.maximum_sctype(object)
+
+np.issctype(object)
+np.issctype("S8")
+
+np.obj2sctype(list)
+np.obj2sctype(list, default=None)
+np.obj2sctype(list, default=np.string_)
+
+np.issubclass_(np.int32, int)
+np.issubclass_(np.float64, float)
+np.issubclass_(np.float64, (int, float))
+
+np.issubsctype("int64", int)
+np.issubsctype(np.array([1]), np.array([1]))
+
+np.issubdtype("S1", np.string_)
+np.issubdtype(np.float64, np.float32)
+
+np.sctype2char("S1")
+np.sctype2char(list)
+
+np.find_common_type([], [np.int64, np.float32, complex])
+np.find_common_type((), (np.int64, np.float32, complex))
+np.find_common_type([np.int64, np.float32], [])
+np.find_common_type([np.float32], [np.int64, np.float64])
diff --git a/numpy/tests/pass/scalars.py b/numpy/tests/pass/scalars.py
new file mode 100644
index 000000000..bd055673b
--- /dev/null
+++ b/numpy/tests/pass/scalars.py
@@ -0,0 +1,88 @@
+import numpy as np
+
+
+# Construction
+class C:
+ def __complex__(self):
+ return 3j
+
+
+class B:
+ def __int__(self):
+ return 4
+
+
+class A:
+ def __float__(self):
+ return 4.0
+
+
+np.complex64(3j)
+np.complex64(C())
+np.complex128(3j)
+np.complex128(C())
+
+np.int8(4)
+np.int16(3.4)
+np.int32(4)
+np.int64(-1)
+np.uint8(B())
+np.uint32()
+
+np.float16(A())
+np.float32(16)
+np.float64(3.0)
+
+np.bytes_(b"hello")
+np.str_("hello")
+
+# Protocols
+float(np.int8(4))
+int(np.int16(5))
+np.int8(np.float32(6))
+
+# TODO(alan): test after https://github.com/python/typeshed/pull/2004
+# complex(np.int32(8))
+
+abs(np.int8(4))
+
+# Array-ish semantics
+np.int8().real
+np.int16().imag
+np.int32().data
+np.int64().flags
+
+np.uint8().itemsize * 2
+np.uint16().ndim + 1
+np.uint32().strides
+np.uint64().shape
+
+# Time structures
+np.datetime64()
+np.datetime64(0, "D")
+np.datetime64("2019")
+np.datetime64("2019", "D")
+
+np.timedelta64()
+np.timedelta64(0)
+np.timedelta64(0, "D")
+
+dt_64 = np.datetime64(0, "D")
+td_64 = np.timedelta64(1, "h")
+
+dt_64 + td_64
+dt_64 - dt_64
+dt_64 - td_64
+
+td_64 + td_64
+td_64 - td_64
+td_64 / 1.0
+td_64 / td_64
+td_64 % td_64
+
+np.void(1)
+np.void(np.int64(1))
+np.void(True)
+np.void(np.bool_(True))
+np.void(b"test")
+np.void(np.bytes_("test"))
diff --git a/numpy/tests/pass/simple.py b/numpy/tests/pass/simple.py
new file mode 100644
index 000000000..b9715da5d
--- /dev/null
+++ b/numpy/tests/pass/simple.py
@@ -0,0 +1,175 @@
+"""Simple expression that should pass with mypy."""
+import operator
+
+import numpy as np
+from typing import Iterable # noqa: F401
+
+# Basic checks
+array = np.array([1, 2])
+
+
+def ndarray_func(x):
+ # type: (np.ndarray) -> np.ndarray
+ return x
+
+
+ndarray_func(np.array([1, 2]))
+array == 1
+array.dtype == float
+
+# Array creation routines checks
+ndarray_func(np.zeros([1, 2]))
+ndarray_func(np.ones([1, 2]))
+ndarray_func(np.empty([1, 2]))
+
+ndarray_func(np.zeros_like(array))
+ndarray_func(np.ones_like(array))
+ndarray_func(np.empty_like(array))
+
+# Dtype construction
+np.dtype(float)
+np.dtype(np.float64)
+np.dtype(None)
+np.dtype("float64")
+np.dtype(np.dtype(float))
+np.dtype(("U", 10))
+np.dtype((np.int32, (2, 2)))
+# Define the arguments on the previous line to prevent bidirectional
+# type inference in mypy from broadening the types.
+two_tuples_dtype = [("R", "u1"), ("G", "u1"), ("B", "u1")]
+np.dtype(two_tuples_dtype)
+
+three_tuples_dtype = [("R", "u1", 2)]
+np.dtype(three_tuples_dtype)
+
+mixed_tuples_dtype = [("R", "u1"), ("G", np.unicode_, 1)]
+np.dtype(mixed_tuples_dtype)
+
+shape_tuple_dtype = [("R", "u1", (2, 2))]
+np.dtype(shape_tuple_dtype)
+
+shape_like_dtype = [("R", "u1", (2, 2)), ("G", np.unicode_, 1)]
+np.dtype(shape_like_dtype)
+
+object_dtype = [("field1", object)]
+np.dtype(object_dtype)
+
+np.dtype({"col1": ("U10", 0), "col2": ("float32", 10)})
+np.dtype((np.int32, {"real": (np.int16, 0), "imag": (np.int16, 2)}))
+np.dtype((np.int32, (np.int8, 4)))
+
+# Dtype comparision
+np.dtype(float) == float
+np.dtype(float) != np.float64
+np.dtype(float) < None
+np.dtype(float) <= "float64"
+np.dtype(float) > np.dtype(float)
+np.dtype(float) >= np.dtype(("U", 10))
+
+# Iteration and indexing
+def iterable_func(x):
+ # type: (Iterable) -> Iterable
+ return x
+
+
+iterable_func(array)
+[element for element in array]
+iter(array)
+zip(array, array)
+array[1]
+array[:]
+array[...]
+array[:] = 0
+
+array_2d = np.ones((3, 3))
+array_2d[:2, :2]
+array_2d[..., 0]
+array_2d[:2, :2] = 0
+
+# Other special methods
+len(array)
+str(array)
+array_scalar = np.array(1)
+int(array_scalar)
+float(array_scalar)
+# currently does not work due to https://github.com/python/typeshed/issues/1904
+# complex(array_scalar)
+bytes(array_scalar)
+operator.index(array_scalar)
+bool(array_scalar)
+
+# comparisons
+array < 1
+array <= 1
+array == 1
+array != 1
+array > 1
+array >= 1
+1 < array
+1 <= array
+1 == array
+1 != array
+1 > array
+1 >= array
+
+# binary arithmetic
+array + 1
+1 + array
+array += 1
+
+array - 1
+1 - array
+array -= 1
+
+array * 1
+1 * array
+array *= 1
+
+array / 1
+1 / array
+float_array = np.array([1.0, 2.0])
+float_array /= 1
+
+array // 1
+1 // array
+array //= 1
+
+array % 1
+1 % array
+array %= 1
+
+divmod(array, 1)
+divmod(1, array)
+
+array ** 1
+1 ** array
+array **= 1
+
+array << 1
+1 << array
+array <<= 1
+
+array >> 1
+1 >> array
+array >>= 1
+
+array & 1
+1 & array
+array &= 1
+
+array ^ 1
+1 ^ array
+array ^= 1
+
+array | 1
+1 | array
+array |= 1
+
+# unary arithmetic
+-array
++array
+abs(array)
+~array
+
+# Other methods
+np.array([1, 2]).transpose()
diff --git a/numpy/tests/pass/simple_py3.py b/numpy/tests/pass/simple_py3.py
new file mode 100644
index 000000000..c05a1ce61
--- /dev/null
+++ b/numpy/tests/pass/simple_py3.py
@@ -0,0 +1,6 @@
+import numpy as np
+
+array = np.array([1, 2])
+
+# The @ operator is not in python 2
+array @ array
diff --git a/numpy/tests/pass/ufuncs.py b/numpy/tests/pass/ufuncs.py
new file mode 100644
index 000000000..c81ac48d1
--- /dev/null
+++ b/numpy/tests/pass/ufuncs.py
@@ -0,0 +1,11 @@
+import numpy as np
+
+np.sin(1)
+np.sin([1, 2, 3])
+np.sin(1, out=np.empty(1))
+np.matmul(np.ones((2, 2, 2)), np.ones((2, 2, 2)), axes=[(0, 1), (0, 1), (0, 1)])
+np.sin(1, signature="D")
+np.sin(1, extobj=[16, 1, lambda: None])
+np.sin(1) + np.sin(1)
+np.sin.types[0]
+np.sin.__name__
diff --git a/numpy/tests/pass/warnings_and_errors.py b/numpy/tests/pass/warnings_and_errors.py
new file mode 100644
index 000000000..5b6ec2626
--- /dev/null
+++ b/numpy/tests/pass/warnings_and_errors.py
@@ -0,0 +1,7 @@
+import numpy as np
+
+np.AxisError(1)
+np.AxisError(1, ndim=2)
+np.AxisError(1, ndim=None)
+np.AxisError(1, ndim=2, msg_prefix="error")
+np.AxisError(1, ndim=2, msg_prefix=None)
diff --git a/numpy/tests/reveal/constants.py b/numpy/tests/reveal/constants.py
new file mode 100644
index 000000000..8e00810bd
--- /dev/null
+++ b/numpy/tests/reveal/constants.py
@@ -0,0 +1,44 @@
+import numpy as np
+
+reveal_type(np.Inf) # E: float
+reveal_type(np.Infinity) # E: float
+reveal_type(np.NAN) # E: float
+reveal_type(np.NINF) # E: float
+reveal_type(np.NZERO) # E: float
+reveal_type(np.NaN) # E: float
+reveal_type(np.PINF) # E: float
+reveal_type(np.PZERO) # E: float
+reveal_type(np.e) # E: float
+reveal_type(np.euler_gamma) # E: float
+reveal_type(np.inf) # E: float
+reveal_type(np.infty) # E: float
+reveal_type(np.nan) # E: float
+reveal_type(np.pi) # E: float
+
+reveal_type(np.ALLOW_THREADS) # E: int
+reveal_type(np.BUFSIZE) # E: int
+reveal_type(np.CLIP) # E: int
+reveal_type(np.ERR_CALL) # E: int
+reveal_type(np.ERR_DEFAULT) # E: int
+reveal_type(np.ERR_IGNORE) # E: int
+reveal_type(np.ERR_LOG) # E: int
+reveal_type(np.ERR_PRINT) # E: int
+reveal_type(np.ERR_RAISE) # E: int
+reveal_type(np.ERR_WARN) # E: int
+reveal_type(np.FLOATING_POINT_SUPPORT) # E: int
+reveal_type(np.FPE_DIVIDEBYZERO) # E: int
+reveal_type(np.FPE_INVALID) # E: int
+reveal_type(np.FPE_OVERFLOW) # E: int
+reveal_type(np.FPE_UNDERFLOW) # E: int
+reveal_type(np.MAXDIMS) # E: int
+reveal_type(np.MAY_SHARE_BOUNDS) # E: int
+reveal_type(np.MAY_SHARE_EXACT) # E: int
+reveal_type(np.RAISE) # E: int
+reveal_type(np.SHIFT_DIVIDEBYZERO) # E: int
+reveal_type(np.SHIFT_INVALID) # E: int
+reveal_type(np.SHIFT_OVERFLOW) # E: int
+reveal_type(np.SHIFT_UNDERFLOW) # E: int
+reveal_type(np.UFUNC_BUFSIZE_DEFAULT) # E: int
+reveal_type(np.WRAP) # E: int
+reveal_type(np.little_endian) # E: int
+reveal_type(np.tracemalloc_domain) # E: int
diff --git a/numpy/tests/reveal/fromnumeric.py b/numpy/tests/reveal/fromnumeric.py
new file mode 100644
index 000000000..7d79d5fa9
--- /dev/null
+++ b/numpy/tests/reveal/fromnumeric.py
@@ -0,0 +1,135 @@
+"""Tests for :mod:`numpy.core.fromnumeric`."""
+
+import numpy as np
+
+A = np.array(True, ndmin=2, dtype=bool)
+B = np.array(1.0, ndmin=2, dtype=np.float32)
+A.setflags(write=False)
+B.setflags(write=False)
+
+a = np.bool_(True)
+b = np.float32(1.0)
+c = 1.0
+
+reveal_type(np.take(a, 0)) # E: numpy.bool_
+reveal_type(np.take(b, 0)) # E: numpy.float32
+reveal_type(
+ np.take(c, 0) # E: Union[numpy.generic, datetime.datetime, datetime.timedelta]
+)
+reveal_type(
+ np.take(A, 0) # E: Union[numpy.generic, datetime.datetime, datetime.timedelta]
+)
+reveal_type(
+ np.take(B, 0) # E: Union[numpy.generic, datetime.datetime, datetime.timedelta]
+)
+reveal_type(
+ np.take( # E: Union[Union[numpy.generic, datetime.datetime, datetime.timedelta], numpy.ndarray]
+ A, [0]
+ )
+)
+reveal_type(
+ np.take( # E: Union[Union[numpy.generic, datetime.datetime, datetime.timedelta], numpy.ndarray]
+ B, [0]
+ )
+)
+
+reveal_type(np.reshape(a, 1)) # E: numpy.ndarray
+reveal_type(np.reshape(b, 1)) # E: numpy.ndarray
+reveal_type(np.reshape(c, 1)) # E: numpy.ndarray
+reveal_type(np.reshape(A, 1)) # E: numpy.ndarray
+reveal_type(np.reshape(B, 1)) # E: numpy.ndarray
+
+reveal_type(np.choose(a, [True, True])) # E: numpy.bool_
+reveal_type(np.choose(A, [True, True])) # E: numpy.ndarray
+
+reveal_type(np.repeat(a, 1)) # E: numpy.ndarray
+reveal_type(np.repeat(b, 1)) # E: numpy.ndarray
+reveal_type(np.repeat(c, 1)) # E: numpy.ndarray
+reveal_type(np.repeat(A, 1)) # E: numpy.ndarray
+reveal_type(np.repeat(B, 1)) # E: numpy.ndarray
+
+# TODO: Add tests for np.put()
+
+reveal_type(np.swapaxes(A, 0, 0)) # E: numpy.ndarray
+reveal_type(np.swapaxes(B, 0, 0)) # E: numpy.ndarray
+
+reveal_type(np.transpose(a)) # E: numpy.ndarray
+reveal_type(np.transpose(b)) # E: numpy.ndarray
+reveal_type(np.transpose(c)) # E: numpy.ndarray
+reveal_type(np.transpose(A)) # E: numpy.ndarray
+reveal_type(np.transpose(B)) # E: numpy.ndarray
+
+reveal_type(np.partition(a, 0, axis=None)) # E: numpy.ndarray
+reveal_type(np.partition(b, 0, axis=None)) # E: numpy.ndarray
+reveal_type(np.partition(c, 0, axis=None)) # E: numpy.ndarray
+reveal_type(np.partition(A, 0)) # E: numpy.ndarray
+reveal_type(np.partition(B, 0)) # E: numpy.ndarray
+
+reveal_type(np.argpartition(a, 0)) # E: numpy.integer
+reveal_type(np.argpartition(b, 0)) # E: numpy.integer
+reveal_type(np.argpartition(c, 0)) # E: numpy.ndarray
+reveal_type(np.argpartition(A, 0)) # E: numpy.ndarray
+reveal_type(np.argpartition(B, 0)) # E: numpy.ndarray
+
+reveal_type(np.sort(A, 0)) # E: numpy.ndarray
+reveal_type(np.sort(B, 0)) # E: numpy.ndarray
+
+reveal_type(np.argsort(A, 0)) # E: numpy.ndarray
+reveal_type(np.argsort(B, 0)) # E: numpy.ndarray
+
+reveal_type(np.argmax(A)) # E: numpy.integer
+reveal_type(np.argmax(B)) # E: numpy.integer
+reveal_type(np.argmax(A, axis=0)) # E: Union[numpy.integer, numpy.ndarray]
+reveal_type(np.argmax(B, axis=0)) # E: Union[numpy.integer, numpy.ndarray]
+
+reveal_type(np.argmin(A)) # E: numpy.integer
+reveal_type(np.argmin(B)) # E: numpy.integer
+reveal_type(np.argmin(A, axis=0)) # E: Union[numpy.integer, numpy.ndarray]
+reveal_type(np.argmin(B, axis=0)) # E: Union[numpy.integer, numpy.ndarray]
+
+reveal_type(np.searchsorted(A[0], 0)) # E: numpy.integer
+reveal_type(np.searchsorted(B[0], 0)) # E: numpy.integer
+reveal_type(np.searchsorted(A[0], [0])) # E: numpy.ndarray
+reveal_type(np.searchsorted(B[0], [0])) # E: numpy.ndarray
+
+reveal_type(np.resize(a, (5, 5))) # E: numpy.ndarray
+reveal_type(np.resize(b, (5, 5))) # E: numpy.ndarray
+reveal_type(np.resize(c, (5, 5))) # E: numpy.ndarray
+reveal_type(np.resize(A, (5, 5))) # E: numpy.ndarray
+reveal_type(np.resize(B, (5, 5))) # E: numpy.ndarray
+
+reveal_type(np.squeeze(a)) # E: numpy.bool_
+reveal_type(np.squeeze(b)) # E: numpy.float32
+reveal_type(np.squeeze(c)) # E: numpy.ndarray
+reveal_type(np.squeeze(A)) # E: numpy.ndarray
+reveal_type(np.squeeze(B)) # E: numpy.ndarray
+
+reveal_type(np.diagonal(A)) # E: numpy.ndarray
+reveal_type(np.diagonal(B)) # E: numpy.ndarray
+
+reveal_type(np.trace(A)) # E: Union[numpy.number, numpy.ndarray]
+reveal_type(np.trace(B)) # E: Union[numpy.number, numpy.ndarray]
+
+reveal_type(np.ravel(a)) # E: numpy.ndarray
+reveal_type(np.ravel(b)) # E: numpy.ndarray
+reveal_type(np.ravel(c)) # E: numpy.ndarray
+reveal_type(np.ravel(A)) # E: numpy.ndarray
+reveal_type(np.ravel(B)) # E: numpy.ndarray
+
+reveal_type(np.nonzero(a)) # E: tuple[numpy.ndarray]
+reveal_type(np.nonzero(b)) # E: tuple[numpy.ndarray]
+reveal_type(np.nonzero(c)) # E: tuple[numpy.ndarray]
+reveal_type(np.nonzero(A)) # E: tuple[numpy.ndarray]
+reveal_type(np.nonzero(B)) # E: tuple[numpy.ndarray]
+
+reveal_type(np.shape(a)) # E: tuple[builtins.int]
+reveal_type(np.shape(b)) # E: tuple[builtins.int]
+reveal_type(np.shape(c)) # E: tuple[builtins.int]
+reveal_type(np.shape(A)) # E: tuple[builtins.int]
+reveal_type(np.shape(B)) # E: tuple[builtins.int]
+
+reveal_type(np.compress([True], a)) # E: numpy.ndarray
+reveal_type(np.compress([True], b)) # E: numpy.ndarray
+reveal_type(np.compress([True], c)) # E: numpy.ndarray
+reveal_type(np.compress([True], A)) # E: numpy.ndarray
+reveal_type(np.compress([True], B)) # E: numpy.ndarray
diff --git a/numpy/tests/reveal/ndarray_conversion.py b/numpy/tests/reveal/ndarray_conversion.py
new file mode 100644
index 000000000..411adcf63
--- /dev/null
+++ b/numpy/tests/reveal/ndarray_conversion.py
@@ -0,0 +1,54 @@
+import numpy as np
+
+nd = np.array([[1, 2], [3, 4]])
+
+# item
+reveal_type(nd.item()) # E: Any
+reveal_type(nd.item(1)) # E: Any
+reveal_type(nd.item(0, 1)) # E: Any
+reveal_type(nd.item((0, 1))) # E: Any
+
+# tolist
+reveal_type(nd.tolist()) # E: builtins.list[Any]
+
+# itemset does not return a value
+# tostring is pretty simple
+# tobytes is pretty simple
+# tofile does not return a value
+# dump does not return a value
+# dumps is pretty simple
+
+# astype
+reveal_type(nd.astype("float")) # E: numpy.ndarray
+reveal_type(nd.astype(float)) # E: numpy.ndarray
+reveal_type(nd.astype(float, "K")) # E: numpy.ndarray
+reveal_type(nd.astype(float, "K", "unsafe")) # E: numpy.ndarray
+reveal_type(nd.astype(float, "K", "unsafe", True)) # E: numpy.ndarray
+reveal_type(nd.astype(float, "K", "unsafe", True, True)) # E: numpy.ndarray
+
+# byteswap
+reveal_type(nd.byteswap()) # E: numpy.ndarray
+reveal_type(nd.byteswap(True)) # E: numpy.ndarray
+
+# copy
+reveal_type(nd.copy()) # E: numpy.ndarray
+reveal_type(nd.copy("C")) # E: numpy.ndarray
+
+# view
+class SubArray(np.ndarray):
+ pass
+
+
+reveal_type(nd.view()) # E: numpy.ndarray
+reveal_type(nd.view(np.int64)) # E: numpy.ndarray
+# replace `Any` with `numpy.matrix` when `matrix` will be added to stubs
+reveal_type(nd.view(np.int64, np.matrix)) # E: Any
+reveal_type(nd.view(np.int64, SubArray)) # E: SubArray
+
+# getfield
+reveal_type(nd.getfield("float")) # E: numpy.ndarray
+reveal_type(nd.getfield(float)) # E: numpy.ndarray
+reveal_type(nd.getfield(float, 8)) # E: numpy.ndarray
+
+# setflags does not return a value
+# fill does not return a value
diff --git a/numpy/tests/reveal/ndarray_shape_manipulation.py b/numpy/tests/reveal/ndarray_shape_manipulation.py
new file mode 100644
index 000000000..a44e1cfa1
--- /dev/null
+++ b/numpy/tests/reveal/ndarray_shape_manipulation.py
@@ -0,0 +1,35 @@
+import numpy as np
+
+nd = np.array([[1, 2], [3, 4]])
+
+# reshape
+reveal_type(nd.reshape()) # E: numpy.ndarray
+reveal_type(nd.reshape(4)) # E: numpy.ndarray
+reveal_type(nd.reshape(2, 2)) # E: numpy.ndarray
+reveal_type(nd.reshape((2, 2))) # E: numpy.ndarray
+
+reveal_type(nd.reshape((2, 2), order="C")) # E: numpy.ndarray
+reveal_type(nd.reshape(4, order="C")) # E: numpy.ndarray
+
+# resize does not return a value
+
+# transpose
+reveal_type(nd.transpose()) # E: numpy.ndarray
+reveal_type(nd.transpose(1, 0)) # E: numpy.ndarray
+reveal_type(nd.transpose((1, 0))) # E: numpy.ndarray
+
+# swapaxes
+reveal_type(nd.swapaxes(0, 1)) # E: numpy.ndarray
+
+# flatten
+reveal_type(nd.flatten()) # E: numpy.ndarray
+reveal_type(nd.flatten("C")) # E: numpy.ndarray
+
+# ravel
+reveal_type(nd.ravel()) # E: numpy.ndarray
+reveal_type(nd.ravel("C")) # E: numpy.ndarray
+
+# squeeze
+reveal_type(nd.squeeze()) # E: numpy.ndarray
+reveal_type(nd.squeeze(0)) # E: numpy.ndarray
+reveal_type(nd.squeeze((0, 2))) # E: numpy.ndarray
diff --git a/numpy/tests/reveal/numerictypes.py b/numpy/tests/reveal/numerictypes.py
new file mode 100644
index 000000000..e026158cd
--- /dev/null
+++ b/numpy/tests/reveal/numerictypes.py
@@ -0,0 +1,18 @@
+import numpy as np
+
+reveal_type(np.issctype(np.generic)) # E: bool
+reveal_type(np.issctype("foo")) # E: bool
+
+reveal_type(np.obj2sctype("S8")) # E: Union[numpy.generic, None]
+reveal_type(np.obj2sctype("S8", default=None)) # E: Union[numpy.generic, None]
+reveal_type(
+ np.obj2sctype("foo", default=int) # E: Union[numpy.generic, Type[builtins.int*]]
+)
+
+reveal_type(np.issubclass_(np.float64, float)) # E: bool
+reveal_type(np.issubclass_(np.float64, (int, float))) # E: bool
+
+reveal_type(np.sctype2char("S8")) # E: str
+reveal_type(np.sctype2char(list)) # E: str
+
+reveal_type(np.find_common_type([np.int64], [np.int64])) # E: numpy.dtype
diff --git a/numpy/tests/reveal/scalars.py b/numpy/tests/reveal/scalars.py
new file mode 100644
index 000000000..8a9555fc3
--- /dev/null
+++ b/numpy/tests/reveal/scalars.py
@@ -0,0 +1,30 @@
+import numpy as np
+
+x = np.complex64(3 + 2j)
+
+reveal_type(x.real) # E: numpy.float32
+reveal_type(x.imag) # E: numpy.float32
+
+reveal_type(x.real.real) # E: numpy.float32
+reveal_type(x.real.imag) # E: numpy.float32
+
+reveal_type(x.itemsize) # E: int
+reveal_type(x.shape) # E: tuple[builtins.int]
+reveal_type(x.strides) # E: tuple[builtins.int]
+
+# Time structures
+dt = np.datetime64(0, "D")
+td = np.timedelta64(0, "D")
+
+reveal_type(dt + td) # E: numpy.datetime64
+reveal_type(dt + 1) # E: numpy.datetime64
+reveal_type(dt - dt) # E: numpy.timedelta64
+reveal_type(dt - 1) # E: numpy.timedelta64
+
+reveal_type(td + td) # E: numpy.timedelta64
+reveal_type(td + 1) # E: numpy.timedelta64
+reveal_type(td - td) # E: numpy.timedelta64
+reveal_type(td - 1) # E: numpy.timedelta64
+reveal_type(td / 1.0) # E: numpy.timedelta64
+reveal_type(td / td) # E: float
+reveal_type(td % td) # E: numpy.timedelta64
diff --git a/numpy/tests/reveal/warnings_and_errors.py b/numpy/tests/reveal/warnings_and_errors.py
new file mode 100644
index 000000000..c428deb7a
--- /dev/null
+++ b/numpy/tests/reveal/warnings_and_errors.py
@@ -0,0 +1,10 @@
+from typing import Type
+
+import numpy as np
+
+reveal_type(np.ModuleDeprecationWarning()) # E: numpy.ModuleDeprecationWarning
+reveal_type(np.VisibleDeprecationWarning()) # E: numpy.VisibleDeprecationWarning
+reveal_type(np.ComplexWarning()) # E: numpy.ComplexWarning
+reveal_type(np.RankWarning()) # E: numpy.RankWarning
+reveal_type(np.TooHardError()) # E: numpy.TooHardError
+reveal_type(np.AxisError(1)) # E: numpy.AxisError
diff --git a/numpy/tests/setup.py b/numpy/tests/setup.py
new file mode 100644
index 000000000..03900a82b
--- /dev/null
+++ b/numpy/tests/setup.py
@@ -0,0 +1,13 @@
+def configuration(parent_package='', top_path=None):
+ from numpy.distutils.misc_util import Configuration
+ config = Configuration('tests', parent_package, top_path)
+ config.add_data_dir('pass')
+ config.add_data_dir('fail')
+ config.add_data_dir('reveal')
+ config.add_data_files('mypy.ini')
+ return config
+
+
+if __name__ == '__main__':
+ from numpy.distutils.core import setup
+ setup(configuration=configuration)
diff --git a/numpy/tests/test_typing.py b/numpy/tests/test_typing.py
new file mode 100644
index 000000000..757ea0b52
--- /dev/null
+++ b/numpy/tests/test_typing.py
@@ -0,0 +1,127 @@
+import importlib.util
+import itertools
+import os
+import re
+from collections import defaultdict
+
+import pytest
+try:
+ from mypy import api
+except ImportError:
+ NO_MYPY = True
+else:
+ NO_MYPY = False
+
+TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
+PASS_DIR = os.path.join(TESTS_DIR, "pass")
+FAIL_DIR = os.path.join(TESTS_DIR, "fail")
+REVEAL_DIR = os.path.join(TESTS_DIR, "reveal")
+MYPY_INI = os.path.join(TESTS_DIR, "mypy.ini")
+CACHE_DIR = os.path.join(TESTS_DIR, ".mypy_cache")
+
+
+def get_test_cases(directory):
+ for root, _, files in os.walk(directory):
+ for fname in files:
+ if os.path.splitext(fname)[-1] == ".py":
+ fullpath = os.path.join(root, fname)
+ # Use relative path for nice py.test name
+ relpath = os.path.relpath(fullpath, start=directory)
+
+ yield pytest.param(
+ fullpath,
+ # Manually specify a name for the test
+ id=relpath,
+ )
+
+
+@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed")
+@pytest.mark.parametrize("path", get_test_cases(PASS_DIR))
+def test_success(path):
+ stdout, stderr, exitcode = api.run([
+ "--config-file",
+ MYPY_INI,
+ "--cache-dir",
+ CACHE_DIR,
+ path,
+ ])
+ assert exitcode == 0, stdout
+ assert re.match(r"Success: no issues found in \d+ source files?", stdout.strip())
+
+
+@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed")
+@pytest.mark.parametrize("path", get_test_cases(FAIL_DIR))
+def test_fail(path):
+ stdout, stderr, exitcode = api.run([
+ "--config-file",
+ MYPY_INI,
+ "--cache-dir",
+ CACHE_DIR,
+ path,
+ ])
+ assert exitcode != 0
+
+ with open(path) as fin:
+ lines = fin.readlines()
+
+ errors = defaultdict(lambda: "")
+ error_lines = stdout.rstrip("\n").split("\n")
+ assert re.match(
+ r"Found \d+ errors? in \d+ files? \(checked \d+ source files?\)",
+ error_lines[-1].strip(),
+ )
+ for error_line in error_lines[:-1]:
+ error_line = error_line.strip()
+ if not error_line:
+ continue
+
+ lineno = int(error_line.split(":")[1])
+ errors[lineno] += error_line
+
+ for i, line in enumerate(lines):
+ lineno = i + 1
+ if " E:" not in line and lineno not in errors:
+ continue
+
+ target_line = lines[lineno - 1]
+ if "# E:" in target_line:
+ marker = target_line.split("# E:")[-1].strip()
+ assert lineno in errors, f'Extra error "{marker}"'
+ assert marker in errors[lineno]
+ else:
+ pytest.fail(f"Error {repr(errors[lineno])} not found")
+
+
+@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed")
+@pytest.mark.parametrize("path", get_test_cases(REVEAL_DIR))
+def test_reveal(path):
+ stdout, stderr, exitcode = api.run([
+ "--config-file",
+ MYPY_INI,
+ "--cache-dir",
+ CACHE_DIR,
+ path,
+ ])
+
+ with open(path) as fin:
+ lines = fin.readlines()
+
+ for error_line in stdout.split("\n"):
+ error_line = error_line.strip()
+ if not error_line:
+ continue
+
+ lineno = int(error_line.split(":")[1])
+ assert "Revealed type is" in error_line
+ marker = lines[lineno - 1].split("# E:")[-1].strip()
+ assert marker in error_line
+
+
+@pytest.mark.skipif(NO_MYPY, reason="Mypy is not installed")
+@pytest.mark.parametrize("path", get_test_cases(PASS_DIR))
+def test_code_runs(path):
+ path_without_extension, _ = os.path.splitext(path)
+ dirname, filename = path.split(os.sep)[-2:]
+ spec = importlib.util.spec_from_file_location(f"{dirname}.{filename}", path)
+ test_module = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(test_module)