demandimportpy3.py
124 lines
| 3.5 KiB
| text/x-python
|
PythonLexer
/ hgdemandimport / demandimportpy3.py
Siddharth Agarwal
|
r32423 | # demandimportpy3 - global demand-loading of modules for Mercurial | ||
# | ||||
# Copyright 2017 Facebook Inc. | ||||
# | ||||
# This software may be used and distributed according to the terms of the | ||||
# GNU General Public License version 2 or any later version. | ||||
"""Lazy loading for Python 3.6 and above. | ||||
This uses the new importlib finder/loader functionality available in Python 3.5 | ||||
and up. The code reuses most of the mechanics implemented inside importlib.util, | ||||
but with a few additions: | ||||
* Allow excluding certain modules from lazy imports. | ||||
* Expose an interface that's substantially the same as demandimport for | ||||
Python 2. | ||||
This also has some limitations compared to the Python 2 implementation: | ||||
* Much of the logic is per-package, not per-module, so any packages loaded | ||||
before demandimport is enabled will not be lazily imported in the future. In | ||||
practice, we only expect builtins to be loaded before demandimport is | ||||
enabled. | ||||
""" | ||||
# This line is unnecessary, but it satisfies test-check-py3-compat.t. | ||||
from __future__ import absolute_import | ||||
import contextlib | ||||
import importlib.abc | ||||
import importlib.machinery | ||||
import importlib.util | ||||
Augie Fackler
|
r33899 | import sys | ||
Siddharth Agarwal
|
r32423 | |||
Augie Fackler
|
r42674 | from . import tracing | ||
Siddharth Agarwal
|
r32423 | _deactivated = False | ||
Augie Fackler
|
r43346 | |||
Siddharth Agarwal
|
r32423 | class _lazyloaderex(importlib.util.LazyLoader): | ||
"""This is a LazyLoader except it also follows the _deactivated global and | ||||
the ignore list. | ||||
""" | ||||
Augie Fackler
|
r43346 | |||
Siddharth Agarwal
|
r32423 | def exec_module(self, module): | ||
"""Make the module load lazily.""" | ||||
Augie Fackler
|
r42674 | with tracing.log('demandimport %s', module): | ||
if _deactivated or module.__name__ in ignores: | ||||
self.loader.exec_module(module) | ||||
else: | ||||
super().exec_module(module) | ||||
Siddharth Agarwal
|
r32423 | |||
Augie Fackler
|
r43346 | |||
Siddharth Agarwal
|
r32423 | # This is 3.6+ because with Python 3.5 it isn't possible to lazily load | ||
Pulkit Goyal
|
r35542 | # extensions. See the discussion in https://bugs.python.org/issue26186 for more. | ||
Siddharth Agarwal
|
r32423 | _extensions_loader = _lazyloaderex.factory( | ||
Augie Fackler
|
r43346 | importlib.machinery.ExtensionFileLoader | ||
) | ||||
Siddharth Agarwal
|
r32423 | _bytecode_loader = _lazyloaderex.factory( | ||
Augie Fackler
|
r43346 | importlib.machinery.SourcelessFileLoader | ||
) | ||||
Siddharth Agarwal
|
r32423 | _source_loader = _lazyloaderex.factory(importlib.machinery.SourceFileLoader) | ||
Augie Fackler
|
r43346 | |||
Siddharth Agarwal
|
r32423 | 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), | ||||
) | ||||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37862 | ignores = set() | ||
Siddharth Agarwal
|
r32423 | |||
Augie Fackler
|
r43346 | |||
Gregory Szorc
|
r37862 | def init(ignoreset): | ||
global ignores | ||||
ignores = ignoreset | ||||
Siddharth Agarwal
|
r32423 | |||
Augie Fackler
|
r43346 | |||
Siddharth Agarwal
|
r32423 | def isenabled(): | ||
return _makefinder in sys.path_hooks and not _deactivated | ||||
Augie Fackler
|
r43346 | |||
Siddharth Agarwal
|
r32423 | def disable(): | ||
try: | ||||
while True: | ||||
sys.path_hooks.remove(_makefinder) | ||||
except ValueError: | ||||
pass | ||||
Augie Fackler
|
r43346 | |||
Siddharth Agarwal
|
r32423 | def enable(): | ||
Jun Wu
|
r33861 | sys.path_hooks.insert(0, _makefinder) | ||
Siddharth Agarwal
|
r32423 | |||
Augie Fackler
|
r43346 | |||
Siddharth Agarwal
|
r32423 | @contextlib.contextmanager | ||
def deactivated(): | ||||
# This implementation is a bit different from Python 2's. Python 3 | ||||
# maintains a per-package finder cache in sys.path_importer_cache (see | ||||
# PEP 302). This means that we can't just call disable + enable. | ||||
# If we do that, in situations like: | ||||
# | ||||
# demandimport.enable() | ||||
# ... | ||||
# from foo.bar import mod1 | ||||
# with demandimport.deactivated(): | ||||
# from foo.bar import mod2 | ||||
# | ||||
# mod2 will be imported lazily. (The converse also holds -- whatever finder | ||||
# first gets cached will be used.) | ||||
# | ||||
# Instead, have a global flag the LazyLoader can use. | ||||
global _deactivated | ||||
demandenabled = isenabled() | ||||
if demandenabled: | ||||
_deactivated = True | ||||
try: | ||||
yield | ||||
finally: | ||||
if demandenabled: | ||||
_deactivated = False | ||||