-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathtest_lazy_loader.py
220 lines (169 loc) · 6.34 KB
/
test_lazy_loader.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import importlib
import os
import subprocess
import sys
import types
from unittest import mock
import pytest
import lazy_loader as lazy
def test_lazy_import_basics():
math = lazy.load("math")
anything_not_real = lazy.load("anything_not_real")
# Now test that accessing attributes does what it should
assert math.sin(math.pi) == pytest.approx(0, 1e-6)
# poor-mans pytest.raises for testing errors on attribute access
try:
anything_not_real.pi
raise AssertionError() # Should not get here
except ModuleNotFoundError:
pass
assert isinstance(anything_not_real, lazy.DelayedImportErrorModule)
# see if it changes for second access
try:
anything_not_real.pi
raise AssertionError() # Should not get here
except ModuleNotFoundError:
pass
def test_lazy_import_subpackages():
with pytest.warns(RuntimeWarning):
hp = lazy.load("html.parser")
assert "html" in sys.modules
assert type(sys.modules["html"]) is type(pytest)
assert isinstance(hp, importlib.util._LazyModule)
assert "html.parser" in sys.modules
assert sys.modules["html.parser"] == hp
def test_lazy_import_impact_on_sys_modules():
math = lazy.load("math")
anything_not_real = lazy.load("anything_not_real")
assert isinstance(math, types.ModuleType)
assert "math" in sys.modules
assert isinstance(anything_not_real, lazy.DelayedImportErrorModule)
assert "anything_not_real" not in sys.modules
# only do this if numpy is installed
pytest.importorskip("numpy")
np = lazy.load("numpy")
assert isinstance(np, types.ModuleType)
assert "numpy" in sys.modules
np.pi # trigger load of numpy
assert isinstance(np, types.ModuleType)
assert "numpy" in sys.modules
def test_lazy_import_nonbuiltins():
np = lazy.load("numpy")
sp = lazy.load("scipy")
if not isinstance(np, lazy.DelayedImportErrorModule):
assert np.sin(np.pi) == pytest.approx(0, 1e-6)
if isinstance(sp, lazy.DelayedImportErrorModule):
try:
sp.pi
raise AssertionError()
except ModuleNotFoundError:
pass
def test_lazy_attach():
name = "mymod"
submods = ["mysubmodule", "anothersubmodule"]
myall = {"not_real_submod": ["some_var_or_func"]}
locls = {
"attach": lazy.attach,
"name": name,
"submods": submods,
"myall": myall,
}
s = "__getattr__, __lazy_dir__, __all__ = attach(name, submods, myall)"
exec(s, {}, locls)
expected = {
"attach": lazy.attach,
"name": name,
"submods": submods,
"myall": myall,
"__getattr__": None,
"__lazy_dir__": None,
"__all__": None,
}
assert locls.keys() == expected.keys()
for k, v in expected.items():
if v is not None:
assert locls[k] == v
def test_attach_same_module_and_attr_name():
from lazy_loader.tests import fake_pkg
# Grab attribute twice, to ensure that importing it does not
# override function by module
assert isinstance(fake_pkg.some_func, types.FunctionType)
assert isinstance(fake_pkg.some_func, types.FunctionType)
# Ensure imports from submodule still work
from lazy_loader.tests.fake_pkg.some_func import some_func
assert isinstance(some_func, types.FunctionType)
FAKE_STUB = """
from . import rank
from ._gaussian import gaussian
from .edges import sobel, scharr, prewitt, roberts
"""
def test_stub_loading(tmp_path):
stub = tmp_path / "stub.pyi"
stub.write_text(FAKE_STUB)
_get, _dir, _all = lazy.attach_stub("my_module", str(stub))
expect = {"gaussian", "sobel", "scharr", "prewitt", "roberts", "rank"}
assert set(_dir()) == set(_all) == expect
def test_stub_loading_parity():
from lazy_loader.tests import fake_pkg
from_stub = lazy.attach_stub(fake_pkg.__name__, fake_pkg.__file__)
stub_getter, stub_dir, stub_all = from_stub
assert stub_all == fake_pkg.__all__
assert stub_dir() == fake_pkg.__lazy_dir__()
assert stub_getter("some_func") == fake_pkg.some_func
FAKE_STUB_OVERRIDE_ALL = """
__all__ = [
"rank",
"gaussian",
"sobel",
"scharr",
"roberts",
# `prewitt` not included!
"__version__", # included but not imported in stub
]
from . import rank
from ._gaussian import gaussian
from .edges import sobel, scharr, prewitt, roberts
"""
def test_stub_override_all(tmp_path):
stub = tmp_path / "stub.pyi"
stub.write_text(FAKE_STUB_OVERRIDE_ALL)
_get, _dir, _all = lazy.attach_stub("my_module", str(stub))
expect_dir = {"gaussian", "sobel", "scharr", "prewitt", "roberts", "rank"}
assert set(_dir()) == expect_dir
expect_all = {"rank", "gaussian", "sobel", "scharr", "roberts", "__version__"}
assert set(_all) == expect_all
def test_stub_loading_errors(tmp_path):
stub = tmp_path / "stub.pyi"
stub.write_text("from ..mod import func\n")
with pytest.raises(ValueError, match="Only within-module imports are supported"):
lazy.attach_stub("name", str(stub))
with pytest.raises(ValueError, match="Cannot load imports from non-existent stub"):
lazy.attach_stub("name", "not a file")
stub2 = tmp_path / "stub2.pyi"
stub2.write_text("from .mod import *\n")
with pytest.raises(ValueError, match=".*does not support star import"):
lazy.attach_stub("name", str(stub2))
def test_require_kwarg():
# Test with a module that definitely exists, behavior hinges on requirement
with mock.patch("importlib.metadata.version") as version:
version.return_value = "1.0.0"
math = lazy.load("math", require="somepkg >= 2.0")
assert isinstance(math, lazy.DelayedImportErrorModule)
math = lazy.load("math", require="somepkg >= 1.0")
assert math.sin(math.pi) == pytest.approx(0, 1e-6)
# We can fail even after a successful import
math = lazy.load("math", require="somepkg >= 2.0")
assert isinstance(math, lazy.DelayedImportErrorModule)
# When a module can be loaded but the version can't be checked,
# raise a ValueError
with pytest.raises(ValueError):
lazy.load("math", require="somepkg >= 1.0")
def test_parallel_load():
pytest.importorskip("numpy")
subprocess.run(
[
sys.executable,
os.path.join(os.path.dirname(__file__), "import_np_parallel.py"),
],
check=True,
)