comparison mercurial/formatter.py @ 29836:18bac830eef3

formatter: factor out format*() functions to separate classes New converter classes will be reused by a nested formatter. See the next patch for details. This change is also good in that the default values are defined uniquely by the baseformatter.
author Yuya Nishihara <yuya@tcha.org>
date Mon, 15 Aug 2016 13:51:14 +0900
parents 4891f3b93182
children 5b886289a1ca
comparison
equal deleted inserted replaced
29835:bff109e6398a 29836:18bac830eef3
23 util, 23 util,
24 ) 24 )
25 25
26 pickle = util.pickle 26 pickle = util.pickle
27 27
28 class _nullconverter(object):
29 '''convert non-primitive data types to be processed by formatter'''
30 @staticmethod
31 def formatdate(date, fmt):
32 '''convert date tuple to appropriate format'''
33 return date
34 @staticmethod
35 def formatdict(data, key, value, fmt, sep):
36 '''convert dict or key-value pairs to appropriate dict format'''
37 # use plain dict instead of util.sortdict so that data can be
38 # serialized as a builtin dict in pickle output
39 return dict(data)
40 @staticmethod
41 def formatlist(data, name, fmt, sep):
42 '''convert iterable to appropriate list format'''
43 return list(data)
44
28 class baseformatter(object): 45 class baseformatter(object):
29 def __init__(self, ui, topic, opts): 46 def __init__(self, ui, topic, opts, converter):
30 self._ui = ui 47 self._ui = ui
31 self._topic = topic 48 self._topic = topic
32 self._style = opts.get("style") 49 self._style = opts.get("style")
33 self._template = opts.get("template") 50 self._template = opts.get("template")
51 self._converter = converter
34 self._item = None 52 self._item = None
35 # function to convert node to string suitable for this output 53 # function to convert node to string suitable for this output
36 self.hexfunc = hex 54 self.hexfunc = hex
37 def __nonzero__(self): 55 def __nonzero__(self):
38 '''return False if we're not doing real templating so we can 56 '''return False if we're not doing real templating so we can
44 def startitem(self): 62 def startitem(self):
45 '''begin an item in the format list''' 63 '''begin an item in the format list'''
46 if self._item is not None: 64 if self._item is not None:
47 self._showitem() 65 self._showitem()
48 self._item = {} 66 self._item = {}
49 @staticmethod 67 def formatdate(self, 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''' 68 '''convert date tuple to appropriate format'''
52 return date 69 return self._converter.formatdate(date, fmt)
53 @staticmethod 70 def formatdict(self, data, key='key', value='value', fmt='%s=%s', sep=' '):
54 def formatdict(data, key='key', value='value', fmt='%s=%s', sep=' '):
55 '''convert dict or key-value pairs to appropriate dict format''' 71 '''convert dict or key-value pairs to appropriate dict format'''
56 # use plain dict instead of util.sortdict so that data can be 72 return self._converter.formatdict(data, key, value, fmt, sep)
57 # serialized as a builtin dict in pickle output 73 def formatlist(self, data, name, fmt='%s', sep=' '):
58 return dict(data)
59 @staticmethod
60 def formatlist(data, name, fmt='%s', sep=' '):
61 '''convert iterable to appropriate list format''' 74 '''convert iterable to appropriate list format'''
62 return list(data) 75 # name is mandatory argument for now, but it could be optional if
76 # we have default template keyword, e.g. {item}
77 return self._converter.formatlist(data, name, fmt, sep)
63 def data(self, **data): 78 def data(self, **data):
64 '''insert data into item that's not shown in default output''' 79 '''insert data into item that's not shown in default output'''
65 self._item.update(data) 80 self._item.update(data)
66 def write(self, fields, deftext, *fielddata, **opts): 81 def write(self, fields, deftext, *fielddata, **opts):
67 '''do default text output while assigning data to item''' 82 '''do default text output while assigning data to item'''
85 '''iterate key-value pairs in stable order''' 100 '''iterate key-value pairs in stable order'''
86 if isinstance(data, dict): 101 if isinstance(data, dict):
87 return sorted(data.iteritems()) 102 return sorted(data.iteritems())
88 return data 103 return data
89 104
105 class _plainconverter(object):
106 '''convert non-primitive data types to text'''
107 @staticmethod
108 def formatdate(date, fmt):
109 '''stringify date tuple in the given format'''
110 return util.datestr(date, fmt)
111 @staticmethod
112 def formatdict(data, key, value, fmt, sep):
113 '''stringify key-value pairs separated by sep'''
114 return sep.join(fmt % (k, v) for k, v in _iteritems(data))
115 @staticmethod
116 def formatlist(data, name, fmt, sep):
117 '''stringify iterable separated by sep'''
118 return sep.join(fmt % e for e in data)
119
90 class plainformatter(baseformatter): 120 class plainformatter(baseformatter):
91 '''the default text output scheme''' 121 '''the default text output scheme'''
92 def __init__(self, ui, topic, opts): 122 def __init__(self, ui, topic, opts):
93 baseformatter.__init__(self, ui, topic, opts) 123 baseformatter.__init__(self, ui, topic, opts, _plainconverter)
94 if ui.debugflag: 124 if ui.debugflag:
95 self.hexfunc = hex 125 self.hexfunc = hex
96 else: 126 else:
97 self.hexfunc = short 127 self.hexfunc = short
98 def __nonzero__(self): 128 def __nonzero__(self):
99 return False 129 return False
100 def startitem(self): 130 def startitem(self):
101 pass 131 pass
102 @staticmethod
103 def formatdate(date, fmt='%a %b %d %H:%M:%S %Y %1%2'):
104 '''stringify date tuple in the given format'''
105 return util.datestr(date, fmt)
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
111 def formatlist(data, name, fmt='%s', sep=' '):
112 '''stringify iterable separated by sep'''
113 return sep.join(fmt % e for e in data)
114 def data(self, **data): 132 def data(self, **data):
115 pass 133 pass
116 def write(self, fields, deftext, *fielddata, **opts): 134 def write(self, fields, deftext, *fielddata, **opts):
117 self._ui.write(deftext % fielddata, **opts) 135 self._ui.write(deftext % fielddata, **opts)
118 def condwrite(self, cond, fields, deftext, *fielddata, **opts): 136 def condwrite(self, cond, fields, deftext, *fielddata, **opts):
124 def end(self): 142 def end(self):
125 pass 143 pass
126 144
127 class debugformatter(baseformatter): 145 class debugformatter(baseformatter):
128 def __init__(self, ui, topic, opts): 146 def __init__(self, ui, topic, opts):
129 baseformatter.__init__(self, ui, topic, opts) 147 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
130 self._ui.write("%s = [\n" % self._topic) 148 self._ui.write("%s = [\n" % self._topic)
131 def _showitem(self): 149 def _showitem(self):
132 self._ui.write(" " + repr(self._item) + ",\n") 150 self._ui.write(" " + repr(self._item) + ",\n")
133 def end(self): 151 def end(self):
134 baseformatter.end(self) 152 baseformatter.end(self)
135 self._ui.write("]\n") 153 self._ui.write("]\n")
136 154
137 class pickleformatter(baseformatter): 155 class pickleformatter(baseformatter):
138 def __init__(self, ui, topic, opts): 156 def __init__(self, ui, topic, opts):
139 baseformatter.__init__(self, ui, topic, opts) 157 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
140 self._data = [] 158 self._data = []
141 def _showitem(self): 159 def _showitem(self):
142 self._data.append(self._item) 160 self._data.append(self._item)
143 def end(self): 161 def end(self):
144 baseformatter.end(self) 162 baseformatter.end(self)
162 else: 180 else:
163 return '"%s"' % encoding.jsonescape(v) 181 return '"%s"' % encoding.jsonescape(v)
164 182
165 class jsonformatter(baseformatter): 183 class jsonformatter(baseformatter):
166 def __init__(self, ui, topic, opts): 184 def __init__(self, ui, topic, opts):
167 baseformatter.__init__(self, ui, topic, opts) 185 baseformatter.__init__(self, ui, topic, opts, _nullconverter)
168 self._ui.write("[") 186 self._ui.write("[")
169 self._ui._first = True 187 self._ui._first = True
170 def _showitem(self): 188 def _showitem(self):
171 if self._ui._first: 189 if self._ui._first:
172 self._ui._first = False 190 self._ui._first = False
184 self._ui.write("\n }") 202 self._ui.write("\n }")
185 def end(self): 203 def end(self):
186 baseformatter.end(self) 204 baseformatter.end(self)
187 self._ui.write("\n]\n") 205 self._ui.write("\n]\n")
188 206
189 class templateformatter(baseformatter): 207 class _templateconverter(object):
190 def __init__(self, ui, topic, opts): 208 '''convert non-primitive data types to be processed by templater'''
191 baseformatter.__init__(self, ui, topic, opts) 209 @staticmethod
192 self._topic = topic 210 def formatdate(date, fmt):
193 self._t = gettemplater(ui, topic, opts.get('template', '')) 211 '''return date tuple'''
194 def _showitem(self): 212 return date
195 g = self._t(self._topic, ui=self._ui, **self._item) 213 @staticmethod
196 self._ui.write(templater.stringify(g)) 214 def formatdict(data, key, value, fmt, sep):
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''' 215 '''build object that can be evaluated as either plain string or dict'''
200 data = util.sortdict(_iteritems(data)) 216 data = util.sortdict(_iteritems(data))
201 def f(): 217 def f():
202 yield plainformatter.formatdict(data, key, value, fmt, sep) 218 yield _plainconverter.formatdict(data, key, value, fmt, sep)
203 return templatekw._hybrid(f(), data, lambda k: {key: k, value: data[k]}, 219 return templatekw._hybrid(f(), data, lambda k: {key: k, value: data[k]},
204 lambda d: fmt % (d[key], d[value])) 220 lambda d: fmt % (d[key], d[value]))
205 @staticmethod 221 @staticmethod
206 def formatlist(data, name, fmt='%s', sep=' '): 222 def formatlist(data, name, fmt, sep):
207 '''build object that can be evaluated as either plain string or list''' 223 '''build object that can be evaluated as either plain string or list'''
208 # name is mandatory argument for now, but it could be optional if
209 # we have default template keyword, e.g. {item}
210 data = list(data) 224 data = list(data)
211 def f(): 225 def f():
212 yield plainformatter.formatlist(data, name, fmt, sep) 226 yield _plainconverter.formatlist(data, name, fmt, sep)
213 return templatekw._hybrid(f(), data, lambda x: {name: x}, 227 return templatekw._hybrid(f(), data, lambda x: {name: x},
214 lambda d: fmt % d[name]) 228 lambda d: fmt % d[name])
229
230 class templateformatter(baseformatter):
231 def __init__(self, ui, topic, opts):
232 baseformatter.__init__(self, ui, topic, opts, _templateconverter)
233 self._topic = topic
234 self._t = gettemplater(ui, topic, opts.get('template', ''))
235 def _showitem(self):
236 g = self._t(self._topic, ui=self._ui, **self._item)
237 self._ui.write(templater.stringify(g))
215 238
216 def lookuptemplate(ui, topic, tmpl): 239 def lookuptemplate(ui, topic, tmpl):
217 # looks like a literal template? 240 # looks like a literal template?
218 if '{' in tmpl: 241 if '{' in tmpl:
219 return tmpl, None 242 return tmpl, None