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