Skip to content

Commit ac85913

Browse files
committed
Remove old code and tests
1 parent 4b985f4 commit ac85913

File tree

2 files changed

+1
-308
lines changed

2 files changed

+1
-308
lines changed

lazy_loader/__init__.py

Lines changed: 1 addition & 242 deletions
Original file line numberDiff line numberDiff line change
@@ -6,252 +6,11 @@
66
"""
77
from __future__ import annotations
88

9-
import ast
10-
import importlib
11-
import importlib.util
12-
import inspect
13-
import os
149
import sys
15-
import types
1610
from contextlib import contextmanager
1711
from importlib.util import LazyLoader
1812

19-
__all__ = ["attach", "load", "attach_stub", "lazy_loader"]
20-
21-
22-
def attach(package_name, submodules=None, submod_attrs=None):
23-
"""Attach lazily loaded submodules, functions, or other attributes.
24-
25-
Typically, modules import submodules and attributes as follows::
26-
27-
import mysubmodule
28-
import anothersubmodule
29-
30-
from .foo import someattr
31-
32-
The idea is to replace a package's `__getattr__`, `__dir__`, and
33-
`__all__`, such that all imports work exactly the way they would
34-
with normal imports, except that the import occurs upon first use.
35-
36-
The typical way to call this function, replacing the above imports, is::
37-
38-
__getattr__, __dir__, __all__ = lazy.attach(
39-
__name__,
40-
['mysubmodule', 'anothersubmodule'],
41-
{'foo': ['someattr']}
42-
)
43-
44-
This functionality requires Python 3.7 or higher.
45-
46-
Parameters
47-
----------
48-
package_name : str
49-
Typically use ``__name__``.
50-
submodules : set
51-
List of submodules to attach.
52-
submod_attrs : dict
53-
Dictionary of submodule -> list of attributes / functions.
54-
These attributes are imported as they are used.
55-
56-
Returns
57-
-------
58-
__getattr__, __dir__, __all__
59-
60-
"""
61-
if submod_attrs is None:
62-
submod_attrs = {}
63-
64-
if submodules is None:
65-
submodules = set()
66-
else:
67-
submodules = set(submodules)
68-
69-
attr_to_modules = {
70-
attr: mod for mod, attrs in submod_attrs.items() for attr in attrs
71-
}
72-
73-
__all__ = list(submodules | attr_to_modules.keys())
74-
75-
def __getattr__(name):
76-
if name in submodules:
77-
return importlib.import_module(f"{package_name}.{name}")
78-
elif name in attr_to_modules:
79-
submod_path = f"{package_name}.{attr_to_modules[name]}"
80-
submod = importlib.import_module(submod_path)
81-
attr = getattr(submod, name)
82-
83-
# If the attribute lives in a file (module) with the same
84-
# name as the attribute, ensure that the attribute and *not*
85-
# the module is accessible on the package.
86-
if name == attr_to_modules[name]:
87-
pkg = sys.modules[package_name]
88-
pkg.__dict__[name] = attr
89-
90-
return attr
91-
else:
92-
raise AttributeError(f"No {package_name} attribute {name}")
93-
94-
def __dir__():
95-
return __all__
96-
97-
if os.environ.get("EAGER_IMPORT", ""):
98-
for attr in set(attr_to_modules.keys()) | submodules:
99-
__getattr__(attr)
100-
101-
return __getattr__, __dir__, list(__all__)
102-
103-
104-
class DelayedImportErrorModule(types.ModuleType):
105-
def __init__(self, frame_data, *args, **kwargs):
106-
self.__frame_data = frame_data
107-
super().__init__(*args, **kwargs)
108-
109-
def __getattr__(self, x):
110-
if x in ("__class__", "__file__", "__frame_data"):
111-
super().__getattr__(x)
112-
else:
113-
fd = self.__frame_data
114-
raise ModuleNotFoundError(
115-
f"No module named '{fd['spec']}'\n\n"
116-
"This error is lazily reported, having originally occured in\n"
117-
f' File {fd["filename"]}, line {fd["lineno"]}, in {fd["function"]}\n\n'
118-
f'----> {"".join(fd["code_context"]).strip()}'
119-
)
120-
121-
122-
def load(fullname, error_on_import=False):
123-
"""Return a lazily imported proxy for a module.
124-
125-
We often see the following pattern::
126-
127-
def myfunc():
128-
from numpy import linalg as la
129-
la.norm(...)
130-
....
131-
132-
This is to prevent a module, in this case `numpy`, from being
133-
imported at function definition time, since that can be slow.
134-
135-
This function provides a proxy module that, upon access, imports
136-
the actual module. So the idiom equivalent to the above example is::
137-
138-
la = lazy.load("numpy.linalg")
139-
140-
def myfunc():
141-
la.norm(...)
142-
....
143-
144-
The initial import time is fast because the actual import is delayed
145-
until the first attribute is requested. The overall import time may
146-
decrease as well for users that don't make use of large portions
147-
of the library.
148-
149-
Parameters
150-
----------
151-
fullname : str
152-
The full name of the module or submodule to import. For example::
153-
154-
sp = lazy.load('scipy') # import scipy as sp
155-
spla = lazy.load('scipy.linalg') # import scipy.linalg as spla
156-
error_on_import : bool
157-
Whether to postpone raising import errors until the module is accessed.
158-
If set to `True`, import errors are raised as soon as `load` is called.
159-
160-
Returns
161-
-------
162-
pm : importlib.util._LazyModule
163-
Proxy module. Can be used like any regularly imported module.
164-
Actual loading of the module occurs upon first attribute request.
165-
166-
"""
167-
try:
168-
return sys.modules[fullname]
169-
except KeyError:
170-
pass
171-
172-
spec = importlib.util.find_spec(fullname)
173-
if spec is None:
174-
if error_on_import:
175-
raise ModuleNotFoundError(f"No module named '{fullname}'")
176-
else:
177-
try:
178-
parent = inspect.stack()[1]
179-
frame_data = {
180-
"spec": fullname,
181-
"filename": parent.filename,
182-
"lineno": parent.lineno,
183-
"function": parent.function,
184-
"code_context": parent.code_context,
185-
}
186-
return DelayedImportErrorModule(frame_data, "DelayedImportErrorModule")
187-
finally:
188-
del parent
189-
190-
module = importlib.util.module_from_spec(spec)
191-
sys.modules[fullname] = module
192-
193-
loader = importlib.util.LazyLoader(spec.loader)
194-
loader.exec_module(module)
195-
196-
return module
197-
198-
199-
class _StubVisitor(ast.NodeVisitor):
200-
"""AST visitor to parse a stub file for submodules and submod_attrs."""
201-
202-
def __init__(self):
203-
self._submodules = set()
204-
self._submod_attrs = {}
205-
206-
def visit_ImportFrom(self, node: ast.ImportFrom):
207-
if node.level != 1:
208-
raise ValueError(
209-
"Only within-module imports are supported (`from .* import`)"
210-
)
211-
if node.module:
212-
attrs: list = self._submod_attrs.setdefault(node.module, [])
213-
attrs.extend(alias.name for alias in node.names)
214-
else:
215-
self._submodules.update(alias.name for alias in node.names)
216-
217-
218-
def attach_stub(package_name: str, filename: str):
219-
"""Attach lazily loaded submodules, functions from a type stub.
220-
221-
This is a variant on ``attach`` that will parse a `.pyi` stub file to
222-
infer ``submodules`` and ``submod_attrs``. This allows static type checkers
223-
to find imports, while still providing lazy loading at runtime.
224-
225-
Parameters
226-
----------
227-
package_name : str
228-
Typically use ``__name__``.
229-
filename : str
230-
Path to `.py` file which has an adjacent `.pyi` file.
231-
Typically use ``__file__``.
232-
233-
Returns
234-
-------
235-
__getattr__, __dir__, __all__
236-
The same output as ``attach``.
237-
238-
Raises
239-
------
240-
ValueError
241-
If a stub file is not found for `filename`, or if the stubfile is formmated
242-
incorrectly (e.g. if it contains an relative import from outside of the module)
243-
"""
244-
stubfile = filename if filename.endswith("i") else f"{filename}i"
245-
246-
if not os.path.exists(stubfile):
247-
raise ValueError(f"Cannot load imports from non-existent stub {stubfile!r}")
248-
249-
with open(stubfile) as f:
250-
stub_node = ast.parse(f.read())
251-
252-
visitor = _StubVisitor()
253-
visitor.visit(stub_node)
254-
return attach(package_name, visitor._submodules, visitor._submod_attrs)
13+
__all__ = ["lazy_loader"]
25514

25615

25716
class LazyFinder:

tests/test_lazy_loader.py

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import pytest
55

6-
import lazy_loader as lazy
76
from lazy_loader import lazy_import
87

98

@@ -58,35 +57,6 @@ def test_non_builtin_is_in_sys_modules():
5857
assert "numpy" in sys.modules
5958

6059

61-
def test_lazy_attach():
62-
name = "mymod"
63-
submods = ["mysubmodule", "anothersubmodule"]
64-
myall = {"not_real_submod": ["some_var_or_func"]}
65-
66-
locls = {
67-
"attach": lazy.attach,
68-
"name": name,
69-
"submods": submods,
70-
"myall": myall,
71-
}
72-
s = "__getattr__, __lazy_dir__, __all__ = attach(name, submods, myall)"
73-
74-
exec(s, {}, locls)
75-
expected = {
76-
"attach": lazy.attach,
77-
"name": name,
78-
"submods": submods,
79-
"myall": myall,
80-
"__getattr__": None,
81-
"__lazy_dir__": None,
82-
"__all__": None,
83-
}
84-
assert locls.keys() == expected.keys()
85-
for k, v in expected.items():
86-
if v is not None:
87-
assert locls[k] == v
88-
89-
9060
def test_attach_same_module_and_attr_name():
9161
import fake_pkg
9262

@@ -99,39 +69,3 @@ def test_attach_same_module_and_attr_name():
9969
from fake_pkg.some_func import some_func
10070

10171
assert isinstance(some_func, types.FunctionType)
102-
103-
104-
FAKE_STUB = """
105-
from . import rank
106-
from ._gaussian import gaussian
107-
from .edges import sobel, scharr, prewitt, roberts
108-
"""
109-
110-
111-
def test_stub_loading(tmp_path):
112-
stub = tmp_path / "stub.pyi"
113-
stub.write_text(FAKE_STUB)
114-
_get, _dir, _all = lazy.attach_stub("my_module", str(stub))
115-
expect = {"gaussian", "sobel", "scharr", "prewitt", "roberts", "rank"}
116-
assert set(_dir()) == set(_all) == expect
117-
118-
119-
def test_stub_loading_parity():
120-
import fake_pkg
121-
122-
from_stub = lazy.attach_stub(fake_pkg.__name__, fake_pkg.__file__)
123-
stub_getter, stub_dir, stub_all = from_stub
124-
assert stub_all == fake_pkg.__all__
125-
assert stub_dir() == fake_pkg.__lazy_dir__()
126-
assert stub_getter("some_func") == fake_pkg.some_func
127-
128-
129-
def test_stub_loading_errors(tmp_path):
130-
stub = tmp_path / "stub.pyi"
131-
stub.write_text("from ..mod import func\n")
132-
133-
with pytest.raises(ValueError, match="Only within-module imports are supported"):
134-
lazy.attach_stub("name", str(stub))
135-
136-
with pytest.raises(ValueError, match="Cannot load imports from non-existent stub"):
137-
lazy.attach_stub("name", "not a file")

0 commit comments

Comments
 (0)