Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/templatefuncs.py @ 43076:2372284d9457
formatting: blacken the codebase
This is using my patch to black
(https://github.com/psf/black/pull/826) so we don't un-wrap collection
literals.
Done with:
hg files 'set:**.py - mercurial/thirdparty/** - "contrib/python-zstandard/**"' | xargs black -S
# skip-blame mass-reformatting only
# no-check-commit reformats foo_bar functions
Differential Revision: https://phab.mercurial-scm.org/D6971
author | Augie Fackler <augie@google.com> |
---|---|
date | Sun, 06 Oct 2019 09:45:02 -0400 |
parents | da3329fe01e3 |
children | 687b865b95ad |
comparison
equal
deleted
inserted
replaced
43075:57875cf423c9 | 43076:2372284d9457 |
---|---|
47 | 47 |
48 # dict of template built-in functions | 48 # dict of template built-in functions |
49 funcs = {} | 49 funcs = {} |
50 templatefunc = registrar.templatefunc(funcs) | 50 templatefunc = registrar.templatefunc(funcs) |
51 | 51 |
52 | |
52 @templatefunc('date(date[, fmt])') | 53 @templatefunc('date(date[, fmt])') |
53 def date(context, mapping, args): | 54 def date(context, mapping, args): |
54 """Format a date. See :hg:`help dates` for formatting | 55 """Format a date. See :hg:`help dates` for formatting |
55 strings. The default is a Unix date format, including the timezone: | 56 strings. The default is a Unix date format, including the timezone: |
56 "Mon Sep 04 15:13:13 2006 0700".""" | 57 "Mon Sep 04 15:13:13 2006 0700".""" |
57 if not (1 <= len(args) <= 2): | 58 if not (1 <= len(args) <= 2): |
58 # i18n: "date" is a keyword | 59 # i18n: "date" is a keyword |
59 raise error.ParseError(_("date expects one or two arguments")) | 60 raise error.ParseError(_("date expects one or two arguments")) |
60 | 61 |
61 date = evaldate(context, mapping, args[0], | 62 date = evaldate( |
62 # i18n: "date" is a keyword | 63 context, |
63 _("date expects a date information")) | 64 mapping, |
65 args[0], | |
66 # i18n: "date" is a keyword | |
67 _("date expects a date information"), | |
68 ) | |
64 fmt = None | 69 fmt = None |
65 if len(args) == 2: | 70 if len(args) == 2: |
66 fmt = evalstring(context, mapping, args[1]) | 71 fmt = evalstring(context, mapping, args[1]) |
67 if fmt is None: | 72 if fmt is None: |
68 return dateutil.datestr(date) | 73 return dateutil.datestr(date) |
69 else: | 74 else: |
70 return dateutil.datestr(date, fmt) | 75 return dateutil.datestr(date, fmt) |
76 | |
71 | 77 |
72 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs') | 78 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs') |
73 def dict_(context, mapping, args): | 79 def dict_(context, mapping, args): |
74 """Construct a dict from key-value pairs. A key may be omitted if | 80 """Construct a dict from key-value pairs. A key may be omitted if |
75 a value expression can provide an unambiguous name.""" | 81 a value expression can provide an unambiguous name.""" |
81 raise error.ParseError(_('dict key cannot be inferred')) | 87 raise error.ParseError(_('dict key cannot be inferred')) |
82 if k in data or k in args['kwargs']: | 88 if k in data or k in args['kwargs']: |
83 raise error.ParseError(_("duplicated dict key '%s' inferred") % k) | 89 raise error.ParseError(_("duplicated dict key '%s' inferred") % k) |
84 data[k] = evalfuncarg(context, mapping, v) | 90 data[k] = evalfuncarg(context, mapping, v) |
85 | 91 |
86 data.update((k, evalfuncarg(context, mapping, v)) | 92 data.update( |
87 for k, v in args['kwargs'].iteritems()) | 93 (k, evalfuncarg(context, mapping, v)) |
94 for k, v in args['kwargs'].iteritems() | |
95 ) | |
88 return templateutil.hybriddict(data) | 96 return templateutil.hybriddict(data) |
89 | 97 |
90 @templatefunc('diff([includepattern [, excludepattern]])', | 98 |
91 requires={'ctx', 'ui'}) | 99 @templatefunc( |
100 'diff([includepattern [, excludepattern]])', requires={'ctx', 'ui'} | |
101 ) | |
92 def diff(context, mapping, args): | 102 def diff(context, mapping, args): |
93 """Show a diff, optionally | 103 """Show a diff, optionally |
94 specifying files to include or exclude.""" | 104 specifying files to include or exclude.""" |
95 if len(args) > 2: | 105 if len(args) > 2: |
96 # i18n: "diff" is a keyword | 106 # i18n: "diff" is a keyword |
104 return [] | 114 return [] |
105 | 115 |
106 ctx = context.resource(mapping, 'ctx') | 116 ctx = context.resource(mapping, 'ctx') |
107 ui = context.resource(mapping, 'ui') | 117 ui = context.resource(mapping, 'ui') |
108 diffopts = diffutil.diffallopts(ui) | 118 diffopts = diffutil.diffallopts(ui) |
109 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)), | 119 chunks = ctx.diff( |
110 opts=diffopts) | 120 match=ctx.match([], getpatterns(0), getpatterns(1)), opts=diffopts |
121 ) | |
111 | 122 |
112 return ''.join(chunks) | 123 return ''.join(chunks) |
124 | |
113 | 125 |
114 @templatefunc('extdata(source)', argspec='source', requires={'ctx', 'cache'}) | 126 @templatefunc('extdata(source)', argspec='source', requires={'ctx', 'cache'}) |
115 def extdata(context, mapping, args): | 127 def extdata(context, mapping, args): |
116 """Show a text read from the specified extdata source. (EXPERIMENTAL)""" | 128 """Show a text read from the specified extdata source. (EXPERIMENTAL)""" |
117 if 'source' not in args: | 129 if 'source' not in args: |
120 | 132 |
121 source = evalstring(context, mapping, args['source']) | 133 source = evalstring(context, mapping, args['source']) |
122 if not source: | 134 if not source: |
123 sym = templateutil.findsymbolicname(args['source']) | 135 sym = templateutil.findsymbolicname(args['source']) |
124 if sym: | 136 if sym: |
125 raise error.ParseError(_('empty data source specified'), | 137 raise error.ParseError( |
126 hint=_("did you mean extdata('%s')?") % sym) | 138 _('empty data source specified'), |
139 hint=_("did you mean extdata('%s')?") % sym, | |
140 ) | |
127 else: | 141 else: |
128 raise error.ParseError(_('empty data source specified')) | 142 raise error.ParseError(_('empty data source specified')) |
129 cache = context.resource(mapping, 'cache').setdefault('extdata', {}) | 143 cache = context.resource(mapping, 'cache').setdefault('extdata', {}) |
130 ctx = context.resource(mapping, 'ctx') | 144 ctx = context.resource(mapping, 'ctx') |
131 if source in cache: | 145 if source in cache: |
132 data = cache[source] | 146 data = cache[source] |
133 else: | 147 else: |
134 data = cache[source] = scmutil.extdatasource(ctx.repo(), source) | 148 data = cache[source] = scmutil.extdatasource(ctx.repo(), source) |
135 return data.get(ctx.rev(), '') | 149 return data.get(ctx.rev(), '') |
150 | |
136 | 151 |
137 @templatefunc('files(pattern)', requires={'ctx'}) | 152 @templatefunc('files(pattern)', requires={'ctx'}) |
138 def files(context, mapping, args): | 153 def files(context, mapping, args): |
139 """All files of the current changeset matching the pattern. See | 154 """All files of the current changeset matching the pattern. See |
140 :hg:`help patterns`.""" | 155 :hg:`help patterns`.""" |
146 ctx = context.resource(mapping, 'ctx') | 161 ctx = context.resource(mapping, 'ctx') |
147 m = ctx.match([raw]) | 162 m = ctx.match([raw]) |
148 files = list(ctx.matches(m)) | 163 files = list(ctx.matches(m)) |
149 return templateutil.compatfileslist(context, mapping, "file", files) | 164 return templateutil.compatfileslist(context, mapping, "file", files) |
150 | 165 |
166 | |
151 @templatefunc('fill(text[, width[, initialident[, hangindent]]])') | 167 @templatefunc('fill(text[, width[, initialident[, hangindent]]])') |
152 def fill(context, mapping, args): | 168 def fill(context, mapping, args): |
153 """Fill many | 169 """Fill many |
154 paragraphs with optional indentation. See the "fill" filter.""" | 170 paragraphs with optional indentation. See the "fill" filter.""" |
155 if not (1 <= len(args) <= 4): | 171 if not (1 <= len(args) <= 4): |
159 text = evalstring(context, mapping, args[0]) | 175 text = evalstring(context, mapping, args[0]) |
160 width = 76 | 176 width = 76 |
161 initindent = '' | 177 initindent = '' |
162 hangindent = '' | 178 hangindent = '' |
163 if 2 <= len(args) <= 4: | 179 if 2 <= len(args) <= 4: |
164 width = evalinteger(context, mapping, args[1], | 180 width = evalinteger( |
165 # i18n: "fill" is a keyword | 181 context, |
166 _("fill expects an integer width")) | 182 mapping, |
183 args[1], | |
184 # i18n: "fill" is a keyword | |
185 _("fill expects an integer width"), | |
186 ) | |
167 try: | 187 try: |
168 initindent = evalstring(context, mapping, args[2]) | 188 initindent = evalstring(context, mapping, args[2]) |
169 hangindent = evalstring(context, mapping, args[3]) | 189 hangindent = evalstring(context, mapping, args[3]) |
170 except IndexError: | 190 except IndexError: |
171 pass | 191 pass |
172 | 192 |
173 return templatefilters.fill(text, width, initindent, hangindent) | 193 return templatefilters.fill(text, width, initindent, hangindent) |
194 | |
174 | 195 |
175 @templatefunc('filter(iterable[, expr])') | 196 @templatefunc('filter(iterable[, expr])') |
176 def filter_(context, mapping, args): | 197 def filter_(context, mapping, args): |
177 """Remove empty elements from a list or a dict. If expr specified, it's | 198 """Remove empty elements from a list or a dict. If expr specified, it's |
178 applied to each element to test emptiness.""" | 199 applied to each element to test emptiness.""" |
179 if not (1 <= len(args) <= 2): | 200 if not (1 <= len(args) <= 2): |
180 # i18n: "filter" is a keyword | 201 # i18n: "filter" is a keyword |
181 raise error.ParseError(_("filter expects one or two arguments")) | 202 raise error.ParseError(_("filter expects one or two arguments")) |
182 iterable = evalwrapped(context, mapping, args[0]) | 203 iterable = evalwrapped(context, mapping, args[0]) |
183 if len(args) == 1: | 204 if len(args) == 1: |
205 | |
184 def select(w): | 206 def select(w): |
185 return w.tobool(context, mapping) | 207 return w.tobool(context, mapping) |
186 else: | 208 |
209 else: | |
210 | |
187 def select(w): | 211 def select(w): |
188 if not isinstance(w, templateutil.mappable): | 212 if not isinstance(w, templateutil.mappable): |
189 raise error.ParseError(_("not filterable by expression")) | 213 raise error.ParseError(_("not filterable by expression")) |
190 lm = context.overlaymap(mapping, w.tomap(context)) | 214 lm = context.overlaymap(mapping, w.tomap(context)) |
191 return evalboolean(context, lm, args[1]) | 215 return evalboolean(context, lm, args[1]) |
216 | |
192 return iterable.filter(context, mapping, select) | 217 return iterable.filter(context, mapping, select) |
218 | |
193 | 219 |
194 @templatefunc('formatnode(node)', requires={'ui'}) | 220 @templatefunc('formatnode(node)', requires={'ui'}) |
195 def formatnode(context, mapping, args): | 221 def formatnode(context, mapping, args): |
196 """Obtain the preferred form of a changeset hash. (DEPRECATED)""" | 222 """Obtain the preferred form of a changeset hash. (DEPRECATED)""" |
197 if len(args) != 1: | 223 if len(args) != 1: |
202 node = evalstring(context, mapping, args[0]) | 228 node = evalstring(context, mapping, args[0]) |
203 if ui.debugflag: | 229 if ui.debugflag: |
204 return node | 230 return node |
205 return templatefilters.short(node) | 231 return templatefilters.short(node) |
206 | 232 |
233 | |
207 @templatefunc('mailmap(author)', requires={'repo', 'cache'}) | 234 @templatefunc('mailmap(author)', requires={'repo', 'cache'}) |
208 def mailmap(context, mapping, args): | 235 def mailmap(context, mapping, args): |
209 """Return the author, updated according to the value | 236 """Return the author, updated according to the value |
210 set in the .mailmap file""" | 237 set in the .mailmap file""" |
211 if len(args) != 1: | 238 if len(args) != 1: |
220 data = repo.wvfs.tryread('.mailmap') | 247 data = repo.wvfs.tryread('.mailmap') |
221 cache['mailmap'] = stringutil.parsemailmap(data) | 248 cache['mailmap'] = stringutil.parsemailmap(data) |
222 | 249 |
223 return stringutil.mapname(cache['mailmap'], author) | 250 return stringutil.mapname(cache['mailmap'], author) |
224 | 251 |
252 | |
225 @templatefunc( | 253 @templatefunc( |
226 'pad(text, width[, fillchar=\' \'[, left=False[, truncate=False]]])', | 254 'pad(text, width[, fillchar=\' \'[, left=False[, truncate=False]]])', |
227 argspec='text width fillchar left truncate') | 255 argspec='text width fillchar left truncate', |
256 ) | |
228 def pad(context, mapping, args): | 257 def pad(context, mapping, args): |
229 """Pad text with a | 258 """Pad text with a |
230 fill character.""" | 259 fill character.""" |
231 if 'text' not in args or 'width' not in args: | 260 if 'text' not in args or 'width' not in args: |
232 # i18n: "pad" is a keyword | 261 # i18n: "pad" is a keyword |
233 raise error.ParseError(_("pad() expects two to four arguments")) | 262 raise error.ParseError(_("pad() expects two to four arguments")) |
234 | 263 |
235 width = evalinteger(context, mapping, args['width'], | 264 width = evalinteger( |
236 # i18n: "pad" is a keyword | 265 context, |
237 _("pad() expects an integer width")) | 266 mapping, |
267 args['width'], | |
268 # i18n: "pad" is a keyword | |
269 _("pad() expects an integer width"), | |
270 ) | |
238 | 271 |
239 text = evalstring(context, mapping, args['text']) | 272 text = evalstring(context, mapping, args['text']) |
240 | 273 |
241 truncate = False | 274 truncate = False |
242 left = False | 275 left = False |
259 if left: | 292 if left: |
260 return fillchar * fillwidth + text | 293 return fillchar * fillwidth + text |
261 else: | 294 else: |
262 return text + fillchar * fillwidth | 295 return text + fillchar * fillwidth |
263 | 296 |
297 | |
264 @templatefunc('indent(text, indentchars[, firstline])') | 298 @templatefunc('indent(text, indentchars[, firstline])') |
265 def indent(context, mapping, args): | 299 def indent(context, mapping, args): |
266 """Indents all non-empty lines | 300 """Indents all non-empty lines |
267 with the characters given in the indentchars string. An optional | 301 with the characters given in the indentchars string. An optional |
268 third parameter will override the indent for the first line only | 302 third parameter will override the indent for the first line only |
280 firstline = indent | 314 firstline = indent |
281 | 315 |
282 # the indent function doesn't indent the first line, so we do it here | 316 # the indent function doesn't indent the first line, so we do it here |
283 return templatefilters.indent(firstline + text, indent) | 317 return templatefilters.indent(firstline + text, indent) |
284 | 318 |
319 | |
285 @templatefunc('get(dict, key)') | 320 @templatefunc('get(dict, key)') |
286 def get(context, mapping, args): | 321 def get(context, mapping, args): |
287 """Get an attribute/key from an object. Some keywords | 322 """Get an attribute/key from an object. Some keywords |
288 are complex types. This function allows you to obtain the value of an | 323 are complex types. This function allows you to obtain the value of an |
289 attribute on these types.""" | 324 attribute on these types.""" |
298 except error.ParseError as err: | 333 except error.ParseError as err: |
299 # i18n: "get" is a keyword | 334 # i18n: "get" is a keyword |
300 hint = _("get() expects a dict as first argument") | 335 hint = _("get() expects a dict as first argument") |
301 raise error.ParseError(bytes(err), hint=hint) | 336 raise error.ParseError(bytes(err), hint=hint) |
302 | 337 |
338 | |
303 @templatefunc('config(section, name[, default])', requires={'ui'}) | 339 @templatefunc('config(section, name[, default])', requires={'ui'}) |
304 def config(context, mapping, args): | 340 def config(context, mapping, args): |
305 """Returns the requested hgrc config option as a string.""" | 341 """Returns the requested hgrc config option as a string.""" |
306 fn = context.resource(mapping, 'ui').config | 342 fn = context.resource(mapping, 'ui').config |
307 return _config(context, mapping, args, fn, evalstring) | 343 return _config(context, mapping, args, fn, evalstring) |
308 | 344 |
345 | |
309 @templatefunc('configbool(section, name[, default])', requires={'ui'}) | 346 @templatefunc('configbool(section, name[, default])', requires={'ui'}) |
310 def configbool(context, mapping, args): | 347 def configbool(context, mapping, args): |
311 """Returns the requested hgrc config option as a boolean.""" | 348 """Returns the requested hgrc config option as a boolean.""" |
312 fn = context.resource(mapping, 'ui').configbool | 349 fn = context.resource(mapping, 'ui').configbool |
313 return _config(context, mapping, args, fn, evalboolean) | 350 return _config(context, mapping, args, fn, evalboolean) |
314 | 351 |
352 | |
315 @templatefunc('configint(section, name[, default])', requires={'ui'}) | 353 @templatefunc('configint(section, name[, default])', requires={'ui'}) |
316 def configint(context, mapping, args): | 354 def configint(context, mapping, args): |
317 """Returns the requested hgrc config option as an integer.""" | 355 """Returns the requested hgrc config option as an integer.""" |
318 fn = context.resource(mapping, 'ui').configint | 356 fn = context.resource(mapping, 'ui').configint |
319 return _config(context, mapping, args, fn, evalinteger) | 357 return _config(context, mapping, args, fn, evalinteger) |
358 | |
320 | 359 |
321 def _config(context, mapping, args, configfn, defaultfn): | 360 def _config(context, mapping, args, configfn, defaultfn): |
322 if not (2 <= len(args) <= 3): | 361 if not (2 <= len(args) <= 3): |
323 raise error.ParseError(_("config expects two or three arguments")) | 362 raise error.ParseError(_("config expects two or three arguments")) |
324 | 363 |
331 default = defaultfn(context, mapping, args[2]) | 370 default = defaultfn(context, mapping, args[2]) |
332 return configfn(section, name, default) | 371 return configfn(section, name, default) |
333 else: | 372 else: |
334 return configfn(section, name) | 373 return configfn(section, name) |
335 | 374 |
375 | |
336 @templatefunc('if(expr, then[, else])') | 376 @templatefunc('if(expr, then[, else])') |
337 def if_(context, mapping, args): | 377 def if_(context, mapping, args): |
338 """Conditionally execute based on the result of | 378 """Conditionally execute based on the result of |
339 an expression.""" | 379 an expression.""" |
340 if not (2 <= len(args) <= 3): | 380 if not (2 <= len(args) <= 3): |
345 if test: | 385 if test: |
346 return evalrawexp(context, mapping, args[1]) | 386 return evalrawexp(context, mapping, args[1]) |
347 elif len(args) == 3: | 387 elif len(args) == 3: |
348 return evalrawexp(context, mapping, args[2]) | 388 return evalrawexp(context, mapping, args[2]) |
349 | 389 |
390 | |
350 @templatefunc('ifcontains(needle, haystack, then[, else])') | 391 @templatefunc('ifcontains(needle, haystack, then[, else])') |
351 def ifcontains(context, mapping, args): | 392 def ifcontains(context, mapping, args): |
352 """Conditionally execute based | 393 """Conditionally execute based |
353 on whether the item "needle" is in "haystack".""" | 394 on whether the item "needle" is in "haystack".""" |
354 if not (3 <= len(args) <= 4): | 395 if not (3 <= len(args) <= 4): |
365 if found: | 406 if found: |
366 return evalrawexp(context, mapping, args[2]) | 407 return evalrawexp(context, mapping, args[2]) |
367 elif len(args) == 4: | 408 elif len(args) == 4: |
368 return evalrawexp(context, mapping, args[3]) | 409 return evalrawexp(context, mapping, args[3]) |
369 | 410 |
411 | |
370 @templatefunc('ifeq(expr1, expr2, then[, else])') | 412 @templatefunc('ifeq(expr1, expr2, then[, else])') |
371 def ifeq(context, mapping, args): | 413 def ifeq(context, mapping, args): |
372 """Conditionally execute based on | 414 """Conditionally execute based on |
373 whether 2 items are equivalent.""" | 415 whether 2 items are equivalent.""" |
374 if not (3 <= len(args) <= 4): | 416 if not (3 <= len(args) <= 4): |
380 if test == match: | 422 if test == match: |
381 return evalrawexp(context, mapping, args[2]) | 423 return evalrawexp(context, mapping, args[2]) |
382 elif len(args) == 4: | 424 elif len(args) == 4: |
383 return evalrawexp(context, mapping, args[3]) | 425 return evalrawexp(context, mapping, args[3]) |
384 | 426 |
427 | |
385 @templatefunc('join(list, sep)') | 428 @templatefunc('join(list, sep)') |
386 def join(context, mapping, args): | 429 def join(context, mapping, args): |
387 """Join items in a list with a delimiter.""" | 430 """Join items in a list with a delimiter.""" |
388 if not (1 <= len(args) <= 2): | 431 if not (1 <= len(args) <= 2): |
389 # i18n: "join" is a keyword | 432 # i18n: "join" is a keyword |
392 joinset = evalwrapped(context, mapping, args[0]) | 435 joinset = evalwrapped(context, mapping, args[0]) |
393 joiner = " " | 436 joiner = " " |
394 if len(args) > 1: | 437 if len(args) > 1: |
395 joiner = evalstring(context, mapping, args[1]) | 438 joiner = evalstring(context, mapping, args[1]) |
396 return joinset.join(context, mapping, joiner) | 439 return joinset.join(context, mapping, joiner) |
440 | |
397 | 441 |
398 @templatefunc('label(label, expr)', requires={'ui'}) | 442 @templatefunc('label(label, expr)', requires={'ui'}) |
399 def label(context, mapping, args): | 443 def label(context, mapping, args): |
400 """Apply a label to generated content. Content with | 444 """Apply a label to generated content. Content with |
401 a label applied can result in additional post-processing, such as | 445 a label applied can result in additional post-processing, such as |
409 # preserve unknown symbol as literal so effects like 'red', 'bold', | 453 # preserve unknown symbol as literal so effects like 'red', 'bold', |
410 # etc. don't need to be quoted | 454 # etc. don't need to be quoted |
411 label = evalstringliteral(context, mapping, args[0]) | 455 label = evalstringliteral(context, mapping, args[0]) |
412 | 456 |
413 return ui.label(thing, label) | 457 return ui.label(thing, label) |
458 | |
414 | 459 |
415 @templatefunc('latesttag([pattern])') | 460 @templatefunc('latesttag([pattern])') |
416 def latesttag(context, mapping, args): | 461 def latesttag(context, mapping, args): |
417 """The global tags matching the given pattern on the | 462 """The global tags matching the given pattern on the |
418 most recent globally tagged ancestor of this changeset. | 463 most recent globally tagged ancestor of this changeset. |
427 pattern = None | 472 pattern = None |
428 if len(args) == 1: | 473 if len(args) == 1: |
429 pattern = evalstring(context, mapping, args[0]) | 474 pattern = evalstring(context, mapping, args[0]) |
430 return templatekw.showlatesttags(context, mapping, pattern) | 475 return templatekw.showlatesttags(context, mapping, pattern) |
431 | 476 |
477 | |
432 @templatefunc('localdate(date[, tz])') | 478 @templatefunc('localdate(date[, tz])') |
433 def localdate(context, mapping, args): | 479 def localdate(context, mapping, args): |
434 """Converts a date to the specified timezone. | 480 """Converts a date to the specified timezone. |
435 The default is local date.""" | 481 The default is local date.""" |
436 if not (1 <= len(args) <= 2): | 482 if not (1 <= len(args) <= 2): |
437 # i18n: "localdate" is a keyword | 483 # i18n: "localdate" is a keyword |
438 raise error.ParseError(_("localdate expects one or two arguments")) | 484 raise error.ParseError(_("localdate expects one or two arguments")) |
439 | 485 |
440 date = evaldate(context, mapping, args[0], | 486 date = evaldate( |
441 # i18n: "localdate" is a keyword | 487 context, |
442 _("localdate expects a date information")) | 488 mapping, |
489 args[0], | |
490 # i18n: "localdate" is a keyword | |
491 _("localdate expects a date information"), | |
492 ) | |
443 if len(args) >= 2: | 493 if len(args) >= 2: |
444 tzoffset = None | 494 tzoffset = None |
445 tz = evalfuncarg(context, mapping, args[1]) | 495 tz = evalfuncarg(context, mapping, args[1]) |
446 if isinstance(tz, bytes): | 496 if isinstance(tz, bytes): |
447 tzoffset, remainder = dateutil.parsetimezone(tz) | 497 tzoffset, remainder = dateutil.parsetimezone(tz) |
455 raise error.ParseError(_("localdate expects a timezone")) | 505 raise error.ParseError(_("localdate expects a timezone")) |
456 else: | 506 else: |
457 tzoffset = dateutil.makedate()[1] | 507 tzoffset = dateutil.makedate()[1] |
458 return templateutil.date((date[0], tzoffset)) | 508 return templateutil.date((date[0], tzoffset)) |
459 | 509 |
510 | |
460 @templatefunc('max(iterable)') | 511 @templatefunc('max(iterable)') |
461 def max_(context, mapping, args, **kwargs): | 512 def max_(context, mapping, args, **kwargs): |
462 """Return the max of an iterable""" | 513 """Return the max of an iterable""" |
463 if len(args) != 1: | 514 if len(args) != 1: |
464 # i18n: "max" is a keyword | 515 # i18n: "max" is a keyword |
470 except error.ParseError as err: | 521 except error.ParseError as err: |
471 # i18n: "max" is a keyword | 522 # i18n: "max" is a keyword |
472 hint = _("max first argument should be an iterable") | 523 hint = _("max first argument should be an iterable") |
473 raise error.ParseError(bytes(err), hint=hint) | 524 raise error.ParseError(bytes(err), hint=hint) |
474 | 525 |
526 | |
475 @templatefunc('min(iterable)') | 527 @templatefunc('min(iterable)') |
476 def min_(context, mapping, args, **kwargs): | 528 def min_(context, mapping, args, **kwargs): |
477 """Return the min of an iterable""" | 529 """Return the min of an iterable""" |
478 if len(args) != 1: | 530 if len(args) != 1: |
479 # i18n: "min" is a keyword | 531 # i18n: "min" is a keyword |
485 except error.ParseError as err: | 537 except error.ParseError as err: |
486 # i18n: "min" is a keyword | 538 # i18n: "min" is a keyword |
487 hint = _("min first argument should be an iterable") | 539 hint = _("min first argument should be an iterable") |
488 raise error.ParseError(bytes(err), hint=hint) | 540 raise error.ParseError(bytes(err), hint=hint) |
489 | 541 |
542 | |
490 @templatefunc('mod(a, b)') | 543 @templatefunc('mod(a, b)') |
491 def mod(context, mapping, args): | 544 def mod(context, mapping, args): |
492 """Calculate a mod b such that a / b + a mod b == a""" | 545 """Calculate a mod b such that a / b + a mod b == a""" |
493 if not len(args) == 2: | 546 if not len(args) == 2: |
494 # i18n: "mod" is a keyword | 547 # i18n: "mod" is a keyword |
495 raise error.ParseError(_("mod expects two arguments")) | 548 raise error.ParseError(_("mod expects two arguments")) |
496 | 549 |
497 func = lambda a, b: a % b | 550 func = lambda a, b: a % b |
498 return templateutil.runarithmetic(context, mapping, | 551 return templateutil.runarithmetic( |
499 (func, args[0], args[1])) | 552 context, mapping, (func, args[0], args[1]) |
553 ) | |
554 | |
500 | 555 |
501 @templatefunc('obsfateoperations(markers)') | 556 @templatefunc('obsfateoperations(markers)') |
502 def obsfateoperations(context, mapping, args): | 557 def obsfateoperations(context, mapping, args): |
503 """Compute obsfate related information based on markers (EXPERIMENTAL)""" | 558 """Compute obsfate related information based on markers (EXPERIMENTAL)""" |
504 if len(args) != 1: | 559 if len(args) != 1: |
512 return templateutil.hybridlist(data, name='operation') | 567 return templateutil.hybridlist(data, name='operation') |
513 except (TypeError, KeyError): | 568 except (TypeError, KeyError): |
514 # i18n: "obsfateoperations" is a keyword | 569 # i18n: "obsfateoperations" is a keyword |
515 errmsg = _("obsfateoperations first argument should be an iterable") | 570 errmsg = _("obsfateoperations first argument should be an iterable") |
516 raise error.ParseError(errmsg) | 571 raise error.ParseError(errmsg) |
572 | |
517 | 573 |
518 @templatefunc('obsfatedate(markers)') | 574 @templatefunc('obsfatedate(markers)') |
519 def obsfatedate(context, mapping, args): | 575 def obsfatedate(context, mapping, args): |
520 """Compute obsfate related information based on markers (EXPERIMENTAL)""" | 576 """Compute obsfate related information based on markers (EXPERIMENTAL)""" |
521 if len(args) != 1: | 577 if len(args) != 1: |
531 except (TypeError, KeyError): | 587 except (TypeError, KeyError): |
532 # i18n: "obsfatedate" is a keyword | 588 # i18n: "obsfatedate" is a keyword |
533 errmsg = _("obsfatedate first argument should be an iterable") | 589 errmsg = _("obsfatedate first argument should be an iterable") |
534 raise error.ParseError(errmsg) | 590 raise error.ParseError(errmsg) |
535 | 591 |
592 | |
536 @templatefunc('obsfateusers(markers)') | 593 @templatefunc('obsfateusers(markers)') |
537 def obsfateusers(context, mapping, args): | 594 def obsfateusers(context, mapping, args): |
538 """Compute obsfate related information based on markers (EXPERIMENTAL)""" | 595 """Compute obsfate related information based on markers (EXPERIMENTAL)""" |
539 if len(args) != 1: | 596 if len(args) != 1: |
540 # i18n: "obsfateusers" is a keyword | 597 # i18n: "obsfateusers" is a keyword |
545 try: | 602 try: |
546 data = obsutil.markersusers(markers) | 603 data = obsutil.markersusers(markers) |
547 return templateutil.hybridlist(data, name='user') | 604 return templateutil.hybridlist(data, name='user') |
548 except (TypeError, KeyError, ValueError): | 605 except (TypeError, KeyError, ValueError): |
549 # i18n: "obsfateusers" is a keyword | 606 # i18n: "obsfateusers" is a keyword |
550 msg = _("obsfateusers first argument should be an iterable of " | 607 msg = _( |
551 "obsmakers") | 608 "obsfateusers first argument should be an iterable of " "obsmakers" |
609 ) | |
552 raise error.ParseError(msg) | 610 raise error.ParseError(msg) |
611 | |
553 | 612 |
554 @templatefunc('obsfateverb(successors, markers)') | 613 @templatefunc('obsfateverb(successors, markers)') |
555 def obsfateverb(context, mapping, args): | 614 def obsfateverb(context, mapping, args): |
556 """Compute obsfate related information based on successors (EXPERIMENTAL)""" | 615 """Compute obsfate related information based on successors (EXPERIMENTAL)""" |
557 if len(args) != 2: | 616 if len(args) != 2: |
565 return obsutil.obsfateverb(successors, markers) | 624 return obsutil.obsfateverb(successors, markers) |
566 except TypeError: | 625 except TypeError: |
567 # i18n: "obsfateverb" is a keyword | 626 # i18n: "obsfateverb" is a keyword |
568 errmsg = _("obsfateverb first argument should be countable") | 627 errmsg = _("obsfateverb first argument should be countable") |
569 raise error.ParseError(errmsg) | 628 raise error.ParseError(errmsg) |
629 | |
570 | 630 |
571 @templatefunc('relpath(path)', requires={'repo'}) | 631 @templatefunc('relpath(path)', requires={'repo'}) |
572 def relpath(context, mapping, args): | 632 def relpath(context, mapping, args): |
573 """Convert a repository-absolute path into a filesystem path relative to | 633 """Convert a repository-absolute path into a filesystem path relative to |
574 the current working directory.""" | 634 the current working directory.""" |
577 raise error.ParseError(_("relpath expects one argument")) | 637 raise error.ParseError(_("relpath expects one argument")) |
578 | 638 |
579 repo = context.resource(mapping, 'repo') | 639 repo = context.resource(mapping, 'repo') |
580 path = evalstring(context, mapping, args[0]) | 640 path = evalstring(context, mapping, args[0]) |
581 return repo.pathto(path) | 641 return repo.pathto(path) |
642 | |
582 | 643 |
583 @templatefunc('revset(query[, formatargs...])', requires={'repo', 'cache'}) | 644 @templatefunc('revset(query[, formatargs...])', requires={'repo', 'cache'}) |
584 def revset(context, mapping, args): | 645 def revset(context, mapping, args): |
585 """Execute a revision set query. See | 646 """Execute a revision set query. See |
586 :hg:`help revset`.""" | 647 :hg:`help revset`.""" |
606 else: | 667 else: |
607 revs = query(raw) | 668 revs = query(raw) |
608 revsetcache[raw] = revs | 669 revsetcache[raw] = revs |
609 return templatekw.showrevslist(context, mapping, "revision", revs) | 670 return templatekw.showrevslist(context, mapping, "revision", revs) |
610 | 671 |
672 | |
611 @templatefunc('rstdoc(text, style)') | 673 @templatefunc('rstdoc(text, style)') |
612 def rstdoc(context, mapping, args): | 674 def rstdoc(context, mapping, args): |
613 """Format reStructuredText.""" | 675 """Format reStructuredText.""" |
614 if len(args) != 2: | 676 if len(args) != 2: |
615 # i18n: "rstdoc" is a keyword | 677 # i18n: "rstdoc" is a keyword |
617 | 679 |
618 text = evalstring(context, mapping, args[0]) | 680 text = evalstring(context, mapping, args[0]) |
619 style = evalstring(context, mapping, args[1]) | 681 style = evalstring(context, mapping, args[1]) |
620 | 682 |
621 return minirst.format(text, style=style, keep=['verbose']) | 683 return minirst.format(text, style=style, keep=['verbose']) |
684 | |
622 | 685 |
623 @templatefunc('search(pattern, text)') | 686 @templatefunc('search(pattern, text)') |
624 def search(context, mapping, args): | 687 def search(context, mapping, args): |
625 """Look for the first text matching the regular expression pattern. | 688 """Look for the first text matching the regular expression pattern. |
626 Groups are accessible as ``{1}``, ``{2}``, ... in %-mapped template.""" | 689 Groups are accessible as ``{1}``, ``{2}``, ... in %-mapped template.""" |
634 patre = re.compile(pat) | 697 patre = re.compile(pat) |
635 except re.error: | 698 except re.error: |
636 # i18n: "search" is a keyword | 699 # i18n: "search" is a keyword |
637 raise error.ParseError(_(b'search got an invalid pattern: %s') % pat) | 700 raise error.ParseError(_(b'search got an invalid pattern: %s') % pat) |
638 # named groups shouldn't shadow *reserved* resource keywords | 701 # named groups shouldn't shadow *reserved* resource keywords |
639 badgroups = (context.knownresourcekeys() | 702 badgroups = context.knownresourcekeys() & set( |
640 & set(pycompat.byteskwargs(patre.groupindex))) | 703 pycompat.byteskwargs(patre.groupindex) |
704 ) | |
641 if badgroups: | 705 if badgroups: |
642 raise error.ParseError( | 706 raise error.ParseError( |
643 # i18n: "search" is a keyword | 707 # i18n: "search" is a keyword |
644 _(b'invalid group %(group)s in search pattern: %(pat)s') | 708 _(b'invalid group %(group)s in search pattern: %(pat)s') |
645 % {b'group': b', '.join("'%s'" % g for g in sorted(badgroups)), | 709 % { |
646 b'pat': pat}) | 710 b'group': b', '.join("'%s'" % g for g in sorted(badgroups)), |
711 b'pat': pat, | |
712 } | |
713 ) | |
647 | 714 |
648 match = patre.search(src) | 715 match = patre.search(src) |
649 if not match: | 716 if not match: |
650 return templateutil.mappingnone() | 717 return templateutil.mappingnone() |
651 | 718 |
652 lm = {b'0': match.group(0)} | 719 lm = {b'0': match.group(0)} |
653 lm.update((b'%d' % i, v) for i, v in enumerate(match.groups(), 1)) | 720 lm.update((b'%d' % i, v) for i, v in enumerate(match.groups(), 1)) |
654 lm.update(pycompat.byteskwargs(match.groupdict())) | 721 lm.update(pycompat.byteskwargs(match.groupdict())) |
655 return templateutil.mappingdict(lm, tmpl=b'{0}') | 722 return templateutil.mappingdict(lm, tmpl=b'{0}') |
723 | |
656 | 724 |
657 @templatefunc('separate(sep, args...)', argspec='sep *args') | 725 @templatefunc('separate(sep, args...)', argspec='sep *args') |
658 def separate(context, mapping, args): | 726 def separate(context, mapping, args): |
659 """Add a separator between non-empty arguments.""" | 727 """Add a separator between non-empty arguments.""" |
660 if 'sep' not in args: | 728 if 'sep' not in args: |
671 first = False | 739 first = False |
672 else: | 740 else: |
673 yield sep | 741 yield sep |
674 yield argstr | 742 yield argstr |
675 | 743 |
744 | |
676 @templatefunc('shortest(node, minlength=4)', requires={'repo', 'cache'}) | 745 @templatefunc('shortest(node, minlength=4)', requires={'repo', 'cache'}) |
677 def shortest(context, mapping, args): | 746 def shortest(context, mapping, args): |
678 """Obtain the shortest representation of | 747 """Obtain the shortest representation of |
679 a node.""" | 748 a node.""" |
680 if not (1 <= len(args) <= 2): | 749 if not (1 <= len(args) <= 2): |
683 | 752 |
684 hexnode = evalstring(context, mapping, args[0]) | 753 hexnode = evalstring(context, mapping, args[0]) |
685 | 754 |
686 minlength = 4 | 755 minlength = 4 |
687 if len(args) > 1: | 756 if len(args) > 1: |
688 minlength = evalinteger(context, mapping, args[1], | 757 minlength = evalinteger( |
689 # i18n: "shortest" is a keyword | 758 context, |
690 _("shortest() expects an integer minlength")) | 759 mapping, |
760 args[1], | |
761 # i18n: "shortest" is a keyword | |
762 _("shortest() expects an integer minlength"), | |
763 ) | |
691 | 764 |
692 repo = context.resource(mapping, 'repo') | 765 repo = context.resource(mapping, 'repo') |
693 if len(hexnode) > 40: | 766 if len(hexnode) > 40: |
694 return hexnode | 767 return hexnode |
695 elif len(hexnode) == 40: | 768 elif len(hexnode) == 40: |
710 try: | 783 try: |
711 return scmutil.shortesthexnodeidprefix(repo, node, minlength, cache) | 784 return scmutil.shortesthexnodeidprefix(repo, node, minlength, cache) |
712 except error.RepoLookupError: | 785 except error.RepoLookupError: |
713 return hexnode | 786 return hexnode |
714 | 787 |
788 | |
715 @templatefunc('strip(text[, chars])') | 789 @templatefunc('strip(text[, chars])') |
716 def strip(context, mapping, args): | 790 def strip(context, mapping, args): |
717 """Strip characters from a string. By default, | 791 """Strip characters from a string. By default, |
718 strips all leading and trailing whitespace.""" | 792 strips all leading and trailing whitespace.""" |
719 if not (1 <= len(args) <= 2): | 793 if not (1 <= len(args) <= 2): |
724 if len(args) == 2: | 798 if len(args) == 2: |
725 chars = evalstring(context, mapping, args[1]) | 799 chars = evalstring(context, mapping, args[1]) |
726 return text.strip(chars) | 800 return text.strip(chars) |
727 return text.strip() | 801 return text.strip() |
728 | 802 |
803 | |
729 @templatefunc('sub(pattern, replacement, expression)') | 804 @templatefunc('sub(pattern, replacement, expression)') |
730 def sub(context, mapping, args): | 805 def sub(context, mapping, args): |
731 """Perform text substitution | 806 """Perform text substitution |
732 using regular expressions.""" | 807 using regular expressions.""" |
733 if len(args) != 3: | 808 if len(args) != 3: |
746 yield patre.sub(rpl, src) | 821 yield patre.sub(rpl, src) |
747 except re.error: | 822 except re.error: |
748 # i18n: "sub" is a keyword | 823 # i18n: "sub" is a keyword |
749 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl) | 824 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl) |
750 | 825 |
826 | |
751 @templatefunc('startswith(pattern, text)') | 827 @templatefunc('startswith(pattern, text)') |
752 def startswith(context, mapping, args): | 828 def startswith(context, mapping, args): |
753 """Returns the value from the "text" argument | 829 """Returns the value from the "text" argument |
754 if it begins with the content from the "pattern" argument.""" | 830 if it begins with the content from the "pattern" argument.""" |
755 if len(args) != 2: | 831 if len(args) != 2: |
760 text = evalstring(context, mapping, args[1]) | 836 text = evalstring(context, mapping, args[1]) |
761 if text.startswith(patn): | 837 if text.startswith(patn): |
762 return text | 838 return text |
763 return '' | 839 return '' |
764 | 840 |
841 | |
765 @templatefunc('word(number, text[, separator])') | 842 @templatefunc('word(number, text[, separator])') |
766 def word(context, mapping, args): | 843 def word(context, mapping, args): |
767 """Return the nth word from a string.""" | 844 """Return the nth word from a string.""" |
768 if not (2 <= len(args) <= 3): | 845 if not (2 <= len(args) <= 3): |
769 # i18n: "word" is a keyword | 846 # i18n: "word" is a keyword |
770 raise error.ParseError(_("word expects two or three arguments, got %d") | 847 raise error.ParseError( |
771 % len(args)) | 848 _("word expects two or three arguments, got %d") % len(args) |
772 | 849 ) |
773 num = evalinteger(context, mapping, args[0], | 850 |
774 # i18n: "word" is a keyword | 851 num = evalinteger( |
775 _("word expects an integer index")) | 852 context, |
853 mapping, | |
854 args[0], | |
855 # i18n: "word" is a keyword | |
856 _("word expects an integer index"), | |
857 ) | |
776 text = evalstring(context, mapping, args[1]) | 858 text = evalstring(context, mapping, args[1]) |
777 if len(args) == 3: | 859 if len(args) == 3: |
778 splitter = evalstring(context, mapping, args[2]) | 860 splitter = evalstring(context, mapping, args[2]) |
779 else: | 861 else: |
780 splitter = None | 862 splitter = None |
782 tokens = text.split(splitter) | 864 tokens = text.split(splitter) |
783 if num >= len(tokens) or num < -len(tokens): | 865 if num >= len(tokens) or num < -len(tokens): |
784 return '' | 866 return '' |
785 else: | 867 else: |
786 return tokens[num] | 868 return tokens[num] |
869 | |
787 | 870 |
788 def loadfunction(ui, extname, registrarobj): | 871 def loadfunction(ui, extname, registrarobj): |
789 """Load template function from specified registrarobj | 872 """Load template function from specified registrarobj |
790 """ | 873 """ |
791 for name, func in registrarobj._table.iteritems(): | 874 for name, func in registrarobj._table.iteritems(): |
792 funcs[name] = func | 875 funcs[name] = func |
793 | 876 |
877 | |
794 # tell hggettext to extract docstrings from these functions: | 878 # tell hggettext to extract docstrings from these functions: |
795 i18nfunctions = funcs.values() | 879 i18nfunctions = funcs.values() |