55 self.loader.exec_module(module) |
53 self.loader.exec_module(module) |
56 else: |
54 else: |
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 ) |
|
63 _bytecode_loader = _lazyloaderex.factory( |
|
64 importlib.machinery.SourcelessFileLoader |
|
65 ) |
|
66 _source_loader = _lazyloaderex.factory(importlib.machinery.SourceFileLoader) |
|
67 |
60 |
|
61 ``sys.meta_path`` finders have their ``find_spec()`` called to locate a |
|
62 module. This returns a ``ModuleSpec`` if found or ``None``. The |
|
63 ``ModuleSpec`` has a ``loader`` attribute, which is called to actually |
|
64 load a module. |
68 |
65 |
69 def _makefinder(path): |
66 Our class wraps an existing finder and overloads its ``find_spec()`` to |
70 return importlib.machinery.FileFinder( |
67 replace the ``loader`` with our lazy loader proxy. |
71 path, |
68 |
72 # This is the order in which loaders are passed in in core Python. |
69 We have to use __getattribute__ to proxy the instance because some meta |
73 (_extensions_loader, importlib.machinery.EXTENSION_SUFFIXES), |
70 path finders don't support monkeypatching. |
74 (_source_loader, importlib.machinery.SOURCE_SUFFIXES), |
71 """ |
75 (_bytecode_loader, importlib.machinery.BYTECODE_SUFFIXES), |
72 |
76 ) |
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")) |
|
85 |
|
86 __bool__ = __nonzero__ |
|
87 |
|
88 def __getattribute__(self, name): |
|
89 if name in ("_finder", "find_spec"): |
|
90 return object.__getattribute__(self, name) |
|
91 |
|
92 return getattr(object.__getattribute__(self, "_finder"), name) |
|
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() |
80 |
116 |
81 |
117 |
83 global ignores |
119 global ignores |
84 ignores = ignoreset |
120 ignores = 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 |
107 def deactivated(): |
151 def deactivated(): |
108 # This implementation is a bit different from Python 2's. Python 3 |
152 # This implementation is a bit different from Python 2's. Python 3 |