[RFC] [PATCH] Support arguments in template expandos (v3)

Rocco Rutte pdmef at gmx.net
Sat May 16 11:05:35 CDT 2009


Hi,

* Martin Geisler wrote:
> Rocco Rutte <pdmef at gmx.net> writes:

> > -    template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
> > -                             r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
> > +    template_re = re.compile(r"(?:(?:#(?=[\w\s\"\|%()]+#))|(?:{(?=[\w\s\"\|%()]+})))"
> > +                             r"(\w+)(?:(?:%(\w+(?:\s\"[^\"]*\")*))|"
> > +                             r"((?:\|\w+(?:\s\"[^\"]*\")*)*))[#}]")

> Wow, this regexp is starting to become a little complicated :-) I don't
> know if it could be simplified, but we loose some speed here.

Yes, it took some time for me to understand it.

> > +    args_re = re.compile(r"\"\s\"")

> I think it better to write this as r'"\s"' -- it's confusing when raw
> strings contain both raw backslashes and backslashes which escape stuff.

Done.

> >      def __init__(self, loader, filters={}, defaults={}):
> >          self.loader = loader
> > @@ -101,7 +104,13 @@ class engine(object):
> >              else:
> >                  if fl:
> >                      for f in fl.split("|")[1:]:
> > -                        v = self.filters[f](v)
> > +                        key = f.split(' ')[0]
> > +                        if '"' in f:
> > +                            # remove initial ' "' and trailing '"'
> > +                            args = self.args_re.split(f[len(key)+2:-1])
> > +                            v = self.filters[key](v, args)

> Using *args when calling filter[key] would be nice because it allows the
> nonempty filter to be defined as

Done.

I updated the patch (attached) and did some more timings though I'm not 
sure why both got closer (except I made a mistake).

Unpatched:

  wall 2.734970 comb 4.566667 user 4.316667 sys 0.250000 (best of 4)

Patched:

  wall 2.797045 comb 4.650000 user 4.416667 sys 0.233333 (best of 4)

Rocco

# HG changeset patch
# User Rocco Rutte <pdmef at gmx.net>
# Date 1242489612 -7200
# Node ID a3983675ce793350b961a40e6bb82c5fe0626ba0
# Parent  3acc6279b3648fc587db571c531ba102d735686f
Support arguments for template filters

The syntax for arguments is:

    {key|filter1 ["arg1" ["arg2" ...]]|...}

Existing filters don't have to be changed unless
they want to accept arguments.

diff --git a/contrib/perf.py b/contrib/perf.py
--- a/contrib/perf.py
+++ b/contrib/perf.py
@@ -110,7 +110,8 @@ def perftemplating(ui, repo):
     ui.pushbuffer()
     timer(lambda: commands.log(ui, repo, rev=[], date='', user='',
                                template='{date|shortdate} [{rev}:{node|short}]'
-                               ' {author|person}: {desc|firstline}\n'))
+                               ' {author|person}: '
+                               '{desc|firstline|nonempty "(no description)"}\n'))
     ui.popbuffer()
 
 cmdtable = {
diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py
--- a/mercurial/templatefilters.py
+++ b/mercurial/templatefilters.py
@@ -171,8 +171,8 @@ def stripdir(text):
     else:
         return dir
 
-def nonempty(str):
-    return str or "(none)"
+def nonempty(str, default="(none)"):
+    return str or default
 
 filters = {
     "addbreaks": nl2br,
diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -40,10 +40,13 @@ class engine(object):
     {key%format}.
 
     filter uses function to transform value. syntax is
-    {key|filter1|filter2|...}.'''
+    {key|filter1 ["arg1" ["arg2" ...]]|filter2 ["arg1" ["arg2" ...]]|...}.'''
 
-    template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
-                             r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
+    template_re = re.compile(r"(?:(?:#(?=[\w\s\"\|%()]+#))|(?:{(?=[\w\s\"\|%()]+})))"
+                             r"(\w+)(?:(?:%(\w+(?:\s\"[^\"]*\")*))|"
+                             r"((?:\|\w+(?:\s\"[^\"]*\")*)*))[#}]")
+
+    args_re = re.compile(r'"\s"')
 
     def __init__(self, loader, filters={}, defaults={}):
         self.loader = loader
@@ -102,7 +105,13 @@ class engine(object):
             else:
                 if fl:
                     for f in fl.split("|")[1:]:
-                        v = self.filters[f](v)
+                        key = f.split(' ')[0]
+                        if '"' in f:
+                            # remove initial ' "' and trailing '"'
+                            args = self.args_re.split(f[len(key)+2:-1])
+                            v = self.filters[key](v, *args)
+                        else:
+                            v = self.filters[key](v)
                 yield v
 
 engines = {'default': engine}


More information about the Mercurial-devel mailing list