--- a/hgdemandimport/demandimportpy3.py Mon Jan 20 23:42:19 2020 -0800
+++ b/hgdemandimport/demandimportpy3.py Mon Jan 20 23:51:25 2020 -0800
@@ -27,8 +27,6 @@
from __future__ import absolute_import
import contextlib
-import importlib.abc
-import importlib.machinery
import importlib.util
import sys
@@ -57,23 +55,61 @@
super().exec_module(module)
-_extensions_loader = _lazyloaderex.factory(
- importlib.machinery.ExtensionFileLoader
-)
-_bytecode_loader = _lazyloaderex.factory(
- importlib.machinery.SourcelessFileLoader
-)
-_source_loader = _lazyloaderex.factory(importlib.machinery.SourceFileLoader)
+class LazyFinder(object):
+ """A wrapper around a ``MetaPathFinder`` that makes loaders lazy.
+
+ ``sys.meta_path`` finders have their ``find_spec()`` called to locate a
+ module. This returns a ``ModuleSpec`` if found or ``None``. The
+ ``ModuleSpec`` has a ``loader`` attribute, which is called to actually
+ load a module.
+
+ Our class wraps an existing finder and overloads its ``find_spec()`` to
+ replace the ``loader`` with our lazy loader proxy.
+ We have to use __getattribute__ to proxy the instance because some meta
+ path finders don't support monkeypatching.
+ """
+
+ __slots__ = ("_finder",)
+
+ def __init__(self, finder):
+ object.__setattr__(self, "_finder", finder)
+
+ def __repr__(self):
+ return "<LazyFinder for %r>" % object.__getattribute__(self, "_finder")
+
+ # __bool__ is canonical Python 3. But check-code insists on __nonzero__ being
+ # defined via `def`.
+ def __nonzero__(self):
+ return bool(object.__getattribute__(self, "_finder"))
-def _makefinder(path):
- return importlib.machinery.FileFinder(
- path,
- # This is the order in which loaders are passed in in core Python.
- (_extensions_loader, importlib.machinery.EXTENSION_SUFFIXES),
- (_source_loader, importlib.machinery.SOURCE_SUFFIXES),
- (_bytecode_loader, importlib.machinery.BYTECODE_SUFFIXES),
- )
+ __bool__ = __nonzero__
+
+ def __getattribute__(self, name):
+ if name in ("_finder", "find_spec"):
+ return object.__getattribute__(self, name)
+
+ return getattr(object.__getattribute__(self, "_finder"), name)
+
+ def __delattr__(self, name):
+ return delattr(object.__getattribute__(self, "_finder"))
+
+ def __setattr__(self, name, value):
+ return setattr(object.__getattribute__(self, "_finder"), name, value)
+
+ def find_spec(self, *args, **kwargs):
+ finder = object.__getattribute__(self, "_finder")
+ spec = finder.find_spec(*args, **kwargs)
+
+ # Lazy loader requires exec_module().
+ if (
+ spec is not None
+ and spec.loader is not None
+ and getattr(spec.loader, "exec_module")
+ ):
+ spec.loader = _lazyloaderex(spec.loader)
+
+ return spec
ignores = set()
@@ -85,22 +121,30 @@
def isenabled():
- return _makefinder in sys.path_hooks and not _deactivated
+ return not _deactivated and any(
+ isinstance(finder, LazyFinder) for finder in sys.meta_path
+ )
def disable():
- try:
- while True:
- sys.path_hooks.remove(_makefinder)
- except ValueError:
- pass
+ new_finders = []
+ for finder in sys.meta_path:
+ new_finders.append(
+ finder._finder if isinstance(finder, LazyFinder) else finder
+ )
+ sys.meta_path[:] = new_finders
def enable():
if not _supported:
return
- sys.path_hooks.insert(0, _makefinder)
+ new_finders = []
+ for finder in sys.meta_path:
+ new_finders.append(
+ LazyFinder(finder) if not isinstance(finder, LazyFinder) else finder
+ )
+ sys.meta_path[:] = new_finders
@contextlib.contextmanager