summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan van der Walt <stefanv@berkeley.edu>2022-03-03 12:55:12 -0800
committerGitHub <noreply@github.com>2022-03-03 12:55:12 -0800
commit8feb4a645c93598bb187bf1707beb673fa27915e (patch)
tree859978a7f07974fd7b404974007ced0810e3cc73
parent2b01a30d6967cc94a0f8caca2252bce7817b2b1c (diff)
downloadnetworkx-8feb4a645c93598bb187bf1707beb673fa27915e.tar.gz
Simplify DelayedImportErrorModule (#5371)
* Simplify DelayedImportErrorModule * Only allow very restrictive dunder access on DelayedImportErrorModule * Improve lazy loading exception
-rw-r--r--networkx/lazy_imports.py60
1 files changed, 31 insertions, 29 deletions
diff --git a/networkx/lazy_imports.py b/networkx/lazy_imports.py
index 248f3b43..9a88ed6d 100644
--- a/networkx/lazy_imports.py
+++ b/networkx/lazy_imports.py
@@ -1,5 +1,6 @@
import importlib
import importlib.util
+import inspect
import types
import os
import sys
@@ -81,6 +82,24 @@ def attach(module_name, submodules=None, submod_attrs=None):
return __getattr__, __dir__, list(__all__)
+class DelayedImportErrorModule(types.ModuleType):
+ def __init__(self, frame_data, *args, **kwargs):
+ self.__frame_data = frame_data
+ super().__init__(*args, **kwargs)
+
+ def __getattr__(self, x):
+ if x in ("__class__", "__file__", "__frame_data"):
+ super().__getattr__(x)
+ else:
+ fd = self.__frame_data
+ raise ModuleNotFoundError(
+ f"No module named '{fd['spec']}'\n\n"
+ "This error is lazily reported, having originally occured in\n"
+ f' File {fd["filename"]}, line {fd["lineno"]}, in {fd["function"]}\n\n'
+ f'----> {"".join(fd["code_context"]).strip()}'
+ )
+
+
def lazy_import(fullname):
"""Return a lazily imported proxy for a module or library.
@@ -132,14 +151,18 @@ def lazy_import(fullname):
spec = importlib.util.find_spec(fullname)
if spec is None:
- # module not found - construct a DelayedImportErrorModule
- spec = importlib.util.spec_from_loader(fullname, loader=None)
- module = importlib.util.module_from_spec(spec)
- tmp_loader = importlib.machinery.SourceFileLoader(module, path=None)
- loader = DelayedImportErrorLoader(tmp_loader)
- loader.exec_module(module)
- # dont add to sys.modules. The module wasn't found.
- return module
+ try:
+ parent = inspect.stack()[1]
+ frame_data = {
+ "spec": fullname,
+ "filename": parent.filename,
+ "lineno": parent.lineno,
+ "function": parent.function,
+ "code_context": parent.code_context,
+ }
+ return DelayedImportErrorModule(frame_data, "DelayedImportErrorModule")
+ finally:
+ del parent
module = importlib.util.module_from_spec(spec)
sys.modules[fullname] = module
@@ -148,24 +171,3 @@ def lazy_import(fullname):
loader.exec_module(module)
return module
-
-
-class DelayedImportErrorLoader(importlib.util.LazyLoader):
- def exec_module(self, module):
- super().exec_module(module)
- module.__class__ = DelayedImportErrorModule
-
-
-class DelayedImportErrorModule(types.ModuleType):
- def __getattribute__(self, attr):
- """Trigger a ModuleNotFoundError upon attribute access"""
- spec = super().__getattribute__("__spec__")
- # allows isinstance and type functions to work without raising error
- if attr in ["__class__"]:
- return super().__getattribute__("__class__")
-
- raise ModuleNotFoundError(
- f"Delayed Report: module named '{spec.name}' not found.\n"
- "Reporting was Lazy -- delayed until module attributes accessed.\n"
- f"Most likely, {spec.name} is not installed"
- )