##// END OF EJS Templates
templater: add exception-raising version of open_template()...
templater: add exception-raising version of open_template() I'm about to add another caller of `open_template()` (in the template loader). That caller will want to get exceptions instead of `(None, None)` if the template doesn't exist. This patch therefore changes `open_template()` to raise exceptions and adds a new `try_open_template()` that returns the `(None, None)` value. Differential Revision: https://phab.mercurial-scm.org/D8905

File last commit:

r45880:4aa484ef default
r45880:4aa484ef default
Show More
formatter.py
865 lines | 26.2 KiB | text/x-python | PythonLexer
Matt Mackall
formatter: add basic formatters
r16134 # formatter.py - generic output formatting for mercurial
#
# Copyright 2012 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 """Generic output formatting for Mercurial
The formatter provides API to show data in various ways. The following
functions should be used in place of ui.write():
- fm.write() for unconditional output
- fm.condwrite() to show some extra data conditionally in plain output
Yuya Nishihara
formatter: add support for changeset templating...
r31172 - fm.context() to provide changectx to template output
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 - fm.data() to provide extra data to JSON or template output
- fm.plain() to show raw text that isn't provided to JSON or template output
To show structured data (e.g. date tuples, dicts, lists), apply fm.format*()
beforehand so the data is converted to the appropriate data type. Use
fm.isplain() if you need to convert or format data conditionally which isn't
supported by the formatter API.
To build nested structure (i.e. a list of dicts), use fm.nested().
See also https://www.mercurial-scm.org/wiki/GenericTemplatingPlan
fm.condwrite() vs 'if cond:':
In most cases, use fm.condwrite() so users can selectively show the data
in template output. If it's costly to build data, use plain 'if cond:' with
fm.write().
fm.nested() vs fm.formatdict() (or fm.formatlist()):
fm.nested() should be used to form a tree structure (a list of dicts of
lists of dicts...) which can be accessed through template keywords, e.g.
"{foo % "{bar % {...}} {baz % {...}}"}". On the other hand, fm.formatdict()
exports a dict-type object to template, which can be accessed by e.g.
"{get(foo, key)}" function.
Doctest helper:
>>> def show(fn, verbose=False, **opts):
... import sys
... from . import ui as uimod
... ui = uimod.ui()
... ui.verbose = verbose
Yuya Nishihara
py3: rewrite stdout hack of doctest by using ui.pushbuffer()...
r34256 ... ui.pushbuffer()
... try:
Yuya Nishihara
py3: convert system strings to bytes in doctest of formatter.py
r34257 ... return fn(ui, ui.formatter(pycompat.sysbytes(fn.__name__),
... pycompat.byteskwargs(opts)))
Yuya Nishihara
py3: rewrite stdout hack of doctest by using ui.pushbuffer()...
r34256 ... finally:
... print(pycompat.sysstr(ui.popbuffer()), end='')
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560
Basic example:
>>> def files(ui, fm):
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 ... files = [(b'foo', 123, (0, 0)), (b'bar', 456, (1, 0))]
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 ... for f in files:
... fm.startitem()
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 ... fm.write(b'path', b'%s', f[0])
... fm.condwrite(ui.verbose, b'date', b' %s',
... fm.formatdate(f[2], b'%Y-%m-%d %H:%M:%S'))
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 ... fm.data(size=f[1])
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 ... fm.plain(b'\\n')
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 ... fm.end()
>>> show(files)
foo
bar
>>> show(files, verbose=True)
foo 1970-01-01 00:00:00
bar 1970-01-01 00:00:01
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> show(files, template=b'json')
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 [
{
"date": [0, 0],
"path": "foo",
"size": 123
},
{
"date": [1, 0],
"path": "bar",
"size": 456
}
]
Yuya Nishihara
doctest: bulk-replace string literals with b'' for Python 3...
r34133 >>> show(files, template=b'path: {path}\\ndate: {date|rfc3339date}\\n')
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 path: foo
date: 1970-01-01T00:00:00+00:00
path: bar
date: 1970-01-01T00:00:01+00:00
Nested example:
>>> def subrepos(ui, fm):
... fm.startitem()
Yuya Nishihara
templater: look up symbols/resources as if they were separated (issue5699)...
r35486 ... fm.write(b'reponame', b'[%s]\\n', b'baz')
Yuya Nishihara
formatter: make nested items somewhat readable in template output
r37518 ... files(ui, fm.nested(b'files', tmpl=b'{reponame}'))
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 ... fm.end()
>>> show(subrepos)
[baz]
foo
bar
Yuya Nishihara
templater: look up symbols/resources as if they were separated (issue5699)...
r35486 >>> show(subrepos, template=b'{reponame}: {join(files % "{path}", ", ")}\\n')
Yuya Nishihara
formatter: add overview of API and example as doctest
r30560 baz: foo, bar
"""
Yuya Nishihara
py3: rewrite stdout hack of doctest by using ui.pushbuffer()...
r34256 from __future__ import absolute_import, print_function
Gregory Szorc
formatter: use absolute_import
r25950
Yuya Nishihara
formatter: add helper to create a formatter optionally backed by file...
r32574 import contextlib
Yuya Nishihara
templater: provide loop counter as "index" keyword...
r31807 import itertools
Matt Mackall
formatter: move most of template option helper to formatter...
r25511 import os
Matt Mackall
formatter: add json formatter
r22428
Gregory Szorc
formatter: use absolute_import
r25950 from .i18n import _
from .node import (
hex,
short,
)
Augie Fackler
formatting: blacken the codebase...
r43346 from .thirdparty import attr
Gregory Szorc
formatter: use absolute_import
r25950
from . import (
Pierre-Yves David
error: get Abort from 'error' instead of 'util'...
r26587 error,
Pulkit Goyal
py3: use pycompat.byteskwargs to converts kwargs to bytes...
r32159 pycompat,
Yuya Nishihara
formatter: use templatefilters.json()...
r31782 templatefilters,
Yuya Nishihara
formatter: add function to convert list to appropriate format (issue5217)...
r29676 templatekw,
Gregory Szorc
formatter: use absolute_import
r25950 templater,
Yuya Nishihara
templater: move stringify() to templateutil module...
r36938 templateutil,
Pulkit Goyal
py3: conditionalize cPickle import by adding in util...
r29324 util,
Gregory Szorc
formatter: use absolute_import
r25950 )
Yuya Nishihara
formatter: use stringutil.pprint() in debug output to drop b''
r40310 from .utils import (
Yuya Nishihara
template: add CBOR output format...
r42163 cborutil,
Yuya Nishihara
formatter: use stringutil.pprint() in debug output to drop b''
r40310 dateutil,
stringutil,
)
Gregory Szorc
formatter: use absolute_import
r25950
Pulkit Goyal
py3: conditionalize cPickle import by adding in util...
r29324 pickle = util.pickle
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
config: fix -Tjson to not crash due to unsupported defaultvalue types...
r43643 def isprintable(obj):
"""Check if the given object can be directly passed in to formatter's
write() and data() functions
Returns False if the object is unsupported or must be pre-processed by
formatdate(), formatdict(), or formatlist().
"""
return isinstance(obj, (type(None), bool, int, pycompat.long, float, bytes))
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 class _nullconverter(object):
'''convert non-primitive data types to be processed by formatter'''
Yuya Nishihara
formatter: proxy fm.context() through converter...
r33090
# set to True if context object should be stored as item
storecontext = False
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
Yuya Nishihara
formatter: make nested items somewhat readable in template output
r37518 def wrapnested(data, tmpl, sep):
'''wrap nested data by appropriate type'''
return data
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: make nested items somewhat readable in template output
r37518 @staticmethod
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 def formatdate(date, fmt):
'''convert date tuple to appropriate format'''
Yuya Nishihara
formatter: convert timestamp to int...
r37788 # timestamp can be float, but the canonical form should be int
ts, tz = date
return (int(ts), tz)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
def formatdict(data, key, value, fmt, sep):
'''convert dict or key-value pairs to appropriate dict format'''
# use plain dict instead of util.sortdict so that data can be
# serialized as a builtin dict in pickle output
return dict(data)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
def formatlist(data, name, fmt, sep):
'''convert iterable to appropriate list format'''
return list(data)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 class baseformatter(object):
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 def __init__(self, ui, topic, opts, converter):
Matt Mackall
formatter: add basic formatters
r16134 self._ui = ui
self._topic = topic
Yuya Nishihara
formatter: carry opts to file-based formatters by basefm...
r37615 self._opts = opts
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 self._converter = converter
Matt Mackall
formatter: add basic formatters
r16134 self._item = None
Yuya Nishihara
formatter: add general way to switch hex/short functions...
r22701 # function to convert node to string suitable for this output
self.hexfunc = hex
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add context manager interface for convenience...
r29882 def __enter__(self):
return self
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add context manager interface for convenience...
r29882 def __exit__(self, exctype, excvalue, traceback):
if exctype is None:
self.end()
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def _showitem(self):
'''show a formatted item once all data is collected'''
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def startitem(self):
'''begin an item in the format list'''
if self._item is not None:
self._showitem()
self._item = {}
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def formatdate(self, date, fmt=b'%a %b %d %H:%M:%S %Y %1%2'):
Yuya Nishihara
formatter: add function to convert date tuple to appropriate format
r29678 '''convert date tuple to appropriate format'''
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 return self._converter.formatdate(date, fmt)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def formatdict(self, data, key=b'key', value=b'value', fmt=None, sep=b' '):
Yuya Nishihara
formatter: add function to convert dict to appropriate format...
r29794 '''convert dict or key-value pairs to appropriate dict format'''
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 return self._converter.formatdict(data, key, value, fmt, sep)
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def formatlist(self, data, name, fmt=None, sep=b' '):
Yuya Nishihara
formatter: add function to convert list to appropriate format (issue5217)...
r29676 '''convert iterable to appropriate list format'''
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 # name is mandatory argument for now, but it could be optional if
# we have default template keyword, e.g. {item}
return self._converter.formatlist(data, name, fmt, sep)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add support for changeset templating...
r31172 def context(self, **ctxs):
'''insert context objects to be used to render template keywords'''
Yuya Nishihara
formatter: proxy fm.context() through converter...
r33090 ctxs = pycompat.byteskwargs(ctxs)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 assert all(k in {b'repo', b'ctx', b'fctx'} for k in ctxs)
Yuya Nishihara
formatter: proxy fm.context() through converter...
r33090 if self._converter.storecontext:
Yuya Nishihara
formatter: fill missing resources by formatter, not by resource mapper...
r39619 # populate missing resources in fctx -> ctx -> repo order
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'fctx' in ctxs and b'ctx' not in ctxs:
ctxs[b'ctx'] = ctxs[b'fctx'].changectx()
if b'ctx' in ctxs and b'repo' not in ctxs:
ctxs[b'repo'] = ctxs[b'ctx'].repo()
Yuya Nishihara
formatter: proxy fm.context() through converter...
r33090 self._item.update(ctxs)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: provide hint of referenced field names...
r38375 def datahint(self):
'''set of field names to be referenced'''
return set()
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def data(self, **data):
'''insert data into item that's not shown in default output'''
Pulkit Goyal
py3: use pycompat.byteskwargs to converts kwargs to bytes...
r32159 data = pycompat.byteskwargs(data)
David M. Carr
formatter: improve implementation of data method...
r17630 self._item.update(data)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def write(self, fields, deftext, *fielddata, **opts):
'''do default text output while assigning data to item'''
Yuya Nishihara
formatter: verify number of arguments passed to write functions...
r26372 fieldkeys = fields.split()
Boris Feld
formatter: more details on assertion failure...
r40175 assert len(fieldkeys) == len(fielddata), (fieldkeys, fielddata)
Yuya Nishihara
formatter: use dict.update() to set arguments passed to write functions...
r26373 self._item.update(zip(fieldkeys, fielddata))
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add condwrite method...
r17909 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
'''do conditional write (primarily for plain formatter)'''
Yuya Nishihara
formatter: verify number of arguments passed to write functions...
r26372 fieldkeys = fields.split()
assert len(fieldkeys) == len(fielddata)
Yuya Nishihara
formatter: use dict.update() to set arguments passed to write functions...
r26373 self._item.update(zip(fieldkeys, fielddata))
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def plain(self, text, **opts):
'''show raw text for non-templated mode'''
Augie Fackler
formatting: blacken the codebase...
r43346
Mathias De Maré
formatter: introduce isplain() to replace (the inverse of) __nonzero__() (API)...
r29949 def isplain(self):
'''check for plain formatter usage'''
return False
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def nested(self, field, tmpl=None, sep=b''):
Yuya Nishihara
formatter: add fm.nested(field) to either write or build sub items...
r29837 '''sub formatter to store nested data in the specified field'''
Yuya Nishihara
formatter: make nested items somewhat readable in template output
r37518 data = []
self._item[field] = self._converter.wrapnested(data, tmpl, sep)
Yuya Nishihara
formatter: add fm.nested(field) to either write or build sub items...
r29837 return _nestedformatter(self._ui, self._converter, data)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def end(self):
'''end output for the formatter'''
if self._item is not None:
self._showitem()
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: carry opts to file-based formatters by basefm...
r37615 def nullformatter(ui, topic, opts):
Yuya Nishihara
formatter: add nullformatter...
r32575 '''formatter that prints nothing'''
Yuya Nishihara
formatter: carry opts to file-based formatters by basefm...
r37615 return baseformatter(ui, topic, opts, converter=_nullconverter)
Yuya Nishihara
formatter: add nullformatter...
r32575
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add fm.nested(field) to either write or build sub items...
r29837 class _nestedformatter(baseformatter):
'''build sub items and store them in the parent formatter'''
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add fm.nested(field) to either write or build sub items...
r29837 def __init__(self, ui, converter, data):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 baseformatter.__init__(
self, ui, topic=b'', opts={}, converter=converter
)
Yuya Nishihara
formatter: add fm.nested(field) to either write or build sub items...
r29837 self._data = data
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add fm.nested(field) to either write or build sub items...
r29837 def _showitem(self):
self._data.append(self._item)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add function to convert dict to appropriate format...
r29794 def _iteritems(data):
'''iterate key-value pairs in stable order'''
if isinstance(data, dict):
Gregory Szorc
py3: finish porting iteritems() to pycompat and remove source transformer...
r43376 return sorted(pycompat.iteritems(data))
Yuya Nishihara
formatter: add function to convert dict to appropriate format...
r29794 return data
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 class _plainconverter(object):
'''convert non-primitive data types to text'''
Yuya Nishihara
formatter: proxy fm.context() through converter...
r33090
storecontext = False
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
Yuya Nishihara
formatter: make nested items somewhat readable in template output
r37518 def wrapnested(data, tmpl, sep):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.ProgrammingError(b'plainformatter should never be nested')
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: make nested items somewhat readable in template output
r37518 @staticmethod
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 def formatdate(date, fmt):
'''stringify date tuple in the given format'''
Boris Feld
util: extract all date-related utils in utils/dateutil module...
r36625 return dateutil.datestr(date, fmt)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
def formatdict(data, key, value, fmt, sep):
'''stringify key-value pairs separated by sep'''
Yuya Nishihara
templater: byte-stringify dict/list values before passing to default format...
r36652 prefmt = pycompat.identity
Yuya Nishihara
templater: allow dynamically switching the default dict/list formatting...
r36651 if fmt is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fmt = b'%s=%s'
Yuya Nishihara
templater: byte-stringify dict/list values before passing to default format...
r36652 prefmt = pycompat.bytestr
Augie Fackler
formatting: blacken the codebase...
r43346 return sep.join(
fmt % (prefmt(k), prefmt(v)) for k, v in _iteritems(data)
)
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
def formatlist(data, name, fmt, sep):
'''stringify iterable separated by sep'''
Yuya Nishihara
templater: byte-stringify dict/list values before passing to default format...
r36652 prefmt = pycompat.identity
Yuya Nishihara
templater: allow dynamically switching the default dict/list formatting...
r36651 if fmt is None:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 fmt = b'%s'
Yuya Nishihara
templater: byte-stringify dict/list values before passing to default format...
r36652 prefmt = pycompat.bytestr
return sep.join(fmt % prefmt(e) for e in data)
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 class plainformatter(baseformatter):
'''the default text output scheme'''
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 def __init__(self, ui, out, topic, opts):
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
Yuya Nishihara
formatter: add general way to switch hex/short functions...
r22701 if ui.debugflag:
self.hexfunc = hex
else:
self.hexfunc = short
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 if ui is out:
self._write = ui.write
else:
self._write = lambda s, **opts: out.write(s)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def startitem(self):
pass
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def data(self, **data):
pass
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def write(self, fields, deftext, *fielddata, **opts):
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 self._write(deftext % fielddata, **opts)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add condwrite method...
r17909 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
'''do conditional write'''
if cond:
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 self._write(deftext % fielddata, **opts)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def plain(self, text, **opts):
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 self._write(text, **opts)
Augie Fackler
formatting: blacken the codebase...
r43346
Mathias De Maré
formatter: introduce isplain() to replace (the inverse of) __nonzero__() (API)...
r29949 def isplain(self):
return True
Augie Fackler
formatting: blacken the codebase...
r43346
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 def nested(self, field, tmpl=None, sep=b''):
Yuya Nishihara
formatter: add fm.nested(field) to either write or build sub items...
r29837 # nested data will be directly written to ui
return self
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def end(self):
pass
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 class debugformatter(baseformatter):
Yuya Nishihara
formatter: add argument to change output file of non-plain formatter...
r31182 def __init__(self, ui, out, topic, opts):
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
Yuya Nishihara
formatter: add argument to change output file of non-plain formatter...
r31182 self._out = out
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._out.write(b"%s = [\n" % self._topic)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add basic formatters
r16134 def _showitem(self):
Augie Fackler
formatting: blacken the codebase...
r43346 self._out.write(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b' %s,\n' % stringutil.pprint(self._item, indent=4, level=1)
Augie Fackler
formatting: blacken the codebase...
r43346 )
Matt Mackall
formatter: add basic formatters
r16134 def end(self):
baseformatter.end(self)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._out.write(b"]\n")
Matt Mackall
formatter: add basic formatters
r16134
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add pickle format...
r22430 class pickleformatter(baseformatter):
Yuya Nishihara
formatter: add argument to change output file of non-plain formatter...
r31182 def __init__(self, ui, out, topic, opts):
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
Yuya Nishihara
formatter: add argument to change output file of non-plain formatter...
r31182 self._out = out
Matt Mackall
formatter: add pickle format...
r22430 self._data = []
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add pickle format...
r22430 def _showitem(self):
self._data.append(self._item)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add pickle format...
r22430 def end(self):
baseformatter.end(self)
Yuya Nishihara
formatter: add argument to change output file of non-plain formatter...
r31182 self._out.write(pickle.dumps(self._data))
Matt Mackall
formatter: add pickle format...
r22430
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
template: add CBOR output format...
r42163 class cborformatter(baseformatter):
'''serialize items as an indefinite-length CBOR array'''
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
template: add CBOR output format...
r42163 def __init__(self, ui, out, topic, opts):
baseformatter.__init__(self, ui, topic, opts, _nullconverter)
self._out = out
self._out.write(cborutil.BEGIN_INDEFINITE_ARRAY)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
template: add CBOR output format...
r42163 def _showitem(self):
self._out.write(b''.join(cborutil.streamencode(self._item)))
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
template: add CBOR output format...
r42163 def end(self):
baseformatter.end(self)
self._out.write(cborutil.BREAK)
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add json formatter
r22428 class jsonformatter(baseformatter):
Yuya Nishihara
formatter: add argument to change output file of non-plain formatter...
r31182 def __init__(self, ui, out, topic, opts):
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
Yuya Nishihara
formatter: add argument to change output file of non-plain formatter...
r31182 self._out = out
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._out.write(b"[")
Martin von Zweigbergk
formatter: set _first on formatter, not ui...
r31298 self._first = True
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add json formatter
r22428 def _showitem(self):
Martin von Zweigbergk
formatter: set _first on formatter, not ui...
r31298 if self._first:
self._first = False
Matt Mackall
formatter: add json formatter
r22428 else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._out.write(b",")
Matt Mackall
formatter: add json formatter
r22428
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._out.write(b"\n {\n")
Matt Mackall
formatter: add json formatter
r22428 first = True
for k, v in sorted(self._item.items()):
if first:
first = False
else:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._out.write(b",\n")
Yuya Nishihara
formatter: use templatefilters.json()...
r31782 u = templatefilters.json(v, paranoid=False)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._out.write(b' "%s": %s' % (k, u))
self._out.write(b"\n }")
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add json formatter
r22428 def end(self):
baseformatter.end(self)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._out.write(b"\n]\n")
Matt Mackall
formatter: add json formatter
r22428
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 class _templateconverter(object):
'''convert non-primitive data types to be processed by templater'''
Yuya Nishihara
formatter: proxy fm.context() through converter...
r33090
storecontext = True
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
Yuya Nishihara
formatter: make nested items somewhat readable in template output
r37518 def wrapnested(data, tmpl, sep):
'''wrap nested data by templatable type'''
return templateutil.mappinglist(data, tmpl=tmpl, sep=sep)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: make nested items somewhat readable in template output
r37518 @staticmethod
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 def formatdate(date, fmt):
'''return date tuple'''
Yuya Nishihara
templater: introduce a wrapper for date tuple (BC)...
r38304 return templateutil.date(date)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
def formatdict(data, key, value, fmt, sep):
'''build object that can be evaluated as either plain string or dict'''
data = util.sortdict(_iteritems(data))
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 def f():
yield _plainconverter.formatdict(data, key, value, fmt, sep)
Augie Fackler
formatting: blacken the codebase...
r43346
return templateutil.hybriddict(
data, key=key, value=value, fmt=fmt, gen=f
)
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 @staticmethod
def formatlist(data, name, fmt, sep):
'''build object that can be evaluated as either plain string or list'''
data = list(data)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 def f():
yield _plainconverter.formatlist(data, name, fmt, sep)
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: move hybrid class and functions to templateutil module...
r36939 return templateutil.hybridlist(data, name=name, fmt=fmt, gen=f)
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836
Augie Fackler
formatting: blacken the codebase...
r43346
Matt Mackall
formatter: add template support...
r25513 class templateformatter(baseformatter):
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 def __init__(self, ui, out, topic, opts, spec, overridetemplates=None):
Yuya Nishihara
formatter: factor out format*() functions to separate classes...
r29836 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
Yuya Nishihara
formatter: add argument to change output file of non-plain formatter...
r31182 self._out = out
Yuya Nishihara
formatter: render template specified by templatespec tuple
r32841 self._tref = spec.ref
Augie Fackler
formatting: blacken the codebase...
r43346 self._t = loadtemplater(
ui,
spec,
defaults=templatekw.keywords,
resources=templateresources(ui),
cache=templatekw.defaulttempl,
)
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 if overridetemplates:
self._t.cache.update(overridetemplates)
Augie Fackler
formatting: blacken the codebase...
r43346 self._parts = templatepartsmap(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 spec, self._t, [b'docheader', b'docfooter', b'separator']
Augie Fackler
formatting: blacken the codebase...
r43346 )
Yuya Nishihara
templater: provide loop counter as "index" keyword...
r31807 self._counter = itertools.count()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._renderitem(b'docheader', {})
Yuya Nishihara
formatter: add support for docheader and docfooter templates...
r32949
Matt Mackall
formatter: add template support...
r25513 def _showitem(self):
Yuya Nishihara
formatter: extract helper function to render template
r32948 item = self._item.copy()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 item[b'index'] = index = next(self._counter)
Yuya Nishihara
formatter: add support for separator template...
r32950 if index > 0:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._renderitem(b'separator', {})
Yuya Nishihara
formatter: extract helper function to render template
r32948 self._renderitem(self._tref, item)
Yuya Nishihara
formatter: add support for docheader and docfooter templates...
r32949 def _renderitem(self, part, item):
if part not in self._parts:
return
ref = self._parts[part]
Yuya Nishihara
formatter: fix handling of None value in templater mapping...
r43645 # None can't be put in the mapping dict since it means <unset>
for k, v in item.items():
if v is None:
item[k] = templateutil.wrappedvalue(v)
Yuya Nishihara
templater: switch 'revcache' based on new mapping items...
r37121 self._out.write(self._t.render(ref, item))
Matt Mackall
formatter: add template support...
r25513
Yuya Nishihara
formatter: provide hint of referenced field names...
r38375 @util.propertycache
def _symbolsused(self):
Yuya Nishihara
formatter: look for template symbols from the associated name...
r38465 return self._t.symbolsused(self._tref)
Yuya Nishihara
formatter: provide hint of referenced field names...
r38375
def datahint(self):
'''set of field names to be referenced from the template'''
return self._symbolsused[0]
Yuya Nishihara
formatter: add support for docheader and docfooter templates...
r32949 def end(self):
baseformatter.end(self)
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 self._renderitem(b'docfooter', {})
Yuya Nishihara
formatter: add support for docheader and docfooter templates...
r32949
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: ditch namedtuple in favor of attr
r37860 @attr.s(frozen=True)
class templatespec(object):
ref = attr.ib()
tmpl = attr.ib()
mapfile = attr.ib()
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 refargs = attr.ib(default=None)
Martin von Zweigbergk
templater: pass opened file-like object to templatespec...
r45870 fp = attr.ib(default=None)
Yuya Nishihara
formatter: wrap (tmpl, mapfile) by named tuple...
r32838
Augie Fackler
formatting: blacken the codebase...
r43346
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 def empty_templatespec():
return templatespec(None, None, None)
def reference_templatespec(ref, refargs=None):
return templatespec(ref, None, None, refargs)
def literal_templatespec(tmpl):
Martin von Zweigbergk
templatespec: move check for non-unicode to lower-level function...
r45826 if pycompat.ispy3:
assert not isinstance(tmpl, str), b'tmpl must not be a str'
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return templatespec(b'', tmpl, None)
Martin von Zweigbergk
templater: pass opened file-like object to templatespec...
r45870 def mapfile_templatespec(topic, mapfile, fp=None):
return templatespec(topic, None, mapfile, fp=fp)
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824
Matt Mackall
formatter: move most of template option helper to formatter...
r25511 def lookuptemplate(ui, topic, tmpl):
Yuya Nishihara
formatter: document lookuptemplate()
r32834 """Find the template matching the given -T/--template spec 'tmpl'
'tmpl' can be any of the following:
- a literal template (e.g. '{rev}')
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370 - a reference to built-in template (i.e. formatter)
Yuya Nishihara
formatter: document lookuptemplate()
r32834 - a map-file name or path (e.g. 'changelog')
- a reference to [templates] in config file
- a path to raw template file
A map file defines a stand-alone template environment. If a map file
selected, all templates defined in the file will be loaded, and the
Yuya Nishihara
templater: load aliases from [templatealias] section in map file...
r34716 template matching the given topic will be rendered. Aliases won't be
loaded from user config, but from the map file.
Yuya Nishihara
formatter: load templates section like a map file...
r32875
If no map file selected, all templates in [templates] section will be
available as well as aliases in [templatealias].
Yuya Nishihara
formatter: document lookuptemplate()
r32834 """
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370 if not tmpl:
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return empty_templatespec()
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370
Matt Mackall
formatter: move most of template option helper to formatter...
r25511 # looks like a literal template?
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'{' in tmpl:
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return literal_templatespec(tmpl)
Matt Mackall
formatter: move most of template option helper to formatter...
r25511
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370 # a reference to built-in (formatter) template
if tmpl in {b'cbor', b'json', b'pickle', b'debug'}:
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return reference_templatespec(tmpl)
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 # a function-style reference to built-in template
func, fsep, ftail = tmpl.partition(b'(')
if func in {b'cbor', b'json'} and fsep and ftail.endswith(b')'):
templater.parseexpr(tmpl) # make sure syntax errors are confined
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return reference_templatespec(func, refargs=ftail[:-1])
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371
Matt Mackall
formatter: move most of template option helper to formatter...
r25511 # perhaps a stock style?
if not os.path.split(tmpl)[0]:
Martin von Zweigbergk
templater: add exception-raising version of open_template()...
r45880 (mapname, fp) = templater.try_open_template(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'map-cmdline.' + tmpl
Martin von Zweigbergk
templater: add exception-raising version of open_template()...
r45880 ) or templater.try_open_template(tmpl)
Martin von Zweigbergk
formatter: remove now-unnecessary check for file-ness...
r45820 if mapname:
Martin von Zweigbergk
templater: pass opened file-like object to templatespec...
r45870 return mapfile_templatespec(topic, mapname, fp)
Matt Mackall
formatter: move most of template option helper to formatter...
r25511
# perhaps it's a reference to [templates]
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if ui.config(b'templates', tmpl):
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return reference_templatespec(tmpl)
Matt Mackall
formatter: move most of template option helper to formatter...
r25511
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if tmpl == b'list':
ui.write(_(b"available styles: %s\n") % templater.stylelist())
raise error.Abort(_(b"specify a template"))
Matt Mackall
formatter: move most of template option helper to formatter...
r25511
# perhaps it's a path to a map or a template
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if (b'/' in tmpl or b'\\' in tmpl) and os.path.isfile(tmpl):
Matt Mackall
formatter: move most of template option helper to formatter...
r25511 # is it a mapfile for a style?
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if os.path.basename(tmpl).startswith(b"map-"):
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return mapfile_templatespec(topic, os.path.realpath(tmpl))
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 with util.posixfile(tmpl, b'rb') as f:
Yuya Nishihara
formatter: close raw template file explicitly
r32827 tmpl = f.read()
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return literal_templatespec(tmpl)
Matt Mackall
formatter: move most of template option helper to formatter...
r25511
# constant string?
Martin von Zweigbergk
templatespec: create a factory function for each type there is...
r45824 return literal_templatespec(tmpl)
Matt Mackall
formatter: move most of template option helper to formatter...
r25511
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add support for docheader and docfooter templates...
r32949 def templatepartsmap(spec, t, partnames):
"""Create a mapping of {part: ref}"""
partsmap = {spec.ref: spec.ref} # initial ref must exist in t
if spec.mapfile:
partsmap.update((p, p) for p in partnames if p in t)
Yuya Nishihara
formatter: add support for parts map of [templates] section...
r32952 elif spec.ref:
for part in partnames:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ref = b'%s:%s' % (spec.ref, part) # select config sub-section
Yuya Nishihara
formatter: add support for parts map of [templates] section...
r32952 if ref in t:
partsmap[part] = ref
Yuya Nishihara
formatter: add support for docheader and docfooter templates...
r32949 return partsmap
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: register keywords to defaults table...
r35499 def loadtemplater(ui, spec, defaults=None, resources=None, cache=None):
Yuya Nishihara
formatter: factor out function to create templater from literal or map file...
r32831 """Create a templater from either a literal template or loading from
a map file"""
Yuya Nishihara
formatter: wrap (tmpl, mapfile) by named tuple...
r32838 assert not (spec.tmpl and spec.mapfile)
if spec.mapfile:
Martin von Zweigbergk
formatter: inline a variable assigned from `templater.templater.frommapfile`...
r45822 return templater.templater.frommapfile(
Martin von Zweigbergk
templater: pass opened file-like object to templatespec...
r45870 spec.mapfile,
spec.fp,
defaults=defaults,
resources=resources,
cache=cache,
Augie Fackler
formatting: blacken the codebase...
r43346 )
return maketemplater(
ui, spec.tmpl, defaults=defaults, resources=resources, cache=cache
)
Yuya Nishihara
templater: factor out function that creates templater from string template...
r28955
Yuya Nishihara
templater: register keywords to defaults table...
r35499 def maketemplater(ui, tmpl, defaults=None, resources=None, cache=None):
Yuya Nishihara
templater: factor out function that creates templater from string template...
r28955 """Create a templater from a string template 'tmpl'"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 aliases = ui.configitems(b'templatealias')
Augie Fackler
formatting: blacken the codebase...
r43346 t = templater.templater(
defaults=defaults, resources=resources, cache=cache, aliases=aliases
)
t.cache.update(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (k, templater.unquotestring(v)) for k, v in ui.configitems(b'templates')
Augie Fackler
formatting: blacken the codebase...
r43346 )
Matt Mackall
formatter: add a method to build a full templater from a -T option
r25512 if tmpl:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 t.cache[b''] = tmpl
Matt Mackall
formatter: add a method to build a full templater from a -T option
r25512 return t
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: populate fctx from ctx and path value...
r39622 # marker to denote a resource to be loaded on demand based on mapping values
# (e.g. (ctx, path) -> fctx)
_placeholder = object()
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 class templateresources(templater.resourcemapper):
"""Resource mapper designed for the default templatekw and function"""
def __init__(self, ui, repo=None):
self._resmap = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'cache': {}, # for templatekw/funcs to store reusable data
b'repo': repo,
b'ui': ui,
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 }
Yuya Nishihara
templater: move repo, ui and cache to per-engine resources
r35485
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 def availablekeys(self, mapping):
Augie Fackler
formatting: blacken the codebase...
r43346 return {
k for k in self.knownkeys() if self._getsome(mapping, k) is not None
}
Yuya Nishihara
templater: drop symbols which should be overridden by new 'ctx' (issue5612)...
r37093
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 def knownkeys(self):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return {b'cache', b'ctx', b'fctx', b'repo', b'revcache', b'ui'}
Yuya Nishihara
templater: introduce resourcemapper class...
r37091
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 def lookup(self, mapping, key):
Yuya Nishihara
formatter: inline _gettermap and _knownkeys
r39620 if key not in self.knownkeys():
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 return None
Yuya Nishihara
formatter: populate fctx from ctx and path value...
r39622 v = self._getsome(mapping, key)
if v is _placeholder:
v = mapping[key] = self._loadermap[key](self, mapping)
return v
Yuya Nishihara
templater: introduce resourcemapper class...
r37091
Yuya Nishihara
templater: add hook point to populate additional mapping items...
r37120 def populatemap(self, context, origmapping, newmapping):
Yuya Nishihara
templater: switch 'revcache' based on new mapping items...
r37121 mapping = {}
Yuya Nishihara
formatter: factor out function that detects node change and document it...
r39621 if self._hasnodespec(newmapping):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 mapping[b'revcache'] = {} # per-ctx cache
Yuya Nishihara
formatter: factor out function that detects node change and document it...
r39621 if self._hasnodespec(origmapping) and self._hasnodespec(newmapping):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 orignode = templateutil.runsymbol(context, origmapping, b'node')
mapping[b'originalnode'] = orignode
Yuya Nishihara
formatter: populate ctx from repo and node value...
r39659 # put marker to override 'ctx'/'fctx' in mapping if any, and flag
Yuya Nishihara
formatter: populate fctx from ctx and path value...
r39622 # its existence to be reported by availablekeys()
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 if b'ctx' not in newmapping and self._hasliteral(newmapping, b'node'):
mapping[b'ctx'] = _placeholder
if b'fctx' not in newmapping and self._hasliteral(newmapping, b'path'):
mapping[b'fctx'] = _placeholder
Yuya Nishihara
templater: switch 'revcache' based on new mapping items...
r37121 return mapping
Yuya Nishihara
templater: add hook point to populate additional mapping items...
r37120
Yuya Nishihara
templater: remove unused context argument from most resourcemapper functions...
r39618 def _getsome(self, mapping, key):
Yuya Nishihara
templater: process mapping dict by resource callables...
r36998 v = mapping.get(key)
if v is not None:
return v
Yuya Nishihara
templater: introduce resourcemapper class...
r37091 return self._resmap.get(key)
Yuya Nishihara
templater: convert resources to a table of callables for future extension...
r36997
Yuya Nishihara
formatter: populate fctx from ctx and path value...
r39622 def _hasliteral(self, mapping, key):
"""Test if a literal value is set or unset in the given mapping"""
return key in mapping and not callable(mapping[key])
def _getliteral(self, mapping, key):
"""Return value of the given name if it is a literal"""
v = mapping.get(key)
if callable(v):
return None
return v
Yuya Nishihara
formatter: factor out function that detects node change and document it...
r39621 def _hasnodespec(self, mapping):
"""Test if context revision is set or unset in the given mapping"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 return b'node' in mapping or b'ctx' in mapping
Yuya Nishihara
formatter: unblock storing fctx as a template resource...
r36999
Yuya Nishihara
formatter: populate ctx from repo and node value...
r39659 def _loadctx(self, mapping):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 repo = self._getsome(mapping, b'repo')
node = self._getliteral(mapping, b'node')
Yuya Nishihara
formatter: populate ctx from repo and node value...
r39659 if repo is None or node is None:
return
try:
return repo[node]
except error.RepoLookupError:
Augie Fackler
formatting: blacken the codebase...
r43346 return None # maybe hidden/non-existent node
Yuya Nishihara
formatter: populate ctx from repo and node value...
r39659
Yuya Nishihara
formatter: populate fctx from ctx and path value...
r39622 def _loadfctx(self, mapping):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 ctx = self._getsome(mapping, b'ctx')
path = self._getliteral(mapping, b'path')
Yuya Nishihara
formatter: populate fctx from ctx and path value...
r39622 if ctx is None or path is None:
return None
try:
return ctx[path]
except error.LookupError:
Augie Fackler
formatting: blacken the codebase...
r43346 return None # maybe removed file?
Yuya Nishihara
formatter: populate fctx from ctx and path value...
r39622
_loadermap = {
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'ctx': _loadctx,
b'fctx': _loadfctx,
Yuya Nishihara
formatter: populate fctx from ctx and path value...
r39622 }
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 def _internaltemplateformatter(
ui,
out,
topic,
opts,
spec,
tmpl,
docheader=b'',
docfooter=b'',
separator=b'',
):
"""Build template formatter that handles customizable built-in templates
such as -Tjson(...)"""
templates = {spec.ref: tmpl}
if docheader:
templates[b'%s:docheader' % spec.ref] = docheader
if docfooter:
templates[b'%s:docfooter' % spec.ref] = docfooter
if separator:
templates[b'%s:separator' % spec.ref] = separator
return templateformatter(
ui, out, topic, opts, spec, overridetemplates=templates
)
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 def formatter(ui, out, topic, opts):
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370 spec = lookuptemplate(ui, topic, opts.get(b'template', b''))
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 if spec.ref == b"cbor" and spec.refargs is not None:
return _internaltemplateformatter(
ui,
out,
topic,
opts,
spec,
tmpl=b'{dict(%s)|cbor}' % spec.refargs,
docheader=cborutil.BEGIN_INDEFINITE_ARRAY,
docfooter=cborutil.BREAK,
)
elif spec.ref == b"cbor":
Yuya Nishihara
template: add CBOR output format...
r42163 return cborformatter(ui, out, topic, opts)
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 elif spec.ref == b"json" and spec.refargs is not None:
return _internaltemplateformatter(
ui,
out,
topic,
opts,
spec,
tmpl=b'{dict(%s)|json}' % spec.refargs,
docheader=b'[\n ',
docfooter=b'\n]\n',
separator=b',\n ',
)
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370 elif spec.ref == b"json":
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 return jsonformatter(ui, out, topic, opts)
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370 elif spec.ref == b"pickle":
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 assert spec.refargs is None, r'function-style not supported'
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 return pickleformatter(ui, out, topic, opts)
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370 elif spec.ref == b"debug":
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 assert spec.refargs is None, r'function-style not supported'
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 return debugformatter(ui, out, topic, opts)
Yuya Nishihara
formatter: parse name of built-in formatter templates in standard way...
r43370 elif spec.ref or spec.tmpl or spec.mapfile:
Yuya Nishihara
formatter: map -Tjson(...) and -Tcbor(...) to templater...
r43371 assert spec.refargs is None, r'function-style not supported'
Yuya Nishihara
formatter: pass in template spec to templateformatter as argument...
r43369 return templateformatter(ui, out, topic, opts, spec)
Matt Mackall
formatter: mark developer options
r25838 # developer config: ui.formatdebug
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif ui.configbool(b'ui', b'formatdebug'):
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 return debugformatter(ui, out, topic, opts)
Matt Mackall
formatter: mark developer options
r25838 # deprecated config: ui.formatjson
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 elif ui.configbool(b'ui', b'formatjson'):
Yuya Nishihara
formatter: add option to redirect output to file object...
r32573 return jsonformatter(ui, out, topic, opts)
return plainformatter(ui, out, topic, opts)
Yuya Nishihara
formatter: add helper to create a formatter optionally backed by file...
r32574
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add helper to create a formatter optionally backed by file...
r32574 @contextlib.contextmanager
def openformatter(ui, filename, topic, opts):
"""Create a formatter that writes outputs to the specified file
Must be invoked using the 'with' statement.
"""
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 with util.posixfile(filename, b'wb') as out:
Yuya Nishihara
formatter: add helper to create a formatter optionally backed by file...
r32574 with formatter(ui, out, topic, opts) as fm:
yield fm
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: add helper to create a formatter optionally backed by file...
r32574 @contextlib.contextmanager
def _neverending(fm):
yield fm
Augie Fackler
formatting: blacken the codebase...
r43346
Yuya Nishihara
formatter: carry opts to file-based formatters by basefm...
r37615 def maybereopen(fm, filename):
Yuya Nishihara
formatter: add helper to create a formatter optionally backed by file...
r32574 """Create a formatter backed by file if filename specified, else return
the given formatter
Must be invoked using the 'with' statement. This will never call fm.end()
of the given formatter.
"""
if filename:
Yuya Nishihara
formatter: carry opts to file-based formatters by basefm...
r37615 return openformatter(fm._ui, filename, fm._topic, fm._opts)
Yuya Nishihara
formatter: add helper to create a formatter optionally backed by file...
r32574 else:
return _neverending(fm)