Mercurial > hg
comparison hgdemandimport/demandimportpy3.py @ 32423:859496bb6db3
demandimport: add python 3 implementation
This implementation uses the new importlib finder/loader functionality
available in Python 3.5 and up.
# no-check-commit
author | Siddharth Agarwal <sid0@fb.com> |
---|---|
date | Sun, 21 May 2017 12:23:04 -0700 |
parents | |
children | 8fb5212652ec |
comparison
equal
deleted
inserted
replaced
32422:f37f9499fea8 | 32423:859496bb6db3 |
---|---|
1 # demandimportpy3 - global demand-loading of modules for Mercurial | |
2 # | |
3 # Copyright 2017 Facebook Inc. | |
4 # | |
5 # This software may be used and distributed according to the terms of the | |
6 # GNU General Public License version 2 or any later version. | |
7 | |
8 """Lazy loading for Python 3.6 and above. | |
9 | |
10 This uses the new importlib finder/loader functionality available in Python 3.5 | |
11 and up. The code reuses most of the mechanics implemented inside importlib.util, | |
12 but with a few additions: | |
13 | |
14 * Allow excluding certain modules from lazy imports. | |
15 * Expose an interface that's substantially the same as demandimport for | |
16 Python 2. | |
17 | |
18 This also has some limitations compared to the Python 2 implementation: | |
19 | |
20 * Much of the logic is per-package, not per-module, so any packages loaded | |
21 before demandimport is enabled will not be lazily imported in the future. In | |
22 practice, we only expect builtins to be loaded before demandimport is | |
23 enabled. | |
24 """ | |
25 | |
26 # This line is unnecessary, but it satisfies test-check-py3-compat.t. | |
27 from __future__ import absolute_import | |
28 | |
29 import contextlib | |
30 import os | |
31 import sys | |
32 | |
33 import importlib.abc | |
34 import importlib.machinery | |
35 import importlib.util | |
36 | |
37 _deactivated = False | |
38 | |
39 class _lazyloaderex(importlib.util.LazyLoader): | |
40 """This is a LazyLoader except it also follows the _deactivated global and | |
41 the ignore list. | |
42 """ | |
43 def exec_module(self, module): | |
44 """Make the module load lazily.""" | |
45 if _deactivated or module.__name__ in ignore: | |
46 self.loader.exec_module(module) | |
47 else: | |
48 super().exec_module(module) | |
49 | |
50 # This is 3.6+ because with Python 3.5 it isn't possible to lazily load | |
51 # extensions. See the discussion in https://python.org/sf/26186 for more. | |
52 _extensions_loader = _lazyloaderex.factory( | |
53 importlib.machinery.ExtensionFileLoader) | |
54 _bytecode_loader = _lazyloaderex.factory( | |
55 importlib.machinery.SourcelessFileLoader) | |
56 _source_loader = _lazyloaderex.factory(importlib.machinery.SourceFileLoader) | |
57 | |
58 def _makefinder(path): | |
59 return importlib.machinery.FileFinder( | |
60 path, | |
61 # This is the order in which loaders are passed in in core Python. | |
62 (_extensions_loader, importlib.machinery.EXTENSION_SUFFIXES), | |
63 (_source_loader, importlib.machinery.SOURCE_SUFFIXES), | |
64 (_bytecode_loader, importlib.machinery.BYTECODE_SUFFIXES), | |
65 ) | |
66 | |
67 ignore = [] | |
68 | |
69 def init(ignorelist): | |
70 global ignore | |
71 ignore = ignorelist | |
72 | |
73 def isenabled(): | |
74 return _makefinder in sys.path_hooks and not _deactivated | |
75 | |
76 def disable(): | |
77 try: | |
78 while True: | |
79 sys.path_hooks.remove(_makefinder) | |
80 except ValueError: | |
81 pass | |
82 | |
83 def enable(): | |
84 if os.environ.get('HGDEMANDIMPORT') != 'disable': | |
85 sys.path_hooks.insert(0, _makefinder) | |
86 | |
87 @contextlib.contextmanager | |
88 def deactivated(): | |
89 # This implementation is a bit different from Python 2's. Python 3 | |
90 # maintains a per-package finder cache in sys.path_importer_cache (see | |
91 # PEP 302). This means that we can't just call disable + enable. | |
92 # If we do that, in situations like: | |
93 # | |
94 # demandimport.enable() | |
95 # ... | |
96 # from foo.bar import mod1 | |
97 # with demandimport.deactivated(): | |
98 # from foo.bar import mod2 | |
99 # | |
100 # mod2 will be imported lazily. (The converse also holds -- whatever finder | |
101 # first gets cached will be used.) | |
102 # | |
103 # Instead, have a global flag the LazyLoader can use. | |
104 global _deactivated | |
105 demandenabled = isenabled() | |
106 if demandenabled: | |
107 _deactivated = True | |
108 try: | |
109 yield | |
110 finally: | |
111 if demandenabled: | |
112 _deactivated = False |