Mercurial > hg
diff mercurial/templater.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | 4591c9791a82 |
children | 687b865b95ad |
line wrap: on
line diff
--- a/mercurial/templater.py Sat Oct 05 10:29:34 2019 -0400 +++ b/mercurial/templater.py Sun Oct 06 09:45:02 2019 -0400 @@ -79,9 +79,7 @@ templateutil, util, ) -from .utils import ( - stringutil, -) +from .utils import stringutil # template parsing @@ -105,6 +103,7 @@ "end": (0, None, None, None, None), } + def tokenize(program, start, end, term=None): """Parse a template expression into a stream of tokens, which must end with term if specified""" @@ -112,22 +111,22 @@ program = pycompat.bytestr(program) while pos < end: c = program[pos] - if c.isspace(): # skip inter-token whitespace + if c.isspace(): # skip inter-token whitespace pass - elif c in "(=,).%|+-*/": # handle simple operators + elif c in "(=,).%|+-*/": # handle simple operators yield (c, None, pos) - elif c in '"\'': # handle quoted templates + elif c in '"\'': # handle quoted templates s = pos + 1 data, pos = _parsetemplate(program, s, end, c) yield ('template', data, s) pos -= 1 - elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'): + elif c == 'r' and program[pos : pos + 2] in ("r'", 'r"'): # handle quoted strings c = program[pos + 1] s = pos = pos + 2 - while pos < end: # find closing quote + while pos < end: # find closing quote d = program[pos] - if d == '\\': # skip over escaped characters + if d == '\\': # skip over escaped characters pos += 2 continue if d == c: @@ -145,8 +144,12 @@ pos += 1 yield ('integer', program[s:pos], s) pos -= 1 - elif (c == '\\' and program[pos:pos + 2] in (br"\'", br'\"') - or c == 'r' and program[pos:pos + 3] in (br"r\'", br'r\"')): + elif ( + c == '\\' + and program[pos : pos + 2] in (br"\'", br'\"') + or c == 'r' + and program[pos : pos + 3] in (br"r\'", br'r\"') + ): # handle escaped quoted strings for compatibility with 2.9.2-3.4, # where some of nested templates were preprocessed as strings and # then compiled. therefore, \"...\" was allowed. (issue4733) @@ -162,11 +165,11 @@ token = 'string' else: token = 'template' - quote = program[pos:pos + 2] + quote = program[pos : pos + 2] s = pos = pos + 2 - while pos < end: # find closing escaped quote + while pos < end: # find closing escaped quote if program.startswith('\\\\\\', pos, end): - pos += 4 # skip over double escaped characters + pos += 4 # skip over double escaped characters continue if program.startswith(quote, pos, end): # interpret as if it were a part of an outer string @@ -182,7 +185,7 @@ elif c.isalnum() or c in '_': s = pos pos += 1 - while pos < end: # find end of symbol + while pos < end: # find end of symbol d = program[pos] if not (d.isalnum() or d == "_"): break @@ -200,6 +203,7 @@ raise error.ParseError(_("unterminated template expansion"), start) yield ('end', None, pos) + def _parsetemplate(tmpl, start, stop, quote=''): r""" >>> _parsetemplate(b'foo{bar}"baz', 0, 12) @@ -225,6 +229,7 @@ raise error.ProgrammingError('unexpected type: %s' % typ) raise error.ProgrammingError('unterminated scanning of template') + def scantemplate(tmpl, raw=False): r"""Scan (type, start, end) positions of outermost elements in template @@ -253,6 +258,7 @@ last = (typ, pos) raise error.ProgrammingError('unterminated scanning of template') + def _scantemplate(tmpl, start, stop, quote='', raw=False): """Parse template string into chunks of strings and template expressions""" sepchars = '{' + quote @@ -261,20 +267,21 @@ p = parser.parser(elements) try: while pos < stop: - n = min((tmpl.find(c, pos, stop) - for c in pycompat.bytestr(sepchars)), - key=lambda n: (n < 0, n)) + n = min( + (tmpl.find(c, pos, stop) for c in pycompat.bytestr(sepchars)), + key=lambda n: (n < 0, n), + ) if n < 0: yield ('string', unescape(tmpl[pos:stop]), pos) pos = stop break - c = tmpl[n:n + 1] + c = tmpl[n : n + 1] bs = 0 # count leading backslashes if not raw: bs = (n - pos) - len(tmpl[pos:n].rstrip('\\')) if bs % 2 == 1: # escaped (e.g. '\{', '\\\{', but not '\\{') - yield ('string', unescape(tmpl[pos:n - 1]) + c, pos) + yield ('string', unescape(tmpl[pos : n - 1]) + c, pos) pos = n + 1 continue if n > pos: @@ -303,11 +310,13 @@ # failed to parse, but in a hint we get a open paren at the # start. Therefore, we print "loc + 1" spaces (instead of "loc") # to line up the caret with the location of the error. - inst.hint = (tmpl + '\n' - + ' ' * (loc + 1 + offset) + '^ ' + _('here')) + inst.hint = ( + tmpl + '\n' + ' ' * (loc + 1 + offset) + '^ ' + _('here') + ) raise yield ('end', None, pos) + def _unnesttemplatelist(tree): """Expand list of templates to node tuple @@ -342,12 +351,14 @@ else: return (op,) + xs + def parse(tmpl): """Parse template string into tree""" parsed, pos = _parsetemplate(tmpl, 0, len(tmpl)) assert pos == len(tmpl), 'unquoted template should be consumed' return _unnesttemplatelist(('template', parsed)) + def _parseexpr(expr): """Parse a template expression into tree @@ -370,9 +381,11 @@ raise error.ParseError(_('invalid token'), pos) return _unnesttemplatelist(tree) + def prettyformat(tree): return parser.prettyformat(tree, ('integer', 'string', 'symbol')) + def compileexp(exp, context, curmethods): """Compile parsed template tree to (func, data) pair""" if not exp: @@ -380,13 +393,16 @@ t = exp[0] return curmethods[t](exp, context) + # template evaluation + def getsymbol(exp): if exp[0] == 'symbol': return exp[1] raise error.ParseError(_("expected a symbol, got '%s'") % exp[0]) + def getlist(x): if not x: return [] @@ -394,6 +410,7 @@ return getlist(x[1]) + [x[2]] return [x] + def gettemplate(exp, context): """Compile given template tree or load named template from map file; returns (func, data) pair""" @@ -406,13 +423,16 @@ return context._load(exp[1]) raise error.ParseError(_("expected template specifier")) + def _runrecursivesymbol(context, mapping, key): raise error.Abort(_("recursive reference '%s' in template") % key) + def buildtemplate(exp, context): ctmpl = [compileexp(e, context, methods) for e in exp[1:]] return (templateutil.runtemplate, ctmpl) + def buildfilter(exp, context): n = getsymbol(exp[2]) if n in context._filters: @@ -425,25 +445,30 @@ return (f, args) raise error.ParseError(_("unknown function '%s'") % n) + def buildmap(exp, context): darg = compileexp(exp[1], context, methods) targ = gettemplate(exp[2], context) return (templateutil.runmap, (darg, targ)) + def buildmember(exp, context): darg = compileexp(exp[1], context, methods) memb = getsymbol(exp[2]) return (templateutil.runmember, (darg, memb)) + def buildnegate(exp, context): arg = compileexp(exp[1], context, exprmethods) return (templateutil.runnegate, arg) + def buildarithmetic(exp, context, func): left = compileexp(exp[1], context, exprmethods) right = compileexp(exp[2], context, exprmethods) return (templateutil.runarithmetic, (func, left, right)) + def buildfunc(exp, context): n = getsymbol(exp[1]) if n in context._funcs: @@ -458,6 +483,7 @@ return (templateutil.runfilter, (args[0], f)) raise error.ParseError(_("unknown function '%s'") % n) + def _buildfuncargs(exp, context, curmethods, funcname, argspec): """Compile parsed tree of function arguments into list or dict of (func, data) pairs @@ -473,9 +499,12 @@ >>> list(args.keys()), list(args[b'opts'].keys()) (['opts'], ['opts', 'k']) """ + def compiledict(xs): - return util.sortdict((k, compileexp(x, context, curmethods)) - for k, x in xs.iteritems()) + return util.sortdict( + (k, compileexp(x, context, curmethods)) for k, x in xs.iteritems() + ) + def compilelist(xs): return [compileexp(x, context, curmethods) for x in xs] @@ -485,8 +514,13 @@ # function with argspec: return dict of named args _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec) - treeargs = parser.buildargsdict(getlist(exp), funcname, argspec, - keyvaluenode='keyvalue', keynode='symbol') + treeargs = parser.buildargsdict( + getlist(exp), + funcname, + argspec, + keyvaluenode='keyvalue', + keynode='symbol', + ) compargs = util.sortdict() if varkey: compargs[varkey] = compilelist(treeargs.pop(varkey)) @@ -495,12 +529,17 @@ compargs.update(compiledict(treeargs)) return compargs + def buildkeyvaluepair(exp, content): raise error.ParseError(_("can't use a key-value pair in this context")) + def buildlist(exp, context): - raise error.ParseError(_("can't use a list in this context"), - hint=_('check place of comma and parens')) + raise error.ParseError( + _("can't use a list in this context"), + hint=_('check place of comma and parens'), + ) + # methods to interpret function arguments or inner expressions (e.g. {_(x)}) exprmethods = { @@ -520,14 +559,16 @@ "negate": buildnegate, "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b), "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b), - } +} # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"}) methods = exprmethods.copy() methods["integer"] = exprmethods["symbol"] # '{1}' as variable + class _aliasrules(parser.basealiasrules): """Parsing and expansion rule set of template aliases""" + _section = _('template alias') _parse = staticmethod(_parseexpr) @@ -540,19 +581,23 @@ if tree[0] == '|' and tree[2][0] == 'symbol': return tree[2][1], [tree[1]] + def expandaliases(tree, aliases): """Return new tree of aliases are expanded""" aliasmap = _aliasrules.buildmap(aliases) return _aliasrules.expand(aliasmap, tree) + # template engine + def unquotestring(s): '''unwrap quotes if any; otherwise returns unmodified string''' if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]: return s return s[1:-1] + class resourcemapper(object): """Mapper of internal template resources""" @@ -575,6 +620,7 @@ """Return a dict of additional mapping items which should be paired with the given new mapping""" + class nullresourcemapper(resourcemapper): def availablekeys(self, mapping): return set() @@ -588,6 +634,7 @@ def populatemap(self, context, origmapping, newmapping): return {} + class engine(object): '''template expansion engine. @@ -630,12 +677,18 @@ # new resources, so the defaults will be re-evaluated (issue5612) knownres = self._resources.knownkeys() newres = self._resources.availablekeys(newmapping) - mapping = {k: v for k, v in origmapping.iteritems() - if (k in knownres # not a symbol per self.symbol() - or newres.isdisjoint(self._defaultrequires(k)))} + mapping = { + k: v + for k, v in origmapping.iteritems() + if ( + k in knownres # not a symbol per self.symbol() + or newres.isdisjoint(self._defaultrequires(k)) + ) + } mapping.update(newmapping) mapping.update( - self._resources.populatemap(self, origmapping, newmapping)) + self._resources.populatemap(self, origmapping, newmapping) + ) return mapping def _defaultrequires(self, key): @@ -668,7 +721,8 @@ v = self._resources.lookup(mapping, key) if v is None: raise templateutil.ResourceUnavailable( - _('template resource not available: %s') % key) + _('template resource not available: %s') % key + ) return v def _load(self, t): @@ -679,7 +733,7 @@ self._cache[t] = (_runrecursivesymbol, t) try: self._cache[t] = compileexp(x, self, methods) - except: # re-raises + except: # re-raises del self._cache[t] raise return self._cache[t] @@ -725,6 +779,7 @@ mapping = extramapping return templateutil.flatten(self, mapping, func(self, mapping, data)) + def stylelist(): paths = templatepaths() if not paths: @@ -739,11 +794,14 @@ stylelist.append(split[1]) return ", ".join(sorted(stylelist)) + def _readmapfile(mapfile): """Load template elements from the given map file""" if not os.path.exists(mapfile): - raise error.Abort(_("style '%s' not found") % mapfile, - hint=_("available styles: %s") % stylelist()) + raise error.Abort( + _("style '%s' not found") % mapfile, + hint=_("available styles: %s") % stylelist(), + ) base = os.path.dirname(mapfile) conf = config.config(includepaths=templatepaths()) @@ -774,18 +832,21 @@ for key, val in conf['templates'].items(): if not val: - raise error.ParseError(_('missing value'), - conf.source('templates', key)) + raise error.ParseError( + _('missing value'), conf.source('templates', key) + ) if val[0] in "'\"": if val[0] != val[-1]: - raise error.ParseError(_('unmatched quotes'), - conf.source('templates', key)) + raise error.ParseError( + _('unmatched quotes'), conf.source('templates', key) + ) cache[key] = unquotestring(val) elif key != '__base__': tmap[key] = os.path.join(base, val) aliases.extend(conf['templatealias'].items()) return cache, tmap, aliases + class loader(object): """Load template fragments optionally from a map file""" @@ -806,11 +867,13 @@ self.cache[t] = util.readfile(self._map[t]) except KeyError as inst: raise templateutil.TemplateNotFound( - _('"%s" not in template map') % inst.args[0]) + _('"%s" not in template map') % inst.args[0] + ) except IOError as inst: - reason = (_('template file %s: %s') - % (self._map[t], - stringutil.forcebytestr(inst.args[1]))) + reason = _('template file %s: %s') % ( + self._map[t], + stringutil.forcebytestr(inst.args[1]), + ) raise IOError(inst.args[0], encoding.strfromlocal(reason)) return self._parse(self.cache[t]) @@ -827,7 +890,7 @@ if op == 'symbol': s = tree[1] if s in syms[0]: - return # avoid recursion: s -> cache[s] -> s + return # avoid recursion: s -> cache[s] -> s syms[0].add(s) if s in self.cache or s in self._map: # s may be a reference for named template @@ -857,10 +920,18 @@ self._findsymbolsused(self.load(t), syms) return syms + class templater(object): - - def __init__(self, filters=None, defaults=None, resources=None, - cache=None, aliases=(), minchunk=1024, maxchunk=65536): + def __init__( + self, + filters=None, + defaults=None, + resources=None, + cache=None, + aliases=(), + minchunk=1024, + maxchunk=65536, + ): """Create template engine optionally with preloaded template fragments - ``filters``: a dict of functions to transform a value into another. @@ -882,8 +953,16 @@ self._minchunk, self._maxchunk = minchunk, maxchunk @classmethod - def frommapfile(cls, mapfile, filters=None, defaults=None, resources=None, - cache=None, minchunk=1024, maxchunk=65536): + def frommapfile( + cls, + mapfile, + filters=None, + defaults=None, + resources=None, + cache=None, + minchunk=1024, + maxchunk=65536, + ): """Create templater from the specified map file""" t = cls(filters, defaults, resources, cache, [], minchunk, maxchunk) cache, tmap, aliases = _readmapfile(mapfile) @@ -941,17 +1020,19 @@ yields chunks""" stream = self._proc.process(t, mapping) if self._minchunk: - stream = util.increasingchunks(stream, min=self._minchunk, - max=self._maxchunk) + stream = util.increasingchunks( + stream, min=self._minchunk, max=self._maxchunk + ) return stream + def templatepaths(): '''return locations used for template files.''' pathsrel = ['templates'] - paths = [os.path.normpath(os.path.join(util.datapath, f)) - for f in pathsrel] + paths = [os.path.normpath(os.path.join(util.datapath, f)) for f in pathsrel] return [p for p in paths if os.path.isdir(p)] + def templatepath(name): '''return location of template file. returns None if not found.''' for p in templatepaths(): @@ -960,6 +1041,7 @@ return f return None + def stylemap(styles, paths=None): """Return path to mapfile for a given style. @@ -979,10 +1061,13 @@ for style in styles: # only plain name is allowed to honor template paths - if (not style + if ( + not style or style in (pycompat.oscurdir, pycompat.ospardir) or pycompat.ossep in style - or pycompat.osaltsep and pycompat.osaltsep in style): + or pycompat.osaltsep + and pycompat.osaltsep in style + ): continue locations = [os.path.join(style, 'map'), 'map-' + style] locations.append('map')