##// END OF EJS Templates
templater: add hex filter.
Dan Villiom Podlaski Christiansen -
r12371:48a4acd1 default
parent child Browse files
Show More
@@ -1,156 +1,159 b''
1 Mercurial allows you to customize output of commands through
1 Mercurial allows you to customize output of commands through
2 templates. You can either pass in a template from the command
2 templates. You can either pass in a template from the command
3 line, via the --template option, or select an existing
3 line, via the --template option, or select an existing
4 template-style (--style).
4 template-style (--style).
5
5
6 You can customize output for any "log-like" command: log,
6 You can customize output for any "log-like" command: log,
7 outgoing, incoming, tip, parents, heads and glog.
7 outgoing, incoming, tip, parents, heads and glog.
8
8
9 Four styles are packaged with Mercurial: default (the style used
9 Four styles are packaged with Mercurial: default (the style used
10 when no explicit preference is passed), compact, changelog,
10 when no explicit preference is passed), compact, changelog,
11 and xml.
11 and xml.
12 Usage::
12 Usage::
13
13
14 $ hg log -r1 --style changelog
14 $ hg log -r1 --style changelog
15
15
16 A template is a piece of text, with markup to invoke variable
16 A template is a piece of text, with markup to invoke variable
17 expansion::
17 expansion::
18
18
19 $ hg log -r1 --template "{node}\n"
19 $ hg log -r1 --template "{node}\n"
20 b56ce7b07c52de7d5fd79fb89701ea538af65746
20 b56ce7b07c52de7d5fd79fb89701ea538af65746
21
21
22 Strings in curly braces are called keywords. The availability of
22 Strings in curly braces are called keywords. The availability of
23 keywords depends on the exact context of the templater. These
23 keywords depends on the exact context of the templater. These
24 keywords are usually available for templating a log-like command:
24 keywords are usually available for templating a log-like command:
25
25
26 :author: String. The unmodified author of the changeset.
26 :author: String. The unmodified author of the changeset.
27
27
28 :branches: String. The name of the branch on which the changeset was
28 :branches: String. The name of the branch on which the changeset was
29 committed. Will be empty if the branch name was default.
29 committed. Will be empty if the branch name was default.
30
30
31 :children: List of strings. The children of the changeset.
31 :children: List of strings. The children of the changeset.
32
32
33 :date: Date information. The date when the changeset was committed.
33 :date: Date information. The date when the changeset was committed.
34
34
35 :desc: String. The text of the changeset description.
35 :desc: String. The text of the changeset description.
36
36
37 :diffstat: String. Statistics of changes with the following format:
37 :diffstat: String. Statistics of changes with the following format:
38 "modified files: +added/-removed lines"
38 "modified files: +added/-removed lines"
39
39
40 :files: List of strings. All files modified, added, or removed by this
40 :files: List of strings. All files modified, added, or removed by this
41 changeset.
41 changeset.
42
42
43 :file_adds: List of strings. Files added by this changeset.
43 :file_adds: List of strings. Files added by this changeset.
44
44
45 :file_copies: List of strings. Files copied in this changeset with
45 :file_copies: List of strings. Files copied in this changeset with
46 their sources.
46 their sources.
47
47
48 :file_copies_switch: List of strings. Like "file_copies" but displayed
48 :file_copies_switch: List of strings. Like "file_copies" but displayed
49 only if the --copied switch is set.
49 only if the --copied switch is set.
50
50
51 :file_mods: List of strings. Files modified by this changeset.
51 :file_mods: List of strings. Files modified by this changeset.
52
52
53 :file_dels: List of strings. Files removed by this changeset.
53 :file_dels: List of strings. Files removed by this changeset.
54
54
55 :node: String. The changeset identification hash, as a 40 hexadecimal
55 :node: String. The changeset identification hash, as a 40 hexadecimal
56 digit string.
56 digit string.
57
57
58 :parents: List of strings. The parents of the changeset.
58 :parents: List of strings. The parents of the changeset.
59
59
60 :rev: Integer. The repository-local changeset revision number.
60 :rev: Integer. The repository-local changeset revision number.
61
61
62 :tags: List of strings. Any tags associated with the changeset.
62 :tags: List of strings. Any tags associated with the changeset.
63
63
64 :latesttag: String. Most recent global tag in the ancestors of this
64 :latesttag: String. Most recent global tag in the ancestors of this
65 changeset.
65 changeset.
66
66
67 :latesttagdistance: Integer. Longest path to the latest tag.
67 :latesttagdistance: Integer. Longest path to the latest tag.
68
68
69 The "date" keyword does not produce human-readable output. If you
69 The "date" keyword does not produce human-readable output. If you
70 want to use a date in your output, you can use a filter to process
70 want to use a date in your output, you can use a filter to process
71 it. Filters are functions which return a string based on the input
71 it. Filters are functions which return a string based on the input
72 variable. Be sure to use the stringify filter first when you're
72 variable. Be sure to use the stringify filter first when you're
73 applying a string-input filter to a list-like input variable.
73 applying a string-input filter to a list-like input variable.
74 You can also use a chain of filters to get the desired output::
74 You can also use a chain of filters to get the desired output::
75
75
76 $ hg tip --template "{date|isodate}\n"
76 $ hg tip --template "{date|isodate}\n"
77 2008-08-21 18:22 +0000
77 2008-08-21 18:22 +0000
78
78
79 List of filters:
79 List of filters:
80
80
81 :addbreaks: Any text. Add an XHTML "<br />" tag before the end of
81 :addbreaks: Any text. Add an XHTML "<br />" tag before the end of
82 every line except the last.
82 every line except the last.
83
83
84 :age: Date. Returns a human-readable date/time difference between the
84 :age: Date. Returns a human-readable date/time difference between the
85 given date/time and the current date/time.
85 given date/time and the current date/time.
86
86
87 :basename: Any text. Treats the text as a path, and returns the last
87 :basename: Any text. Treats the text as a path, and returns the last
88 component of the path after splitting by the path separator
88 component of the path after splitting by the path separator
89 (ignoring trailing separators). For example, "foo/bar/baz" becomes
89 (ignoring trailing separators). For example, "foo/bar/baz" becomes
90 "baz" and "foo/bar//" becomes "bar".
90 "baz" and "foo/bar//" becomes "bar".
91
91
92 :stripdir: Treat the text as path and strip a directory level, if
92 :stripdir: Treat the text as path and strip a directory level, if
93 possible. For example, "foo" and "foo/bar" becomes "foo".
93 possible. For example, "foo" and "foo/bar" becomes "foo".
94
94
95 :date: Date. Returns a date in a Unix date format, including the
95 :date: Date. Returns a date in a Unix date format, including the
96 timezone: "Mon Sep 04 15:13:13 2006 0700".
96 timezone: "Mon Sep 04 15:13:13 2006 0700".
97
97
98 :domain: Any text. Finds the first string that looks like an email
98 :domain: Any text. Finds the first string that looks like an email
99 address, and extracts just the domain component. Example: ``User
99 address, and extracts just the domain component. Example: ``User
100 <user@example.com>`` becomes ``example.com``.
100 <user@example.com>`` becomes ``example.com``.
101
101
102 :email: Any text. Extracts the first string that looks like an email
102 :email: Any text. Extracts the first string that looks like an email
103 address. Example: ``User <user@example.com>`` becomes
103 address. Example: ``User <user@example.com>`` becomes
104 ``user@example.com``.
104 ``user@example.com``.
105
105
106 :escape: Any text. Replaces the special XML/XHTML characters "&", "<"
106 :escape: Any text. Replaces the special XML/XHTML characters "&", "<"
107 and ">" with XML entities.
107 and ">" with XML entities.
108
108
109 :hex: Any text. Convert a binary Mercurial node identifier into
110 its long hexadecimal representation.
111
109 :fill68: Any text. Wraps the text to fit in 68 columns.
112 :fill68: Any text. Wraps the text to fit in 68 columns.
110
113
111 :fill76: Any text. Wraps the text to fit in 76 columns.
114 :fill76: Any text. Wraps the text to fit in 76 columns.
112
115
113 :firstline: Any text. Returns the first line of text.
116 :firstline: Any text. Returns the first line of text.
114
117
115 :nonempty: Any text. Returns '(none)' if the string is empty.
118 :nonempty: Any text. Returns '(none)' if the string is empty.
116
119
117 :hgdate: Date. Returns the date as a pair of numbers: "1157407993
120 :hgdate: Date. Returns the date as a pair of numbers: "1157407993
118 25200" (Unix timestamp, timezone offset).
121 25200" (Unix timestamp, timezone offset).
119
122
120 :isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
123 :isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
121 +0200".
124 +0200".
122
125
123 :isodatesec: Date. Returns the date in ISO 8601 format, including
126 :isodatesec: Date. Returns the date in ISO 8601 format, including
124 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
127 seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
125 filter.
128 filter.
126
129
127 :localdate: Date. Converts a date to local date.
130 :localdate: Date. Converts a date to local date.
128
131
129 :obfuscate: Any text. Returns the input text rendered as a sequence of
132 :obfuscate: Any text. Returns the input text rendered as a sequence of
130 XML entities.
133 XML entities.
131
134
132 :person: Any text. Returns the text before an email address.
135 :person: Any text. Returns the text before an email address.
133
136
134 :rfc822date: Date. Returns a date using the same format used in email
137 :rfc822date: Date. Returns a date using the same format used in email
135 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
138 headers: "Tue, 18 Aug 2009 13:00:13 +0200".
136
139
137 :rfc3339date: Date. Returns a date using the Internet date format
140 :rfc3339date: Date. Returns a date using the Internet date format
138 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
141 specified in RFC 3339: "2009-08-18T13:00:13+02:00".
139
142
140 :short: Changeset hash. Returns the short form of a changeset hash,
143 :short: Changeset hash. Returns the short form of a changeset hash,
141 i.e. a 12 hexadecimal digit string.
144 i.e. a 12 hexadecimal digit string.
142
145
143 :shortdate: Date. Returns a date like "2006-09-18".
146 :shortdate: Date. Returns a date like "2006-09-18".
144
147
145 :stringify: Any type. Turns the value into text by converting values into
148 :stringify: Any type. Turns the value into text by converting values into
146 text and concatenating them.
149 text and concatenating them.
147
150
148 :strip: Any text. Strips all leading and trailing whitespace.
151 :strip: Any text. Strips all leading and trailing whitespace.
149
152
150 :tabindent: Any text. Returns the text, with every line except the
153 :tabindent: Any text. Returns the text, with every line except the
151 first starting with a tab character.
154 first starting with a tab character.
152
155
153 :urlescape: Any text. Escapes all "special" characters. For example,
156 :urlescape: Any text. Escapes all "special" characters. For example,
154 "foo bar" becomes "foo%20bar".
157 "foo bar" becomes "foo%20bar".
155
158
156 :user: Any text. Returns the user portion of an email address.
159 :user: Any text. Returns the user portion of an email address.
@@ -1,227 +1,228 b''
1 # template-filters.py - common template expansion filters
1 # template-filters.py - common template expansion filters
2 #
2 #
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2008 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import cgi, re, os, time, urllib
8 import cgi, re, os, time, urllib
9 import util, encoding
9 import encoding, node, util
10
10
11 def stringify(thing):
11 def stringify(thing):
12 '''turn nested template iterator into string.'''
12 '''turn nested template iterator into string.'''
13 if hasattr(thing, '__iter__') and not isinstance(thing, str):
13 if hasattr(thing, '__iter__') and not isinstance(thing, str):
14 return "".join([stringify(t) for t in thing if t is not None])
14 return "".join([stringify(t) for t in thing if t is not None])
15 return str(thing)
15 return str(thing)
16
16
17 agescales = [("year", 3600 * 24 * 365),
17 agescales = [("year", 3600 * 24 * 365),
18 ("month", 3600 * 24 * 30),
18 ("month", 3600 * 24 * 30),
19 ("week", 3600 * 24 * 7),
19 ("week", 3600 * 24 * 7),
20 ("day", 3600 * 24),
20 ("day", 3600 * 24),
21 ("hour", 3600),
21 ("hour", 3600),
22 ("minute", 60),
22 ("minute", 60),
23 ("second", 1)]
23 ("second", 1)]
24
24
25 def age(date):
25 def age(date):
26 '''turn a (timestamp, tzoff) tuple into an age string.'''
26 '''turn a (timestamp, tzoff) tuple into an age string.'''
27
27
28 def plural(t, c):
28 def plural(t, c):
29 if c == 1:
29 if c == 1:
30 return t
30 return t
31 return t + "s"
31 return t + "s"
32 def fmt(t, c):
32 def fmt(t, c):
33 return "%d %s" % (c, plural(t, c))
33 return "%d %s" % (c, plural(t, c))
34
34
35 now = time.time()
35 now = time.time()
36 then = date[0]
36 then = date[0]
37 if then > now:
37 if then > now:
38 return 'in the future'
38 return 'in the future'
39
39
40 delta = max(1, int(now - then))
40 delta = max(1, int(now - then))
41 if delta > agescales[0][1] * 2:
41 if delta > agescales[0][1] * 2:
42 return util.shortdate(date)
42 return util.shortdate(date)
43
43
44 for t, s in agescales:
44 for t, s in agescales:
45 n = delta // s
45 n = delta // s
46 if n >= 2 or s == 1:
46 if n >= 2 or s == 1:
47 return '%s ago' % fmt(t, n)
47 return '%s ago' % fmt(t, n)
48
48
49 para_re = None
49 para_re = None
50 space_re = None
50 space_re = None
51
51
52 def fill(text, width):
52 def fill(text, width):
53 '''fill many paragraphs.'''
53 '''fill many paragraphs.'''
54 global para_re, space_re
54 global para_re, space_re
55 if para_re is None:
55 if para_re is None:
56 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
56 para_re = re.compile('(\n\n|\n\\s*[-*]\\s*)', re.M)
57 space_re = re.compile(r' +')
57 space_re = re.compile(r' +')
58
58
59 def findparas():
59 def findparas():
60 start = 0
60 start = 0
61 while True:
61 while True:
62 m = para_re.search(text, start)
62 m = para_re.search(text, start)
63 if not m:
63 if not m:
64 uctext = unicode(text[start:], encoding.encoding)
64 uctext = unicode(text[start:], encoding.encoding)
65 w = len(uctext)
65 w = len(uctext)
66 while 0 < w and uctext[w - 1].isspace():
66 while 0 < w and uctext[w - 1].isspace():
67 w -= 1
67 w -= 1
68 yield (uctext[:w].encode(encoding.encoding),
68 yield (uctext[:w].encode(encoding.encoding),
69 uctext[w:].encode(encoding.encoding))
69 uctext[w:].encode(encoding.encoding))
70 break
70 break
71 yield text[start:m.start(0)], m.group(1)
71 yield text[start:m.start(0)], m.group(1)
72 start = m.end(1)
72 start = m.end(1)
73
73
74 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
74 return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
75 for para, rest in findparas()])
75 for para, rest in findparas()])
76
76
77 def firstline(text):
77 def firstline(text):
78 '''return the first line of text'''
78 '''return the first line of text'''
79 try:
79 try:
80 return text.splitlines(True)[0].rstrip('\r\n')
80 return text.splitlines(True)[0].rstrip('\r\n')
81 except IndexError:
81 except IndexError:
82 return ''
82 return ''
83
83
84 def nl2br(text):
84 def nl2br(text):
85 '''replace raw newlines with xhtml line breaks.'''
85 '''replace raw newlines with xhtml line breaks.'''
86 return text.replace('\n', '<br/>\n')
86 return text.replace('\n', '<br/>\n')
87
87
88 def obfuscate(text):
88 def obfuscate(text):
89 text = unicode(text, encoding.encoding, 'replace')
89 text = unicode(text, encoding.encoding, 'replace')
90 return ''.join(['&#%d;' % ord(c) for c in text])
90 return ''.join(['&#%d;' % ord(c) for c in text])
91
91
92 def domain(author):
92 def domain(author):
93 '''get domain of author, or empty string if none.'''
93 '''get domain of author, or empty string if none.'''
94 f = author.find('@')
94 f = author.find('@')
95 if f == -1:
95 if f == -1:
96 return ''
96 return ''
97 author = author[f + 1:]
97 author = author[f + 1:]
98 f = author.find('>')
98 f = author.find('>')
99 if f >= 0:
99 if f >= 0:
100 author = author[:f]
100 author = author[:f]
101 return author
101 return author
102
102
103 def person(author):
103 def person(author):
104 '''get name of author, or else username.'''
104 '''get name of author, or else username.'''
105 if not '@' in author:
105 if not '@' in author:
106 return author
106 return author
107 f = author.find('<')
107 f = author.find('<')
108 if f == -1:
108 if f == -1:
109 return util.shortuser(author)
109 return util.shortuser(author)
110 return author[:f].rstrip()
110 return author[:f].rstrip()
111
111
112 def indent(text, prefix):
112 def indent(text, prefix):
113 '''indent each non-empty line of text after first with prefix.'''
113 '''indent each non-empty line of text after first with prefix.'''
114 lines = text.splitlines()
114 lines = text.splitlines()
115 num_lines = len(lines)
115 num_lines = len(lines)
116 endswithnewline = text[-1:] == '\n'
116 endswithnewline = text[-1:] == '\n'
117 def indenter():
117 def indenter():
118 for i in xrange(num_lines):
118 for i in xrange(num_lines):
119 l = lines[i]
119 l = lines[i]
120 if i and l.strip():
120 if i and l.strip():
121 yield prefix
121 yield prefix
122 yield l
122 yield l
123 if i < num_lines - 1 or endswithnewline:
123 if i < num_lines - 1 or endswithnewline:
124 yield '\n'
124 yield '\n'
125 return "".join(indenter())
125 return "".join(indenter())
126
126
127 def permissions(flags):
127 def permissions(flags):
128 if "l" in flags:
128 if "l" in flags:
129 return "lrwxrwxrwx"
129 return "lrwxrwxrwx"
130 if "x" in flags:
130 if "x" in flags:
131 return "-rwxr-xr-x"
131 return "-rwxr-xr-x"
132 return "-rw-r--r--"
132 return "-rw-r--r--"
133
133
134 def xmlescape(text):
134 def xmlescape(text):
135 text = (text
135 text = (text
136 .replace('&', '&amp;')
136 .replace('&', '&amp;')
137 .replace('<', '&lt;')
137 .replace('<', '&lt;')
138 .replace('>', '&gt;')
138 .replace('>', '&gt;')
139 .replace('"', '&quot;')
139 .replace('"', '&quot;')
140 .replace("'", '&#39;')) # &apos; invalid in HTML
140 .replace("'", '&#39;')) # &apos; invalid in HTML
141 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
141 return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
142
142
143 def uescape(c):
143 def uescape(c):
144 if ord(c) < 0x80:
144 if ord(c) < 0x80:
145 return c
145 return c
146 else:
146 else:
147 return '\\u%04x' % ord(c)
147 return '\\u%04x' % ord(c)
148
148
149 _escapes = [
149 _escapes = [
150 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
150 ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
151 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
151 ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
152 ]
152 ]
153
153
154 def jsonescape(s):
154 def jsonescape(s):
155 for k, v in _escapes:
155 for k, v in _escapes:
156 s = s.replace(k, v)
156 s = s.replace(k, v)
157 return ''.join(uescape(c) for c in s)
157 return ''.join(uescape(c) for c in s)
158
158
159 def json(obj):
159 def json(obj):
160 if obj is None or obj is False or obj is True:
160 if obj is None or obj is False or obj is True:
161 return {None: 'null', False: 'false', True: 'true'}[obj]
161 return {None: 'null', False: 'false', True: 'true'}[obj]
162 elif isinstance(obj, int) or isinstance(obj, float):
162 elif isinstance(obj, int) or isinstance(obj, float):
163 return str(obj)
163 return str(obj)
164 elif isinstance(obj, str):
164 elif isinstance(obj, str):
165 u = unicode(obj, encoding.encoding, 'replace')
165 u = unicode(obj, encoding.encoding, 'replace')
166 return '"%s"' % jsonescape(u)
166 return '"%s"' % jsonescape(u)
167 elif isinstance(obj, unicode):
167 elif isinstance(obj, unicode):
168 return '"%s"' % jsonescape(obj)
168 return '"%s"' % jsonescape(obj)
169 elif hasattr(obj, 'keys'):
169 elif hasattr(obj, 'keys'):
170 out = []
170 out = []
171 for k, v in obj.iteritems():
171 for k, v in obj.iteritems():
172 s = '%s: %s' % (json(k), json(v))
172 s = '%s: %s' % (json(k), json(v))
173 out.append(s)
173 out.append(s)
174 return '{' + ', '.join(out) + '}'
174 return '{' + ', '.join(out) + '}'
175 elif hasattr(obj, '__iter__'):
175 elif hasattr(obj, '__iter__'):
176 out = []
176 out = []
177 for i in obj:
177 for i in obj:
178 out.append(json(i))
178 out.append(json(i))
179 return '[' + ', '.join(out) + ']'
179 return '[' + ', '.join(out) + ']'
180 else:
180 else:
181 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
181 raise TypeError('cannot encode type %s' % obj.__class__.__name__)
182
182
183 def stripdir(text):
183 def stripdir(text):
184 '''Treat the text as path and strip a directory level, if possible.'''
184 '''Treat the text as path and strip a directory level, if possible.'''
185 dir = os.path.dirname(text)
185 dir = os.path.dirname(text)
186 if dir == "":
186 if dir == "":
187 return os.path.basename(text)
187 return os.path.basename(text)
188 else:
188 else:
189 return dir
189 return dir
190
190
191 def nonempty(str):
191 def nonempty(str):
192 return str or "(none)"
192 return str or "(none)"
193
193
194 filters = {
194 filters = {
195 "addbreaks": nl2br,
195 "addbreaks": nl2br,
196 "basename": os.path.basename,
196 "basename": os.path.basename,
197 "stripdir": stripdir,
197 "stripdir": stripdir,
198 "age": age,
198 "age": age,
199 "date": lambda x: util.datestr(x),
199 "date": lambda x: util.datestr(x),
200 "domain": domain,
200 "domain": domain,
201 "email": util.email,
201 "email": util.email,
202 "escape": lambda x: cgi.escape(x, True),
202 "escape": lambda x: cgi.escape(x, True),
203 "fill68": lambda x: fill(x, width=68),
203 "fill68": lambda x: fill(x, width=68),
204 "fill76": lambda x: fill(x, width=76),
204 "fill76": lambda x: fill(x, width=76),
205 "firstline": firstline,
205 "firstline": firstline,
206 "tabindent": lambda x: indent(x, '\t'),
206 "tabindent": lambda x: indent(x, '\t'),
207 "hgdate": lambda x: "%d %d" % x,
207 "hgdate": lambda x: "%d %d" % x,
208 "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
208 "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
209 "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
209 "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
210 "json": json,
210 "json": json,
211 "jsonescape": jsonescape,
211 "jsonescape": jsonescape,
212 "localdate": lambda x: (x[0], util.makedate()[1]),
212 "localdate": lambda x: (x[0], util.makedate()[1]),
213 "nonempty": nonempty,
213 "nonempty": nonempty,
214 "obfuscate": obfuscate,
214 "obfuscate": obfuscate,
215 "permissions": permissions,
215 "permissions": permissions,
216 "person": person,
216 "person": person,
217 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
217 "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
218 "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
218 "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
219 "hex": node.hex,
219 "short": lambda x: x[:12],
220 "short": lambda x: x[:12],
220 "shortdate": util.shortdate,
221 "shortdate": util.shortdate,
221 "stringify": stringify,
222 "stringify": stringify,
222 "strip": lambda x: x.strip(),
223 "strip": lambda x: x.strip(),
223 "urlescape": lambda x: urllib.quote(x),
224 "urlescape": lambda x: urllib.quote(x),
224 "user": lambda x: util.shortuser(x),
225 "user": lambda x: util.shortuser(x),
225 "stringescape": lambda x: x.encode('string_escape'),
226 "stringescape": lambda x: x.encode('string_escape'),
226 "xmlescape": xmlescape,
227 "xmlescape": xmlescape,
227 }
228 }
General Comments 0
You need to be logged in to leave comments. Login now