about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/networkx/lazy_imports.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/lazy_imports.py')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/lazy_imports.py188
1 files changed, 188 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/lazy_imports.py b/.venv/lib/python3.12/site-packages/networkx/lazy_imports.py
new file mode 100644
index 00000000..396404ba
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/lazy_imports.py
@@ -0,0 +1,188 @@
+import importlib
+import importlib.util
+import inspect
+import os
+import sys
+import types
+
+__all__ = ["attach", "_lazy_import"]
+
+
+def attach(module_name, submodules=None, submod_attrs=None):
+    """Attach lazily loaded submodules, and functions or other attributes.
+
+    Typically, modules import submodules and attributes as follows::
+
+      import mysubmodule
+      import anothersubmodule
+
+      from .foo import someattr
+
+    The idea of  this function is to replace the `__init__.py`
+    module's `__getattr__`, `__dir__`, and `__all__` attributes such that
+    all imports work exactly the way they normally would, except that the
+    actual import is delayed until the resulting module object is first used.
+
+    The typical way to call this function, replacing the above imports, is::
+
+      __getattr__, __lazy_dir__, __all__ = lazy.attach(
+          __name__, ["mysubmodule", "anothersubmodule"], {"foo": "someattr"}
+      )
+
+    This functionality requires Python 3.7 or higher.
+
+    Parameters
+    ----------
+    module_name : str
+        Typically use __name__.
+    submodules : set
+        List of submodules to lazily import.
+    submod_attrs : dict
+        Dictionary of submodule -> list of attributes / functions.
+        These attributes are imported as they are used.
+
+    Returns
+    -------
+    __getattr__, __dir__, __all__
+
+    """
+    if submod_attrs is None:
+        submod_attrs = {}
+
+    if submodules is None:
+        submodules = set()
+    else:
+        submodules = set(submodules)
+
+    attr_to_modules = {
+        attr: mod for mod, attrs in submod_attrs.items() for attr in attrs
+    }
+
+    __all__ = list(submodules | attr_to_modules.keys())
+
+    def __getattr__(name):
+        if name in submodules:
+            return importlib.import_module(f"{module_name}.{name}")
+        elif name in attr_to_modules:
+            submod = importlib.import_module(f"{module_name}.{attr_to_modules[name]}")
+            return getattr(submod, name)
+        else:
+            raise AttributeError(f"No {module_name} attribute {name}")
+
+    def __dir__():
+        return __all__
+
+    if os.environ.get("EAGER_IMPORT", ""):
+        for attr in set(attr_to_modules.keys()) | submodules:
+            __getattr__(attr)
+
+    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 occurred in\n"
+                f'  File {fd["filename"]}, line {fd["lineno"]}, in {fd["function"]}\n\n'
+                f'----> {"".join(fd["code_context"] or "").strip()}'
+            )
+
+
+def _lazy_import(fullname):
+    """Return a lazily imported proxy for a module or library.
+
+    Warning
+    -------
+    Importing using this function can currently cause trouble
+    when the user tries to import from a subpackage of a module before
+    the package is fully imported. In particular, this idiom may not work:
+
+      np = lazy_import("numpy")
+      from numpy.lib import recfunctions
+
+    This is due to a difference in the way Python's LazyLoader handles
+    subpackage imports compared to the normal import process. Hopefully
+    we will get Python's LazyLoader to fix this, or find a workaround.
+    In the meantime, this is a potential problem.
+
+    The workaround is to import numpy before importing from the subpackage.
+
+    Notes
+    -----
+    We often see the following pattern::
+
+      def myfunc():
+          import scipy as sp
+          sp.argmin(...)
+          ....
+
+    This is to prevent a library, in this case `scipy`, from being
+    imported at function definition time, since that can be slow.
+
+    This function provides a proxy module that, upon access, imports
+    the actual module.  So the idiom equivalent to the above example is::
+
+      sp = lazy.load("scipy")
+
+      def myfunc():
+          sp.argmin(...)
+          ....
+
+    The initial import time is fast because the actual import is delayed
+    until the first attribute is requested. The overall import time may
+    decrease as well for users that don't make use of large portions
+    of the library.
+
+    Parameters
+    ----------
+    fullname : str
+        The full name of the package or subpackage to import.  For example::
+
+          sp = lazy.load("scipy")  # import scipy as sp
+          spla = lazy.load("scipy.linalg")  # import scipy.linalg as spla
+
+    Returns
+    -------
+    pm : importlib.util._LazyModule
+        Proxy module. Can be used like any regularly imported module.
+        Actual loading of the module occurs upon first attribute request.
+
+    """
+    try:
+        return sys.modules[fullname]
+    except:
+        pass
+
+    # Not previously loaded -- look it up
+    spec = importlib.util.find_spec(fullname)
+
+    if spec is None:
+        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
+
+    loader = importlib.util.LazyLoader(spec.loader)
+    loader.exec_module(module)
+
+    return module