formatter: add overview of API and example as doctest
authorYuya Nishihara <yuya@tcha.org>
Sat, 22 Oct 2016 15:02:11 +0900
changeset 30560 783016005122
parent 30559 d83ca854fa21
child 30561 7c0c722d568d
formatter: add overview of API and example as doctest
mercurial/formatter.py
tests/test-doctest.py
--- a/mercurial/formatter.py	Sat Oct 22 14:35:10 2016 +0900
+++ b/mercurial/formatter.py	Sat Oct 22 15:02:11 2016 +0900
@@ -5,6 +5,101 @@
 # This software may be used and distributed according to the terms of the
 # GNU General Public License version 2 or any later version.
 
+"""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
+- 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.fout = sys.stdout  # redirect to doctest
+...     ui.verbose = verbose
+...     return fn(ui, ui.formatter(fn.__name__, opts))
+
+Basic example:
+
+>>> def files(ui, fm):
+...     files = [('foo', 123, (0, 0)), ('bar', 456, (1, 0))]
+...     for f in files:
+...         fm.startitem()
+...         fm.write('path', '%s', f[0])
+...         fm.condwrite(ui.verbose, 'date', '  %s',
+...                      fm.formatdate(f[2], '%Y-%m-%d %H:%M:%S'))
+...         fm.data(size=f[1])
+...         fm.plain('\\n')
+...     fm.end()
+>>> show(files)
+foo
+bar
+>>> show(files, verbose=True)
+foo  1970-01-01 00:00:00
+bar  1970-01-01 00:00:01
+>>> show(files, template='json')
+[
+ {
+  "date": [0, 0],
+  "path": "foo",
+  "size": 123
+ },
+ {
+  "date": [1, 0],
+  "path": "bar",
+  "size": 456
+ }
+]
+>>> show(files, template='path: {path}\\ndate: {date|rfc3339date}\\n')
+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()
+...     fm.write('repo', '[%s]\\n', 'baz')
+...     files(ui, fm.nested('files'))
+...     fm.end()
+>>> show(subrepos)
+[baz]
+foo
+bar
+>>> show(subrepos, template='{repo}: {join(files % "{path}", ", ")}\\n')
+baz: foo, bar
+"""
+
 from __future__ import absolute_import
 
 import os
--- a/tests/test-doctest.py	Sat Oct 22 14:35:10 2016 +0900
+++ b/tests/test-doctest.py	Sat Oct 22 15:02:11 2016 +0900
@@ -20,6 +20,7 @@
 testmod('mercurial.dagparser', optionflags=doctest.NORMALIZE_WHITESPACE)
 testmod('mercurial.dispatch')
 testmod('mercurial.encoding')
+testmod('mercurial.formatter')
 testmod('mercurial.hg')
 testmod('mercurial.hgweb.hgwebdir_mod')
 testmod('mercurial.match')