[PATCH 1 of 3] hgweb, templates: add websub template filter

Mads Kiilerich mads at kiilerich.com
Sat Feb 9 05:10:01 CST 2013


On 02/09/2013 11:02 AM, Angel Ezquerra wrote:
> # HG changeset patch
> # User Angel Ezquerra <angel.ezquerra at gmail.com>
> # Date 1360343132 -3600
> # Node ID d2fe4130209f6877d841926df79ba02842c83e20
> # Parent  766ad3e48bdff8ee2b2a3a9276eff398dcaafa02
> hgweb, templates: add websub template filter
>
> The purpose of this new filter is to make it possible to partially replace the
> functionality of the interhg extension. The idea is to be able to define regular
> expression based substitutions on a new "websub" config section. hgweb will then
> be able to apply these substitutions wherever the "websub" filter is used on a
> template.
>
> This first revision just adds the code necessary to load the websub expressions
> and adds the websub filter, but it does not add any calls to the websub filter
> itself on any of the templates. That will be done on the following revisions.
>
> diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py
> --- a/mercurial/hgweb/hgweb_mod.py
> +++ b/mercurial/hgweb/hgweb_mod.py
> @@ -8,6 +8,7 @@
>   
>   import os
>   from mercurial import ui, hg, hook, error, encoding, templater, util, repoview
> +from mercurial import templatefilters
>   from common import get_stat, ErrorResponse, permhooks, caching
>   from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
>   from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
> @@ -49,6 +50,9 @@
>           urlel = os.path.dirname(urlel)
>       return reversed(breadcrumb)
>   
> +from mercurial.i18n import _
> +import re

Please keep the imports at the top and properly grouped.

> +websubtable = []
>   
>   class hgweb(object):
>       def __init__(self, repo, name=None, baseui=None):
> @@ -73,6 +77,7 @@
>           # a repo owner may set web.templates in .hg/hgrc to get any file
>           # readable by the user running the CGI script
>           self.templatepath = self.config('web', 'templates')
> +        self.websubtable = self.loadwebsub()
>   
>       # The CGI scripts are often run by a user different from the repo owner.
>       # Trust the settings from the .hg/hgrc files by default.
> @@ -258,6 +263,43 @@
>                   return ['']
>               return tmpl('error', error=inst.message)
>   
> +    def loadwebsub(self):
> +        websubtable = []
> +        for key, pattern in self.repo.ui.configitems('websub'):
> +            # grab the delimiter from the character after the "s"
> +            unesc = pattern[1]

I guess this will crash on single character patterns? Handling of this 
should be covered by some kind of test.

> +            delim = re.escape(unesc)
> +
> +            # identify portions of the pattern, taking care to avoid escaped
> +            # delimiters. the replace format and flags are optional, but delimiters
> +            # are required.
> +            match = re.match(r'^s%s(.+)(?:(?<=\\\\)|(?<!\\))%s(.*)%s([ilmsux])*$'
> +                             % (delim, delim, delim), pattern)

We usually don't rely on the internal re cache. Instead we prefer to 
compile regexps and store them in 'global' variables.

> +            if not match:
> +                self.repo.ui.warn(_("websub: invalid pattern for %s: %s\n")
> +                                  % (key, pattern))
> +                continue
> +
> +            # we need to unescape the delimiter for regexp and format
> +            delim_re = re.compile(r'(?<!\\)\\%s' % delim)
> +            regexp = delim_re.sub(unesc, match.group(1))
> +            format = delim_re.sub(unesc, match.group(2))
> +
> +            # the pattern allows for 6 regexp flags, so set them if necessary
> +            flagin = match.group(3)
> +            flags = 0
> +            if flagin:
> +                for flag in flagin.upper():
> +                    flags |= re.__dict__[flag]
> +
> +            try:
> +                regexp = re.compile(regexp, flags)
> +                websubtable.append((regexp, format))
> +            except re.error:
> +                self.repo.ui.warn(_("websub: invalid regexp for %s: %s\n")
> +                                  % (key, regexp))

The body of this loop looks like something that it would be nice to have 
in a unit tested util function.

> +        return websubtable
> +
>       def templater(self, req):
>   
>           # determine scheme, port and server name
> @@ -312,8 +354,8 @@
>                                or req.url.strip('/') or self.repo.root)
>   
>           # create the templater
> -
>           tmpl = templater.templater(mapfile,
> +                                   filters={"websub": lambda text: templatefilters.websub(text, self.websubtable)},

Did this really pass check-code.py?

>                                      defaults={"url": req.url,
>                                                "logourl": logourl,
>                                                "logoimg": logoimg,
> @@ -325,6 +367,7 @@
>                                                "motd": motd,
>                                                "sessionvars": sessionvars,
>                                                "pathdef": makebreadcrumb(req.url),
> +                                             "websubtable": self.websubtable,
>                                               })
>           return tmpl
>   
> diff --git a/mercurial/templatefilters.py b/mercurial/templatefilters.py
> --- a/mercurial/templatefilters.py
> +++ b/mercurial/templatefilters.py
> @@ -391,6 +391,15 @@
>       "xmlescape": xmlescape,
>   }
>   
> +def websub(text, websubtable):
> +    """:websub: Any text. Only applies to hgweb. Applies the regular
> +    expression replacements defined in the websub section.
> +    """
> +    if websubtable:
> +        for regexp, format in websubtable:
> +            text = regexp.sub(format, text)
> +    return text

Why do this filtering have to be a hgweb specific feature? Couldn't it 
be a generic templating filter?

> +
>   def fillfunc(context, mapping, args):
>       if not (1 <= len(args) <= 2):
>           raise error.ParseError(_("fill expects one or two arguments"))
> @@ -418,6 +427,7 @@
>   funcs = {
>       "fill": fillfunc,
>       "date": datefunc,
> +    #"linkify": linkify,

???

It seems like we don't have any tests for using custom styles in hgweb - 
tests that would ensure that we stayed backward compatible. That would 
have been the obvious place to test this ... Making it a generic 
templating feature would of course make it even simpler to test it ;-)

/Mads



More information about the Mercurial-devel mailing list