# HG changeset patch # User Yuya Nishihara # Date 1523633439 -32400 # Node ID d6970628b95f8fac7e88ac2a60c70441c4a8a085 # Parent 0f084741cd669f5a3a1e5208e3e371cbddffd00b fix: use templater to substitute values in command string bytes.format() isn't supported on Python 3. Luckily, our template syntax is similar so we can reuse it. We need a hack to disable \-escapes as '\' is a directory separator on Windows. diff -r 0f084741cd66 -r d6970628b95f hgext/fix.py --- a/hgext/fix.py Fri Apr 13 23:07:12 2018 +0900 +++ b/hgext/fix.py Sat Apr 14 00:30:39 2018 +0900 @@ -387,7 +387,7 @@ for fixername, fixer in fixers.iteritems(): if fixer.affects(opts, fixctx, path): ranges = lineranges(opts, path, basectxs, fixctx, newdata) - command = fixer.command(path, ranges) + command = fixer.command(ui, path, ranges) if command is None: continue ui.debug('subprocess: %s\n' % (command,)) @@ -534,18 +534,20 @@ """Should this fixer run on the file at the given path and context?""" return scmutil.match(fixctx, [self._fileset], opts)(path) - def command(self, path, ranges): + def command(self, ui, path, ranges): """A shell command to use to invoke this fixer on the given file/lines May return None if there is no appropriate command to run for the given parameters. """ - parts = [self._command.format(rootpath=path, - basename=os.path.basename(path))] + expand = cmdutil.rendercommandtemplate + parts = [expand(ui, self._command, + {'rootpath': path, 'basename': os.path.basename(path)})] if self._linerange: if not ranges: # No line ranges to fix, so don't run the fixer. return None for first, last in ranges: - parts.append(self._linerange.format(first=first, last=last)) + parts.append(expand(ui, self._linerange, + {'first': first, 'last': last})) return ' '.join(parts) diff -r 0f084741cd66 -r d6970628b95f mercurial/cmdutil.py --- a/mercurial/cmdutil.py Fri Apr 13 23:07:12 2018 +0900 +++ b/mercurial/cmdutil.py Sat Apr 14 00:30:39 2018 +0900 @@ -904,6 +904,33 @@ else: return commiteditor +def _escapecommandtemplate(tmpl): + parts = [] + for typ, start, end in templater.scantemplate(tmpl, raw=True): + if typ == b'string': + parts.append(stringutil.escapestr(tmpl[start:end])) + else: + parts.append(tmpl[start:end]) + return b''.join(parts) + +def rendercommandtemplate(ui, tmpl, props): + r"""Expand a literal template 'tmpl' in a way suitable for command line + + '\' in outermost string is not taken as an escape character because it + is a directory separator on Windows. + + >>> from . import ui as uimod + >>> ui = uimod.ui() + >>> rendercommandtemplate(ui, b'c:\\{path}', {b'path': b'foo'}) + 'c:\\foo' + >>> rendercommandtemplate(ui, b'{"c:\\{path}"}', {'path': b'foo'}) + 'c:{path}' + """ + if not tmpl: + return tmpl + t = formatter.maketemplater(ui, _escapecommandtemplate(tmpl)) + return t.renderdefault(props) + def rendertemplate(ctx, tmpl, props=None): """Expand a literal template 'tmpl' byte-string against one changeset