2016-03-09 22:55:45 +03:00
|
|
|
# policy.py - module policy logic for Mercurial.
|
|
|
|
#
|
|
|
|
# Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com>
|
|
|
|
#
|
|
|
|
# This software may be used and distributed according to the terms of the
|
|
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
|
|
|
|
from __future__ import absolute_import
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
|
|
|
|
# Rules for how modules can be loaded. Values are:
|
|
|
|
#
|
|
|
|
# c - require C extensions
|
|
|
|
# allow - allow pure Python implementation when C loading fails
|
2016-06-07 16:35:58 +03:00
|
|
|
# cffi - required cffi versions (implemented within pure module)
|
|
|
|
# cffi-allow - allow pure Python implementation if cffi version is missing
|
2016-03-09 22:55:45 +03:00
|
|
|
# py - only load pure Python modules
|
|
|
|
#
|
2017-04-26 17:02:43 +03:00
|
|
|
# By default, fall back to the pure modules so the in-place build can
|
|
|
|
# run without recompiling the C extensions. This will be overridden by
|
|
|
|
# __modulepolicy__ generated by setup.py.
|
|
|
|
policy = b'allow'
|
2016-08-12 05:30:17 +03:00
|
|
|
_packageprefs = {
|
|
|
|
# policy: (versioned package, pure package)
|
|
|
|
b'c': (r'cext', None),
|
|
|
|
b'allow': (r'cext', r'pure'),
|
2017-05-02 15:15:31 +03:00
|
|
|
b'cffi': (r'cffi', None),
|
|
|
|
b'cffi-allow': (r'cffi', r'pure'),
|
2016-08-12 05:30:17 +03:00
|
|
|
b'py': (None, r'pure'),
|
|
|
|
}
|
2016-06-07 16:35:58 +03:00
|
|
|
|
2016-03-09 22:55:45 +03:00
|
|
|
try:
|
|
|
|
from . import __modulepolicy__
|
|
|
|
policy = __modulepolicy__.modulepolicy
|
|
|
|
except ImportError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
# PyPy doesn't load C extensions.
|
|
|
|
#
|
|
|
|
# The canonical way to do this is to test platform.python_implementation().
|
|
|
|
# But we don't import platform and don't bloat for it here.
|
2017-05-02 12:35:09 +03:00
|
|
|
if r'__pypy__' in sys.builtin_module_names:
|
|
|
|
policy = b'cffi'
|
2016-03-09 22:55:45 +03:00
|
|
|
|
|
|
|
# Our C extensions aren't yet compatible with Python 3. So use pure Python
|
|
|
|
# on Python 3 for now.
|
|
|
|
if sys.version_info[0] >= 3:
|
2017-03-09 02:11:41 +03:00
|
|
|
policy = b'py'
|
2016-03-09 22:55:45 +03:00
|
|
|
|
|
|
|
# Environment variable can always force settings.
|
py3: add "b" prefix to string literals related to module policy
String literals without explicit prefix in __init__.py and policy.py
are treated as unicode object on Python3, because these modules are
loaded before setup of our specific code transformation (the later
module is imported at the beginning of __init__.py).
BTW, "modulepolicy" in __init__.py is initialized by "policy.policy".
This causes issues below;
- checking "policy" value in other modules causes unintentional result
For example, "b'py' not in (u'c', u'py')" returns True
unintentionally on Python3.
- writing "policy" out fails at conversion from unicode to bytes
db1ebf457295 fixed this issue for default code path, but "policy"
can be overridden by HGMODULEPOLICY environment variable (it should
be rare case for developer using Python3, though).
This patch does:
- add "b" prefix to all string literals, which are related to module
policy, in modules above.
- check existence of HGMODULEPOLICY, and overwrite "policy" only if
it exists
For simplicity, this patch omits checking "supports_bytes_environ",
switching os.environ/os.environb, and so on (Yuya agreed this in
personal talking)
2017-03-12 22:06:36 +03:00
|
|
|
if sys.version_info[0] >= 3:
|
2017-05-02 12:35:09 +03:00
|
|
|
if r'HGMODULEPOLICY' in os.environ:
|
|
|
|
policy = os.environ[r'HGMODULEPOLICY'].encode(r'utf-8')
|
py3: add "b" prefix to string literals related to module policy
String literals without explicit prefix in __init__.py and policy.py
are treated as unicode object on Python3, because these modules are
loaded before setup of our specific code transformation (the later
module is imported at the beginning of __init__.py).
BTW, "modulepolicy" in __init__.py is initialized by "policy.policy".
This causes issues below;
- checking "policy" value in other modules causes unintentional result
For example, "b'py' not in (u'c', u'py')" returns True
unintentionally on Python3.
- writing "policy" out fails at conversion from unicode to bytes
db1ebf457295 fixed this issue for default code path, but "policy"
can be overridden by HGMODULEPOLICY environment variable (it should
be rare case for developer using Python3, though).
This patch does:
- add "b" prefix to all string literals, which are related to module
policy, in modules above.
- check existence of HGMODULEPOLICY, and overwrite "policy" only if
it exists
For simplicity, this patch omits checking "supports_bytes_environ",
switching os.environ/os.environb, and so on (Yuya agreed this in
personal talking)
2017-03-12 22:06:36 +03:00
|
|
|
else:
|
2017-05-02 12:35:09 +03:00
|
|
|
policy = os.environ.get(r'HGMODULEPOLICY', policy)
|
2016-08-12 05:30:17 +03:00
|
|
|
|
|
|
|
def _importfrom(pkgname, modname):
|
|
|
|
# from .<pkgname> import <modname> (where . is looked through this module)
|
|
|
|
fakelocals = {}
|
|
|
|
pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1)
|
|
|
|
try:
|
|
|
|
fakelocals[modname] = mod = getattr(pkg, modname)
|
|
|
|
except AttributeError:
|
|
|
|
raise ImportError(r'cannot import name %s' % modname)
|
|
|
|
# force import; fakelocals[modname] may be replaced with the real module
|
|
|
|
getattr(mod, r'__doc__', None)
|
|
|
|
return fakelocals[modname]
|
|
|
|
|
2017-05-23 08:59:40 +03:00
|
|
|
# keep in sync with "version" in C modules
|
|
|
|
_cextversions = {
|
2017-05-28 09:45:52 +03:00
|
|
|
(r'cext', r'base85'): 1,
|
|
|
|
(r'cext', r'bdiff'): 1,
|
|
|
|
(r'cext', r'diffhelpers'): 1,
|
|
|
|
(r'cext', r'mpatch'): 1,
|
|
|
|
(r'cext', r'osutil'): 1,
|
|
|
|
(r'cext', r'parsers'): 1,
|
2017-05-23 08:59:40 +03:00
|
|
|
}
|
|
|
|
|
2016-08-12 05:30:17 +03:00
|
|
|
def _checkmod(pkgname, modname, mod):
|
2017-05-28 09:45:52 +03:00
|
|
|
expected = _cextversions.get((pkgname, modname))
|
2016-08-12 05:30:17 +03:00
|
|
|
actual = getattr(mod, r'version', None)
|
|
|
|
if actual != expected:
|
|
|
|
raise ImportError(r'cannot import module %s.%s '
|
|
|
|
r'(expected version: %d, actual: %r)'
|
|
|
|
% (pkgname, modname, expected, actual))
|
|
|
|
|
|
|
|
def importmod(modname):
|
|
|
|
"""Import module according to policy and check API version"""
|
|
|
|
try:
|
|
|
|
verpkg, purepkg = _packageprefs[policy]
|
|
|
|
except KeyError:
|
|
|
|
raise ImportError(r'invalid HGMODULEPOLICY %r' % policy)
|
|
|
|
assert verpkg or purepkg
|
|
|
|
if verpkg:
|
|
|
|
try:
|
|
|
|
mod = _importfrom(verpkg, modname)
|
|
|
|
_checkmod(verpkg, modname, mod)
|
|
|
|
return mod
|
|
|
|
except ImportError:
|
|
|
|
if not purepkg:
|
|
|
|
raise
|
|
|
|
return _importfrom(purepkg, modname)
|