##// END OF EJS Templates
mercurial: support loading modules from zipimporter...
Gregory Szorc -
r27225:30a20167 default
parent child Browse files
Show More
@@ -1,113 +1,144 b''
1 1 # __init__.py - Startup and module loading logic for Mercurial.
2 2 #
3 3 # Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import imp
11 11 import os
12 12 import sys
13 import zipimport
13 14
14 15 __all__ = []
15 16
16 17 # Rules for how modules can be loaded. Values are:
17 18 #
18 19 # c - require C extensions
19 20 # allow - allow pure Python implementation when C loading fails
20 21 # py - only load pure Python modules
21 22 modulepolicy = '@MODULELOADPOLICY@'
22 23
23 24 # By default, require the C extensions for performance reasons.
24 25 if modulepolicy == '@' 'MODULELOADPOLICY' '@':
25 26 modulepolicy = 'c'
26 27
27 28 # PyPy doesn't load C extensions.
28 29 #
29 30 # The canonical way to do this is to test platform.python_implementation().
30 31 # But we don't import platform and don't bloat for it here.
31 32 if '__pypy__' in sys.builtin_module_names:
32 33 modulepolicy = 'py'
33 34
34 35 # Environment variable can always force settings.
35 36 modulepolicy = os.environ.get('HGMODULEPOLICY', modulepolicy)
36 37
37 38 # Modules that have both Python and C implementations. See also the
38 39 # set of .py files under mercurial/pure/.
39 40 _dualmodules = set([
40 41 'mercurial.base85',
41 42 'mercurial.bdiff',
42 43 'mercurial.diffhelpers',
43 44 'mercurial.mpatch',
44 45 'mercurial.osutil',
45 46 'mercurial.parsers',
46 47 ])
47 48
48 49 class hgimporter(object):
49 50 """Object that conforms to import hook interface defined in PEP-302."""
50 51 def find_module(self, name, path=None):
51 52 # We only care about modules that have both C and pure implementations.
52 53 if name in _dualmodules:
53 54 return self
54 55 return None
55 56
56 57 def load_module(self, name):
57 58 mod = sys.modules.get(name, None)
58 59 if mod:
59 60 return mod
60 61
61 62 mercurial = sys.modules['mercurial']
62 63
64 # The zip importer behaves sufficiently differently from the default
65 # importer to warrant its own code path.
66 loader = getattr(mercurial, '__loader__', None)
67 if isinstance(loader, zipimport.zipimporter):
68 def ziploader(*paths):
69 """Obtain a zipimporter for a directory under the main zip."""
70 path = os.path.join(loader.archive, *paths)
71 zl = sys.path_importer_cache.get(path)
72 if not zl:
73 zl = zipimport.zipimporter(path)
74 return zl
75
76 try:
77 if modulepolicy == 'py':
78 raise ImportError()
79
80 zl = ziploader('mercurial')
81 mod = zl.load_module(name)
82 # Unlike imp, ziploader doesn't expose module metadata that
83 # indicates the type of module. So just assume what we found
84 # is OK (even though it could be a pure Python module).
85 except ImportError:
86 if modulepolicy == 'c':
87 raise
88 zl = ziploader('mercurial', 'pure')
89 mod = zl.load_module(name)
90
91 sys.modules[name] = mod
92 return mod
93
63 94 # Unlike the default importer which searches special locations and
64 95 # sys.path, we only look in the directory where "mercurial" was
65 96 # imported from.
66 97
67 98 # imp.find_module doesn't support submodules (modules with ".").
68 99 # Instead you have to pass the parent package's __path__ attribute
69 100 # as the path argument.
70 101 stem = name.split('.')[-1]
71 102
72 103 try:
73 104 if modulepolicy == 'py':
74 105 raise ImportError()
75 106
76 107 modinfo = imp.find_module(stem, mercurial.__path__)
77 108
78 109 # The Mercurial installer used to copy files from
79 110 # mercurial/pure/*.py to mercurial/*.py. Therefore, it's possible
80 111 # for some installations to have .py files under mercurial/*.
81 112 # Loading Python modules when we expected C versions could result
82 113 # in a) poor performance b) loading a version from a previous
83 114 # Mercurial version, potentially leading to incompatibility. Either
84 115 # scenario is bad. So we verify that modules loaded from
85 116 # mercurial/* are C extensions. If the current policy allows the
86 117 # loading of .py modules, the module will be re-imported from
87 118 # mercurial/pure/* below.
88 119 if modinfo[2][2] != imp.C_EXTENSION:
89 120 raise ImportError('.py version of %s found where C '
90 121 'version should exist' % name)
91 122
92 123 except ImportError:
93 124 if modulepolicy == 'c':
94 125 raise
95 126
96 127 # Could not load the C extension and pure Python is allowed. So
97 128 # try to load them.
98 129 from . import pure
99 130 modinfo = imp.find_module(stem, pure.__path__)
100 131 if not modinfo:
101 132 raise ImportError('could not find mercurial module %s' %
102 133 name)
103 134
104 135 mod = imp.load_module(name, *modinfo)
105 136 sys.modules[name] = mod
106 137 return mod
107 138
108 139 # We automagically register our custom importer as a side-effect of loading.
109 140 # This is necessary to ensure that any entry points are able to import
110 141 # mercurial.* modules without having to perform this registration themselves.
111 142 if not any(isinstance(x, hgimporter) for x in sys.meta_path):
112 143 # meta_path is used before any implicit finders and before sys.path.
113 144 sys.meta_path.insert(0, hgimporter())
General Comments 0
You need to be logged in to leave comments. Login now