This simplifies the way that `InteractiveConsole` does stream redirections and adds stream redirection for `stdin`. I also moved `banner` from a class method of `InteractiveConsole` to a module level constant `BANNER`.
Rearrange InteractiveConsole stream redirections. This simplifies the way that `InteractiveConsole` does stream redirections and adds stream redirection for `stdin`. I also moved `banner` from a class method of `InteractiveConsole` to a module level constant `BANNER`.
Delay ctypes imports

Included in numpy 1.20.0 (https://github.com/numpy/numpy/pull/16387)

diff --git a/numpy/core/_dtype_ctypes.py b/numpy/core/_dtype_ctypes.py
index 708241289..6d7cbb244 100644
--- a/numpy/core/_dtype_ctypes.py
+++ b/numpy/core/_dtype_ctypes.py
@@ -22,9 +22,10 @@ Unfortunately, this fails because:
 * PEP3118 cannot represent unions, but both numpy and ctypes can
 * ctypes cannot handle big-endian structs with PEP3118 (bpo-32780)
 """
-import _ctypes
-import ctypes
 
+# We delay-import ctypes for distributions that do not include it.
+# While this module is not used unless the user passes in ctypes
+# members, it is eagerly imported from numpy/core/__init__.py.
 import numpy as np
 
 
@@ -39,6 +40,7 @@ def _from_ctypes_structure(t):
                 "ctypes bitfields have no dtype equivalent")
 
     if hasattr(t, "_pack_"):
+        import ctypes
         formats = []
         offsets = []
         names = []
@@ -79,6 +81,7 @@ def _from_ctypes_scalar(t):
 
 
 def _from_ctypes_union(t):
+    import ctypes
     formats = []
     offsets = []
     names = []
@@ -98,6 +101,7 @@ def dtype_from_ctypes_type(t):
     """
     Construct a dtype object from a ctypes type
     """
+    import _ctypes
     if issubclass(t, _ctypes.Array):
         return _from_ctypes_array(t)
     elif issubclass(t, _ctypes._Pointer):
diff --git a/tools/openblas_support.py b/tools/openblas_support.py
index 6b2ad0f8c..cbb6a5e43 100644
--- a/tools/openblas_support.py
+++ b/tools/openblas_support.py
@@ -207,12 +207,12 @@ def make_init(dirname):
             and is created as part of the scripts that build the wheel.
             '''
             import os
-            from ctypes import WinDLL
             import glob
             if os.name == 'nt':
                 # convention for storing / loading the DLL from
                 # numpy/.libs/, if present
                 try:
+                    from ctypes import WinDLL
                     basedir = os.path.dirname(__file__)
                 except:
                     pass
@@ -221,16 +221,16 @@ def make_init(dirname):
                     DLL_filenames = []
                     if os.path.isdir(libs_dir):
                         for filename in glob.glob(os.path.join(libs_dir,
-                                                             '*openblas*dll')):
+                                                               '*openblas*dll')):
                             # NOTE: would it change behavior to load ALL
                             # DLLs at this path vs. the name restriction?
                             WinDLL(os.path.abspath(filename))
                             DLL_filenames.append(filename)
-                if len(DLL_filenames) > 1:
-                    import warnings
-                    warnings.warn("loaded more than 1 DLL from .libs:\\n%s" %
-                              "\\n".join(DLL_filenames),
-                              stacklevel=1)
+                    if len(DLL_filenames) > 1:
+                        import warnings
+                        warnings.warn("loaded more than 1 DLL from .libs:\\n%s" %
+                                      "\\n".join(DLL_filenames),
+                                      stacklevel=1)
     """))
 
 def test_setup(arches):

Delay ctypes imports

Included in numpy 1.20.0 (https://github.com/numpy/numpy/pull/16387)

diff --git a/numpy/core/_dtype_ctypes.py b/numpy/core/_dtype_ctypes.py
index 708241289..6d7cbb244 100644
--- a/numpy/core/_dtype_ctypes.py
+++ b/numpy/core/_dtype_ctypes.py
@@ -22,9 +22,10 @@ Unfortunately, this fails because:
 * PEP3118 cannot represent unions, but both numpy and ctypes can
 * ctypes cannot handle big-endian structs with PEP3118 (bpo-32780)
 """
-import _ctypes
-import ctypes
 
+# We delay-import ctypes for distributions that do not include it.
+# While this module is not used unless the user passes in ctypes
+# members, it is eagerly imported from numpy/core/__init__.py.
 import numpy as np
 
 
@@ -39,6 +40,7 @@ def _from_ctypes_structure(t):
                 "ctypes bitfields have no dtype equivalent")
 
     if hasattr(t, "_pack_"):
+        import ctypes
         formats = []
         offsets = []
         names = []
@@ -79,6 +81,7 @@ def _from_ctypes_scalar(t):
 
 
 def _from_ctypes_union(t):
+    import ctypes
     formats = []
     offsets = []
     names = []
@@ -98,6 +101,7 @@ def dtype_from_ctypes_type(t):
     """
     Construct a dtype object from a ctypes type
     """
+    import _ctypes
     if issubclass(t, _ctypes.Array):
         return _from_ctypes_array(t)
     elif issubclass(t, _ctypes._Pointer):
diff --git a/tools/openblas_support.py b/tools/openblas_support.py
index 6b2ad0f8c..cbb6a5e43 100644
--- a/tools/openblas_support.py
+++ b/tools/openblas_support.py
@@ -207,12 +207,12 @@ def make_init(dirname):
             and is created as part of the scripts that build the wheel.
             '''
             import os
-            from ctypes import WinDLL
             import glob
             if os.name == 'nt':
                 # convention for storing / loading the DLL from
                 # numpy/.libs/, if present
                 try:
+                    from ctypes import WinDLL
                     basedir = os.path.dirname(__file__)
                 except:
                     pass
@@ -221,16 +221,16 @@ def make_init(dirname):
                     DLL_filenames = []
                     if os.path.isdir(libs_dir):
                         for filename in glob.glob(os.path.join(libs_dir,
-                                                             '*openblas*dll')):
+                                                               '*openblas*dll')):
                             # NOTE: would it change behavior to load ALL
                             # DLLs at this path vs. the name restriction?
                             WinDLL(os.path.abspath(filename))
                             DLL_filenames.append(filename)
-                if len(DLL_filenames) > 1:
-                    import warnings
-                    warnings.warn("loaded more than 1 DLL from .libs:\\n%s" %
-                              "\\n".join(DLL_filenames),
-                              stacklevel=1)
+                    if len(DLL_filenames) > 1:
+                        import warnings
+                        warnings.warn("loaded more than 1 DLL from .libs:\\n%s" %
+                                      "\\n".join(DLL_filenames),
+                                      stacklevel=1)
     """))
 
 def test_setup(arches):
