policy: add helper to import cext/pure module
These functions are sysstr API since __import__() and getattr() hate byte
strings on Python 3.
There's a minor BC, which is ImportError will be raised if invalid
HGMODULEPOLICY is specified. I think this is more desired behavior.
We're planning to add strict checking for C API compatibility. This patch
includes the stub for it.
--- a/mercurial/policy.py Sat May 20 15:09:14 2017 +0900
+++ b/mercurial/policy.py Fri Aug 12 11:30:17 2016 +0900
@@ -24,6 +24,14 @@
policy = b'allow'
policynoc = (b'cffi', b'cffi-allow', b'py')
policynocffi = (b'c', b'py')
+_packageprefs = {
+ # policy: (versioned package, pure package)
+ b'c': (r'cext', None),
+ b'allow': (r'cext', r'pure'),
+ b'cffi': (None, r'pure'), # TODO: (r'cffi', None)
+ b'cffi-allow': (None, r'pure'), # TODO: (r'cffi', r'pure')
+ b'py': (None, r'pure'),
+}
try:
from . import __modulepolicy__
@@ -49,3 +57,40 @@
policy = os.environ[r'HGMODULEPOLICY'].encode(r'utf-8')
else:
policy = os.environ.get(r'HGMODULEPOLICY', policy)
+
+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]
+
+def _checkmod(pkgname, modname, mod):
+ expected = 1 # TODO: maybe defined in table?
+ 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)