##// END OF EJS Templates
dirstate-v2: add devel config option to control write behavior...
dirstate-v2: add devel config option to control write behavior This will help us to write predictable tests checking behavior in each case.

File last commit:

r50742:31bbf7a2 default
r51117:ecd28d89 stable
Show More
demandimportpy3.py
174 lines | 5.1 KiB | text/x-python | PythonLexer
Siddharth Agarwal
demandimport: add python 3 implementation...
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.
import contextlib
import importlib.util
Augie Fackler
demandimportpy3: update to pass import checker
r33899 import sys
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423
Augie Fackler
demandimport: add tracing coverage for Python 3...
r42674 from . import tracing
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423 _deactivated = False
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423 class _lazyloaderex(importlib.util.LazyLoader):
"""This is a LazyLoader except it also follows the _deactivated global and
the ignore list.
"""
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423 def exec_module(self, module):
"""Make the module load lazily."""
Augie Fackler
demandimport: add tracing coverage for Python 3...
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
demandimport: add python 3 implementation...
r32423
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
py3: use class X: instead of class X(object):...
r49801 class LazyFinder:
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577 """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.
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577 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"))
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577 __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):
Matt Harbison
demandimport: fix a crash in LazyFinder.__delattr__...
r50669 return delattr(object.__getattribute__(self, "_finder"), name)
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577
def __setattr__(self, name, value):
return setattr(object.__getattribute__(self, "_finder"), name, value)
Manuel Jacob
demandimport: fix compatibility with meta path finders w/o find_spec() method...
r45337 def find_spec(self, fullname, path, target=None):
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577 finder = object.__getattribute__(self, "_finder")
Manuel Jacob
demandimport: fix compatibility with meta path finders w/o find_spec() method...
r45337 try:
find_spec = finder.find_spec
except AttributeError:
loader = finder.find_module(fullname, path)
if loader is None:
spec = None
else:
spec = importlib.util.spec_from_loader(fullname, loader)
else:
spec = find_spec(fullname, path, target)
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577
# Lazy loader requires exec_module().
if (
spec is not None
and spec.loader is not None
Matt Harbison
demandimport: don't raise AttributeError if `exec_module` is missing...
r46347 and getattr(spec.loader, "exec_module", None)
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577 ):
spec.loader = _lazyloaderex(spec.loader)
return spec
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
demandimport: make module ignores a set (API)...
r37862 ignores = set()
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423
Augie Fackler
formatting: blacken the codebase...
r43346
Gregory Szorc
demandimport: make module ignores a set (API)...
r37862 def init(ignoreset):
global ignores
ignores = ignoreset
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423 def isenabled():
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577 return not _deactivated and any(
isinstance(finder, LazyFinder) for finder in sys.meta_path
)
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423 def disable():
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577 new_finders = []
for finder in sys.meta_path:
new_finders.append(
finder._finder if isinstance(finder, LazyFinder) else finder
)
sys.meta_path[:] = new_finders
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423 def enable():
Gregory Szorc
hgdemandimport: apply lazy module loading to sys.meta_path finders...
r44577 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
Siddharth Agarwal
demandimport: add python 3 implementation...
r32423
Augie Fackler
formatting: blacken the codebase...
r43346
Siddharth Agarwal
demandimport: add python 3 implementation...
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