##// END OF EJS Templates
util: optimize cost auditing on insert...
util: optimize cost auditing on insert Calling popoldest() on insert with cost auditing enabled introduces significant overhead. The primary reason for this overhead is that popoldest() needs to walk the linked list to find the first non-empty node. When we call popoldest() within a loop, this can become quadratic. The performance impact is more pronounced on caches with large capacities. This commit effectively inlines the popoldest() call into _enforcecostlimit(). By doing so, we only do the backwards walk to find the first empty node once. However, we still may still perform this work on insert when the cache is near cost capacity. So this is only a partial performance win. $ hg perflrucachedict --size 4 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 100 ! gets w/ cost limit ! wall 0.598737 comb 0.590000 user 0.590000 sys 0.000000 (best of 17) ! inserts w/ cost limit ! wall 1.694282 comb 1.700000 user 1.700000 sys 0.000000 (best of 6) ! wall 1.659181 comb 1.650000 user 1.650000 sys 0.000000 (best of 7) ! mixed w/ cost limit ! wall 1.157655 comb 1.150000 user 1.150000 sys 0.000000 (best of 9) ! wall 1.139955 comb 1.140000 user 1.140000 sys 0.000000 (best of 9) $ hg perflrucachedict --size 1000 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 10000 ! gets w/ cost limit ! wall 0.598526 comb 0.600000 user 0.600000 sys 0.000000 (best of 17) ! wall 0.601993 comb 0.600000 user 0.600000 sys 0.000000 (best of 17) ! inserts w/ cost limit ! wall 37.838315 comb 37.840000 user 37.840000 sys 0.000000 (best of 3) ! wall 25.105273 comb 25.080000 user 25.080000 sys 0.000000 (best of 3) ! mixed w/ cost limit ! wall 18.060198 comb 18.060000 user 18.060000 sys 0.000000 (best of 3) ! wall 12.104470 comb 12.070000 user 12.070000 sys 0.000000 (best of 3) $ hg perflrucachedict --size 1000 --gets 1000000 --sets 1000000 --mixed 1000000 --costlimit 10000 --mixedgetfreq 90 ! gets w/ cost limit ! wall 0.600024 comb 0.600000 user 0.600000 sys 0.000000 (best of 17) ! wall 0.614439 comb 0.620000 user 0.620000 sys 0.000000 (best of 17) ! inserts w/ cost limit ! wall 37.154547 comb 37.120000 user 37.120000 sys 0.000000 (best of 3) ! wall 25.963028 comb 25.960000 user 25.960000 sys 0.000000 (best of 3) ! mixed w/ cost limit ! wall 4.381602 comb 4.380000 user 4.370000 sys 0.010000 (best of 3) ! wall 3.174256 comb 3.170000 user 3.170000 sys 0.000000 (best of 4) Differential Revision: https://phab.mercurial-scm.org/D4504

File last commit:

r39403:83f8f7b9 default
r39605:cc23c09b default
Show More
templatefuncs.py
718 lines | 25.1 KiB | text/x-python | PythonLexer
Yuya Nishihara
templater: split template functions to new module...
r36940 # templatefuncs.py - common template functions
#
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
from __future__ import absolute_import
import re
from .i18n import _
Martin von Zweigbergk
scmutil: make shortesthexnodeidprefix() take a full binary nodeid...
r37727 from .node import (
bin,
Martin von Zweigbergk
shortest: make {shortest("fffffffff")} work again...
r37876 wdirid,
Martin von Zweigbergk
scmutil: make shortesthexnodeidprefix() take a full binary nodeid...
r37727 )
Yuya Nishihara
templater: split template functions to new module...
r36940 from . import (
color,
encoding,
error,
minirst,
obsutil,
registrar,
revset as revsetmod,
revsetlang,
scmutil,
templatefilters,
templatekw,
templateutil,
util,
)
Connor Sheehan
templatefuncs: add mailmap template function...
r37227 from .utils import (
dateutil,
stringutil,
)
Yuya Nishihara
templater: split template functions to new module...
r36940
evalrawexp = templateutil.evalrawexp
Yuya Nishihara
templater: always join() over a wrapped object (BC)...
r38229 evalwrapped = templateutil.evalwrapped
Yuya Nishihara
templater: split template functions to new module...
r36940 evalfuncarg = templateutil.evalfuncarg
evalboolean = templateutil.evalboolean
Yuya Nishihara
templater: factor out function that parses argument as date tuple
r37241 evaldate = templateutil.evaldate
Yuya Nishihara
templater: split template functions to new module...
r36940 evalinteger = templateutil.evalinteger
evalstring = templateutil.evalstring
evalstringliteral = templateutil.evalstringliteral
# dict of template built-in functions
funcs = {}
templatefunc = registrar.templatefunc(funcs)
@templatefunc('date(date[, fmt])')
def date(context, mapping, args):
"""Format a date. See :hg:`help dates` for formatting
strings. The default is a Unix date format, including the timezone:
"Mon Sep 04 15:13:13 2006 0700"."""
if not (1 <= len(args) <= 2):
# i18n: "date" is a keyword
raise error.ParseError(_("date expects one or two arguments"))
Yuya Nishihara
templatefuncs: use evaldate() where seems appropriate...
r37242 date = evaldate(context, mapping, args[0],
# i18n: "date" is a keyword
_("date expects a date information"))
Yuya Nishihara
templater: split template functions to new module...
r36940 fmt = None
if len(args) == 2:
fmt = evalstring(context, mapping, args[1])
Yuya Nishihara
templatefuncs: use evaldate() where seems appropriate...
r37242 if fmt is None:
return dateutil.datestr(date)
else:
return dateutil.datestr(date, fmt)
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
def dict_(context, mapping, args):
"""Construct a dict from key-value pairs. A key may be omitted if
a value expression can provide an unambiguous name."""
data = util.sortdict()
for v in args['args']:
k = templateutil.findsymbolicname(v)
if not k:
raise error.ParseError(_('dict key cannot be inferred'))
if k in data or k in args['kwargs']:
raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
data[k] = evalfuncarg(context, mapping, v)
data.update((k, evalfuncarg(context, mapping, v))
for k, v in args['kwargs'].iteritems())
return templateutil.hybriddict(data)
Yuya Nishihara
templatefuncs: declare resource requirements for future use
r38447 @templatefunc('diff([includepattern [, excludepattern]])', requires={'ctx'})
Yuya Nishihara
templater: split template functions to new module...
r36940 def diff(context, mapping, args):
"""Show a diff, optionally
specifying files to include or exclude."""
if len(args) > 2:
# i18n: "diff" is a keyword
raise error.ParseError(_("diff expects zero, one, or two arguments"))
def getpatterns(i):
if i < len(args):
s = evalstring(context, mapping, args[i]).strip()
if s:
return [s]
return []
ctx = context.resource(mapping, 'ctx')
chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
return ''.join(chunks)
Yuya Nishihara
templatefuncs: declare resource requirements for future use
r38447 @templatefunc('extdata(source)', argspec='source', requires={'ctx', 'cache'})
Yuya Nishihara
templater: split template functions to new module...
r36940 def extdata(context, mapping, args):
"""Show a text read from the specified extdata source. (EXPERIMENTAL)"""
if 'source' not in args:
# i18n: "extdata" is a keyword
raise error.ParseError(_('extdata expects one argument'))
source = evalstring(context, mapping, args['source'])
Yuya Nishihara
templatefuncs: show hint if extdata source is evaluated to empty (issue5843)
r37950 if not source:
sym = templateutil.findsymbolicname(args['source'])
if sym:
raise error.ParseError(_('empty data source specified'),
hint=_("did you mean extdata('%s')?") % sym)
else:
raise error.ParseError(_('empty data source specified'))
Yuya Nishihara
templater: split template functions to new module...
r36940 cache = context.resource(mapping, 'cache').setdefault('extdata', {})
ctx = context.resource(mapping, 'ctx')
if source in cache:
data = cache[source]
else:
data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
return data.get(ctx.rev(), '')
Yuya Nishihara
templatefuncs: declare resource requirements for future use
r38447 @templatefunc('files(pattern)', requires={'ctx'})
Yuya Nishihara
templater: split template functions to new module...
r36940 def files(context, mapping, args):
"""All files of the current changeset matching the pattern. See
:hg:`help patterns`."""
if not len(args) == 1:
# i18n: "files" is a keyword
raise error.ParseError(_("files expects one argument"))
raw = evalstring(context, mapping, args[0])
ctx = context.resource(mapping, 'ctx')
m = ctx.match([raw])
files = list(ctx.matches(m))
Yuya Nishihara
templatekw: alias {file} of files list to {path}...
r39403 return templateutil.compatfileslist(context, mapping, "file", files)
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('fill(text[, width[, initialident[, hangindent]]])')
def fill(context, mapping, args):
"""Fill many
paragraphs with optional indentation. See the "fill" filter."""
if not (1 <= len(args) <= 4):
# i18n: "fill" is a keyword
raise error.ParseError(_("fill expects one to four arguments"))
text = evalstring(context, mapping, args[0])
width = 76
initindent = ''
hangindent = ''
if 2 <= len(args) <= 4:
width = evalinteger(context, mapping, args[1],
# i18n: "fill" is a keyword
_("fill expects an integer width"))
try:
initindent = evalstring(context, mapping, args[2])
hangindent = evalstring(context, mapping, args[3])
except IndexError:
pass
return templatefilters.fill(text, width, initindent, hangindent)
Yuya Nishihara
templater: extend filter() to accept template expression for emptiness test...
r38468 @templatefunc('filter(iterable[, expr])')
Yuya Nishihara
templater: introduce filter() function to remove empty items from list...
r38467 def filter_(context, mapping, args):
Yuya Nishihara
templater: extend filter() to accept template expression for emptiness test...
r38468 """Remove empty elements from a list or a dict. If expr specified, it's
applied to each element to test emptiness."""
if not (1 <= len(args) <= 2):
Yuya Nishihara
templater: introduce filter() function to remove empty items from list...
r38467 # i18n: "filter" is a keyword
Yuya Nishihara
templater: extend filter() to accept template expression for emptiness test...
r38468 raise error.ParseError(_("filter expects one or two arguments"))
Yuya Nishihara
templater: introduce filter() function to remove empty items from list...
r38467 iterable = evalwrapped(context, mapping, args[0])
Yuya Nishihara
templater: extend filter() to accept template expression for emptiness test...
r38468 if len(args) == 1:
def select(w):
return w.tobool(context, mapping)
else:
def select(w):
if not isinstance(w, templateutil.mappable):
raise error.ParseError(_("not filterable by expression"))
lm = context.overlaymap(mapping, w.tomap(context))
return evalboolean(context, lm, args[1])
Yuya Nishihara
templater: introduce filter() function to remove empty items from list...
r38467 return iterable.filter(context, mapping, select)
Yuya Nishihara
templatefuncs: declare resource requirements for future use
r38447 @templatefunc('formatnode(node)', requires={'ui'})
Yuya Nishihara
templater: split template functions to new module...
r36940 def formatnode(context, mapping, args):
"""Obtain the preferred form of a changeset hash. (DEPRECATED)"""
if len(args) != 1:
# i18n: "formatnode" is a keyword
raise error.ParseError(_("formatnode expects one argument"))
ui = context.resource(mapping, 'ui')
node = evalstring(context, mapping, args[0])
if ui.debugflag:
return node
return templatefilters.short(node)
Yuya Nishihara
templatefuncs: declare resource requirements for future use
r38447 @templatefunc('mailmap(author)', requires={'repo', 'cache'})
Connor Sheehan
templatefuncs: add mailmap template function...
r37227 def mailmap(context, mapping, args):
"""Return the author, updated according to the value
set in the .mailmap file"""
if len(args) != 1:
raise error.ParseError(_("mailmap expects one argument"))
Yuya Nishihara
templatefuncs: do not crash because of invalid value fed to mailmap()
r37277 author = evalstring(context, mapping, args[0])
Connor Sheehan
templatefuncs: add mailmap template function...
r37227
cache = context.resource(mapping, 'cache')
repo = context.resource(mapping, 'repo')
if 'mailmap' not in cache:
data = repo.wvfs.tryread('.mailmap')
cache['mailmap'] = stringutil.parsemailmap(data)
Connor Sheehan
templatefuncs: remove redundant "or author" from mailmap return statement...
r37261 return stringutil.mapname(cache['mailmap'], author)
Connor Sheehan
templatefuncs: add mailmap template function...
r37227
Yuya Nishihara
templater: split template functions to new module...
r36940 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
argspec='text width fillchar left')
def pad(context, mapping, args):
"""Pad text with a
fill character."""
if 'text' not in args or 'width' not in args:
# i18n: "pad" is a keyword
raise error.ParseError(_("pad() expects two to four arguments"))
width = evalinteger(context, mapping, args['width'],
# i18n: "pad" is a keyword
_("pad() expects an integer width"))
text = evalstring(context, mapping, args['text'])
left = False
fillchar = ' '
if 'fillchar' in args:
fillchar = evalstring(context, mapping, args['fillchar'])
if len(color.stripeffects(fillchar)) != 1:
# i18n: "pad" is a keyword
raise error.ParseError(_("pad() expects a single fill character"))
if 'left' in args:
left = evalboolean(context, mapping, args['left'])
fillwidth = width - encoding.colwidth(color.stripeffects(text))
if fillwidth <= 0:
return text
if left:
return fillchar * fillwidth + text
else:
return text + fillchar * fillwidth
@templatefunc('indent(text, indentchars[, firstline])')
def indent(context, mapping, args):
"""Indents all non-empty lines
with the characters given in the indentchars string. An optional
third parameter will override the indent for the first line only
if present."""
if not (2 <= len(args) <= 3):
# i18n: "indent" is a keyword
raise error.ParseError(_("indent() expects two or three arguments"))
text = evalstring(context, mapping, args[0])
indent = evalstring(context, mapping, args[1])
if len(args) == 3:
firstline = evalstring(context, mapping, args[2])
else:
firstline = indent
# the indent function doesn't indent the first line, so we do it here
return templatefilters.indent(firstline + text, indent)
@templatefunc('get(dict, key)')
def get(context, mapping, args):
"""Get an attribute/key from an object. Some keywords
are complex types. This function allows you to obtain the value of an
attribute on these types."""
if len(args) != 2:
# i18n: "get" is a keyword
raise error.ParseError(_("get() expects two arguments"))
Yuya Nishihara
templater: do dict lookup over a wrapped object...
r38258 dictarg = evalwrapped(context, mapping, args[0])
Yuya Nishihara
templater: resolve type of dict key in getmember()...
r38262 key = evalrawexp(context, mapping, args[1])
Yuya Nishihara
templater: promote getmember() to an interface of wrapped types
r38261 try:
return dictarg.getmember(context, mapping, key)
except error.ParseError as err:
Yuya Nishihara
templater: split template functions to new module...
r36940 # i18n: "get" is a keyword
Yuya Nishihara
templater: promote getmember() to an interface of wrapped types
r38261 hint = _("get() expects a dict as first argument")
raise error.ParseError(bytes(err), hint=hint)
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('if(expr, then[, else])')
def if_(context, mapping, args):
"""Conditionally execute based on the result of
an expression."""
if not (2 <= len(args) <= 3):
# i18n: "if" is a keyword
raise error.ParseError(_("if expects two or three arguments"))
test = evalboolean(context, mapping, args[0])
if test:
Yuya Nishihara
templatefuncs: do not stringify result of if*() expression...
r37033 return evalrawexp(context, mapping, args[1])
Yuya Nishihara
templater: split template functions to new module...
r36940 elif len(args) == 3:
Yuya Nishihara
templatefuncs: do not stringify result of if*() expression...
r37033 return evalrawexp(context, mapping, args[2])
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('ifcontains(needle, haystack, then[, else])')
def ifcontains(context, mapping, args):
"""Conditionally execute based
on whether the item "needle" is in "haystack"."""
if not (3 <= len(args) <= 4):
# i18n: "ifcontains" is a keyword
raise error.ParseError(_("ifcontains expects three or four arguments"))
Yuya Nishihara
templater: abstract ifcontains() over wrapped types...
r38286 haystack = evalwrapped(context, mapping, args[1])
Yuya Nishihara
templater: split template functions to new module...
r36940 try:
Yuya Nishihara
templater: factor out unwrapastype() from evalastype()...
r37180 needle = evalrawexp(context, mapping, args[0])
Yuya Nishihara
templater: abstract ifcontains() over wrapped types...
r38286 found = haystack.contains(context, mapping, needle)
Yuya Nishihara
templater: split template functions to new module...
r36940 except error.ParseError:
found = False
if found:
Yuya Nishihara
templatefuncs: do not stringify result of if*() expression...
r37033 return evalrawexp(context, mapping, args[2])
Yuya Nishihara
templater: split template functions to new module...
r36940 elif len(args) == 4:
Yuya Nishihara
templatefuncs: do not stringify result of if*() expression...
r37033 return evalrawexp(context, mapping, args[3])
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('ifeq(expr1, expr2, then[, else])')
def ifeq(context, mapping, args):
"""Conditionally execute based on
whether 2 items are equivalent."""
if not (3 <= len(args) <= 4):
# i18n: "ifeq" is a keyword
raise error.ParseError(_("ifeq expects three or four arguments"))
test = evalstring(context, mapping, args[0])
match = evalstring(context, mapping, args[1])
if test == match:
Yuya Nishihara
templatefuncs: do not stringify result of if*() expression...
r37033 return evalrawexp(context, mapping, args[2])
Yuya Nishihara
templater: split template functions to new module...
r36940 elif len(args) == 4:
Yuya Nishihara
templatefuncs: do not stringify result of if*() expression...
r37033 return evalrawexp(context, mapping, args[3])
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('join(list, sep)')
def join(context, mapping, args):
"""Join items in a list with a delimiter."""
if not (1 <= len(args) <= 2):
# i18n: "join" is a keyword
raise error.ParseError(_("join expects one or two arguments"))
Yuya Nishihara
templater: always join() over a wrapped object (BC)...
r38229 joinset = evalwrapped(context, mapping, args[0])
Yuya Nishihara
templater: split template functions to new module...
r36940 joiner = " "
if len(args) > 1:
joiner = evalstring(context, mapping, args[1])
Yuya Nishihara
templater: always join() over a wrapped object (BC)...
r38229 return joinset.join(context, mapping, joiner)
Yuya Nishihara
templater: split template functions to new module...
r36940
Yuya Nishihara
templatefuncs: declare resource requirements for future use
r38447 @templatefunc('label(label, expr)', requires={'ui'})
Yuya Nishihara
templater: split template functions to new module...
r36940 def label(context, mapping, args):
"""Apply a label to generated content. Content with
a label applied can result in additional post-processing, such as
automatic colorization."""
if len(args) != 2:
# i18n: "label" is a keyword
raise error.ParseError(_("label expects two arguments"))
ui = context.resource(mapping, 'ui')
thing = evalstring(context, mapping, args[1])
# preserve unknown symbol as literal so effects like 'red', 'bold',
# etc. don't need to be quoted
label = evalstringliteral(context, mapping, args[0])
return ui.label(thing, label)
@templatefunc('latesttag([pattern])')
def latesttag(context, mapping, args):
"""The global tags matching the given pattern on the
most recent globally tagged ancestor of this changeset.
If no such tags exist, the "{tag}" template resolves to
Yuya Nishihara
help: mention pattern syntax of latesttag() template function
r38161 the string "null". See :hg:`help revisions.patterns` for the pattern
syntax.
"""
Yuya Nishihara
templater: split template functions to new module...
r36940 if len(args) > 1:
# i18n: "latesttag" is a keyword
raise error.ParseError(_("latesttag expects at most one argument"))
pattern = None
if len(args) == 1:
pattern = evalstring(context, mapping, args[0])
return templatekw.showlatesttags(context, mapping, pattern)
@templatefunc('localdate(date[, tz])')
def localdate(context, mapping, args):
"""Converts a date to the specified timezone.
The default is local date."""
if not (1 <= len(args) <= 2):
# i18n: "localdate" is a keyword
raise error.ParseError(_("localdate expects one or two arguments"))
Yuya Nishihara
templater: factor out function that parses argument as date tuple
r37241 date = evaldate(context, mapping, args[0],
# i18n: "localdate" is a keyword
_("localdate expects a date information"))
Yuya Nishihara
templater: split template functions to new module...
r36940 if len(args) >= 2:
tzoffset = None
tz = evalfuncarg(context, mapping, args[1])
if isinstance(tz, bytes):
tzoffset, remainder = dateutil.parsetimezone(tz)
if remainder:
tzoffset = None
if tzoffset is None:
try:
tzoffset = int(tz)
except (TypeError, ValueError):
# i18n: "localdate" is a keyword
raise error.ParseError(_("localdate expects a timezone"))
else:
tzoffset = dateutil.makedate()[1]
Yuya Nishihara
templater: introduce a wrapper for date tuple (BC)...
r38304 return templateutil.date((date[0], tzoffset))
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('max(iterable)')
def max_(context, mapping, args, **kwargs):
"""Return the max of an iterable"""
if len(args) != 1:
# i18n: "max" is a keyword
raise error.ParseError(_("max expects one argument"))
Yuya Nishihara
templater: abstract min/max away...
r38284 iterable = evalwrapped(context, mapping, args[0])
Yuya Nishihara
templater: split template functions to new module...
r36940 try:
Yuya Nishihara
templater: abstract min/max away...
r38284 return iterable.getmax(context, mapping)
except error.ParseError as err:
Yuya Nishihara
templater: split template functions to new module...
r36940 # i18n: "max" is a keyword
Yuya Nishihara
templater: abstract min/max away...
r38284 hint = _("max first argument should be an iterable")
raise error.ParseError(bytes(err), hint=hint)
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('min(iterable)')
def min_(context, mapping, args, **kwargs):
"""Return the min of an iterable"""
if len(args) != 1:
# i18n: "min" is a keyword
raise error.ParseError(_("min expects one argument"))
Yuya Nishihara
templater: abstract min/max away...
r38284 iterable = evalwrapped(context, mapping, args[0])
Yuya Nishihara
templater: split template functions to new module...
r36940 try:
Yuya Nishihara
templater: abstract min/max away...
r38284 return iterable.getmin(context, mapping)
except error.ParseError as err:
Yuya Nishihara
templater: split template functions to new module...
r36940 # i18n: "min" is a keyword
Yuya Nishihara
templater: abstract min/max away...
r38284 hint = _("min first argument should be an iterable")
raise error.ParseError(bytes(err), hint=hint)
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('mod(a, b)')
def mod(context, mapping, args):
"""Calculate a mod b such that a / b + a mod b == a"""
if not len(args) == 2:
# i18n: "mod" is a keyword
raise error.ParseError(_("mod expects two arguments"))
func = lambda a, b: a % b
return templateutil.runarithmetic(context, mapping,
(func, args[0], args[1]))
@templatefunc('obsfateoperations(markers)')
def obsfateoperations(context, mapping, args):
"""Compute obsfate related information based on markers (EXPERIMENTAL)"""
if len(args) != 1:
# i18n: "obsfateoperations" is a keyword
raise error.ParseError(_("obsfateoperations expects one argument"))
markers = evalfuncarg(context, mapping, args[0])
try:
data = obsutil.markersoperations(markers)
return templateutil.hybridlist(data, name='operation')
except (TypeError, KeyError):
# i18n: "obsfateoperations" is a keyword
errmsg = _("obsfateoperations first argument should be an iterable")
raise error.ParseError(errmsg)
@templatefunc('obsfatedate(markers)')
def obsfatedate(context, mapping, args):
"""Compute obsfate related information based on markers (EXPERIMENTAL)"""
if len(args) != 1:
# i18n: "obsfatedate" is a keyword
raise error.ParseError(_("obsfatedate expects one argument"))
markers = evalfuncarg(context, mapping, args[0])
try:
Yuya Nishihara
templater: introduce a wrapper for date tuple (BC)...
r38304 # TODO: maybe this has to be a wrapped list of date wrappers?
Yuya Nishihara
templater: split template functions to new module...
r36940 data = obsutil.markersdates(markers)
return templateutil.hybridlist(data, name='date', fmt='%d %d')
except (TypeError, KeyError):
# i18n: "obsfatedate" is a keyword
errmsg = _("obsfatedate first argument should be an iterable")
raise error.ParseError(errmsg)
@templatefunc('obsfateusers(markers)')
def obsfateusers(context, mapping, args):
"""Compute obsfate related information based on markers (EXPERIMENTAL)"""
if len(args) != 1:
# i18n: "obsfateusers" is a keyword
raise error.ParseError(_("obsfateusers expects one argument"))
markers = evalfuncarg(context, mapping, args[0])
try:
data = obsutil.markersusers(markers)
return templateutil.hybridlist(data, name='user')
except (TypeError, KeyError, ValueError):
# i18n: "obsfateusers" is a keyword
msg = _("obsfateusers first argument should be an iterable of "
"obsmakers")
raise error.ParseError(msg)
@templatefunc('obsfateverb(successors, markers)')
def obsfateverb(context, mapping, args):
"""Compute obsfate related information based on successors (EXPERIMENTAL)"""
if len(args) != 2:
# i18n: "obsfateverb" is a keyword
raise error.ParseError(_("obsfateverb expects two arguments"))
successors = evalfuncarg(context, mapping, args[0])
markers = evalfuncarg(context, mapping, args[1])
try:
return obsutil.obsfateverb(successors, markers)
except TypeError:
# i18n: "obsfateverb" is a keyword
errmsg = _("obsfateverb first argument should be countable")
raise error.ParseError(errmsg)
Yuya Nishihara
templatefuncs: declare resource requirements for future use
r38447 @templatefunc('relpath(path)', requires={'repo'})
Yuya Nishihara
templater: split template functions to new module...
r36940 def relpath(context, mapping, args):
"""Convert a repository-absolute path into a filesystem path relative to
the current working directory."""
if len(args) != 1:
# i18n: "relpath" is a keyword
raise error.ParseError(_("relpath expects one argument"))
Yuya Nishihara
templatefuncs: minimize resource requirements
r38446 repo = context.resource(mapping, 'repo')
Yuya Nishihara
templater: split template functions to new module...
r36940 path = evalstring(context, mapping, args[0])
return repo.pathto(path)
Yuya Nishihara
templatefuncs: declare resource requirements for future use
r38447 @templatefunc('revset(query[, formatargs...])', requires={'repo', 'cache'})
Yuya Nishihara
templater: split template functions to new module...
r36940 def revset(context, mapping, args):
"""Execute a revision set query. See
:hg:`help revset`."""
if not len(args) > 0:
# i18n: "revset" is a keyword
raise error.ParseError(_("revset expects one or more arguments"))
raw = evalstring(context, mapping, args[0])
Yuya Nishihara
templatefuncs: minimize resource requirements
r38446 repo = context.resource(mapping, 'repo')
Yuya Nishihara
templater: split template functions to new module...
r36940
def query(expr):
Yuya Nishihara
revset: pass in lookup function instead of repo (API)...
r37692 m = revsetmod.match(repo.ui, expr, lookup=revsetmod.lookupfn(repo))
Yuya Nishihara
templater: split template functions to new module...
r36940 return m(repo)
if len(args) > 1:
formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
revs = query(revsetlang.formatspec(raw, *formatargs))
revs = list(revs)
else:
cache = context.resource(mapping, 'cache')
revsetcache = cache.setdefault("revsetcache", {})
if raw in revsetcache:
revs = revsetcache[raw]
else:
revs = query(raw)
revs = list(revs)
revsetcache[raw] = revs
return templatekw.showrevslist(context, mapping, "revision", revs)
@templatefunc('rstdoc(text, style)')
def rstdoc(context, mapping, args):
"""Format reStructuredText."""
if len(args) != 2:
# i18n: "rstdoc" is a keyword
raise error.ParseError(_("rstdoc expects two arguments"))
text = evalstring(context, mapping, args[0])
style = evalstring(context, mapping, args[1])
Yuya Nishihara
minirst: make format() simply return a formatted text...
r39346 return minirst.format(text, style=style, keep=['verbose'])
Yuya Nishihara
templater: split template functions to new module...
r36940
Yuya Nishihara
help: correct signature of separate() template function...
r38179 @templatefunc('separate(sep, args...)', argspec='sep *args')
Yuya Nishihara
templater: split template functions to new module...
r36940 def separate(context, mapping, args):
"""Add a separator between non-empty arguments."""
if 'sep' not in args:
# i18n: "separate" is a keyword
raise error.ParseError(_("separate expects at least one argument"))
sep = evalstring(context, mapping, args['sep'])
first = True
for arg in args['args']:
argstr = evalstring(context, mapping, arg)
if not argstr:
continue
if first:
first = False
else:
yield sep
yield argstr
Martin von Zweigbergk
shortest: cache disambiguation revset...
r38889 @templatefunc('shortest(node, minlength=4)', requires={'repo', 'cache'})
Yuya Nishihara
templater: split template functions to new module...
r36940 def shortest(context, mapping, args):
"""Obtain the shortest representation of
a node."""
if not (1 <= len(args) <= 2):
# i18n: "shortest" is a keyword
raise error.ParseError(_("shortest() expects one or two arguments"))
Martin von Zweigbergk
scmutil: make shortesthexnodeidprefix() take a full binary nodeid...
r37727 hexnode = evalstring(context, mapping, args[0])
Yuya Nishihara
templater: split template functions to new module...
r36940
minlength = 4
if len(args) > 1:
minlength = evalinteger(context, mapping, args[1],
# i18n: "shortest" is a keyword
_("shortest() expects an integer minlength"))
Yuya Nishihara
templatefuncs: minimize resource requirements
r38446 repo = context.resource(mapping, 'repo')
Martin von Zweigbergk
scmutil: make shortesthexnodeidprefix() take a full binary nodeid...
r37727 if len(hexnode) > 40:
return hexnode
elif len(hexnode) == 40:
try:
node = bin(hexnode)
except TypeError:
return hexnode
else:
try:
node = scmutil.resolvehexnodeidprefix(repo, hexnode)
Martin von Zweigbergk
shortest: make {shortest("fffffffff")} work again...
r37876 except error.WdirUnsupported:
node = wdirid
except error.LookupError:
Martin von Zweigbergk
scmutil: make shortesthexnodeidprefix() take a full binary nodeid...
r37727 return hexnode
if not node:
return hexnode
Martin von Zweigbergk
shortest: cache disambiguation revset...
r38889 cache = context.resource(mapping, 'cache')
Martin von Zweigbergk
shortest: don't keep checking for longer prefix if node doesn't exist (API)...
r37882 try:
Martin von Zweigbergk
shortest: cache disambiguation revset...
r38889 return scmutil.shortesthexnodeidprefix(repo, node, minlength, cache)
Martin von Zweigbergk
shortest: don't keep checking for longer prefix if node doesn't exist (API)...
r37882 except error.RepoLookupError:
return hexnode
Yuya Nishihara
templater: split template functions to new module...
r36940
@templatefunc('strip(text[, chars])')
def strip(context, mapping, args):
"""Strip characters from a string. By default,
strips all leading and trailing whitespace."""
if not (1 <= len(args) <= 2):
# i18n: "strip" is a keyword
raise error.ParseError(_("strip expects one or two arguments"))
text = evalstring(context, mapping, args[0])
if len(args) == 2:
chars = evalstring(context, mapping, args[1])
return text.strip(chars)
return text.strip()
@templatefunc('sub(pattern, replacement, expression)')
def sub(context, mapping, args):
"""Perform text substitution
using regular expressions."""
if len(args) != 3:
# i18n: "sub" is a keyword
raise error.ParseError(_("sub expects three arguments"))
pat = evalstring(context, mapping, args[0])
rpl = evalstring(context, mapping, args[1])
src = evalstring(context, mapping, args[2])
try:
patre = re.compile(pat)
except re.error:
# i18n: "sub" is a keyword
raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
try:
yield patre.sub(rpl, src)
except re.error:
# i18n: "sub" is a keyword
raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
@templatefunc('startswith(pattern, text)')
def startswith(context, mapping, args):
"""Returns the value from the "text" argument
if it begins with the content from the "pattern" argument."""
if len(args) != 2:
# i18n: "startswith" is a keyword
raise error.ParseError(_("startswith expects two arguments"))
patn = evalstring(context, mapping, args[0])
text = evalstring(context, mapping, args[1])
if text.startswith(patn):
return text
return ''
@templatefunc('word(number, text[, separator])')
def word(context, mapping, args):
"""Return the nth word from a string."""
if not (2 <= len(args) <= 3):
# i18n: "word" is a keyword
raise error.ParseError(_("word expects two or three arguments, got %d")
% len(args))
num = evalinteger(context, mapping, args[0],
# i18n: "word" is a keyword
_("word expects an integer index"))
text = evalstring(context, mapping, args[1])
if len(args) == 3:
splitter = evalstring(context, mapping, args[2])
else:
splitter = None
tokens = text.split(splitter)
if num >= len(tokens) or num < -len(tokens):
return ''
else:
return tokens[num]
def loadfunction(ui, extname, registrarobj):
"""Load template function from specified registrarobj
"""
for name, func in registrarobj._table.iteritems():
funcs[name] = func
# tell hggettext to extract docstrings from these functions:
i18nfunctions = funcs.values()