diff --git a/mercurial/formatter.py b/mercurial/formatter.py --- a/mercurial/formatter.py +++ b/mercurial/formatter.py @@ -124,6 +124,7 @@ from . import ( templatefilters, templatekw, templater, + templateutil, util, ) from .utils import dateutil @@ -403,7 +404,7 @@ class templateformatter(baseformatter): props['revcache'] = {} props = pycompat.strkwargs(props) g = self._t(ref, **props) - self._out.write(templater.stringify(g)) + self._out.write(templateutil.stringify(g)) def end(self): baseformatter.end(self) diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -30,6 +30,7 @@ from .. import ( repoview, templatefilters, templater, + templateutil, ui as uimod, util, wireprotoserver, @@ -378,7 +379,7 @@ class hgweb(object): try: rctx.tmpl = rctx.templater(req) ctype = rctx.tmpl('mimetype', encoding=encoding.encoding) - ctype = templater.stringify(ctype) + ctype = templateutil.stringify(ctype) # check read permissions non-static content if cmd != 'static': diff --git a/mercurial/hgweb/hgwebdir_mod.py b/mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py +++ b/mercurial/hgweb/hgwebdir_mod.py @@ -34,6 +34,7 @@ from .. import ( pycompat, scmutil, templater, + templateutil, ui as uimod, util, ) @@ -370,7 +371,7 @@ class hgwebdir(object): virtual = req.dispatchpath.strip('/') tmpl = self.templater(req, nonce) ctype = tmpl('mimetype', encoding=encoding.encoding) - ctype = templater.stringify(ctype) + ctype = templateutil.stringify(ctype) # Global defaults. These can be overridden by any handler. res.status = '200 Script output follows' diff --git a/mercurial/logcmdutil.py b/mercurial/logcmdutil.py --- a/mercurial/logcmdutil.py +++ b/mercurial/logcmdutil.py @@ -33,6 +33,7 @@ from . import ( smartset, templatekw, templater, + templateutil, util, ) from .utils import dateutil @@ -449,13 +450,15 @@ class changesettemplater(changesetprinte self._parts.update(m) if self._parts['docheader']: - self.ui.write(templater.stringify(self.t(self._parts['docheader']))) + self.ui.write( + templateutil.stringify(self.t(self._parts['docheader']))) def close(self): if self._parts['docfooter']: if not self.footer: self.footer = "" - self.footer += templater.stringify(self.t(self._parts['docfooter'])) + self.footer += templateutil.stringify( + self.t(self._parts['docfooter'])) return super(changesettemplater, self).close() def _show(self, ctx, copies, props): @@ -470,11 +473,12 @@ class changesettemplater(changesetprinte # since there's inherently a conflict between header (across items) and # separator (per item) if self._parts['separator'] and index > 0: - self.ui.write(templater.stringify(self.t(self._parts['separator']))) + self.ui.write( + templateutil.stringify(self.t(self._parts['separator']))) # write header if self._parts['header']: - h = templater.stringify(self.t(self._parts['header'], **props)) + h = templateutil.stringify(self.t(self._parts['header'], **props)) if self.buffered: self.header[ctx.rev()] = h else: @@ -484,12 +488,12 @@ class changesettemplater(changesetprinte # write changeset metadata, then patch if requested key = self._parts[self._tref] - self.ui.write(templater.stringify(self.t(key, **props))) + self.ui.write(templateutil.stringify(self.t(key, **props))) self._showpatch(ctx) if self._parts['footer']: if not self.footer: - self.footer = templater.stringify( + self.footer = templateutil.stringify( self.t(self._parts['footer'], **props)) def templatespec(tmpl, mapfile): diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py --- a/mercurial/templatefilters.py +++ b/mercurial/templatefilters.py @@ -18,6 +18,7 @@ from . import ( pycompat, registrar, templatekw, + templateutil, url, util, ) @@ -376,18 +377,7 @@ def stringify(thing): """Any type. Turns the value into text by converting values into text and concatenating them. """ - thing = templatekw.unwraphybrid(thing) - if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes): - if isinstance(thing, str): - # This is only reachable on Python 3 (otherwise - # isinstance(thing, bytes) would have been true), and is - # here to prevent infinite recursion bugs on Python 3. - raise error.ProgrammingError( - 'stringify got unexpected unicode string: %r' % thing) - return "".join([stringify(t) for t in thing if t is not None]) - if thing is None: - return "" - return pycompat.bytestr(thing) + return templateutil.stringify(thing) @templatefilter('stripdir') def stripdir(text): diff --git a/mercurial/templater.py b/mercurial/templater.py --- a/mercurial/templater.py +++ b/mercurial/templater.py @@ -1118,8 +1118,6 @@ def expandaliases(tree, aliases): # template engine -stringify = templatefilters.stringify - def _flatten(thing): '''yield a single stream from a possibly nested set of iterators''' thing = templatekw.unwraphybrid(thing) @@ -1366,7 +1364,7 @@ class templater(object): def render(self, mapping): """Render the default unnamed template and return result as string""" mapping = pycompat.strkwargs(mapping) - return stringify(self('', **mapping)) + return templateutil.stringify(self('', **mapping)) def __call__(self, t, **mapping): mapping = pycompat.byteskwargs(mapping) diff --git a/mercurial/templateutil.py b/mercurial/templateutil.py --- a/mercurial/templateutil.py +++ b/mercurial/templateutil.py @@ -13,7 +13,6 @@ from .i18n import _ from . import ( error, pycompat, - templatefilters, templatekw, util, ) @@ -24,6 +23,21 @@ class ResourceUnavailable(error.Abort): class TemplateNotFound(error.Abort): pass +def stringify(thing): + """Turn values into bytes by converting into text and concatenating them""" + thing = templatekw.unwraphybrid(thing) + if util.safehasattr(thing, '__iter__') and not isinstance(thing, bytes): + if isinstance(thing, str): + # This is only reachable on Python 3 (otherwise + # isinstance(thing, bytes) would have been true), and is + # here to prevent infinite recursion bugs on Python 3. + raise error.ProgrammingError( + 'stringify got unexpected unicode string: %r' % thing) + return "".join([stringify(t) for t in thing if t is not None]) + if thing is None: + return "" + return pycompat.bytestr(thing) + def findsymbolicname(arg): """Find symbolic name for the given compiled expression; returns None if nothing found reliably""" @@ -223,5 +237,3 @@ def getdictitem(dictarg, key): if val is None: return return templatekw.wraphybridvalue(dictarg, key, val) - -stringify = templatefilters.stringify diff --git a/tests/test-template-engine.t b/tests/test-template-engine.t --- a/tests/test-template-engine.t +++ b/tests/test-template-engine.t @@ -1,7 +1,10 @@ $ cat > engine.py << EOF > - > from mercurial import templater + > from mercurial import ( + > templater, + > templateutil, + > ) > > class mytemplater(object): > def __init__(self, loader, filters, defaults, resources, aliases): @@ -31,7 +34,7 @@ > v = v(**props) > elif callable(v): > v = v(self, props) - > v = templater.stringify(v) + > v = templateutil.stringify(v) > tmpl = tmpl.replace('{{%s}}' % k, v) > yield tmpl >