Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9ed23a3

Browse files
committed
MNT: imporve importing time by lazy importing Tester
1 parent 544094a commit 9ed23a3

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

numpy/__init__.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@
166166
# now that numpy modules are imported, can initialize limits
167167
core.getlimits._register_known_types()
168168

169+
__all__.extend(['bool', 'int', 'float', 'complex', 'object', 'unicode',
170+
'str'])
171+
169172
__all__.extend(['__version__', 'show_config'])
170173
__all__.extend(core.__all__)
171174
__all__.extend(_mat.__all__)
@@ -182,9 +185,35 @@
182185
oldnumeric = 'removed'
183186
numarray = 'removed'
184187

185-
# We don't actually use this ourselves anymore, but I'm not 100% sure that
186-
# no-one else in the world is using it (though I hope not)
187-
from .testing import Tester
188+
if sys.version_info[:2] >= (3, 7):
189+
# Importing Tester requires importing all of UnitTest which is not a
190+
# cheap import Since it is mainly used in test suits, we lazy import it
191+
# here to save on the order of 10 ms of import time for most users
192+
#
193+
# The previous way Tester was imported also had a side effect of adding
194+
# the full `numpy.testing` namespace
195+
#
196+
# module level getattr is only supported in 3.7 onwards
197+
# https://www.python.org/dev/peps/pep-0562/
198+
def __getattr__(attr):
199+
if attr == 'testing':
200+
import numpy.testing as testing
201+
return testing
202+
elif attr == 'Tester':
203+
from .testing import Tester
204+
return Tester
205+
else:
206+
raise AttributeError(
207+
"module %s has no attribute $s".format(__name__, attr))
208+
209+
210+
def __dir__():
211+
return __all__ + ['Tester', 'testing']
212+
213+
else:
214+
# We don't actually use this ourselves anymore, but I'm not 100% sure that
215+
# no-one else in the world is using it (though I hope not)
216+
from .testing import Tester
188217

189218
# Pytest testing
190219
from numpy._pytesttester import PytestTester

numpy/tests/test_public_api.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import division, absolute_import, print_function
22

33
import sys
4+
import subprocess
45

56
import numpy as np
67
import pytest
@@ -69,6 +70,28 @@ def test_numpy_namespace():
6970
assert bad_results == whitelist
7071

7172

73+
@pytest.mark.parametrize('name', ['testing', 'Tester'])
74+
def test_import_lazy_import(name):
75+
"""Make sure we can actually the the modules we lazy load.
76+
77+
While not exported as part of the public API, it was accessible. With the
78+
use of __getattr__ and __dir__, this isn't always true It can happen that
79+
an infinite recursion may happen.
80+
81+
This is the only way I found that would force the failure to appear on the
82+
badly implemented code.
83+
84+
We also test for the presence of the lazily imported modules in dir
85+
86+
"""
87+
exe = (sys.executable, '-c', "import numpy; numpy." + name)
88+
result = subprocess.check_output(exe)
89+
assert not result
90+
91+
# Make sure they are still in the __dir__
92+
assert name in dir(np)
93+
94+
7295
def test_numpy_linalg():
7396
bad_results = check_dir(np.linalg)
7497
assert bad_results == {}

0 commit comments

Comments
 (0)