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()