Repository: numpy/numpy
Pull request: MAINT: Ensure that re-exported sub-modules are properly annotated
closes https://github.com/numpy/numpy/issues/18601.

Sub-modules, such as `np.linalg`, must be explicitly imported in the main namespaces' stub file if one wants 
to access the sub-module via a `getattr` operation, _i.e._ so that one can directly use `np.linalg.norm` rather 
than `import numpy.linalg; np.linalg.norm`.

While this was taken care of in the main numpy namespace, the relevant annotations were missing for others 
such as `np.lib.*`. This PR fixes aforementioned issue.

Examples
---------
The behavior prior to this PR:
``` python
>>> import numpy as np

>>> x = np.arange(6)
>>> out = np.lib.stride_tricks.sliding_window_view(x, 3)
```
Mypy output:
```
test.pyi:4:7: error: Module has no attribute "stride_tricks"  [attr-defined]
```

Problem statement:
Annotations don't import np.lib.*
When one imports numpy, the various `np.lib.*` modules are automatically imported. However, the type annotations don't reflect this.

### Reproducing code example:

```python
import numpy as np
np.lib.stride_tricks.DummyArray```

### Error message:

When run with mypy 0.790:
```
st.py:2: error: Module has no attribute "stride_tricks"
Found 1 error in 1 file (checked 1 source file)
```

### NumPy/Python version information:

1.20.1 3.8.5 (default, Jan 27 2021, 15:41:15) 
[GCC 9.3.0]

Hints from issue discussion:
The `test_public_api` tests claim that the `np.lib.stride_tricks` is a private module though.
Can anyone comment on how up to date the list below is?
https://github.com/numpy/numpy/blob/e376a885bc5545b79e88c155f0aebba27bab639d/numpy/tests/test_public_api.py#L273-L288
Some of the methods in stride_tricks are [publicly documented](https://numpy.org/doc/stable/reference/routines.indexing.html#indexing-like-operations).

The bug also applies to modules that are listed as public in that test e.g. `np.lib.recfunctions`.

Commit message: MAINT: Re-export a number of sub-modules

Ensures that type checkers will allow the likes of:

>>> import numpy as np
>>> out = np.lib.stride_tricks.sliding_window_view(...)

Patch to review:
diff --git a/numpy/lib/__init__.pyi b/numpy/lib/__init__.pyi
index ec528d2200d0..02ed56c8b8c8 100644
--- a/numpy/lib/__init__.pyi
+++ b/numpy/lib/__init__.pyi
@@ -5,6 +5,13 @@ from numpy import (
     ndindex as ndindex,
 )
 
+from numpy.lib import (
+    format as format,
+    mixins as mixins,
+    scimath as scimath,
+    stride_tricks as stride_stricks,
+)
+
 from numpy.lib.arrayterator import (
     Arrayterator as Arrayterator,
 )
diff --git a/numpy/ma/__init__.pyi b/numpy/ma/__init__.pyi
index 66dfe40de6a5..16e026272605 100644
--- a/numpy/ma/__init__.pyi
+++ b/numpy/ma/__init__.pyi
@@ -1,5 +1,7 @@
 from typing import Any, List
 
+from numpy.ma import extras as extras
+
 __all__: List[str]
 
 core: Any
diff --git a/numpy/polynomial/__init__.pyi b/numpy/polynomial/__init__.pyi
index 817ba22ac83f..5f4d11e9097a 100644
--- a/numpy/polynomial/__init__.pyi
+++ b/numpy/polynomial/__init__.pyi
@@ -1,5 +1,15 @@
 from typing import Any
 
+from numpy.polynomial import (
+    chebyshev as chebyshev,
+    hermite as hermite,
+    hermite_e as hermite_e,
+    laguerre as laguerre,
+    legendre as legendre,
+    polynomial as polynomial,
+    polyutils as polyutils,
+)
+
 Polynomial: Any
 Chebyshev: Any
 Legendre: Any
diff --git a/numpy/tests/test_public_api.py b/numpy/tests/test_public_api.py
index 69430eeda089..c7a789b21521 100644
--- a/numpy/tests/test_public_api.py
+++ b/numpy/tests/test_public_api.py
@@ -154,6 +154,7 @@ def test_NPY_NO_EXPORT():
     "lib.mixins",
     "lib.recfunctions",
     "lib.scimath",
+    "lib.stride_tricks",
     "linalg",
     "ma",
     "ma.extras",
@@ -280,7 +281,6 @@ def test_NPY_NO_EXPORT():
     "lib.npyio",
     "lib.polynomial",
     "lib.shape_base",
-    "lib.stride_tricks",
     "lib.twodim_base",
     "lib.type_check",
     "lib.ufunclike",
diff --git a/numpy/typing/tests/data/fail/modules.py b/numpy/typing/tests/data/fail/modules.py
index b80fd9edeae2..7b9309329ac8 100644
--- a/numpy/typing/tests/data/fail/modules.py
+++ b/numpy/typing/tests/data/fail/modules.py
@@ -9,6 +9,11 @@
 np.os  # E: Module has no attribute
 np.math  # E: Module has no attribute
 
+# Public sub-modules that are not imported to their parent module by default;
+# e.g. one must first execute `import numpy.lib.recfunctions`
+np.lib.recfunctions  # E: Module has no attribute
+np.ma.mrecords  # E: Module has no attribute
+
 np.__NUMPY_SETUP__  # E: Module has no attribute
 np.__deprecated_attrs__  # E: Module has no attribute
 np.__expired_functions__  # E: Module has no attribute
diff --git a/numpy/typing/tests/data/pass/modules.py b/numpy/typing/tests/data/pass/modules.py
index 2fdb69eb3eab..5ca9691ac880 100644
--- a/numpy/typing/tests/data/pass/modules.py
+++ b/numpy/typing/tests/data/pass/modules.py
@@ -15,6 +15,19 @@
 np.testing
 np.version
 
+np.lib.format
+np.lib.mixins
+np.lib.scimath
+np.lib.stride_tricks
+np.ma.extras
+np.polynomial.chebyshev
+np.polynomial.hermite
+np.polynomial.hermite_e
+np.polynomial.laguerre
+np.polynomial.legendre
+np.polynomial.polynomial
+np.polynomial.polyutils
+
 np.__path__
 np.__version__
 np.__git_version__
diff --git a/numpy/typing/tests/data/reveal/modules.py b/numpy/typing/tests/data/reveal/modules.py
index 75513f2b08c9..4852d60b79e9 100644
--- a/numpy/typing/tests/data/reveal/modules.py
+++ b/numpy/typing/tests/data/reveal/modules.py
@@ -17,6 +17,19 @@
 reveal_type(np.testing)  # E: ModuleType
 reveal_type(np.version)  # E: ModuleType
 
+reveal_type(np.lib.format)  # E: ModuleType
+reveal_type(np.lib.mixins)  # E: ModuleType
+reveal_type(np.lib.scimath)  # E: ModuleType
+reveal_type(np.lib.stride_tricks)  # E: ModuleType
+reveal_type(np.ma.extras)  # E: ModuleType
+reveal_type(np.polynomial.chebyshev)  # E: ModuleType
+reveal_type(np.polynomial.hermite)  # E: ModuleType
+reveal_type(np.polynomial.hermite_e)  # E: ModuleType
+reveal_type(np.polynomial.laguerre)  # E: ModuleType
+reveal_type(np.polynomial.legendre)  # E: ModuleType
+reveal_type(np.polynomial.polynomial)  # E: ModuleType
+reveal_type(np.polynomial.polyutils)  # E: ModuleType
+
 # TODO: Remove when annotations have been added to `np.testing.assert_equal`
 reveal_type(np.testing.assert_equal)  # E: Any
 


Code review comments:
diff_hunk: @@ -1,5 +1,15 @@
 from typing import Any
 
+from numpy.polynomial import (
+    chebyshev as chebyshev,
+    hermite as hermite,
+    hermite_e as hermite_e,
+    laguerre as laguerre,
+    legendre as legendre,
+    polynomial as polynomial,
+    polyutils as polyutils,
path: numpy/polynomial/__init__.pyi
text: @user1:
Hmm. The polyutils module should be private except for the exceptions. The polynomial module could use cleanup... 

@author:
Would it be worthwhile to move it to the `PRIVATE_BUT_PRESENT_MODULES` list?

@author:
Done as of https://github.com/numpy/numpy/pull/18618/commits/f0fa0662bb1cf116193b3a6e628c18f595937da7.