1# policy.py - module policy logic for Mercurial. 2# 3# Copyright 2015 Gregory Szorc <gregory.szorc@gmail.com> 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 8from __future__ import absolute_import 9 10import os 11import sys 12 13from .pycompat import getattr 14 15# Rules for how modules can be loaded. Values are: 16# 17# c - require C extensions 18# rust+c - require Rust and C extensions 19# rust+c-allow - allow Rust and C extensions with fallback to pure Python 20# for each 21# allow - allow pure Python implementation when C loading fails 22# cffi - required cffi versions (implemented within pure module) 23# cffi-allow - allow pure Python implementation if cffi version is missing 24# py - only load pure Python modules 25# 26# By default, fall back to the pure modules so the in-place build can 27# run without recompiling the C extensions. This will be overridden by 28# __modulepolicy__ generated by setup.py. 29policy = b'allow' 30_packageprefs = { 31 # policy: (versioned package, pure package) 32 b'c': ('cext', None), 33 b'allow': ('cext', 'pure'), 34 b'cffi': ('cffi', None), 35 b'cffi-allow': ('cffi', 'pure'), 36 b'py': (None, 'pure'), 37 # For now, rust policies impact importrust only 38 b'rust+c': ('cext', None), 39 b'rust+c-allow': ('cext', 'pure'), 40} 41 42try: 43 from . import __modulepolicy__ 44 45 policy = __modulepolicy__.modulepolicy 46except ImportError: 47 pass 48 49# PyPy doesn't load C extensions. 50# 51# The canonical way to do this is to test platform.python_implementation(). 52# But we don't import platform and don't bloat for it here. 53if '__pypy__' in sys.builtin_module_names: 54 policy = b'cffi' 55 56# Environment variable can always force settings. 57if sys.version_info[0] >= 3: 58 if 'HGMODULEPOLICY' in os.environ: 59 policy = os.environ['HGMODULEPOLICY'].encode('utf-8') 60else: 61 policy = os.environ.get('HGMODULEPOLICY', policy) 62 63 64def _importfrom(pkgname, modname): 65 # from .<pkgname> import <modname> (where . is looked through this module) 66 fakelocals = {} 67 pkg = __import__(pkgname, globals(), fakelocals, [modname], level=1) 68 try: 69 fakelocals[modname] = mod = getattr(pkg, modname) 70 except AttributeError: 71 raise ImportError('cannot import name %s' % modname) 72 # force import; fakelocals[modname] may be replaced with the real module 73 getattr(mod, '__doc__', None) 74 return fakelocals[modname] 75 76 77# keep in sync with "version" in C modules 78_cextversions = { 79 ('cext', 'base85'): 1, 80 ('cext', 'bdiff'): 3, 81 ('cext', 'mpatch'): 1, 82 ('cext', 'osutil'): 4, 83 ('cext', 'parsers'): 20, 84} 85 86# map import request to other package or module 87_modredirects = { 88 ('cext', 'charencode'): ('cext', 'parsers'), 89 ('cffi', 'base85'): ('pure', 'base85'), 90 ('cffi', 'charencode'): ('pure', 'charencode'), 91 ('cffi', 'parsers'): ('pure', 'parsers'), 92} 93 94 95def _checkmod(pkgname, modname, mod): 96 expected = _cextversions.get((pkgname, modname)) 97 actual = getattr(mod, 'version', None) 98 if actual != expected: 99 raise ImportError( 100 'cannot import module %s.%s ' 101 '(expected version: %d, actual: %r)' 102 % (pkgname, modname, expected, actual) 103 ) 104 105 106def importmod(modname): 107 """Import module according to policy and check API version""" 108 try: 109 verpkg, purepkg = _packageprefs[policy] 110 except KeyError: 111 raise ImportError('invalid HGMODULEPOLICY %r' % policy) 112 assert verpkg or purepkg 113 if verpkg: 114 pn, mn = _modredirects.get((verpkg, modname), (verpkg, modname)) 115 try: 116 mod = _importfrom(pn, mn) 117 if pn == verpkg: 118 _checkmod(pn, mn, mod) 119 return mod 120 except ImportError: 121 if not purepkg: 122 raise 123 pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname)) 124 return _importfrom(pn, mn) 125 126 127def _isrustpermissive(): 128 """Assuming the policy is a Rust one, tell if it's permissive.""" 129 return policy.endswith(b'-allow') 130 131 132def importrust(modname, member=None, default=None): 133 """Import Rust module according to policy and availability. 134 135 If policy isn't a Rust one, this returns `default`. 136 137 If either the module or its member is not available, this returns `default` 138 if policy is permissive and raises `ImportError` if not. 139 """ 140 if not policy.startswith(b'rust'): 141 return default 142 143 try: 144 mod = _importfrom('rustext', modname) 145 except ImportError: 146 if _isrustpermissive(): 147 return default 148 raise 149 if member is None: 150 return mod 151 152 try: 153 return getattr(mod, member) 154 except AttributeError: 155 if _isrustpermissive(): 156 return default 157 raise ImportError("Cannot import name %s" % member) 158