##// END OF EJS Templates
templater: handle None returned from templatedir()...
templater: handle None returned from templatedir() My recent 91aa9bba3dc9 (templater: make templatepaths() return a single path, or None, 2020-07-21) didn't account for the fact that `templatedir()` returns `None` in frozen binaries. That is ironic, since the reason I'm working on this is to add support for built-in mapfiles in frozen binaries. This patch updates the callers to handle the `None` case. It's somewhat ugly, but I will have to revisit this soon anyway, since my goal is to make all callers handle that case by trying to read the map file using the resources API instead. Differential Revision: https://phab.mercurial-scm.org/D8810

File last commit:

r45773:d5ccc059 default
r45773:d5ccc059 default
Show More
templater.py
1114 lines | 36.1 KiB | text/x-python | PythonLexer
Vadim Gelfer
add doc comments to template code.
r1909 # templater.py - template expansion for output
#
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
#
Martin Geisler
updated license to be explicit about GPL version 2
r8225 # This software may be used and distributed according to the terms of the
Matt Mackall
Update license to GPLv2+
r10263 # GNU General Public License version 2 or any later version.
Vadim Gelfer
add doc comments to template code.
r1909
Yuya Nishihara
templater: add brief doc about internal data types...
r37032 """Slightly complicated template engine for commands and hgweb
This module provides low-level interface to the template engine. See the
formatter and cmdutil modules if you are looking for high-level functions
such as ``cmdutil.rendertemplate(ctx, tmpl)``.
Internal Data Types
-------------------
Template keywords and functions take a dictionary of current symbols and
resources (a "mapping") and return result. Inputs and outputs must be one
of the following data types:
bytes
a byte string, which is generally a human-readable text in local encoding.
generator
a lazily-evaluated byte string, which is a possibly nested generator of
values of any printable types, and will be folded by ``stringify()``
or ``flatten()``.
None
sometimes represents an empty value, which can be stringified to ''.
True, False, int, float
can be stringified as such.
Yuya Nishihara
templater: add wrapped types for pure non-list/dict values...
r38228 wrappedbytes, wrappedvalue
a wrapper for the above printable types.
Yuya Nishihara
templater: introduce a wrapper for date tuple (BC)...
r38304 date
represents a (unixtime, offset) tuple.
Yuya Nishihara
templater: add brief doc about internal data types...
r37032
hybrid
represents a list/dict of printable values, which can also be converted
to mappings by % operator.
Yuya Nishihara
templater: rename mappable to hybriditem as it is the primary use case...
r38302 hybriditem
Yuya Nishihara
templater: add brief doc about internal data types...
r37032 represents a scalar printable value, also supports % operator.
Yuya Nishihara
templater: add class representing a nested mappings...
r37417
Yuya Nishihara
templater: introduce wrapper for smartset (API)...
r45080 revslist
represents a list of revision numbers.
Yuya Nishihara
templater: add class representing a nested mappings...
r37417 mappinggenerator, mappinglist
represents mappings (i.e. a list of dicts), which may have default
output format.
Yuya Nishihara
templater: wrap result of '%' operation so it never looks like a thunk...
r37517
Yuya Nishihara
templater: add wrapper for a single template mapping...
r40509 mappingdict
represents a single mapping (i.e. a dict), which may have default output
format.
Yuya Nishihara
templatefuncs: specialize "no match" value of search() to allow % operation...
r40971 mappingnone
represents None of Optional[mappable], which will be mapped to an empty
string by % operation.
Yuya Nishihara
templater: wrap result of '%' operation so it never looks like a thunk...
r37517 mappedgenerator
a lazily-evaluated list of byte strings, which is e.g. a result of %
operation.
Yuya Nishihara
templater: add brief doc about internal data types...
r37032 """
Yuya Nishihara
doctest: use print_function and convert bytes to unicode where needed
r34139 from __future__ import absolute_import, print_function
Gregory Szorc
templater: use absolute_import
r25985
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 import abc
Gregory Szorc
templater: use absolute_import
r25985 import os
from .i18n import _
Gregory Szorc
py3: manually import getattr where it is needed...
r43359 from .pycompat import getattr
Gregory Szorc
templater: use absolute_import
r25985 from . import (
config,
Yuya Nishihara
templater: make pad() compute actual width...
r31520 encoding,
Gregory Szorc
templater: use absolute_import
r25985 error,
parser,
Pulkit Goyal
py3: replace os.sep with pycompat.ossep (part 3 of 4)
r30615 pycompat,
Gregory Szorc
templater: use absolute_import
r25985 templatefilters,
Yuya Nishihara
templater: split template functions to new module...
r36940 templatefuncs,
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 templateutil,
Gregory Szorc
templater: use absolute_import
r25985 util,
)
Martin von Zweigbergk
util: remove datapath and swith users over to resourceutil...
r44070 from .utils import (
resourceutil,
stringutil,
)
Yuya Nishihara
templater: move specialized exception types to top...
r36461
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 # template parsing
elements = {
Yuya Nishihara
parser: separate actions for primary expression and prefix operator...
r25815 # token-type: binding-strength, primary, prefix, infix, suffix
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"(": (20, None, (b"group", 1, b")"), (b"func", 1, b")"), None),
b".": (18, None, None, (b".", 18), None),
b"%": (15, None, None, (b"%", 15), None),
b"|": (15, None, None, (b"|", 15), None),
b"*": (5, None, None, (b"*", 5), None),
b"/": (5, None, None, (b"/", 5), None),
b"+": (4, None, None, (b"+", 4), None),
b"-": (4, None, (b"negate", 19), (b"-", 4), None),
b"=": (3, None, None, (b"keyvalue", 3), None),
b",": (2, None, None, (b"list", 2), None),
b")": (0, None, None, None, None),
b"integer": (0, b"integer", None, None, None),
b"symbol": (0, b"symbol", None, None, None),
b"string": (0, b"string", None, None, None),
b"template": (0, b"template", None, None, None),
b"end": (0, None, None, None, None),
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 }
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 def tokenize(program, start, end, term=None):
"""Parse a template expression into a stream of tokens, which must end
with term if specified"""
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos = start
Pulkit Goyal
py3: use pycompat.bytestr instead of bytes
r32154 program = pycompat.bytestr(program)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 while pos < end:
c = program[pos]
Augie Fackler
formatting: blacken the codebase...
r43346 if c.isspace(): # skip inter-token whitespace
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pass
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif c in b"(=,).%|+-*/": # handle simple operators
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 yield (c, None, pos)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif c in b'"\'': # handle quoted templates
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 s = pos + 1
data, pos = _parsetemplate(program, s, end, c)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'template', data, s)
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 pos -= 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif c == b'r' and program[pos : pos + 2] in (b"r'", b'r"'):
Yuya Nishihara
templater: remove processing of "string" literals from tokenizer...
r25784 # handle quoted strings
c = program[pos + 1]
s = pos = pos + 2
Augie Fackler
formatting: blacken the codebase...
r43346 while pos < end: # find closing quote
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 d = program[pos]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if d == b'\\': # skip over escaped characters
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos += 2
continue
if d == c:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'string', program[s:pos], s)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 break
pos += 1
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"unterminated string"), s)
Simon Farnsworth
templater: provide arithmetic operations on integers...
r30115 elif c.isdigit():
Yuya Nishihara
templater: tokenize decimal integer literal (issue4638) (BC)...
r25002 s = pos
while pos < end:
d = program[pos]
if not d.isdigit():
break
pos += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'integer', program[s:pos], s)
Yuya Nishihara
templater: tokenize decimal integer literal (issue4638) (BC)...
r25002 pos -= 1
Augie Fackler
formatting: blacken the codebase...
r43346 elif (
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 c == b'\\'
Augie Fackler
formatting: blacken the codebase...
r43346 and program[pos : pos + 2] in (br"\'", br'\"')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 or c == b'r'
Augie Fackler
formatting: blacken the codebase...
r43346 and program[pos : pos + 3] in (br"r\'", br'r\"')
):
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 # 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)
#
# processing flow of _evalifliteral() at 5ab28a2e9962:
# outer template string -> stringify() -> compiletemplate()
# ------------------------ ------------ ------------------
# {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
# ~~~~~~~~
# escaped quoted string
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if c == b'r':
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 pos += 1
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 token = b'string'
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 token = b'template'
Augie Fackler
formatting: blacken the codebase...
r43346 quote = program[pos : pos + 2]
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 s = pos = pos + 2
Augie Fackler
formatting: blacken the codebase...
r43346 while pos < end: # find closing escaped quote
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if program.startswith(b'\\\\\\', pos, end):
Augie Fackler
formatting: blacken the codebase...
r43346 pos += 4 # skip over double escaped characters
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 continue
if program.startswith(quote, pos, end):
Matt Mackall
templater: create string unescape helper (issue4798)...
r26215 # interpret as if it were a part of an outer string
Yuya Nishihara
parser: move unescape helper from templater...
r26231 data = parser.unescapestr(program[s:pos])
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if token == b'template':
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 data = _parsetemplate(data, 0, len(data))[0]
Yuya Nishihara
templater: parse \"...\" as string for 2.9.2-3.4 compatibility (issue4733)...
r25676 yield (token, data, s)
pos += 1
break
pos += 1
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"unterminated string"), s)
elif c.isalnum() or c in b'_':
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 s = pos
pos += 1
Augie Fackler
formatting: blacken the codebase...
r43346 while pos < end: # find end of symbol
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 d = program[pos]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if not (d.isalnum() or d == b"_"):
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 break
pos += 1
sym = program[s:pos]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'symbol', sym, s)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos -= 1
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 elif c == term:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'end', None, pos)
Yuya Nishihara
templater: check existence of closing brace of template string
r25782 return
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"syntax error"), pos)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 pos += 1
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 if term:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"unterminated template expansion"), start)
yield (b'end', None, pos)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def _parsetemplate(tmpl, start, stop, quote=b''):
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 r"""
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 ([('string', 'foo'), ('symbol', 'bar')], 9)
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 ([('string', 'foo')], 4)
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 ([('string', 'foo"'), ('string', 'bar')], 9)
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
Yuya Nishihara
templater: unify "string" and "rawstring"...
r25785 ([('string', 'foo\\')], 6)
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783 """
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 parsed = []
Yuya Nishihara
templater: extract function scanning template string...
r36258 for typ, val, pos in _scantemplate(tmpl, start, stop, quote):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if typ == b'string':
Yuya Nishihara
templater: extract function scanning template string...
r36258 parsed.append((typ, val))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif typ == b'template':
Yuya Nishihara
templater: extract function scanning template string...
r36258 parsed.append(val)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif typ == b'end':
Yuya Nishihara
templater: extract function scanning template string...
r36258 return parsed, pos
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'unexpected type: %s' % typ)
raise error.ProgrammingError(b'unterminated scanning of template')
Yuya Nishihara
templater: extract function scanning template string...
r36258
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add option to parse template string just like raw string literal...
r36527 def scantemplate(tmpl, raw=False):
r"""Scan (type, start, end) positions of outermost elements in template
If raw=True, a backslash is not taken as an escape character just like
r'' string in Python. Note that this is different from r'' literal in
template in that no template fragment can appear in r'', e.g. r'{foo}'
is a literal '{foo}', but ('{foo}', raw=True) is a template expression
'foo'.
Yuya Nishihara
templater: add function to help substituting patterns in template string...
r36259
>>> list(scantemplate(b'foo{bar}"baz'))
[('string', 0, 3), ('template', 3, 8), ('string', 8, 12)]
>>> list(scantemplate(b'outer{"inner"}outer'))
[('string', 0, 5), ('template', 5, 14), ('string', 14, 19)]
>>> list(scantemplate(b'foo\\{escaped}'))
[('string', 0, 5), ('string', 5, 13)]
Yuya Nishihara
templater: add option to parse template string just like raw string literal...
r36527 >>> list(scantemplate(b'foo\\{escaped}', raw=True))
[('string', 0, 4), ('template', 4, 13)]
Yuya Nishihara
templater: add function to help substituting patterns in template string...
r36259 """
last = None
Yuya Nishihara
templater: add option to parse template string just like raw string literal...
r36527 for typ, val, pos in _scantemplate(tmpl, 0, len(tmpl), raw=raw):
Yuya Nishihara
templater: add function to help substituting patterns in template string...
r36259 if last:
yield last + (pos,)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if typ == b'end':
Yuya Nishihara
templater: add function to help substituting patterns in template string...
r36259 return
else:
last = (typ, pos)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'unterminated scanning of template')
Yuya Nishihara
templater: add function to help substituting patterns in template string...
r36259
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def _scantemplate(tmpl, start, stop, quote=b'', raw=False):
Yuya Nishihara
templater: extract function scanning template string...
r36258 """Parse template string into chunks of strings and template expressions"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 sepchars = b'{' + quote
Yuya Nishihara
templater: add option to parse template string just like raw string literal...
r36527 unescape = [parser.unescapestr, pycompat.identity][raw]
Yuya Nishihara
templater: extract function that parses template string...
r25781 pos = start
Yuya Nishihara
parser: accept iterator of tokens instead of tokenizer function and program...
r25654 p = parser.parser(elements)
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 try:
while pos < stop:
Augie Fackler
formatting: blacken the codebase...
r43346 n = min(
(tmpl.find(c, pos, stop) for c in pycompat.bytestr(sepchars)),
key=lambda n: (n < 0, n),
)
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 if n < 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'string', unescape(tmpl[pos:stop]), pos)
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 pos = stop
break
Augie Fackler
formatting: blacken the codebase...
r43346 c = tmpl[n : n + 1]
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 bs = 0 # count leading backslashes
if not raw:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 bs = (n - pos) - len(tmpl[pos:n].rstrip(b'\\'))
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 if bs % 2 == 1:
# escaped (e.g. '\{', '\\\{', but not '\\{')
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'string', unescape(tmpl[pos : n - 1]) + c, pos)
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 pos = n + 1
continue
if n > pos:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'string', unescape(tmpl[pos:n]), pos)
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 if c == quote:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'end', None, n + 1)
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 return
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, b'}'))
if not tmpl.startswith(b'}', pos):
raise error.ParseError(_(b"invalid token"), pos)
yield (b'template', parseres, n)
Yuya Nishihara
templater: fix position of terminator character in error message...
r36709 pos += 1
Yuya Nishihara
templater: introduce one-pass parsing of nested template strings...
r25783
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 if quote:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"unterminated string"), start)
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 except error.ParseError as inst:
Yuya Nishihara
templater: add public parseexpr() function to parse "-Tjson(...)"...
r43368 _addparseerrorhint(inst, tmpl)
Ryan McElroy
templater: add hint to template parse errors to help locate issues...
r36687 raise
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 yield (b'end', None, pos)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add public parseexpr() function to parse "-Tjson(...)"...
r43368 def _addparseerrorhint(inst, tmpl):
if len(inst.args) <= 1:
return # no location
loc = inst.args[1]
# Offset the caret location by the number of newlines before the
# location of the error, since we will replace one-char newlines
# with the two-char literal r'\n'.
offset = tmpl[:loc].count(b'\n')
tmpl = tmpl.replace(b'\n', br'\n')
# We want the caret to point to the place in the template that
# 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 + b'\n' + b' ' * (loc + 1 + offset) + b'^ ' + _(b'here')
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 def _unnesttemplatelist(tree):
"""Expand list of templates to node tuple
>>> def f(tree):
Yuya Nishihara
doctest: use print_function and convert bytes to unicode where needed
r34139 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> f((b'template', []))
Yuya Nishihara
parser: stabilize output of prettyformat() by using byte-safe repr()...
r34075 (string '')
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> f((b'template', [(b'string', b'foo')]))
Yuya Nishihara
parser: stabilize output of prettyformat() by using byte-safe repr()...
r34075 (string 'foo')
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 (template
Yuya Nishihara
parser: stabilize output of prettyformat() by using byte-safe repr()...
r34075 (string 'foo')
(symbol 'rev'))
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 (template
Yuya Nishihara
parser: stabilize output of prettyformat() by using byte-safe repr()...
r34075 (symbol 'rev'))
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
Yuya Nishihara
parser: stabilize output of prettyformat() by using byte-safe repr()...
r34075 (string 'foo')
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 """
if not isinstance(tree, tuple):
return tree
op = tree[0]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if op != b'template':
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
assert len(tree) == 2
xs = tuple(_unnesttemplatelist(x) for x in tree[1])
if not xs:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return (b'string', b'') # empty template ""
elif len(xs) == 1 and xs[0][0] == b'string':
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 return xs[0] # fast path for string with no template fragment "x"
else:
return (op,) + xs
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 def parse(tmpl):
"""Parse template string into tree"""
Yuya Nishihara
templater: extract function that parses template string...
r25781 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 assert pos == len(tmpl), b'unquoted template should be consumed'
return _unnesttemplatelist((b'template', parsed))
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add public parseexpr() function to parse "-Tjson(...)"...
r43368 def parseexpr(expr):
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 """Parse a template expression into tree
Yuya Nishihara
templater: add public parseexpr() function to parse "-Tjson(...)"...
r43368 >>> parseexpr(b'"foo"')
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 ('string', 'foo')
Yuya Nishihara
templater: add public parseexpr() function to parse "-Tjson(...)"...
r43368 >>> parseexpr(b'foo(bar)')
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
Yuya Nishihara
templater: add public parseexpr() function to parse "-Tjson(...)"...
r43368 >>> parseexpr(b'foo(')
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 Traceback (most recent call last):
...
ParseError: ('not a prefix: end', 4)
Yuya Nishihara
templater: add public parseexpr() function to parse "-Tjson(...)"...
r43368 >>> parseexpr(b'"foo" "bar"')
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 Traceback (most recent call last):
...
ParseError: ('invalid token', 7)
"""
Yuya Nishihara
templater: add public parseexpr() function to parse "-Tjson(...)"...
r43368 try:
return _parseexpr(expr)
except error.ParseError as inst:
_addparseerrorhint(inst, expr)
raise
def _parseexpr(expr):
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 p = parser.parser(elements)
tree, pos = p.parse(tokenize(expr, 0, len(expr)))
if pos != len(expr):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b'invalid token'), pos)
Yuya Nishihara
templater: add function to parse whole string as template expression...
r28911 return _unnesttemplatelist(tree)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 def prettyformat(tree):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return parser.prettyformat(tree, (b'integer', b'string', b'symbol'))
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 def compileexp(exp, context, curmethods):
Yuya Nishihara
templater: inline compiletemplate() function into engine...
r28956 """Compile parsed template tree to (func, data) pair"""
Yuya Nishihara
templater: fix crash by empty group expression...
r35762 if not exp:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"missing argument"))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 t = exp[0]
Yuya Nishihara
templater: check invalid use of list expression properly (issue5920)...
r40652 return curmethods[t](exp, context)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 # template evaluation
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def getsymbol(exp):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if exp[0] == b'symbol':
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 return exp[1]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"expected a symbol, got '%s'") % exp[0])
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def getlist(x):
if not x:
return []
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if x[0] == b'list':
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 return getlist(x[1]) + [x[2]]
return [x]
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def gettemplate(exp, context):
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 """Compile given template tree or load named template from map file;
returns (func, data) pair"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if exp[0] in (b'template', b'string'):
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 return compileexp(exp, context, methods)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if exp[0] == b'symbol':
Yuya Nishihara
templater: comment that gettemplate() has different name resolution order...
r25599 # unlike runsymbol(), here 'symbol' is always taken as template name
# even if it exists in mapping. this allows us to override mapping
# by web templates, e.g. 'changelogtag' is redefined in map file.
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 return context._load(exp[1])
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"expected template specifier"))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940 def _runrecursivesymbol(context, mapping, key):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b"recursive reference '%s' in template") % key)
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: take any string literals as template, but not for rawstring (BC)...
r25596 def buildtemplate(exp, context):
Yuya Nishihara
templater: expand list of parsed templates to template node...
r28547 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 return (templateutil.runtemplate, ctmpl)
Yuya Nishihara
templater: move runtemplate function out of buildmap/runmap pair...
r25595
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def buildfilter(exp, context):
Yuya Nishihara
templater: inline getfilter() to buildfilter()...
r26104 n = getsymbol(exp[2])
if n in context._filters:
filt = context._filters[n]
Yuya Nishihara
templater: add support for keyword arguments...
r31886 arg = compileexp(exp[1], context, methods)
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 return (templateutil.runfilter, (arg, filt))
Yuya Nishihara
templater: move function table to the "context" object...
r36930 if n in context._funcs:
f = context._funcs[n]
Yuya Nishihara
templater: add support for keyword arguments...
r31886 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
return (f, args)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"unknown function '%s'") % n)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def buildmap(exp, context):
Yuya Nishihara
templater: do not destructure operands in buildmap()...
r34326 darg = compileexp(exp[1], context, methods)
targ = gettemplate(exp[2], context)
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 return (templateutil.runmap, (darg, targ))
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add dot operator to easily access a sub item...
r34536 def buildmember(exp, context):
darg = compileexp(exp[1], context, methods)
memb = getsymbol(exp[2])
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 return (templateutil.runmember, (darg, memb))
Yuya Nishihara
templater: add dot operator to easily access a sub item...
r34536
Augie Fackler
formatting: blacken the codebase...
r43346
Simon Farnsworth
templater: provide arithmetic operations on integers...
r30115 def buildnegate(exp, context):
arg = compileexp(exp[1], context, exprmethods)
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 return (templateutil.runnegate, arg)
Simon Farnsworth
templater: provide arithmetic operations on integers...
r30115
Augie Fackler
formatting: blacken the codebase...
r43346
Simon Farnsworth
templater: provide arithmetic operations on integers...
r30115 def buildarithmetic(exp, context, func):
left = compileexp(exp[1], context, exprmethods)
right = compileexp(exp[2], context, exprmethods)
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 return (templateutil.runarithmetic, (func, left, right))
Simon Farnsworth
templater: provide arithmetic operations on integers...
r30115
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def buildfunc(exp, context):
n = getsymbol(exp[1])
Yuya Nishihara
templater: move function table to the "context" object...
r36930 if n in context._funcs:
f = context._funcs[n]
Yuya Nishihara
templater: add support for keyword arguments...
r31886 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
Matt Mackall
templater: use a global funcs table
r14925 return (f, args)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 if n in context._filters:
Yuya Nishihara
templater: add support for keyword arguments...
r31886 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 if len(args) != 1:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"filter %s expects one argument") % n)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 f = context._filters[n]
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 return (templateutil.runfilter, (args[0], f))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"unknown function '%s'") % n)
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add support for keyword arguments...
r31886 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
"""Compile parsed tree of function arguments into list or dict of
Yuya Nishihara
parser: extend buildargsdict() to support arbitrary number of **kwargs...
r31921 (func, data) pairs
Yuya Nishihara
templater: fix invalid reference of runsymbol in doctest...
r37002 >>> context = engine(lambda t: (templateutil.runsymbol, t))
Yuya Nishihara
parser: extend buildargsdict() to support arbitrary number of **kwargs...
r31921 >>> def fargs(expr, argspec):
... x = _parseexpr(expr)
... n = getsymbol(x[1])
... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
Yuya Nishihara
doctest: coerce dict.keys() to list...
r34141 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
Yuya Nishihara
parser: preserve order of keyword arguments...
r31922 ['l', 'k']
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
Yuya Nishihara
doctest: coerce dict.keys() to list...
r34141 >>> list(args.keys()), list(args[b'opts'].keys())
Yuya Nishihara
parser: preserve order of keyword arguments...
r31922 (['opts'], ['opts', 'k'])
Yuya Nishihara
parser: extend buildargsdict() to support arbitrary number of **kwargs...
r31921 """
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add support for keyword arguments...
r31886 def compiledict(xs):
Augie Fackler
formatting: blacken the codebase...
r43346 return util.sortdict(
Gregory Szorc
py3: finish porting iteritems() to pycompat and remove source transformer...
r43376 (k, compileexp(x, context, curmethods))
for k, x in pycompat.iteritems(xs)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Yuya Nishihara
templater: add support for keyword arguments...
r31886 def compilelist(xs):
return [compileexp(x, context, curmethods) for x in xs]
if not argspec:
# filter or function with no argspec: return list of positional args
return compilelist(getlist(exp))
# function with argspec: return dict of named args
Yuya Nishihara
parser: extend buildargsdict() to support arbitrary number of **kwargs...
r31921 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
Augie Fackler
formatting: blacken the codebase...
r43346 treeargs = parser.buildargsdict(
getlist(exp),
funcname,
argspec,
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 keyvaluenode=b'keyvalue',
keynode=b'symbol',
Augie Fackler
formatting: blacken the codebase...
r43346 )
Yuya Nishihara
parser: preserve order of keyword arguments...
r31922 compargs = util.sortdict()
Yuya Nishihara
templater: add support for keyword arguments...
r31886 if varkey:
compargs[varkey] = compilelist(treeargs.pop(varkey))
Yuya Nishihara
parser: extend buildargsdict() to support arbitrary number of **kwargs...
r31921 if optkey:
compargs[optkey] = compiledict(treeargs.pop(optkey))
Yuya Nishihara
templater: add support for keyword arguments...
r31886 compargs.update(compiledict(treeargs))
return compargs
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add parsing rule for key-value pair...
r31885 def buildkeyvaluepair(exp, content):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ParseError(_(b"can't use a key-value pair in this context"))
Yuya Nishihara
templater: add parsing rule for key-value pair...
r31885
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: check invalid use of list expression properly (issue5920)...
r40652 def buildlist(exp, context):
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.ParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"can't use a list in this context"),
hint=_(b'check place of comma and parens'),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Yuya Nishihara
templater: check invalid use of list expression properly (issue5920)...
r40652
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
exprmethods = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b"integer": lambda e, c: (templateutil.runinteger, e[1]),
b"string": lambda e, c: (templateutil.runstring, e[1]),
b"symbol": lambda e, c: (templateutil.runsymbol, e[1]),
b"template": buildtemplate,
b"group": lambda e, c: compileexp(e[1], c, exprmethods),
b".": buildmember,
b"|": buildfilter,
b"%": buildmap,
b"func": buildfunc,
b"keyvalue": buildkeyvaluepair,
b"list": buildlist,
b"+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
b"-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
b"negate": buildnegate,
b"*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
b"/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
Augie Fackler
formatting: blacken the codebase...
r43346 }
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
methods = exprmethods.copy()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 methods[b"integer"] = exprmethods[b"symbol"] # '{1}' as variable
Yuya Nishihara
templater: switch methods table on compileexp() of func args and inner expr...
r25001
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add parsing and expansion rules to process "templatealias" section...
r28912 class _aliasrules(parser.basealiasrules):
"""Parsing and expansion rule set of template aliases"""
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _section = _(b'template alias')
Yuya Nishihara
templater: add parsing and expansion rules to process "templatealias" section...
r28912 _parse = staticmethod(_parseexpr)
@staticmethod
def _trygetfunc(tree):
"""Return (name, args) if tree is func(...) or ...|filter; otherwise
None"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if tree[0] == b'func' and tree[1][0] == b'symbol':
Yuya Nishihara
templater: add parsing and expansion rules to process "templatealias" section...
r28912 return tree[1][1], getlist(tree[2])
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if tree[0] == b'|' and tree[2][0] == b'symbol':
Yuya Nishihara
templater: add parsing and expansion rules to process "templatealias" section...
r28912 return tree[2][1], [tree[1]]
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: add parsing and expansion rules to process "templatealias" section...
r28912 def expandaliases(tree, aliases):
"""Return new tree of aliases are expanded"""
aliasmap = _aliasrules.buildmap(aliases)
return _aliasrules.expand(aliasmap, tree)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 # template engine
Vadim Gelfer
use safer string parser for template engine.
r1901
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: rename parsestring() to unquotestring() (API)...
r24988 def unquotestring(s):
Yuya Nishihara
templater: relax unquotestring() to fall back to bare string...
r28630 '''unwrap quotes if any; otherwise returns unmodified string'''
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if len(s) < 2 or s[0] not in b"'\"" or s[0] != s[-1]:
Yuya Nishihara
templater: relax unquotestring() to fall back to bare string...
r28630 return s
Yuya Nishihara
templater: remove workaround for escaped quoted string in quoted template...
r25696 return s[1:-1]
Vadim Gelfer
move hgweb template code out to templater
r1896
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
pytype: don't warn us about ignored-on-py3 metaclasses...
r43775 class resourcemapper(object): # pytype: disable=ignored-metaclass
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 """Mapper of internal template resources"""
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 def availablekeys(self, mapping):
Yuya Nishihara
templater: drop symbols which should be overridden by new 'ctx' (issue5612)...
r37093 """Return a set of available resource keys based on the given mapping"""
@abc.abstractmethod
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 def knownkeys(self):
"""Return a set of supported resource keys"""
@abc.abstractmethod
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 def lookup(self, mapping, key):
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 """Return a resource for the key if available; otherwise None"""
Yuya Nishihara
templater: add hook point to populate additional mapping items...
r37120 @abc.abstractmethod
def populatemap(self, context, origmapping, newmapping):
"""Return a dict of additional mapping items which should be paired
with the given new mapping"""
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 class nullresourcemapper(resourcemapper):
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 def availablekeys(self, mapping):
Yuya Nishihara
templater: drop symbols which should be overridden by new 'ctx' (issue5612)...
r37093 return set()
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 def knownkeys(self):
return set()
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 def lookup(self, mapping, key):
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 return None
Yuya Nishihara
templater: add hook point to populate additional mapping items...
r37120 def populatemap(self, context, origmapping, newmapping):
return {}
Augie Fackler
formatting: blacken the codebase...
r43346
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218 class engine(object):
Vadim Gelfer
add doc comments to template code.
r1909 '''template expansion engine.
template expansion works like this. a map file contains key=value
pairs. if value is quoted, it is treated as string. otherwise, it
is treated as name of template file.
templater is asked to expand a key in map. it looks up key, and
TK Soh
minor typo fix in templater's docstring
r4334 looks for strings like this: {foo}. it expands {foo} by looking up
Vadim Gelfer
add doc comments to template code.
r1909 foo in map, and substituting it. expansion is recursive: it stops
when there is no more {foo} to replace.
expansion also allows formatting and filtering.
format uses key to expand each item in list. syntax is
{key%format}.
filter uses function to transform value. syntax is
{key|filter1|filter2|...}.'''
Yuya Nishihara
templater: parse template string to tree by templater class...
r38373 def __init__(self, loader, filters=None, defaults=None, resources=None):
Matt Mackall
templater: privatize class variables
r10848 self._loader = loader
Pierre-Yves David
templater: remove a mutable default argument...
r26330 if filters is None:
filters = {}
Matt Mackall
templater: privatize class variables
r10848 self._filters = filters
Yuya Nishihara
templater: split template functions to new module...
r36940 self._funcs = templatefuncs.funcs # make this a parameter if needed
Pierre-Yves David
templater: remove a mutable default argument...
r26331 if defaults is None:
defaults = {}
Yuya Nishihara
templater: keep default resources per template engine (API)...
r35484 if resources is None:
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 resources = nullresourcemapper()
Matt Mackall
templater: privatize class variables
r10848 self._defaults = defaults
Yuya Nishihara
templater: keep default resources per template engine (API)...
r35484 self._resources = resources
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 self._cache = {} # key: (func, data)
Yuya Nishihara
templater: add function that expands internal literal templates...
r37416 self._tmplcache = {} # literal template: (func, data)
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218
Yuya Nishihara
templater: factor out function to create mapping dict for nested evaluation...
r37092 def overlaymap(self, origmapping, newmapping):
"""Create combined mapping from the original mapping and partial
mapping to override the original"""
Yuya Nishihara
templater: drop symbols which should be overridden by new 'ctx' (issue5612)...
r37093 # do not copy symbols which overrides the defaults depending on
# new resources, so the defaults will be re-evaluated (issue5612)
knownres = self._resources.knownkeys()
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 newres = self._resources.availablekeys(newmapping)
Augie Fackler
formatting: blacken the codebase...
r43346 mapping = {
k: v
Gregory Szorc
py3: finish porting iteritems() to pycompat and remove source transformer...
r43376 for k, v in pycompat.iteritems(origmapping)
Augie Fackler
formatting: blacken the codebase...
r43346 if (
k in knownres # not a symbol per self.symbol()
or newres.isdisjoint(self._defaultrequires(k))
)
}
Yuya Nishihara
templater: factor out function to create mapping dict for nested evaluation...
r37092 mapping.update(newmapping)
Yuya Nishihara
templater: add hook point to populate additional mapping items...
r37120 mapping.update(
Augie Fackler
formatting: blacken the codebase...
r43346 self._resources.populatemap(self, origmapping, newmapping)
)
Yuya Nishihara
templater: factor out function to create mapping dict for nested evaluation...
r37092 return mapping
Yuya Nishihara
templater: drop symbols which should be overridden by new 'ctx' (issue5612)...
r37093 def _defaultrequires(self, key):
"""Resource keys required by the specified default symbol function"""
v = self._defaults.get(key)
if v is None or not callable(v):
return ()
return getattr(v, '_requires', ())
Yuya Nishihara
templater: look up mapping table through template engine...
r35483 def symbol(self, mapping, key):
"""Resolve symbol to value or function; None if nothing found"""
Yuya Nishihara
templater: look up symbols/resources as if they were separated (issue5699)...
r35486 v = None
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 if key not in self._resources.knownkeys():
Yuya Nishihara
templater: look up symbols/resources as if they were separated (issue5699)...
r35486 v = mapping.get(key)
Yuya Nishihara
templater: look up mapping table through template engine...
r35483 if v is None:
v = self._defaults.get(key)
return v
Yuya Nishihara
templater: add public interface returning a set of resource keys...
r37519 def availableresourcekeys(self, mapping):
"""Return a set of available resource keys based on the given mapping"""
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 return self._resources.availablekeys(mapping)
Yuya Nishihara
templater: add public interface returning a set of resource keys...
r37519
def knownresourcekeys(self):
"""Return a set of supported resource keys"""
return self._resources.knownkeys()
Yuya Nishihara
templater: look up mapping table through template engine...
r35483 def resource(self, mapping, key):
"""Return internal data (e.g. cache) used for keyword/function
evaluation"""
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 v = self._resources.lookup(mapping, key)
Yuya Nishihara
templater: keep default resources per template engine (API)...
r35484 if v is None:
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 raise templateutil.ResourceUnavailable(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'template resource not available: %s') % key
Augie Fackler
formatting: blacken the codebase...
r43346 )
Yuya Nishihara
templater: keep default resources per template engine (API)...
r35484 return v
Yuya Nishihara
templater: look up mapping table through template engine...
r35483
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 def _load(self, t):
'''load, parse, and cache a template'''
if t not in self._cache:
Yuya Nishihara
templater: parse template string to tree by templater class...
r38373 x = self._loader(t)
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940 # put poison to cut recursion while compiling 't'
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 self._cache[t] = (_runrecursivesymbol, t)
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940 try:
Yuya Nishihara
templater: inline compiletemplate() function into engine...
r28956 self._cache[t] = compileexp(x, self, methods)
Augie Fackler
formatting: blacken the codebase...
r43346 except: # re-raises
Yuya Nishihara
templater: abort if infinite recursion detected while compiling...
r27940 del self._cache[t]
raise
Matt Mackall
templater: use the parser.py parser to extend the templater syntax
r13176 return self._cache[t]
Yuya Nishihara
templater: add function that expands internal literal templates...
r37416 def _parse(self, tmpl):
"""Parse and cache a literal template"""
if tmpl not in self._tmplcache:
x = parse(tmpl)
self._tmplcache[tmpl] = compileexp(x, self, methods)
return self._tmplcache[tmpl]
Yuya Nishihara
templater: add context.preload(t) to test if the specified template exists...
r37085 def preload(self, t):
"""Load, parse, and cache the specified template if available"""
try:
self._load(t)
return True
except templateutil.TemplateNotFound:
return False
Matt Mackall
templater: drop raw method
r10853 def process(self, t, mapping):
'''Perform expansion. t is name of map element to expand.
mapping contains added elements for use during expansion. Is a
generator.'''
Yuya Nishihara
templater: lift parsed and compiled templates to generic data types...
r28545 func, data = self._load(t)
Yuya Nishihara
templater: add function that expands internal literal templates...
r37416 return self._expand(func, data, mapping)
def expand(self, tmpl, mapping):
"""Perform expansion over a literal template
No user aliases will be expanded since this is supposed to be called
with an internal template string.
"""
func, data = self._parse(tmpl)
return self._expand(func, data, mapping)
def _expand(self, func, data, mapping):
Yuya Nishihara
templater: add hook point to populate additional mapping items...
r37120 # populate additional items only if they don't exist in the given
# mapping. this is slightly different from overlaymap() because the
# initial 'revcache' may contain pre-computed items.
extramapping = self._resources.populatemap(self, {}, mapping)
if extramapping:
extramapping.update(mapping)
mapping = extramapping
Yuya Nishihara
templater: pass (context, mapping) down to unwraphybrid()...
r37290 return templateutil.flatten(self, mapping, func(self, mapping, data))
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218
Augie Fackler
formatting: blacken the codebase...
r43346
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125 def stylelist():
Martin von Zweigbergk
templater: make templatepaths() return a single path, or None...
r45755 path = templatedir()
if not path:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return _(b'no templates found, try `hg debuginstall` for more info')
Martin von Zweigbergk
templater: make templatepaths() return a single path, or None...
r45755 dirlist = os.listdir(path)
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125 stylelist = []
for file in dirlist:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 split = file.split(b".")
if split[-1] in (b'orig', b'rej'):
timeless
templater: ignore orig/rej files...
r28403 continue
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if split[0] == b"map-cmdline":
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125 stylelist.append(split[1])
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b", ".join(sorted(stylelist))
Iulian Stana
templater: show the style list when I try to use a wrong one...
r19125
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: extract function that loads template map file...
r28953 def _readmapfile(mapfile):
"""Load template elements from the given map file"""
if not os.path.exists(mapfile):
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b"style '%s' not found") % mapfile,
hint=_(b"available styles: %s") % stylelist(),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Yuya Nishihara
templater: extract function that loads template map file...
r28953
base = os.path.dirname(mapfile)
Martin von Zweigbergk
templater: do search for include of unqualified builtin outside of config code...
r45770 conf = config.config()
Martin von Zweigbergk
templater: switch to lower-level config.parse() in _readmapfile()...
r45769
def include(rel, abs, remap, sections):
Martin von Zweigbergk
templater: do search for include of unqualified builtin outside of config code...
r45770 templatedirs = [base, templatedir()]
for dir in templatedirs:
Martin von Zweigbergk
templater: handle None returned from templatedir()...
r45773 if dir is None:
continue
Martin von Zweigbergk
templater: do search for include of unqualified builtin outside of config code...
r45770 abs = os.path.normpath(os.path.join(dir, rel))
if os.path.isfile(abs):
data = util.posixfile(abs, b'rb').read()
conf.parse(
abs, data, sections=sections, remap=remap, include=include
)
break
Martin von Zweigbergk
templater: switch to lower-level config.parse() in _readmapfile()...
r45769
data = util.posixfile(mapfile, b'rb').read()
conf.parse(mapfile, data, remap={b'': b'templates'}, include=include)
Yuya Nishihara
templater: extract function that loads template map file...
r28953
cache = {}
tmap = {}
Yuya Nishihara
templater: load aliases from [templatealias] section in map file...
r34716 aliases = []
Yuya Nishihara
templater: simplify merge of __base__ dicts by reading it first
r34713
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 val = conf.get(b'templates', b'__base__')
if val and val[0] not in b"'\"":
Yuya Nishihara
templater: simplify merge of __base__ dicts by reading it first
r34713 # treat as a pointer to a base class for this style
path = util.normpath(os.path.join(base, val))
# fallback check in template paths
if not os.path.exists(path):
Martin von Zweigbergk
templater: handle None returned from templatedir()...
r45773 dir = templatedir()
if dir is not None:
p2 = util.normpath(os.path.join(dir, val))
if os.path.isfile(p2):
path = p2
else:
p3 = util.normpath(os.path.join(p2, b"map"))
if os.path.isfile(p3):
path = p3
Yuya Nishihara
templater: simplify merge of __base__ dicts by reading it first
r34713
Yuya Nishihara
templater: load aliases from [templatealias] section in map file...
r34716 cache, tmap, aliases = _readmapfile(path)
Yuya Nishihara
templater: simplify merge of __base__ dicts by reading it first
r34713
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 for key, val in conf[b'templates'].items():
Yuya Nishihara
templater: extract function that loads template map file...
r28953 if not val:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.ParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'missing value'), conf.source(b'templates', key)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if val[0] in b"'\"":
Yuya Nishihara
templater: extract function that loads template map file...
r28953 if val[0] != val[-1]:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.ParseError(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'unmatched quotes'), conf.source(b'templates', key)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Yuya Nishihara
templater: extract function that loads template map file...
r28953 cache[key] = unquotestring(val)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif key != b'__base__':
Yuya Nishihara
templater: drop extension point of engine classes (API)...
r38372 tmap[key] = os.path.join(base, val)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 aliases.extend(conf[b'templatealias'].items())
Yuya Nishihara
templater: load aliases from [templatealias] section in map file...
r34716 return cache, tmap, aliases
Yuya Nishihara
templater: extract function that loads template map file...
r28953
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: extract template loader to separate class...
r38457 class loader(object):
"""Load template fragments optionally from a map file"""
Yuya Nishihara
templater: rewrite docstring of templater.__init__()...
r35497
Yuya Nishihara
templater: extract template loader to separate class...
r38457 def __init__(self, cache, aliases):
Pierre-Yves David
templater: remove a mutable default argument...
r26334 if cache is None:
cache = {}
Shun-ichi Goto
Duplicate cache when creating templater.
r1975 self.cache = cache.copy()
Yuya Nishihara
templater: mark most attributes as private
r38370 self._map = {}
Yuya Nishihara
templater: parse template string to tree by templater class...
r38373 self._aliasmap = _aliasrules.buildmap(aliases)
Vadim Gelfer
move hgweb template code out to templater
r1896
Vadim Gelfer
many small changes to templater....
r1899 def __contains__(self, key):
Yuya Nishihara
templater: mark most attributes as private
r38370 return key in self.cache or key in self._map
Vadim Gelfer
many small changes to templater....
r1899
Dirkjan Ochtman
templater: separate template management and actual string processing
r8218 def load(self, t):
Yuya Nishihara
templater: parse template string to tree by templater class...
r38373 """Get parsed tree for the given template name. Use a local cache."""
Brodie Rao
cleanup: "not x in y" -> "x not in y"
r16686 if t not in self.cache:
Vadim Gelfer
improve template errors when something is wrong.
r1905 try:
Yuya Nishihara
templater: drop extension point of engine classes (API)...
r38372 self.cache[t] = util.readfile(self._map[t])
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except KeyError as inst:
Yuya Nishihara
templater: extract template evaluation utility to new module...
r36931 raise templateutil.TemplateNotFound(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'"%s" not in template map') % inst.args[0]
Augie Fackler
formatting: blacken the codebase...
r43346 )
Gregory Szorc
global: mass rewrite to use modern exception syntax...
r25660 except IOError as inst:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 reason = _(b'template file %s: %s') % (
Augie Fackler
formatting: blacken the codebase...
r43346 self._map[t],
stringutil.forcebytestr(inst.args[1]),
)
Yuya Nishihara
py3: move between bytes and unicode when re-raising IOError...
r36518 raise IOError(inst.args[0], encoding.strfromlocal(reason))
Yuya Nishihara
templater: parse template string to tree by templater class...
r38373 return self._parse(self.cache[t])
def _parse(self, tmpl):
x = parse(tmpl)
if self._aliasmap:
x = _aliasrules.expand(self._aliasmap, x)
return x
Vadim Gelfer
move hgweb template code out to templater
r1896
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374 def _findsymbolsused(self, tree, syms):
if not tree:
return
op = tree[0]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if op == b'symbol':
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374 s = tree[1]
if s in syms[0]:
Augie Fackler
formatting: blacken the codebase...
r43346 return # avoid recursion: s -> cache[s] -> s
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374 syms[0].add(s)
if s in self.cache or s in self._map:
# s may be a reference for named template
self._findsymbolsused(self.load(s), syms)
return
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if op in {b'integer', b'string'}:
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374 return
# '{arg|func}' == '{func(arg)}'
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if op == b'|':
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374 syms[1].add(getsymbol(tree[2]))
self._findsymbolsused(tree[1], syms)
return
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if op == b'func':
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374 syms[1].add(getsymbol(tree[1]))
self._findsymbolsused(tree[2], syms)
return
for x in tree[1:]:
self._findsymbolsused(x, syms)
Yuya Nishihara
templater: extract template loader to separate class...
r38457 def symbolsused(self, t):
"""Look up (keywords, filters/functions) referenced from the name
template 't'
This may load additional templates from the map file.
"""
syms = (set(), set())
self._findsymbolsused(self.load(t), syms)
return syms
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: extract template loader to separate class...
r38457 class templater(object):
Augie Fackler
formatting: blacken the codebase...
r43346 def __init__(
self,
filters=None,
defaults=None,
resources=None,
cache=None,
aliases=(),
minchunk=1024,
maxchunk=65536,
):
Yuya Nishihara
templater: extract template loader to separate class...
r38457 """Create template engine optionally with preloaded template fragments
- ``filters``: a dict of functions to transform a value into another.
- ``defaults``: a dict of symbol values/functions; may be overridden
by a ``mapping`` dict.
- ``resources``: a resourcemapper object to look up internal data
(e.g. cache), inaccessible from user template.
- ``cache``: a dict of preloaded template fragments.
- ``aliases``: a list of alias (name, replacement) pairs.
self.cache may be updated later to register additional template
fragments.
"""
Yuya Nishihara
templater: remove redundant member variables from templater class...
r38459 allfilters = templatefilters.filters.copy()
if filters:
allfilters.update(filters)
Yuya Nishihara
templater: extract template loader to separate class...
r38457 self._loader = loader(cache, aliases)
Yuya Nishihara
templater: remove redundant member variables from templater class...
r38459 self._proc = engine(self._loader.load, allfilters, defaults, resources)
Yuya Nishihara
templater: extract template loader to separate class...
r38457 self._minchunk, self._maxchunk = minchunk, maxchunk
@classmethod
Augie Fackler
formatting: blacken the codebase...
r43346 def frommapfile(
cls,
mapfile,
filters=None,
defaults=None,
resources=None,
cache=None,
minchunk=1024,
maxchunk=65536,
):
Yuya Nishihara
templater: extract template loader to separate class...
r38457 """Create templater from the specified map file"""
t = cls(filters, defaults, resources, cache, [], minchunk, maxchunk)
cache, tmap, aliases = _readmapfile(mapfile)
t._loader.cache.update(cache)
t._loader._map = tmap
t._loader._aliasmap = _aliasrules.buildmap(aliases)
return t
def __contains__(self, key):
return key in self._loader
@property
def cache(self):
return self._loader.cache
Yuya Nishihara
templater: remove redundant member variables from templater class...
r38459 # for highlight extension to insert one-time 'colorize' filter
@property
def _filters(self):
return self._proc._filters
@property
def defaults(self):
return self._proc._defaults
Yuya Nishihara
templater: extract template loader to separate class...
r38457 def load(self, t):
"""Get parsed tree for the given template name. Use a local cache."""
return self._loader.load(t)
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374 def symbolsuseddefault(self):
"""Look up (keywords, filters/functions) referenced from the default
unnamed template
This may load additional templates from the map file.
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return self.symbolsused(b'')
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374
def symbolsused(self, t):
"""Look up (keywords, filters/functions) referenced from the name
template 't'
This may load additional templates from the map file.
"""
Yuya Nishihara
templater: extract template loader to separate class...
r38457 return self._loader.symbolsused(t)
Yuya Nishihara
templater: add function to look up symbols used in template...
r38374
Yuya Nishihara
templater: rename .render(mapping) to .renderdefault(mapping) (API)...
r37003 def renderdefault(self, mapping):
Yuya Nishihara
templater: add simple interface for unnamed template (API)...
r32873 """Render the default unnamed template and return result as string"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return self.render(b'', mapping)
Yuya Nishihara
templater: factor out helper that renders named template as string...
r37004
def render(self, t, mapping):
"""Render the specified named template and return result as string"""
Yuya Nishihara
templater: do not use stringify() to concatenate flattened template output
r37176 return b''.join(self.generate(t, mapping))
Yuya Nishihara
templater: add simple interface for unnamed template (API)...
r32873
Yuya Nishihara
templater: use named function to expand template against mapping dict (API)...
r37037 def generate(self, t, mapping):
"""Return a generator that renders the specified named template and
yields chunks"""
Yuya Nishihara
templater: resurrect cache of engine instance...
r38458 stream = self._proc.process(t, mapping)
Yuya Nishihara
templater: mark most attributes as private
r38370 if self._minchunk:
Augie Fackler
formatting: blacken the codebase...
r43346 stream = util.increasingchunks(
stream, min=self._minchunk, max=self._maxchunk
)
Brendan Cully
templater: return data in increasing chunk sizes...
r7396 return stream
Dirkjan Ochtman
kill some trailing spaces
r7434
Augie Fackler
formatting: blacken the codebase...
r43346
Martin von Zweigbergk
templater: make templatepaths() return a single path, or None...
r45755 def templatedir():
'''return the directory used for template files, or None.'''
Martin von Zweigbergk
templater: simplify templatepaths() to avoid iterating a singleton list...
r45754 path = os.path.normpath(os.path.join(resourceutil.datapath, b'templates'))
Martin von Zweigbergk
templater: make templatepaths() return a single path, or None...
r45755 return path if os.path.isdir(path) else None
Vadim Gelfer
move changeset_templater into templater module.
r2189
Augie Fackler
formatting: blacken the codebase...
r43346
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 def templatepath(name):
'''return location of template file. returns None if not found.'''
Martin von Zweigbergk
templater: handle None returned from templatedir()...
r45773 dir = templatedir()
if dir is None:
return None
Martin von Zweigbergk
templater: make templatepaths() return a single path, or None...
r45755 f = os.path.join(templatedir(), name)
if f and os.path.exists(f):
return f
Mads Kiilerich
templater: introduce templatepaths for getting paths searched for templates...
r22634 return None
Vadim Gelfer
move changeset_templater into templater module.
r2189
Augie Fackler
formatting: blacken the codebase...
r43346
Martin von Zweigbergk
templater: simplify stylemap() now that templatedir() returns a single path...
r45756 def stylemap(styles, path=None):
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966 """Return path to mapfile for a given style.
Searches mapfile in the following locations:
1. templatepath/style/map
2. templatepath/map-style
3. templatepath/map
"""
Martin von Zweigbergk
templater: simplify stylemap() now that templatedir() returns a single path...
r45756 if path is None:
path = templatedir()
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966
Yuya Nishihara
py3: replace type 'str' by 'bytes' in templater.py
r36568 if isinstance(styles, bytes):
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842 styles = [styles]
Martin von Zweigbergk
templater: handle None returned from templatedir()...
r45773 if path is not None:
for style in styles:
# only plain name is allowed to honor template paths
if (
not style
or style in (pycompat.oscurdir, pycompat.ospardir)
or pycompat.ossep in style
or pycompat.osaltsep
and pycompat.osaltsep in style
):
continue
locations = [os.path.join(style, b'map'), b'map-' + style]
locations.append(b'map')
Dirkjan Ochtman
hgweb: don't choke when an inexistent style is requested (issue1901)
r9842
Martin von Zweigbergk
templater: handle None returned from templatedir()...
r45773 for location in locations:
mapfile = os.path.join(path, location)
if os.path.isfile(mapfile):
return style, mapfile
Dirkjan Ochtman
templater: move stylemap function from hgweb to templater
r7966
Martin von Zweigbergk
templater: simplify stylemap() now that templatedir() returns a single path...
r45756 raise RuntimeError(b"No hgweb templates found in %r" % path)