D7954: hgdemandimport: apply lazy module loading to sys.meta_path finders
indygreg (Gregory Szorc)
phabricator at mercurial-scm.org
Tue Jan 21 11:26:11 EST 2020
Closed by commit rHGf81c17ec303c: hgdemandimport: apply lazy module loading to sys.meta_path finders (authored by indygreg).
This revision was automatically updated to reflect the committed changes.
REPOSITORY
rHG Mercurial
CHANGES SINCE LAST UPDATE
https://phab.mercurial-scm.org/D7954?vs=19461&id=19477
CHANGES SINCE LAST ACTION
https://phab.mercurial-scm.org/D7954/new/
REVISION DETAIL
https://phab.mercurial-scm.org/D7954
AFFECTED FILES
hgdemandimport/demandimportpy3.py
tests/test-demandimport.py
CHANGE DETAILS
diff --git a/tests/test-demandimport.py b/tests/test-demandimport.py
--- a/tests/test-demandimport.py
+++ b/tests/test-demandimport.py
@@ -137,7 +137,7 @@
from mercurial import hgweb
if ispy3:
- assert not isinstance(hgweb, _LazyModule)
+ assert isinstance(hgweb, _LazyModule)
assert f(hgweb) == "<module 'mercurial.hgweb' from '?'>", f(hgweb)
assert isinstance(hgweb.hgweb_mod, _LazyModule)
assert (
@@ -210,7 +210,7 @@
import telnetlib
if ispy3:
- assert not isinstance(telnetlib, _LazyModule)
+ assert isinstance(telnetlib, _LazyModule)
assert f(telnetlib) == "<module 'telnetlib' from '?'>"
else:
assert f(telnetlib) == "<unloaded module 'telnetlib'>", f(telnetlib)
diff --git a/hgdemandimport/demandimportpy3.py b/hgdemandimport/demandimportpy3.py
--- a/hgdemandimport/demandimportpy3.py
+++ b/hgdemandimport/demandimportpy3.py
@@ -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
To: indygreg, #hg-reviewers, pulkit
Cc: mercurial-devel
More information about the Mercurial-devel
mailing list