##// END OF EJS Templates
hgdemandimport: delete check for Python 3.5...
Gregory Szorc -
r49813:b8eb29ab default
parent child Browse files
Show More
@@ -1,183 +1,174 b''
1 1 # demandimportpy3 - global demand-loading of modules for Mercurial
2 2 #
3 3 # Copyright 2017 Facebook Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 """Lazy loading for Python 3.6 and above.
9 9
10 10 This uses the new importlib finder/loader functionality available in Python 3.5
11 11 and up. The code reuses most of the mechanics implemented inside importlib.util,
12 12 but with a few additions:
13 13
14 14 * Allow excluding certain modules from lazy imports.
15 15 * Expose an interface that's substantially the same as demandimport for
16 16 Python 2.
17 17
18 18 This also has some limitations compared to the Python 2 implementation:
19 19
20 20 * Much of the logic is per-package, not per-module, so any packages loaded
21 21 before demandimport is enabled will not be lazily imported in the future. In
22 22 practice, we only expect builtins to be loaded before demandimport is
23 23 enabled.
24 24 """
25 25
26 26 # This line is unnecessary, but it satisfies test-check-py3-compat.t.
27 27
28 28 import contextlib
29 29 import importlib.util
30 30 import sys
31 31
32 32 from . import tracing
33 33
34 34 _deactivated = False
35 35
36 # Python 3.5's LazyLoader doesn't work for some reason.
37 # https://bugs.python.org/issue26186 is a known issue with extension
38 # importing. But it appears to not have a meaningful effect with
39 # Mercurial.
40 _supported = sys.version_info[0:2] >= (3, 6)
41
42 36
43 37 class _lazyloaderex(importlib.util.LazyLoader):
44 38 """This is a LazyLoader except it also follows the _deactivated global and
45 39 the ignore list.
46 40 """
47 41
48 42 def exec_module(self, module):
49 43 """Make the module load lazily."""
50 44 with tracing.log('demandimport %s', module):
51 45 if _deactivated or module.__name__ in ignores:
52 46 self.loader.exec_module(module)
53 47 else:
54 48 super().exec_module(module)
55 49
56 50
57 51 class LazyFinder:
58 52 """A wrapper around a ``MetaPathFinder`` that makes loaders lazy.
59 53
60 54 ``sys.meta_path`` finders have their ``find_spec()`` called to locate a
61 55 module. This returns a ``ModuleSpec`` if found or ``None``. The
62 56 ``ModuleSpec`` has a ``loader`` attribute, which is called to actually
63 57 load a module.
64 58
65 59 Our class wraps an existing finder and overloads its ``find_spec()`` to
66 60 replace the ``loader`` with our lazy loader proxy.
67 61
68 62 We have to use __getattribute__ to proxy the instance because some meta
69 63 path finders don't support monkeypatching.
70 64 """
71 65
72 66 __slots__ = ("_finder",)
73 67
74 68 def __init__(self, finder):
75 69 object.__setattr__(self, "_finder", finder)
76 70
77 71 def __repr__(self):
78 72 return "<LazyFinder for %r>" % object.__getattribute__(self, "_finder")
79 73
80 74 # __bool__ is canonical Python 3. But check-code insists on __nonzero__ being
81 75 # defined via `def`.
82 76 def __nonzero__(self):
83 77 return bool(object.__getattribute__(self, "_finder"))
84 78
85 79 __bool__ = __nonzero__
86 80
87 81 def __getattribute__(self, name):
88 82 if name in ("_finder", "find_spec"):
89 83 return object.__getattribute__(self, name)
90 84
91 85 return getattr(object.__getattribute__(self, "_finder"), name)
92 86
93 87 def __delattr__(self, name):
94 88 return delattr(object.__getattribute__(self, "_finder"))
95 89
96 90 def __setattr__(self, name, value):
97 91 return setattr(object.__getattribute__(self, "_finder"), name, value)
98 92
99 93 def find_spec(self, fullname, path, target=None):
100 94 finder = object.__getattribute__(self, "_finder")
101 95 try:
102 96 find_spec = finder.find_spec
103 97 except AttributeError:
104 98 loader = finder.find_module(fullname, path)
105 99 if loader is None:
106 100 spec = None
107 101 else:
108 102 spec = importlib.util.spec_from_loader(fullname, loader)
109 103 else:
110 104 spec = find_spec(fullname, path, target)
111 105
112 106 # Lazy loader requires exec_module().
113 107 if (
114 108 spec is not None
115 109 and spec.loader is not None
116 110 and getattr(spec.loader, "exec_module", None)
117 111 ):
118 112 spec.loader = _lazyloaderex(spec.loader)
119 113
120 114 return spec
121 115
122 116
123 117 ignores = set()
124 118
125 119
126 120 def init(ignoreset):
127 121 global ignores
128 122 ignores = ignoreset
129 123
130 124
131 125 def isenabled():
132 126 return not _deactivated and any(
133 127 isinstance(finder, LazyFinder) for finder in sys.meta_path
134 128 )
135 129
136 130
137 131 def disable():
138 132 new_finders = []
139 133 for finder in sys.meta_path:
140 134 new_finders.append(
141 135 finder._finder if isinstance(finder, LazyFinder) else finder
142 136 )
143 137 sys.meta_path[:] = new_finders
144 138
145 139
146 140 def enable():
147 if not _supported:
148 return
149
150 141 new_finders = []
151 142 for finder in sys.meta_path:
152 143 new_finders.append(
153 144 LazyFinder(finder) if not isinstance(finder, LazyFinder) else finder
154 145 )
155 146 sys.meta_path[:] = new_finders
156 147
157 148
158 149 @contextlib.contextmanager
159 150 def deactivated():
160 151 # This implementation is a bit different from Python 2's. Python 3
161 152 # maintains a per-package finder cache in sys.path_importer_cache (see
162 153 # PEP 302). This means that we can't just call disable + enable.
163 154 # If we do that, in situations like:
164 155 #
165 156 # demandimport.enable()
166 157 # ...
167 158 # from foo.bar import mod1
168 159 # with demandimport.deactivated():
169 160 # from foo.bar import mod2
170 161 #
171 162 # mod2 will be imported lazily. (The converse also holds -- whatever finder
172 163 # first gets cached will be used.)
173 164 #
174 165 # Instead, have a global flag the LazyLoader can use.
175 166 global _deactivated
176 167 demandenabled = isenabled()
177 168 if demandenabled:
178 169 _deactivated = True
179 170 try:
180 171 yield
181 172 finally:
182 173 if demandenabled:
183 174 _deactivated = False
General Comments 0
You need to be logged in to leave comments. Login now