Show More
@@ -0,0 +1,112 b'' | |||||
|
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 |
@@ -15,7 +15,10 b' from __future__ import absolute_import' | |||||
15 |
|
15 | |||
16 | import sys |
|
16 | import sys | |
17 |
|
17 | |||
18 | from . import demandimportpy2 as demandimport |
|
18 | if sys.version_info[0] >= 3: | |
|
19 | from . import demandimportpy3 as demandimport | |||
|
20 | else: | |||
|
21 | from . import demandimportpy2 as demandimport | |||
19 |
|
22 | |||
20 | # Extensions can add to this list if necessary. |
|
23 | # Extensions can add to this list if necessary. | |
21 | ignore = [ |
|
24 | ignore = [ |
General Comments 0
You need to be logged in to leave comments.
Login now