##// END OF EJS Templates
formatter: add function to convert dict to appropriate format...
Yuya Nishihara -
r29794:4891f3b9 default
parent child Browse files
Show More
@@ -1,251 +1,279 b''
1 # formatter.py - generic output formatting for mercurial
1 # formatter.py - generic output formatting for mercurial
2 #
2 #
3 # Copyright 2012 Matt Mackall <mpm@selenic.com>
3 # Copyright 2012 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import os
10 import os
11
11
12 from .i18n import _
12 from .i18n import _
13 from .node import (
13 from .node import (
14 hex,
14 hex,
15 short,
15 short,
16 )
16 )
17
17
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 error,
20 error,
21 templatekw,
21 templatekw,
22 templater,
22 templater,
23 util,
23 util,
24 )
24 )
25
25
26 pickle = util.pickle
26 pickle = util.pickle
27
27
28 class baseformatter(object):
28 class baseformatter(object):
29 def __init__(self, ui, topic, opts):
29 def __init__(self, ui, topic, opts):
30 self._ui = ui
30 self._ui = ui
31 self._topic = topic
31 self._topic = topic
32 self._style = opts.get("style")
32 self._style = opts.get("style")
33 self._template = opts.get("template")
33 self._template = opts.get("template")
34 self._item = None
34 self._item = None
35 # function to convert node to string suitable for this output
35 # function to convert node to string suitable for this output
36 self.hexfunc = hex
36 self.hexfunc = hex
37 def __nonzero__(self):
37 def __nonzero__(self):
38 '''return False if we're not doing real templating so we can
38 '''return False if we're not doing real templating so we can
39 skip extra work'''
39 skip extra work'''
40 return True
40 return True
41 def _showitem(self):
41 def _showitem(self):
42 '''show a formatted item once all data is collected'''
42 '''show a formatted item once all data is collected'''
43 pass
43 pass
44 def startitem(self):
44 def startitem(self):
45 '''begin an item in the format list'''
45 '''begin an item in the format list'''
46 if self._item is not None:
46 if self._item is not None:
47 self._showitem()
47 self._showitem()
48 self._item = {}
48 self._item = {}
49 @staticmethod
49 @staticmethod
50 def formatdate(date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
50 def formatdate(date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
51 '''convert date tuple to appropriate format'''
51 '''convert date tuple to appropriate format'''
52 return date
52 return date
53 @staticmethod
53 @staticmethod
54 def formatdict(data, key='key', value='value', fmt='%s=%s', sep=' '):
55 '''convert dict or key-value pairs to appropriate dict format'''
56 # use plain dict instead of util.sortdict so that data can be
57 # serialized as a builtin dict in pickle output
58 return dict(data)
59 @staticmethod
54 def formatlist(data, name, fmt='%s', sep=' '):
60 def formatlist(data, name, fmt='%s', sep=' '):
55 '''convert iterable to appropriate list format'''
61 '''convert iterable to appropriate list format'''
56 return list(data)
62 return list(data)
57 def data(self, **data):
63 def data(self, **data):
58 '''insert data into item that's not shown in default output'''
64 '''insert data into item that's not shown in default output'''
59 self._item.update(data)
65 self._item.update(data)
60 def write(self, fields, deftext, *fielddata, **opts):
66 def write(self, fields, deftext, *fielddata, **opts):
61 '''do default text output while assigning data to item'''
67 '''do default text output while assigning data to item'''
62 fieldkeys = fields.split()
68 fieldkeys = fields.split()
63 assert len(fieldkeys) == len(fielddata)
69 assert len(fieldkeys) == len(fielddata)
64 self._item.update(zip(fieldkeys, fielddata))
70 self._item.update(zip(fieldkeys, fielddata))
65 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
71 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
66 '''do conditional write (primarily for plain formatter)'''
72 '''do conditional write (primarily for plain formatter)'''
67 fieldkeys = fields.split()
73 fieldkeys = fields.split()
68 assert len(fieldkeys) == len(fielddata)
74 assert len(fieldkeys) == len(fielddata)
69 self._item.update(zip(fieldkeys, fielddata))
75 self._item.update(zip(fieldkeys, fielddata))
70 def plain(self, text, **opts):
76 def plain(self, text, **opts):
71 '''show raw text for non-templated mode'''
77 '''show raw text for non-templated mode'''
72 pass
78 pass
73 def end(self):
79 def end(self):
74 '''end output for the formatter'''
80 '''end output for the formatter'''
75 if self._item is not None:
81 if self._item is not None:
76 self._showitem()
82 self._showitem()
77
83
84 def _iteritems(data):
85 '''iterate key-value pairs in stable order'''
86 if isinstance(data, dict):
87 return sorted(data.iteritems())
88 return data
89
78 class plainformatter(baseformatter):
90 class plainformatter(baseformatter):
79 '''the default text output scheme'''
91 '''the default text output scheme'''
80 def __init__(self, ui, topic, opts):
92 def __init__(self, ui, topic, opts):
81 baseformatter.__init__(self, ui, topic, opts)
93 baseformatter.__init__(self, ui, topic, opts)
82 if ui.debugflag:
94 if ui.debugflag:
83 self.hexfunc = hex
95 self.hexfunc = hex
84 else:
96 else:
85 self.hexfunc = short
97 self.hexfunc = short
86 def __nonzero__(self):
98 def __nonzero__(self):
87 return False
99 return False
88 def startitem(self):
100 def startitem(self):
89 pass
101 pass
90 @staticmethod
102 @staticmethod
91 def formatdate(date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
103 def formatdate(date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
92 '''stringify date tuple in the given format'''
104 '''stringify date tuple in the given format'''
93 return util.datestr(date, fmt)
105 return util.datestr(date, fmt)
94 @staticmethod
106 @staticmethod
107 def formatdict(data, key='key', value='value', fmt='%s=%s', sep=' '):
108 '''stringify key-value pairs separated by sep'''
109 return sep.join(fmt % (k, v) for k, v in _iteritems(data))
110 @staticmethod
95 def formatlist(data, name, fmt='%s', sep=' '):
111 def formatlist(data, name, fmt='%s', sep=' '):
96 '''stringify iterable separated by sep'''
112 '''stringify iterable separated by sep'''
97 return sep.join(fmt % e for e in data)
113 return sep.join(fmt % e for e in data)
98 def data(self, **data):
114 def data(self, **data):
99 pass
115 pass
100 def write(self, fields, deftext, *fielddata, **opts):
116 def write(self, fields, deftext, *fielddata, **opts):
101 self._ui.write(deftext % fielddata, **opts)
117 self._ui.write(deftext % fielddata, **opts)
102 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
118 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
103 '''do conditional write'''
119 '''do conditional write'''
104 if cond:
120 if cond:
105 self._ui.write(deftext % fielddata, **opts)
121 self._ui.write(deftext % fielddata, **opts)
106 def plain(self, text, **opts):
122 def plain(self, text, **opts):
107 self._ui.write(text, **opts)
123 self._ui.write(text, **opts)
108 def end(self):
124 def end(self):
109 pass
125 pass
110
126
111 class debugformatter(baseformatter):
127 class debugformatter(baseformatter):
112 def __init__(self, ui, topic, opts):
128 def __init__(self, ui, topic, opts):
113 baseformatter.__init__(self, ui, topic, opts)
129 baseformatter.__init__(self, ui, topic, opts)
114 self._ui.write("%s = [\n" % self._topic)
130 self._ui.write("%s = [\n" % self._topic)
115 def _showitem(self):
131 def _showitem(self):
116 self._ui.write(" " + repr(self._item) + ",\n")
132 self._ui.write(" " + repr(self._item) + ",\n")
117 def end(self):
133 def end(self):
118 baseformatter.end(self)
134 baseformatter.end(self)
119 self._ui.write("]\n")
135 self._ui.write("]\n")
120
136
121 class pickleformatter(baseformatter):
137 class pickleformatter(baseformatter):
122 def __init__(self, ui, topic, opts):
138 def __init__(self, ui, topic, opts):
123 baseformatter.__init__(self, ui, topic, opts)
139 baseformatter.__init__(self, ui, topic, opts)
124 self._data = []
140 self._data = []
125 def _showitem(self):
141 def _showitem(self):
126 self._data.append(self._item)
142 self._data.append(self._item)
127 def end(self):
143 def end(self):
128 baseformatter.end(self)
144 baseformatter.end(self)
129 self._ui.write(pickle.dumps(self._data))
145 self._ui.write(pickle.dumps(self._data))
130
146
131 def _jsonifyobj(v):
147 def _jsonifyobj(v):
132 if isinstance(v, (list, tuple)):
148 if isinstance(v, dict):
149 xs = ['"%s": %s' % (encoding.jsonescape(k), _jsonifyobj(u))
150 for k, u in sorted(v.iteritems())]
151 return '{' + ', '.join(xs) + '}'
152 elif isinstance(v, (list, tuple)):
133 return '[' + ', '.join(_jsonifyobj(e) for e in v) + ']'
153 return '[' + ', '.join(_jsonifyobj(e) for e in v) + ']'
134 elif v is None:
154 elif v is None:
135 return 'null'
155 return 'null'
136 elif v is True:
156 elif v is True:
137 return 'true'
157 return 'true'
138 elif v is False:
158 elif v is False:
139 return 'false'
159 return 'false'
140 elif isinstance(v, (int, float)):
160 elif isinstance(v, (int, float)):
141 return str(v)
161 return str(v)
142 else:
162 else:
143 return '"%s"' % encoding.jsonescape(v)
163 return '"%s"' % encoding.jsonescape(v)
144
164
145 class jsonformatter(baseformatter):
165 class jsonformatter(baseformatter):
146 def __init__(self, ui, topic, opts):
166 def __init__(self, ui, topic, opts):
147 baseformatter.__init__(self, ui, topic, opts)
167 baseformatter.__init__(self, ui, topic, opts)
148 self._ui.write("[")
168 self._ui.write("[")
149 self._ui._first = True
169 self._ui._first = True
150 def _showitem(self):
170 def _showitem(self):
151 if self._ui._first:
171 if self._ui._first:
152 self._ui._first = False
172 self._ui._first = False
153 else:
173 else:
154 self._ui.write(",")
174 self._ui.write(",")
155
175
156 self._ui.write("\n {\n")
176 self._ui.write("\n {\n")
157 first = True
177 first = True
158 for k, v in sorted(self._item.items()):
178 for k, v in sorted(self._item.items()):
159 if first:
179 if first:
160 first = False
180 first = False
161 else:
181 else:
162 self._ui.write(",\n")
182 self._ui.write(",\n")
163 self._ui.write(' "%s": %s' % (k, _jsonifyobj(v)))
183 self._ui.write(' "%s": %s' % (k, _jsonifyobj(v)))
164 self._ui.write("\n }")
184 self._ui.write("\n }")
165 def end(self):
185 def end(self):
166 baseformatter.end(self)
186 baseformatter.end(self)
167 self._ui.write("\n]\n")
187 self._ui.write("\n]\n")
168
188
169 class templateformatter(baseformatter):
189 class templateformatter(baseformatter):
170 def __init__(self, ui, topic, opts):
190 def __init__(self, ui, topic, opts):
171 baseformatter.__init__(self, ui, topic, opts)
191 baseformatter.__init__(self, ui, topic, opts)
172 self._topic = topic
192 self._topic = topic
173 self._t = gettemplater(ui, topic, opts.get('template', ''))
193 self._t = gettemplater(ui, topic, opts.get('template', ''))
174 def _showitem(self):
194 def _showitem(self):
175 g = self._t(self._topic, ui=self._ui, **self._item)
195 g = self._t(self._topic, ui=self._ui, **self._item)
176 self._ui.write(templater.stringify(g))
196 self._ui.write(templater.stringify(g))
177 @staticmethod
197 @staticmethod
198 def formatdict(data, key='key', value='value', fmt='%s=%s', sep=' '):
199 '''build object that can be evaluated as either plain string or dict'''
200 data = util.sortdict(_iteritems(data))
201 def f():
202 yield plainformatter.formatdict(data, key, value, fmt, sep)
203 return templatekw._hybrid(f(), data, lambda k: {key: k, value: data[k]},
204 lambda d: fmt % (d[key], d[value]))
205 @staticmethod
178 def formatlist(data, name, fmt='%s', sep=' '):
206 def formatlist(data, name, fmt='%s', sep=' '):
179 '''build object that can be evaluated as either plain string or list'''
207 '''build object that can be evaluated as either plain string or list'''
180 # name is mandatory argument for now, but it could be optional if
208 # name is mandatory argument for now, but it could be optional if
181 # we have default template keyword, e.g. {item}
209 # we have default template keyword, e.g. {item}
182 data = list(data)
210 data = list(data)
183 def f():
211 def f():
184 yield plainformatter.formatlist(data, name, fmt, sep)
212 yield plainformatter.formatlist(data, name, fmt, sep)
185 return templatekw._hybrid(f(), data, lambda x: {name: x},
213 return templatekw._hybrid(f(), data, lambda x: {name: x},
186 lambda d: fmt % d[name])
214 lambda d: fmt % d[name])
187
215
188 def lookuptemplate(ui, topic, tmpl):
216 def lookuptemplate(ui, topic, tmpl):
189 # looks like a literal template?
217 # looks like a literal template?
190 if '{' in tmpl:
218 if '{' in tmpl:
191 return tmpl, None
219 return tmpl, None
192
220
193 # perhaps a stock style?
221 # perhaps a stock style?
194 if not os.path.split(tmpl)[0]:
222 if not os.path.split(tmpl)[0]:
195 mapname = (templater.templatepath('map-cmdline.' + tmpl)
223 mapname = (templater.templatepath('map-cmdline.' + tmpl)
196 or templater.templatepath(tmpl))
224 or templater.templatepath(tmpl))
197 if mapname and os.path.isfile(mapname):
225 if mapname and os.path.isfile(mapname):
198 return None, mapname
226 return None, mapname
199
227
200 # perhaps it's a reference to [templates]
228 # perhaps it's a reference to [templates]
201 t = ui.config('templates', tmpl)
229 t = ui.config('templates', tmpl)
202 if t:
230 if t:
203 return templater.unquotestring(t), None
231 return templater.unquotestring(t), None
204
232
205 if tmpl == 'list':
233 if tmpl == 'list':
206 ui.write(_("available styles: %s\n") % templater.stylelist())
234 ui.write(_("available styles: %s\n") % templater.stylelist())
207 raise error.Abort(_("specify a template"))
235 raise error.Abort(_("specify a template"))
208
236
209 # perhaps it's a path to a map or a template
237 # perhaps it's a path to a map or a template
210 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
238 if ('/' in tmpl or '\\' in tmpl) and os.path.isfile(tmpl):
211 # is it a mapfile for a style?
239 # is it a mapfile for a style?
212 if os.path.basename(tmpl).startswith("map-"):
240 if os.path.basename(tmpl).startswith("map-"):
213 return None, os.path.realpath(tmpl)
241 return None, os.path.realpath(tmpl)
214 tmpl = open(tmpl).read()
242 tmpl = open(tmpl).read()
215 return tmpl, None
243 return tmpl, None
216
244
217 # constant string?
245 # constant string?
218 return tmpl, None
246 return tmpl, None
219
247
220 def gettemplater(ui, topic, spec):
248 def gettemplater(ui, topic, spec):
221 tmpl, mapfile = lookuptemplate(ui, topic, spec)
249 tmpl, mapfile = lookuptemplate(ui, topic, spec)
222 assert not (tmpl and mapfile)
250 assert not (tmpl and mapfile)
223 if mapfile:
251 if mapfile:
224 return templater.templater.frommapfile(mapfile)
252 return templater.templater.frommapfile(mapfile)
225 return maketemplater(ui, topic, tmpl)
253 return maketemplater(ui, topic, tmpl)
226
254
227 def maketemplater(ui, topic, tmpl, filters=None, cache=None):
255 def maketemplater(ui, topic, tmpl, filters=None, cache=None):
228 """Create a templater from a string template 'tmpl'"""
256 """Create a templater from a string template 'tmpl'"""
229 aliases = ui.configitems('templatealias')
257 aliases = ui.configitems('templatealias')
230 t = templater.templater(filters=filters, cache=cache, aliases=aliases)
258 t = templater.templater(filters=filters, cache=cache, aliases=aliases)
231 if tmpl:
259 if tmpl:
232 t.cache[topic] = tmpl
260 t.cache[topic] = tmpl
233 return t
261 return t
234
262
235 def formatter(ui, topic, opts):
263 def formatter(ui, topic, opts):
236 template = opts.get("template", "")
264 template = opts.get("template", "")
237 if template == "json":
265 if template == "json":
238 return jsonformatter(ui, topic, opts)
266 return jsonformatter(ui, topic, opts)
239 elif template == "pickle":
267 elif template == "pickle":
240 return pickleformatter(ui, topic, opts)
268 return pickleformatter(ui, topic, opts)
241 elif template == "debug":
269 elif template == "debug":
242 return debugformatter(ui, topic, opts)
270 return debugformatter(ui, topic, opts)
243 elif template != "":
271 elif template != "":
244 return templateformatter(ui, topic, opts)
272 return templateformatter(ui, topic, opts)
245 # developer config: ui.formatdebug
273 # developer config: ui.formatdebug
246 elif ui.configbool('ui', 'formatdebug'):
274 elif ui.configbool('ui', 'formatdebug'):
247 return debugformatter(ui, topic, opts)
275 return debugformatter(ui, topic, opts)
248 # deprecated config: ui.formatjson
276 # deprecated config: ui.formatjson
249 elif ui.configbool('ui', 'formatjson'):
277 elif ui.configbool('ui', 'formatjson'):
250 return jsonformatter(ui, topic, opts)
278 return jsonformatter(ui, topic, opts)
251 return plainformatter(ui, topic, opts)
279 return plainformatter(ui, topic, opts)
General Comments 0
You need to be logged in to leave comments. Login now