templates: move filters to their own module
This eliminates just about all Mercurial dependencies in templater.py
--- a/contrib/churn.py Thu Jan 31 14:44:19 2008 -0600
+++ b/contrib/churn.py Thu Jan 31 14:44:19 2008 -0600
@@ -12,7 +12,7 @@
# <alias email> <actual email>
from mercurial.i18n import gettext as _
-from mercurial import hg, mdiff, cmdutil, ui, util, templater, node
+from mercurial import hg, mdiff, cmdutil, ui, util, templatefilters, node
import os, sys
def get_tty_width():
--- a/hgext/interhg.py Thu Jan 31 14:44:19 2008 -0600
+++ b/hgext/interhg.py Thu Jan 31 14:44:19 2008 -0600
@@ -27,9 +27,9 @@
import re
from mercurial.hgweb import hgweb_mod
-from mercurial import templater
+from mercurial import templatefilters
-orig_escape = templater.common_filters["escape"]
+orig_escape = templatefilters.filters["escape"]
interhg_table = []
@@ -39,7 +39,7 @@
escstr = regexp.sub(format, escstr)
return escstr
-templater.common_filters["escape"] = interhg_escape
+templatefilters.filters["escape"] = interhg_escape
orig_refresh = hgweb_mod.hgweb.refresh
--- a/hgext/keyword.py Thu Jan 31 14:44:19 2008 -0600
+++ b/hgext/keyword.py Thu Jan 31 14:44:19 2008 -0600
@@ -78,8 +78,8 @@
"Log = {desc}" expands to the first line of the changeset description.
'''
-from mercurial import commands, cmdutil, context, dispatch, filelog
-from mercurial import patch, localrepo, revlog, templater, util
+from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
+from mercurial import patch, localrepo, templater, templatefilters, util
from mercurial.node import *
from mercurial.i18n import _
import re, shutil, sys, tempfile, time
@@ -130,7 +130,7 @@
kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
self.re_kw = re.compile(kwpat)
- templater.common_filters['utcdate'] = utcdate
+ templatefilters.filters['utcdate'] = utcdate
self.ct = cmdutil.changeset_templater(self.ui, self.repo,
False, '', False)
@@ -149,7 +149,8 @@
self.ct.use_template(self.templates[kw])
self.ui.pushbuffer()
self.ct.show(changenode=fnode, root=self.repo.root, file=self.path)
- return '$%s: %s $' % (kw, templater.firstline(self.ui.popbuffer()))
+ return '$%s: %s $' % (kw, templatefilters.firstline(
+ self.ui.popbuffer()))
return subfunc(kwsub, data)
--- a/mercurial/cmdutil.py Thu Jan 31 14:44:19 2008 -0600
+++ b/mercurial/cmdutil.py Thu Jan 31 14:44:19 2008 -0600
@@ -8,7 +8,7 @@
from node import *
from i18n import _
import os, sys, bisect, stat
-import mdiff, bdiff, util, templater, patch, errno
+import mdiff, bdiff, util, templater, templatefilters, patch, errno
revrangesep = ':'
@@ -673,7 +673,7 @@
def __init__(self, ui, repo, patch, mapfile, buffered):
changeset_printer.__init__(self, ui, repo, patch, buffered)
- filters = templater.common_filters.copy()
+ filters = templatefilters.filters.copy()
filters['formatnode'] = (ui.debugflag and (lambda x: x)
or (lambda x: x[:12]))
self.t = templater.templater(mapfile, filters,
--- a/mercurial/hgweb/hgweb_mod.py Thu Jan 31 14:44:19 2008 -0600
+++ b/mercurial/hgweb/hgweb_mod.py Thu Jan 31 14:44:19 2008 -0600
@@ -9,7 +9,7 @@
import os, mimetypes, re
from mercurial.node import *
from mercurial import mdiff, ui, hg, util, archival, patch, hook
-from mercurial import revlog, templater
+from mercurial import revlog, templater, templatefilters
from common import ErrorResponse, get_mtime, style_map, paritygen, get_contact
from request import wsgirequest
import webcommands, protocol
@@ -288,7 +288,7 @@
# create the templater
- tmpl = templater.templater(mapfile, templater.common_filters,
+ tmpl = templater.templater(mapfile, templatefilters.filters,
defaults={"url": req.url,
"staticurl": staticurl,
"urlbase": urlbase,
--- a/mercurial/hgweb/hgwebdir_mod.py Thu Jan 31 14:44:19 2008 -0600
+++ b/mercurial/hgweb/hgwebdir_mod.py Thu Jan 31 14:44:19 2008 -0600
@@ -8,8 +8,8 @@
import os
from mercurial.i18n import gettext as _
-from mercurial import ui, hg, util, templater
-from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen, \
+from mercurial import ui, hg, util, templater, templatefilters
+from common import ErrorResponse, get_mtime, staticfile, style_map, paritygen,\
get_contact
from hgweb_mod import hgweb
from request import wsgirequest
@@ -266,7 +266,7 @@
if self.stripecount is None:
self.stripecount = int(config('web', 'stripes', 1))
mapfile = style_map(templater.templatepath(), style)
- tmpl = templater.templater(mapfile, templater.common_filters,
+ tmpl = templater.templater(mapfile, templatefilters.filters,
defaults={"header": header,
"footer": footer,
"motd": motd,
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templatefilters.py Thu Jan 31 14:44:19 2008 -0600
@@ -0,0 +1,155 @@
+# template-filters.py - common template expansion filters
+#
+# Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+import cgi, re, os, time, urllib, textwrap
+import util, templater
+
+agescales = [("second", 1),
+ ("minute", 60),
+ ("hour", 3600),
+ ("day", 3600 * 24),
+ ("week", 3600 * 24 * 7),
+ ("month", 3600 * 24 * 30),
+ ("year", 3600 * 24 * 365)]
+
+agescales.reverse()
+
+def age(date):
+ '''turn a (timestamp, tzoff) tuple into an age string.'''
+
+ def plural(t, c):
+ if c == 1:
+ return t
+ return t + "s"
+ def fmt(t, c):
+ return "%d %s" % (c, plural(t, c))
+
+ now = time.time()
+ then = date[0]
+ delta = max(1, int(now - then))
+
+ for t, s in agescales:
+ n = delta / s
+ if n >= 2 or s == 1:
+ return fmt(t, n)
+
+para_re = None
+space_re = None
+
+def fill(text, width):
+ '''fill many paragraphs.'''
+ global para_re, space_re
+ if para_re is None:
+ para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
+ space_re = re.compile(r' +')
+
+ def findparas():
+ start = 0
+ while True:
+ m = para_re.search(text, start)
+ if not m:
+ w = len(text)
+ while w > start and text[w-1].isspace(): w -= 1
+ yield text[start:w], text[w:]
+ break
+ yield text[start:m.start(0)], m.group(1)
+ start = m.end(1)
+
+ return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest
+ for para, rest in findparas()])
+
+def firstline(text):
+ '''return the first line of text'''
+ try:
+ return text.splitlines(1)[0].rstrip('\r\n')
+ except IndexError:
+ return ''
+
+def isodate(date):
+ '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
+ return util.datestr(date, format='%Y-%m-%d %H:%M')
+
+def hgdate(date):
+ '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
+ return "%d %d" % date
+
+def nl2br(text):
+ '''replace raw newlines with xhtml line breaks.'''
+ return text.replace('\n', '<br/>\n')
+
+def obfuscate(text):
+ text = unicode(text, util._encoding, 'replace')
+ return ''.join(['&#%d;' % ord(c) for c in text])
+
+def domain(author):
+ '''get domain of author, or empty string if none.'''
+ f = author.find('@')
+ if f == -1: return ''
+ author = author[f+1:]
+ f = author.find('>')
+ if f >= 0: author = author[:f]
+ return author
+
+def person(author):
+ '''get name of author, or else username.'''
+ f = author.find('<')
+ if f == -1: return util.shortuser(author)
+ return author[:f].rstrip()
+
+def shortdate(date):
+ '''turn (timestamp, tzoff) tuple into iso 8631 date.'''
+ return util.datestr(date, format='%Y-%m-%d', timezone=False)
+
+def indent(text, prefix):
+ '''indent each non-empty line of text after first with prefix.'''
+ lines = text.splitlines()
+ num_lines = len(lines)
+ def indenter():
+ for i in xrange(num_lines):
+ l = lines[i]
+ if i and l.strip():
+ yield prefix
+ yield l
+ if i < num_lines - 1 or text.endswith('\n'):
+ yield '\n'
+ return "".join(indenter())
+
+def permissions(flags):
+ if "l" in flags:
+ return "lrwxrwxrwx"
+ if "x" in flags:
+ return "-rwxr-xr-x"
+ return "-rw-r--r--"
+
+filters = {
+ "addbreaks": nl2br,
+ "basename": os.path.basename,
+ "age": age,
+ "date": lambda x: util.datestr(x),
+ "domain": domain,
+ "email": util.email,
+ "escape": lambda x: cgi.escape(x, True),
+ "fill68": lambda x: fill(x, width=68),
+ "fill76": lambda x: fill(x, width=76),
+ "firstline": firstline,
+ "tabindent": lambda x: indent(x, '\t'),
+ "hgdate": hgdate,
+ "isodate": isodate,
+ "obfuscate": obfuscate,
+ "permissions": permissions,
+ "person": person,
+ "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
+ "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S", True, "%+03d:%02d"),
+ "short": lambda x: x[:12],
+ "shortdate": shortdate,
+ "stringify": templater.stringify,
+ "strip": lambda x: x.strip(),
+ "urlescape": lambda x: urllib.quote(x),
+ "user": lambda x: util.shortuser(x),
+ "stringescape": lambda x: x.encode('string_escape'),
+ }
+
--- a/mercurial/templater.py Thu Jan 31 14:44:19 2008 -0600
+++ b/mercurial/templater.py Thu Jan 31 14:44:19 2008 -0600
@@ -6,7 +6,7 @@
# of the GNU General Public License, incorporated herein by reference.
from i18n import _
-import cgi, re, sys, os, time, urllib, util, textwrap
+import re, sys, os
def parsestring(s, quoted=True):
'''parse a string using simple c-like syntax.
@@ -122,157 +122,6 @@
v = self.filters[f](v)
yield v
-agescales = [("second", 1),
- ("minute", 60),
- ("hour", 3600),
- ("day", 3600 * 24),
- ("week", 3600 * 24 * 7),
- ("month", 3600 * 24 * 30),
- ("year", 3600 * 24 * 365)]
-
-agescales.reverse()
-
-def age(date):
- '''turn a (timestamp, tzoff) tuple into an age string.'''
-
- def plural(t, c):
- if c == 1:
- return t
- return t + "s"
- def fmt(t, c):
- return "%d %s" % (c, plural(t, c))
-
- now = time.time()
- then = date[0]
- delta = max(1, int(now - then))
-
- for t, s in agescales:
- n = delta / s
- if n >= 2 or s == 1:
- return fmt(t, n)
-
-def stringify(thing):
- '''turn nested template iterator into string.'''
- if hasattr(thing, '__iter__'):
- return "".join([stringify(t) for t in thing if t is not None])
- return str(thing)
-
-para_re = None
-space_re = None
-
-def fill(text, width):
- '''fill many paragraphs.'''
- global para_re, space_re
- if para_re is None:
- para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
- space_re = re.compile(r' +')
-
- def findparas():
- start = 0
- while True:
- m = para_re.search(text, start)
- if not m:
- w = len(text)
- while w > start and text[w-1].isspace(): w -= 1
- yield text[start:w], text[w:]
- break
- yield text[start:m.start(0)], m.group(1)
- start = m.end(1)
-
- return "".join([space_re.sub(' ', textwrap.fill(para, width)) + rest
- for para, rest in findparas()])
-
-def firstline(text):
- '''return the first line of text'''
- try:
- return text.splitlines(1)[0].rstrip('\r\n')
- except IndexError:
- return ''
-
-def isodate(date):
- '''turn a (timestamp, tzoff) tuple into an iso 8631 date and time.'''
- return util.datestr(date, format='%Y-%m-%d %H:%M')
-
-def hgdate(date):
- '''turn a (timestamp, tzoff) tuple into an hg cset timestamp.'''
- return "%d %d" % date
-
-def nl2br(text):
- '''replace raw newlines with xhtml line breaks.'''
- return text.replace('\n', '<br/>\n')
-
-def obfuscate(text):
- text = unicode(text, util._encoding, 'replace')
- return ''.join(['&#%d;' % ord(c) for c in text])
-
-def domain(author):
- '''get domain of author, or empty string if none.'''
- f = author.find('@')
- if f == -1: return ''
- author = author[f+1:]
- f = author.find('>')
- if f >= 0: author = author[:f]
- return author
-
-def person(author):
- '''get name of author, or else username.'''
- f = author.find('<')
- if f == -1: return util.shortuser(author)
- return author[:f].rstrip()
-
-def shortdate(date):
- '''turn (timestamp, tzoff) tuple into iso 8631 date.'''
- return util.datestr(date, format='%Y-%m-%d', timezone=False)
-
-def indent(text, prefix):
- '''indent each non-empty line of text after first with prefix.'''
- lines = text.splitlines()
- num_lines = len(lines)
- def indenter():
- for i in xrange(num_lines):
- l = lines[i]
- if i and l.strip():
- yield prefix
- yield l
- if i < num_lines - 1 or text.endswith('\n'):
- yield '\n'
- return "".join(indenter())
-
-def permissions(flags):
- if "l" in flags:
- return "lrwxrwxrwx"
- if "x" in flags:
- return "-rwxr-xr-x"
- return "-rw-r--r--"
-
-common_filters = {
- "addbreaks": nl2br,
- "basename": os.path.basename,
- "age": age,
- "date": lambda x: util.datestr(x),
- "domain": domain,
- "email": util.email,
- "escape": lambda x: cgi.escape(x, True),
- "fill68": lambda x: fill(x, width=68),
- "fill76": lambda x: fill(x, width=76),
- "firstline": firstline,
- "tabindent": lambda x: indent(x, '\t'),
- "hgdate": hgdate,
- "isodate": isodate,
- "obfuscate": obfuscate,
- "permissions": permissions,
- "person": person,
- "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S"),
- "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S", True, "%+03d:%02d"),
- "short": lambda x: x[:12],
- "shortdate": shortdate,
- "stringify": stringify,
- "strip": lambda x: x.strip(),
- "urlescape": lambda x: urllib.quote(x),
- "user": lambda x: util.shortuser(x),
- "stringescape": lambda x: x.encode('string_escape'),
- }
-
def templatepath(name=None):
'''return location of template file or directory (if no name).
returns None if not found.'''
@@ -289,3 +138,9 @@
if (name and os.path.exists(p)) or os.path.isdir(p):
return os.path.normpath(p)
+def stringify(thing):
+ '''turn nested template iterator into string.'''
+ if hasattr(thing, '__iter__'):
+ return "".join([stringify(t) for t in thing if t is not None])
+ return str(thing)
+