##// END OF EJS Templates
hgdemandimport: apply lazy module loading to sys.meta_path finders...
Gregory Szorc -
r44577:f81c17ec default
parent child Browse files
Show More
@@ -27,8 +27,6 b' This also has some limitations compared '
27 from __future__ import absolute_import
27 from __future__ import absolute_import
28
28
29 import contextlib
29 import contextlib
30 import importlib.abc
31 import importlib.machinery
32 import importlib.util
30 import importlib.util
33 import sys
31 import sys
34
32
@@ -57,23 +55,61 b' class _lazyloaderex(importlib.util.LazyL'
57 super().exec_module(module)
55 super().exec_module(module)
58
56
59
57
60 _extensions_loader = _lazyloaderex.factory(
58 class LazyFinder(object):
61 importlib.machinery.ExtensionFileLoader
59 """A wrapper around a ``MetaPathFinder`` that makes loaders lazy.
62 )
60
63 _bytecode_loader = _lazyloaderex.factory(
61 ``sys.meta_path`` finders have their ``find_spec()`` called to locate a
64 importlib.machinery.SourcelessFileLoader
62 module. This returns a ``ModuleSpec`` if found or ``None``. The
65 )
63 ``ModuleSpec`` has a ``loader`` attribute, which is called to actually
66 _source_loader = _lazyloaderex.factory(importlib.machinery.SourceFileLoader)
64 load a module.
65
66 Our class wraps an existing finder and overloads its ``find_spec()`` to
67 replace the ``loader`` with our lazy loader proxy.
67
68
69 We have to use __getattribute__ to proxy the instance because some meta
70 path finders don't support monkeypatching.
71 """
72
73 __slots__ = ("_finder",)
74
75 def __init__(self, finder):
76 object.__setattr__(self, "_finder", finder)
77
78 def __repr__(self):
79 return "<LazyFinder for %r>" % object.__getattribute__(self, "_finder")
80
81 # __bool__ is canonical Python 3. But check-code insists on __nonzero__ being
82 # defined via `def`.
83 def __nonzero__(self):
84 return bool(object.__getattribute__(self, "_finder"))
68
85
69 def _makefinder(path):
86 __bool__ = __nonzero__
70 return importlib.machinery.FileFinder(
87
71 path,
88 def __getattribute__(self, name):
72 # This is the order in which loaders are passed in in core Python.
89 if name in ("_finder", "find_spec"):
73 (_extensions_loader, importlib.machinery.EXTENSION_SUFFIXES),
90 return object.__getattribute__(self, name)
74 (_source_loader, importlib.machinery.SOURCE_SUFFIXES),
91
75 (_bytecode_loader, importlib.machinery.BYTECODE_SUFFIXES),
92 return getattr(object.__getattribute__(self, "_finder"), name)
76 )
93
94 def __delattr__(self, name):
95 return delattr(object.__getattribute__(self, "_finder"))
96
97 def __setattr__(self, name, value):
98 return setattr(object.__getattribute__(self, "_finder"), name, value)
99
100 def find_spec(self, *args, **kwargs):
101 finder = object.__getattribute__(self, "_finder")
102 spec = finder.find_spec(*args, **kwargs)
103
104 # Lazy loader requires exec_module().
105 if (
106 spec is not None
107 and spec.loader is not None
108 and getattr(spec.loader, "exec_module")
109 ):
110 spec.loader = _lazyloaderex(spec.loader)
111
112 return spec
77
113
78
114
79 ignores = set()
115 ignores = set()
@@ -85,22 +121,30 b' def init(ignoreset):'
85
121
86
122
87 def isenabled():
123 def isenabled():
88 return _makefinder in sys.path_hooks and not _deactivated
124 return not _deactivated and any(
125 isinstance(finder, LazyFinder) for finder in sys.meta_path
126 )
89
127
90
128
91 def disable():
129 def disable():
92 try:
130 new_finders = []
93 while True:
131 for finder in sys.meta_path:
94 sys.path_hooks.remove(_makefinder)
132 new_finders.append(
95 except ValueError:
133 finder._finder if isinstance(finder, LazyFinder) else finder
96 pass
134 )
135 sys.meta_path[:] = new_finders
97
136
98
137
99 def enable():
138 def enable():
100 if not _supported:
139 if not _supported:
101 return
140 return
102
141
103 sys.path_hooks.insert(0, _makefinder)
142 new_finders = []
143 for finder in sys.meta_path:
144 new_finders.append(
145 LazyFinder(finder) if not isinstance(finder, LazyFinder) else finder
146 )
147 sys.meta_path[:] = new_finders
104
148
105
149
106 @contextlib.contextmanager
150 @contextlib.contextmanager
@@ -137,7 +137,7 b" assert 'mercurial.hgweb' not in sys.modu"
137 from mercurial import hgweb
137 from mercurial import hgweb
138
138
139 if ispy3:
139 if ispy3:
140 assert not isinstance(hgweb, _LazyModule)
140 assert isinstance(hgweb, _LazyModule)
141 assert f(hgweb) == "<module 'mercurial.hgweb' from '?'>", f(hgweb)
141 assert f(hgweb) == "<module 'mercurial.hgweb' from '?'>", f(hgweb)
142 assert isinstance(hgweb.hgweb_mod, _LazyModule)
142 assert isinstance(hgweb.hgweb_mod, _LazyModule)
143 assert (
143 assert (
@@ -210,7 +210,7 b" assert 'telnetlib' not in sys.modules"
210 import telnetlib
210 import telnetlib
211
211
212 if ispy3:
212 if ispy3:
213 assert not isinstance(telnetlib, _LazyModule)
213 assert isinstance(telnetlib, _LazyModule)
214 assert f(telnetlib) == "<module 'telnetlib' from '?'>"
214 assert f(telnetlib) == "<module 'telnetlib' from '?'>"
215 else:
215 else:
216 assert f(telnetlib) == "<unloaded module 'telnetlib'>", f(telnetlib)
216 assert f(telnetlib) == "<unloaded module 'telnetlib'>", f(telnetlib)
General Comments 0
You need to be logged in to leave comments. Login now