@@ -1278,16 +1278,28 @@
fm.write('hgmodules', _("checking installed modules (%s)...\n"),
os.path.dirname(pycompat.fsencode(__file__)))
- if policy.policy in ('c', 'allow'):
+ rustandc = policy.policy in ('rust+c', 'rust+c-allow')
+ rustext = rustandc # for now, that's the only case
+ cext = policy.policy in ('c', 'allow') or rustandc
+ nopure = cext or rustext
+ if nopure:
err = None
try:
- from .cext import (
- base85,
- bdiff,
- mpatch,
- osutil,
- )
- dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
+ if cext:
+ from .cext import (
+ base85,
+ bdiff,
+ mpatch,
+ osutil,
+ )
+ # quiet pyflakes
+ dir(bdiff), dir(mpatch), dir(base85), dir(osutil)
+ if rustext:
+ from .rustext import (
+ ancestor,
+ dirstate,
+ )
+ dir(ancestor), dir(dirstate) # quiet pyflakes
except Exception as inst:
err = stringutil.forcebytestr(inst)
problems += 1
@@ -13,6 +13,9 @@
# Rules for how modules can be loaded. Values are:
#
# c - require C extensions
+# rust+c - require Rust and C extensions
+# rust+c-allow - allow Rust and C extensions with fallback to pure Python
+# for each
# allow - allow pure Python implementation when C loading fails
# cffi - required cffi versions (implemented within pure module)
# cffi-allow - allow pure Python implementation if cffi version is missing
@@ -29,6 +32,9 @@
b'cffi': (r'cffi', None),
b'cffi-allow': (r'cffi', r'pure'),
b'py': (None, r'pure'),
+ # For now, rust policies impact importrust only
+ b'rust+c': (r'cext', None),
+ b'rust+c-allow': (r'cext', r'pure'),
}
try:
@@ -107,3 +113,34 @@
raise
pn, mn = _modredirects.get((purepkg, modname), (purepkg, modname))
return _importfrom(pn, mn)
+
+def _isrustpermissive():
+ """Assuming the policy is a Rust one, tell if it's permissive."""
+ return policy.endswith(b'-allow')
+
+def importrust(modname, member=None, default=None):
+ """Import Rust module according to policy and availability.
+
+ If policy isn't a Rust one, this returns `default`.
+
+ If either the module or its member is non available, this returns `default`
+ if policy is permissive and raises `ImportError` if not.
+ """
+ if not policy.startswith(b'rust'):
+ return default
+
+ try:
+ mod = _importfrom(r'rustext', modname)
+ except ImportError:
+ if _isrustpermissive():
+ return default
+ raise
+ if member is None:
+ return mod
+
+ try:
+ return getattr(mod, member)
+ except AttributeError:
+ if _isrustpermissive():
+ return default
+ raise ImportError(r"Cannot import name %s" % member)
@@ -556,10 +556,17 @@
if self.distribution.pure:
modulepolicy = 'py'
elif self.build_lib == '.':
- # in-place build should run without rebuilding C extensions
- modulepolicy = 'allow'
+ # in-place build should run without rebuilding C
+ # and Rust extensions
+ if hgrustext == 'cpython':
+ modulepolicy = 'rust+c-allow'
+ else:
+ modulepolicy = 'allow'
else:
- modulepolicy = 'c'
+ if hgrustext == 'cpython':
+ modulepolicy = 'rust+c'
+ else:
+ modulepolicy = 'c'
content = b''.join([
b'# this file is autogenerated by setup.py\n',