dynamic-import: use sysstr for importing extension and others
This logic is used by extensions, and python hooks and merge-tools. All this
logic eventually deals with native string (unicode in Python 3). This patch
makes it handle `str` directly instead of relying on some pycompat low lever
layer to do the conversion at the last minutes.
We adjust the Python version filtering of a test as the output seems to be present with Python 3.7 too.
--- a/mercurial/extensions.py Thu Aug 31 01:54:48 2023 +0200
+++ b/mercurial/extensions.py Thu Aug 31 02:41:33 2023 +0200
@@ -84,9 +84,8 @@
def loadpath(path, module_name):
- module_name = module_name.replace(b'.', b'_')
+ module_name = module_name.replace('.', '_')
path = util.normpath(util.expandpath(path))
- module_name = pycompat.fsdecode(module_name)
path = pycompat.fsdecode(path)
if os.path.isdir(path):
# module/__init__.py style
@@ -106,30 +105,31 @@
def _importh(name):
"""import and return the <name> module"""
- mod = __import__(pycompat.sysstr(name))
- components = name.split(b'.')
+ mod = __import__(name)
+ components = name.split('.')
for comp in components[1:]:
mod = getattr(mod, comp)
return mod
def _importext(name, path=None, reportfunc=None):
+ name = pycompat.fsdecode(name)
if path:
# the module will be loaded in sys.modules
# choose an unique name so that it doesn't
# conflicts with other modules
- mod = loadpath(path, b'hgext.%s' % name)
+ mod = loadpath(path, 'hgext.%s' % name)
else:
try:
- mod = _importh(b"hgext.%s" % name)
+ mod = _importh("hgext.%s" % name)
except ImportError as err:
if reportfunc:
- reportfunc(err, b"hgext.%s" % name, b"hgext3rd.%s" % name)
+ reportfunc(err, "hgext.%s" % name, "hgext3rd.%s" % name)
try:
- mod = _importh(b"hgext3rd.%s" % name)
+ mod = _importh("hgext3rd.%s" % name)
except ImportError as err:
if reportfunc:
- reportfunc(err, b"hgext3rd.%s" % name, name)
+ reportfunc(err, "hgext3rd.%s" % name, name)
mod = _importh(name)
return mod
@@ -140,9 +140,9 @@
ui.log(
b'extension',
b' - could not import %s (%s): trying %s\n',
- failed,
+ stringutil.forcebytestr(failed),
stringutil.forcebytestr(err),
- next,
+ stringutil.forcebytestr(next),
)
if ui.debugflag and ui.configbool(b'devel', b'debug.extensions'):
ui.traceback()
--- a/mercurial/filemerge.py Thu Aug 31 01:54:48 2023 +0200
+++ b/mercurial/filemerge.py Thu Aug 31 02:41:33 2023 +0200
@@ -834,12 +834,13 @@
# avoid cycle cmdutil->merge->filemerge->extensions->cmdutil
from . import extensions
- mod = extensions.loadpath(toolpath, b'hgmerge.%s' % tool)
+ mod_name = 'hgmerge.%s' % pycompat.sysstr(tool)
+ mod = extensions.loadpath(toolpath, mod_name)
except Exception:
raise error.Abort(
_(b"loading python merge script failed: %s") % toolpath
)
- mergefn = getattr(mod, scriptfn, None)
+ mergefn = getattr(mod, pycompat.sysstr(scriptfn), None)
if mergefn is None:
raise error.Abort(
_(b"%s does not have function: %s") % (toolpath, scriptfn)
--- a/mercurial/hook.py Thu Aug 31 01:54:48 2023 +0200
+++ b/mercurial/hook.py Thu Aug 31 02:41:33 2023 +0200
@@ -40,13 +40,14 @@
if callable(funcname):
obj = funcname
- funcname = pycompat.sysbytes(obj.__module__ + "." + obj.__name__)
+ funcname = obj.__module__ + "." + obj.__name__
else:
- d = funcname.rfind(b'.')
+ funcname = pycompat.sysstr(funcname)
+ d = funcname.rfind('.')
if d == -1:
raise error.HookLoadError(
_(b'%s hook is invalid: "%s" not in a module')
- % (hname, funcname)
+ % (hname, stringutil.forcebytestr(funcname))
)
modname = funcname[:d]
oldpaths = sys.path
@@ -89,27 +90,30 @@
)
else:
tracebackhint = None
- raise error.HookLoadError(
- _(b'%s hook is invalid: import of "%s" failed')
- % (hname, modname),
- hint=tracebackhint,
+ msg = _(b'%s hook is invalid: import of "%s" failed')
+ msg %= (
+ stringutil.forcebytestr(hname),
+ stringutil.forcebytestr(modname),
)
+ raise error.HookLoadError(msg, hint=tracebackhint)
sys.path = oldpaths
try:
- for p in funcname.split(b'.')[1:]:
+ for p in funcname.split('.')[1:]:
obj = getattr(obj, p)
except AttributeError:
raise error.HookLoadError(
_(b'%s hook is invalid: "%s" is not defined')
- % (hname, funcname)
+ % (hname, stringutil.forcebytestr(funcname))
)
if not callable(obj):
raise error.HookLoadError(
_(b'%s hook is invalid: "%s" is not callable')
- % (hname, funcname)
+ % (hname, stringutil.forcebytestr(funcname))
)
- ui.note(_(b"calling hook %s: %s\n") % (hname, funcname))
+ ui.note(
+ _(b"calling hook %s: %s\n") % (hname, stringutil.forcebytestr(funcname))
+ )
starttime = util.timer()
try:
@@ -134,7 +138,7 @@
b'pythonhook',
b'pythonhook-%s: %s finished in %0.2f seconds\n',
htype,
- funcname,
+ stringutil.forcebytestr(funcname),
duration,
)
if r:
@@ -347,11 +351,12 @@
if repo:
path = os.path.join(repo.root, path)
try:
- mod = extensions.loadpath(path, b'hghook.%s' % hname)
+ mod_name = 'hghook.%s' % pycompat.sysstr(hname)
+ mod = extensions.loadpath(path, mod_name)
except Exception:
ui.write(_(b"loading %s hook failed:\n") % hname)
raise
- hookfn = getattr(mod, cmd)
+ hookfn = getattr(mod, pycompat.sysstr(cmd))
else:
hookfn = cmd[7:].strip()
r, raised = pythonhook(
--- a/tests/test-hook.t Thu Aug 31 01:54:48 2023 +0200
+++ b/tests/test-hook.t Thu Aug 31 02:41:33 2023 +0200
@@ -991,7 +991,7 @@
Traceback (most recent call last):
ModuleNotFoundError: No module named 'hgext_syntaxerror'
Traceback (most recent call last):
- raise error.HookLoadError( (py38 !)
+ raise error.HookLoadError(msg, hint=tracebackhint) (py37 !)
mercurial.error.HookLoadError: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
abort: preoutgoing.syntaxerror hook is invalid: import of "syntaxerror" failed
@@ -1156,7 +1156,7 @@
Traceback (most recent call last):
ModuleNotFoundError: No module named 'hgext_importfail'
Traceback (most recent call last):
- raise error.HookLoadError( (py38 !)
+ raise error.HookLoadError(msg, hint=tracebackhint) (py37 !)
mercurial.error.HookLoadError: precommit.importfail hook is invalid: import of "importfail" failed
abort: precommit.importfail hook is invalid: import of "importfail" failed