2 A small templating language
4 This implements a small templating language. This language implements
5 if/elif/else, for/continue/break, expressions, and blocks of Python
8 {{any expression (function calls etc)}}
9 {{any expression | filter}}
10 {{for x in y}}...{{endfor}}
11 {{if x}}x{{elif y}}y{{else}}z{{endif}}
17 {{default var = default_value}}
20 You use this with the ``Template`` class or the ``sub`` shortcut.
21 The ``Template`` class takes the template string and the name of
22 the template (for errors) and a default namespace. Then (like
23 ``string.Template``) you can call the ``tmpl.substitute(**kw)``
24 method to make a substitution (or ``tmpl.substitute(a_dict)``).
26 ``sub(content, **kw)`` substitutes the template immediately. You
27 can use ``__name='tmpl.html'`` to set the name of the template.
29 If there are syntax errors ``TemplateError`` will be raised.
36 from urllib
import quote
as url_quote
37 except ImportError: # Py3
38 from urllib
.parse
import quote
as url_quote
42 from io
import StringIO
44 from cStringIO
import StringIO
45 from Cython
.Tempita
._looper
import looper
46 from Cython
.Tempita
.compat3
import bytes
, basestring_
, next
, is_unicode
, coerce_text
48 __all__
= ['TemplateError', 'Template', 'sub', 'HTMLTemplate',
49 'sub_html', 'html', 'bunch']
51 in_re
= re
.compile(r
'\s+in\s+')
52 var_re
= re
.compile(r
'^[a-z_][a-z0-9_]*$', re
.I
)
55 class TemplateError(Exception):
56 """Exception raised while parsing a template
59 def __init__(self
, message
, position
, name
=None):
60 Exception.__init
__(self
, message
)
61 self
.position
= position
65 msg
= ' '.join(self
.args
)
67 msg
= '%s at line %s column %s' % (
68 msg
, self
.position
[0], self
.position
[1])
70 msg
+= ' in %s' % self
.name
74 class _TemplateContinue(Exception):
78 class _TemplateBreak(Exception):
82 def get_file_template(name
, from_template
):
83 path
= os
.path
.join(os
.path
.dirname(from_template
.name
), name
)
84 return from_template
.__class
__.from_filename(
85 path
, namespace
=from_template
.namespace
,
86 get_template
=from_template
.get_template
)
89 class Template(object):
97 default_encoding
= 'utf8'
98 default_inherit
= None
100 def __init__(self
, content
, name
=None, namespace
=None, stacklevel
=None,
101 get_template
=None, default_inherit
=None, line_offset
=0,
103 self
.content
= content
106 if delimeters
is None:
107 delimeters
= (self
.default_namespace
['start_braces'],
108 self
.default_namespace
['end_braces'])
110 #assert len(delimeters) == 2 and all([isinstance(delimeter, basestring)
111 # for delimeter in delimeters])
112 self
.default_namespace
= self
.__class
__.default_namespace
.copy()
113 self
.default_namespace
['start_braces'] = delimeters
[0]
114 self
.default_namespace
['end_braces'] = delimeters
[1]
115 self
.delimeters
= delimeters
117 self
._unicode
= is_unicode(content
)
118 if name
is None and stacklevel
is not None:
120 caller
= sys
._getframe
(stacklevel
)
124 globals = caller
.f_globals
125 lineno
= caller
.f_lineno
126 if '__file__' in globals:
127 name
= globals['__file__']
128 if name
.endswith('.pyc') or name
.endswith('.pyo'):
130 elif '__name__' in globals:
131 name
= globals['__name__']
135 name
+= ':%s' % lineno
137 self
._parsed
= parse(content
, name
=name
, line_offset
=line_offset
, delimeters
=self
.delimeters
)
138 if namespace
is None:
140 self
.namespace
= namespace
141 self
.get_template
= get_template
142 if default_inherit
is not None:
143 self
.default_inherit
= default_inherit
145 def from_filename(cls
, filename
, namespace
=None, encoding
=None,
146 default_inherit
=None, get_template
=get_file_template
):
147 f
= open(filename
, 'rb')
151 c
= c
.decode(encoding
)
152 return cls(content
=c
, name
=filename
, namespace
=namespace
,
153 default_inherit
=default_inherit
, get_template
=get_template
)
155 from_filename
= classmethod(from_filename
)
158 return '<%s %s name=%r>' % (
159 self
.__class
__.__name
__,
160 hex(id(self
))[2:], self
.name
)
162 def substitute(self
, *args
, **kw
):
166 "You can only give positional *or* keyword arguments")
169 "You can only give one positional argument")
170 if not hasattr(args
[0], 'items'):
172 "If you pass in a single argument, you must pass in a dictionary-like object (with a .items() method); you gave %r"
176 ns
['__template_name__'] = self
.name
178 ns
.update(self
.namespace
)
179 result
, defs
, inherit
= self
._interpret
(ns
)
181 inherit
= self
.default_inherit
183 result
= self
._interpret
_inherit
(result
, defs
, inherit
, ns
)
186 def _interpret(self
, ns
):
187 __traceback_hide__
= True
190 self
._interpret
_codes
(self
._parsed
, ns
, out
=parts
, defs
=defs
)
191 if '__inherit__' in defs
:
192 inherit
= defs
.pop('__inherit__')
195 return ''.join(parts
), defs
, inherit
197 def _interpret_inherit(self
, body
, defs
, inherit_template
, ns
):
198 __traceback_hide__
= True
199 if not self
.get_template
:
201 'You cannot use inheritance without passing in get_template',
202 position
=None, name
=self
.name
)
203 templ
= self
.get_template(inherit_template
, self
)
204 self_
= TemplateObject(self
.name
)
205 for name
, value
in defs
.iteritems():
206 setattr(self_
, name
, value
)
210 return templ
.substitute(ns
)
212 def _interpret_codes(self
, codes
, ns
, out
, defs
):
213 __traceback_hide__
= True
215 if isinstance(item
, basestring_
):
218 self
._interpret
_code
(item
, ns
, out
, defs
)
220 def _interpret_code(self
, code
, ns
, out
, defs
):
221 __traceback_hide__
= True
222 name
, pos
= code
[0], code
[1]
224 self
._exec
(code
[2], ns
, pos
)
225 elif name
== 'continue':
226 raise _TemplateContinue()
227 elif name
== 'break':
228 raise _TemplateBreak()
230 vars, expr
, content
= code
[2], code
[3], code
[4]
231 expr
= self
._eval
(expr
, ns
, pos
)
232 self
._interpret
_for
(vars, expr
, content
, ns
, out
, defs
)
235 self
._interpret
_if
(parts
, ns
, out
, defs
)
237 parts
= code
[2].split('|')
238 base
= self
._eval
(parts
[0], ns
, pos
)
239 for part
in parts
[1:]:
240 func
= self
._eval
(part
, ns
, pos
)
242 out
.append(self
._repr
(base
, pos
))
243 elif name
== 'default':
244 var
, expr
= code
[2], code
[3]
246 result
= self
._eval
(expr
, ns
, pos
)
248 elif name
== 'inherit':
250 value
= self
._eval
(expr
, ns
, pos
)
251 defs
['__inherit__'] = value
256 ns
[name
] = defs
[name
] = TemplateDef(self
, name
, signature
, body
=parts
, ns
=ns
,
258 elif name
== 'comment':
261 assert 0, "Unknown code: %r" % name
263 def _interpret_for(self
, vars, expr
, content
, ns
, out
, defs
):
264 __traceback_hide__
= True
269 if len(vars) != len(item
):
271 'Need %i items to unpack (got %i items)'
272 % (len(vars), len(item
)))
273 for name
, value
in zip(vars, item
):
276 self
._interpret
_codes
(content
, ns
, out
, defs
)
277 except _TemplateContinue
:
279 except _TemplateBreak
:
282 def _interpret_if(self
, parts
, ns
, out
, defs
):
283 __traceback_hide__
= True
284 # @@: if/else/else gets through
286 assert not isinstance(part
, basestring_
)
287 name
, pos
= part
[0], part
[1]
291 result
= self
._eval
(part
[2], ns
, pos
)
293 self
._interpret
_codes
(part
[3], ns
, out
, defs
)
296 def _eval(self
, code
, ns
, pos
):
297 __traceback_hide__
= True
300 value
= eval(code
, self
.default_namespace
, ns
)
301 except SyntaxError, e
:
303 'invalid syntax in expression: %s' % code
)
306 exc_info
= sys
.exc_info()
308 if getattr(e
, 'args', None):
311 arg0
= coerce_text(e
)
312 e
.args
= (self
._add
_line
_info
(arg0
, pos
),)
313 raise exc_info
[0], e
, exc_info
[2]
315 def _exec(self
, code
, ns
, pos
):
316 __traceback_hide__
= True
318 exec code
in self
.default_namespace
, ns
320 exc_info
= sys
.exc_info()
323 e
.args
= (self
._add
_line
_info
(e
.args
[0], pos
),)
325 e
.args
= (self
._add
_line
_info
(None, pos
),)
326 raise exc_info
[0], e
, exc_info
[2]
328 def _repr(self
, value
, pos
):
329 __traceback_hide__
= True
335 value
= unicode(value
)
336 except UnicodeDecodeError:
339 if not isinstance(value
, basestring_
):
340 value
= coerce_text(value
)
341 if (is_unicode(value
)
342 and self
.default_encoding
):
343 value
= value
.encode(self
.default_encoding
)
345 exc_info
= sys
.exc_info()
347 e
.args
= (self
._add
_line
_info
(e
.args
[0], pos
),)
348 raise exc_info
[0], e
, exc_info
[2]
350 if self
._unicode
and isinstance(value
, bytes
):
351 if not self
.default_encoding
:
352 raise UnicodeDecodeError(
353 'Cannot decode bytes value %r into unicode '
354 '(no default_encoding provided)' % value
)
356 value
= value
.decode(self
.default_encoding
)
357 except UnicodeDecodeError, e
:
358 raise UnicodeDecodeError(
363 e
.reason
+ ' in string %r' % value
)
364 elif not self
._unicode
and is_unicode(value
):
365 if not self
.default_encoding
:
366 raise UnicodeEncodeError(
367 'Cannot encode unicode value %r into bytes '
368 '(no default_encoding provided)' % value
)
369 value
= value
.encode(self
.default_encoding
)
372 def _add_line_info(self
, msg
, pos
):
373 msg
= "%s at line %s column %s" % (
376 msg
+= " in file %s" % self
.name
380 def sub(content
, delimeters
=None, **kw
):
381 name
= kw
.get('__name')
382 tmpl
= Template(content
, name
=name
, delimeters
=delimeters
)
383 return tmpl
.substitute(kw
)
386 def paste_script_template_renderer(content
, vars, filename
=None):
387 tmpl
= Template(content
, name
=filename
)
388 return tmpl
.substitute(vars)
393 def __init__(self
, **kw
):
394 for name
, value
in kw
.iteritems():
395 setattr(self
, name
, value
)
397 def __setattr__(self
, name
, value
):
400 def __getattr__(self
, name
):
404 raise AttributeError(name
)
406 def __getitem__(self
, key
):
407 if 'default' in self
:
409 return dict.__getitem
__(self
, key
)
411 return dict.__getitem
__(self
, 'default')
413 return dict.__getitem
__(self
, key
)
417 (k
, v
) for k
, v
in self
.iteritems()]
420 self
.__class
__.__name
__,
421 ' '.join(['%s=%r' % (k
, v
) for k
, v
in items
]))
423 ############################################################
425 ############################################################
430 def __init__(self
, value
):
441 self
.__class
__.__name
__, self
.value
)
444 def html_quote(value
, force
=True):
445 if not force
and hasattr(value
, '__html__'):
446 return value
.__html
__()
449 if not isinstance(value
, basestring_
):
450 value
= coerce_text(value
)
451 if sys
.version
>= "3" and isinstance(value
, bytes
):
452 value
= cgi
.escape(value
.decode('latin1'), 1)
453 value
= value
.encode('latin1')
455 value
= cgi
.escape(value
, 1)
456 if sys
.version
< "3":
457 if is_unicode(value
):
458 value
= value
.encode('ascii', 'xmlcharrefreplace')
470 kw
= list(kw
.iteritems())
473 for name
, value
in kw
:
476 if name
.endswith('_'):
478 parts
.append('%s="%s"' % (html_quote(name
), html_quote(value
)))
479 return html(' '.join(parts
))
482 class HTMLTemplate(Template
):
484 default_namespace
= Template
.default_namespace
.copy()
485 default_namespace
.update(dict(
489 html_quote
=html_quote
,
492 def _repr(self
, value
, pos
):
493 if hasattr(value
, '__html__'):
494 value
= value
.__html
__()
498 plain
= Template
._repr
(self
, value
, pos
)
500 return html_quote(plain
)
505 def sub_html(content
, **kw
):
506 name
= kw
.get('__name')
507 tmpl
= HTMLTemplate(content
, name
=name
)
508 return tmpl
.substitute(kw
)
511 class TemplateDef(object):
512 def __init__(self
, template
, func_name
, func_signature
,
513 body
, ns
, pos
, bound_self
=None):
514 self
._template
= template
515 self
._func
_name
= func_name
516 self
._func
_signature
= func_signature
520 self
._bound
_self
= bound_self
523 return '<tempita function %s(%s) at %s:%s>' % (
524 self
._func
_name
, self
._func
_signature
,
525 self
._template
.name
, self
._pos
)
530 def __call__(self
, *args
, **kw
):
531 values
= self
._parse
_signature
(args
, kw
)
534 if self
._bound
_self
is not None:
535 ns
['self'] = self
._bound
_self
538 self
._template
._interpret
_codes
(self
._body
, ns
, out
, subdefs
)
541 def __get__(self
, obj
, type=None):
544 return self
.__class
__(
545 self
._template
, self
._func
_name
, self
._func
_signature
,
546 self
._body
, self
._ns
, self
._pos
, bound_self
=obj
)
548 def _parse_signature(self
, args
, kw
):
550 sig_args
, var_args
, var_kw
, defaults
= self
._func
_signature
552 for name
, value
in kw
.iteritems():
553 if not var_kw
and name
not in sig_args
:
555 'Unexpected argument %s' % name
)
557 values
[sig_args
] = value
559 extra_kw
[name
] = value
561 sig_args
= list(sig_args
)
563 while sig_args
and sig_args
[0] in values
:
566 name
= sig_args
.pop(0)
567 values
[name
] = args
.pop(0)
569 values
[var_args
] = tuple(args
)
573 'Extra position arguments: %s'
574 % ', '.join([repr(v
) for v
in args
]))
575 for name
, value_expr
in defaults
.iteritems():
576 if name
not in values
:
577 values
[name
] = self
._template
._eval
(
578 value_expr
, self
._ns
, self
._pos
)
579 for name
in sig_args
:
580 if name
not in values
:
582 'Missing argument: %s' % name
)
584 values
[var_kw
] = extra_kw
588 class TemplateObject(object):
590 def __init__(self
, name
):
592 self
.get
= TemplateObjectGetter(self
)
595 return '<%s %s>' % (self
.__class
__.__name
__, self
.__name
)
598 class TemplateObjectGetter(object):
600 def __init__(self
, template_obj
):
601 self
.__template
_obj
= template_obj
603 def __getattr__(self
, attr
):
604 return getattr(self
.__template
_obj
, attr
, Empty
)
607 return '<%s around %r>' % (self
.__class
__.__name
__, self
.__template
_obj
)
610 class _Empty(object):
611 def __call__(self
, *args
, **kw
):
620 def __unicode__(self
):
629 if sys
.version
< "3":
630 __nonzero__
= __bool__
635 ############################################################
636 ## Lexing and Parsing
637 ############################################################
640 def lex(s
, name
=None, trim_whitespace
=True, line_offset
=0, delimeters
=None):
642 Lex a string into chunks:
646 >>> lex('hey {{you}}')
647 ['hey ', ('you', (1, 7))]
649 Traceback (most recent call last):
651 TemplateError: No }} to finish last expression at line 1 column 7
653 Traceback (most recent call last):
655 TemplateError: }} outside expression at line 1 column 7
657 Traceback (most recent call last):
659 TemplateError: {{ inside expression at line 1 column 10
662 if delimeters
is None:
663 delimeters
= ( Template
.default_namespace
['start_braces'],
664 Template
.default_namespace
['end_braces'] )
668 last_pos
= (line_offset
+ 1, 1)
670 token_re
= re
.compile(r
'%s|%s' % (re
.escape(delimeters
[0]),
671 re
.escape(delimeters
[1])))
672 for match
in token_re
.finditer(s
):
673 expr
= match
.group(0)
674 pos
= find_position(s
, match
.end(), last
, last_pos
)
675 if expr
== delimeters
[0] and in_expr
:
676 raise TemplateError('%s inside expression' % delimeters
[0],
679 elif expr
== delimeters
[1] and not in_expr
:
680 raise TemplateError('%s outside expression' % delimeters
[1],
683 if expr
== delimeters
[0]:
684 part
= s
[last
:match
.start()]
689 chunks
.append((s
[last
:match
.start()], last_pos
))
694 raise TemplateError('No %s to finish last expression' % delimeters
[1],
695 name
=name
, position
=last_pos
)
700 chunks
= trim_lex(chunks
)
703 statement_re
= re
.compile(r
'^(?:if |elif |for |def |inherit |default |py:)')
704 single_statements
= ['else', 'endif', 'endfor', 'enddef', 'continue', 'break']
705 trail_whitespace_re
= re
.compile(r
'\n\r?[\t ]*$')
706 lead_whitespace_re
= re
.compile(r
'^[\t ]*\n')
709 def trim_lex(tokens
):
711 Takes a lexed set of tokens, and removes whitespace when there is
712 a directive on a line by itself:
714 >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False)
716 [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny']
718 [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y']
721 for i
, current
in enumerate(tokens
):
722 if isinstance(current
, basestring_
):
726 if not statement_re
.search(item
) and item
not in single_statements
:
732 if i
+ 1 >= len(tokens
):
735 next_chunk
= tokens
[i
+ 1]
736 if (not isinstance(next_chunk
, basestring_
)
737 or not isinstance(prev
, basestring_
)):
739 prev_ok
= not prev
or trail_whitespace_re
.search(prev
)
740 if i
== 1 and not prev
.strip():
742 if last_trim
is not None and last_trim
+ 2 == i
and not prev
.strip():
745 and (not next_chunk
or lead_whitespace_re
.search(next_chunk
)
746 or (i
== len(tokens
) - 2 and not next_chunk
.strip()))):
748 if ((i
== 1 and not prev
.strip())
749 or prev_ok
== 'last'):
752 m
= trail_whitespace_re
.search(prev
)
753 # +1 to leave the leading \n on:
754 prev
= prev
[:m
.start() + 1]
758 if i
== len(tokens
) - 2 and not next_chunk
.strip():
761 m
= lead_whitespace_re
.search(next_chunk
)
762 next_chunk
= next_chunk
[m
.end():]
763 tokens
[i
+ 1] = next_chunk
767 def find_position(string
, index
, last_index
, last_pos
):
768 """Given a string and index, return (line, column)"""
769 lines
= string
.count('\n', last_index
, index
)
771 column
= index
- string
.rfind('\n', last_index
, index
)
773 column
= last_pos
[1] + (index
- last_index
)
774 return (last_pos
[0] + lines
, column
)
777 def parse(s
, name
=None, line_offset
=0, delimeters
=None):
779 Parses a string into a kind of AST
782 [('expr', (1, 3), 'x')]
785 >>> parse('{{if x}}test{{endif}}')
786 [('cond', (1, 3), ('if', (1, 3), 'x', ['test']))]
787 >>> parse('series->{{for x in y}}x={{x}}{{endfor}}')
788 ['series->', ('for', (1, 11), ('x',), 'y', ['x=', ('expr', (1, 27), 'x')])]
789 >>> parse('{{for x, y in z:}}{{continue}}{{endfor}}')
790 [('for', (1, 3), ('x', 'y'), 'z', [('continue', (1, 21))])]
791 >>> parse('{{py:x=1}}')
792 [('py', (1, 3), 'x=1')]
793 >>> parse('{{if x}}a{{elif y}}b{{else}}c{{endif}}')
794 [('cond', (1, 3), ('if', (1, 3), 'x', ['a']), ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))]
798 >>> parse('{{continue}}')
799 Traceback (most recent call last):
801 TemplateError: continue outside of for loop at line 1 column 3
802 >>> parse('{{if x}}foo')
803 Traceback (most recent call last):
805 TemplateError: No {{endif}} at line 1 column 3
806 >>> parse('{{else}}')
807 Traceback (most recent call last):
809 TemplateError: else outside of an if block at line 1 column 3
810 >>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}')
811 Traceback (most recent call last):
813 TemplateError: Unexpected endif at line 1 column 25
814 >>> parse('{{if}}{{endif}}')
815 Traceback (most recent call last):
817 TemplateError: if with no expression at line 1 column 3
818 >>> parse('{{for x y}}{{endfor}}')
819 Traceback (most recent call last):
821 TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
822 >>> parse('{{py:x=1\ny=2}}')
823 Traceback (most recent call last):
825 TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
827 if delimeters
is None:
828 delimeters
= ( Template
.default_namespace
['start_braces'],
829 Template
.default_namespace
['end_braces'] )
830 tokens
= lex(s
, name
=name
, line_offset
=line_offset
, delimeters
=delimeters
)
833 next_chunk
, tokens
= parse_expr(tokens
, name
)
834 result
.append(next_chunk
)
838 def parse_expr(tokens
, name
, context
=()):
839 if isinstance(tokens
[0], basestring_
):
840 return tokens
[0], tokens
[1:]
841 expr
, pos
= tokens
[0]
843 if expr
.startswith('py:'):
844 expr
= expr
[3:].lstrip(' \t')
845 if expr
.startswith('\n') or expr
.startswith('\r'):
846 expr
= expr
.lstrip('\r\n')
848 expr
= expr
.replace('\r\n', '\n')
849 expr
= expr
.replace('\r', '')
854 'Multi-line py blocks must start with a newline',
855 position
=pos
, name
=name
)
856 return ('py', pos
, expr
), tokens
[1:]
857 elif expr
in ('continue', 'break'):
858 if 'for' not in context
:
860 'continue outside of for loop',
861 position
=pos
, name
=name
)
862 return (expr
, pos
), tokens
[1:]
863 elif expr
.startswith('if '):
864 return parse_cond(tokens
, name
, context
)
865 elif (expr
.startswith('elif ')
868 '%s outside of an if block' % expr
.split()[0],
869 position
=pos
, name
=name
)
870 elif expr
in ('if', 'elif', 'for'):
872 '%s with no expression' % expr
,
873 position
=pos
, name
=name
)
874 elif expr
in ('endif', 'endfor', 'enddef'):
876 'Unexpected %s' % expr
,
877 position
=pos
, name
=name
)
878 elif expr
.startswith('for '):
879 return parse_for(tokens
, name
, context
)
880 elif expr
.startswith('default '):
881 return parse_default(tokens
, name
, context
)
882 elif expr
.startswith('inherit '):
883 return parse_inherit(tokens
, name
, context
)
884 elif expr
.startswith('def '):
885 return parse_def(tokens
, name
, context
)
886 elif expr
.startswith('#'):
887 return ('comment', pos
, tokens
[0][0]), tokens
[1:]
888 return ('expr', pos
, tokens
[0][0]), tokens
[1:]
891 def parse_cond(tokens
, name
, context
):
894 context
= context
+ ('if',)
899 position
=start
, name
=name
)
900 if (isinstance(tokens
[0], tuple)
901 and tokens
[0][0] == 'endif'):
902 return ('cond', start
) + tuple(pieces
), tokens
[1:]
903 next_chunk
, tokens
= parse_one_cond(tokens
, name
, context
)
904 pieces
.append(next_chunk
)
907 def parse_one_cond(tokens
, name
, context
):
908 (first
, pos
), tokens
= tokens
[0], tokens
[1:]
910 if first
.endswith(':'):
912 if first
.startswith('if '):
913 part
= ('if', pos
, first
[3:].lstrip(), content
)
914 elif first
.startswith('elif '):
915 part
= ('elif', pos
, first
[5:].lstrip(), content
)
916 elif first
== 'else':
917 part
= ('else', pos
, None, content
)
919 assert 0, "Unexpected token %r at %s" % (first
, pos
)
924 position
=pos
, name
=name
)
925 if (isinstance(tokens
[0], tuple)
926 and (tokens
[0][0] == 'endif'
927 or tokens
[0][0].startswith('elif ')
928 or tokens
[0][0] == 'else')):
930 next_chunk
, tokens
= parse_expr(tokens
, name
, context
)
931 content
.append(next_chunk
)
934 def parse_for(tokens
, name
, context
):
935 first
, pos
= tokens
[0]
937 context
= ('for',) + context
939 assert first
.startswith('for ')
940 if first
.endswith(':'):
942 first
= first
[3:].strip()
943 match
= in_re
.search(first
)
946 'Bad for (no "in") in %r' % first
,
947 position
=pos
, name
=name
)
948 vars = first
[:match
.start()]
951 'You cannot have () in the variable section of a for loop (%r)'
952 % vars, position
=pos
, name
=name
)
954 v
.strip() for v
in first
[:match
.start()].split(',')
956 expr
= first
[match
.end():]
961 position
=pos
, name
=name
)
962 if (isinstance(tokens
[0], tuple)
963 and tokens
[0][0] == 'endfor'):
964 return ('for', pos
, vars, expr
, content
), tokens
[1:]
965 next_chunk
, tokens
= parse_expr(tokens
, name
, context
)
966 content
.append(next_chunk
)
969 def parse_default(tokens
, name
, context
):
970 first
, pos
= tokens
[0]
971 assert first
.startswith('default ')
972 first
= first
.split(None, 1)[1]
973 parts
= first
.split('=', 1)
976 "Expression must be {{default var=value}}; no = found in %r" % first
,
977 position
=pos
, name
=name
)
978 var
= parts
[0].strip()
981 "{{default x, y = ...}} is not supported",
982 position
=pos
, name
=name
)
983 if not var_re
.search(var
):
985 "Not a valid variable name for {{default}}: %r"
986 % var
, position
=pos
, name
=name
)
987 expr
= parts
[1].strip()
988 return ('default', pos
, var
, expr
), tokens
[1:]
991 def parse_inherit(tokens
, name
, context
):
992 first
, pos
= tokens
[0]
993 assert first
.startswith('inherit ')
994 expr
= first
.split(None, 1)[1]
995 return ('inherit', pos
, expr
), tokens
[1:]
998 def parse_def(tokens
, name
, context
):
999 first
, start
= tokens
[0]
1001 assert first
.startswith('def ')
1002 first
= first
.split(None, 1)[1]
1003 if first
.endswith(':'):
1005 if '(' not in first
:
1007 sig
= ((), None, None, {})
1008 elif not first
.endswith(')'):
1009 raise TemplateError("Function definition doesn't end with ): %s" % first
,
1010 position
=start
, name
=name
)
1013 func_name
, sig_text
= first
.split('(', 1)
1014 sig
= parse_signature(sig_text
, name
, start
)
1015 context
= context
+ ('def',)
1019 raise TemplateError(
1020 'Missing {{enddef}}',
1021 position
=start
, name
=name
)
1022 if (isinstance(tokens
[0], tuple)
1023 and tokens
[0][0] == 'enddef'):
1024 return ('def', start
, func_name
, sig
, content
), tokens
[1:]
1025 next_chunk
, tokens
= parse_expr(tokens
, name
, context
)
1026 content
.append(next_chunk
)
1029 def parse_signature(sig_text
, name
, pos
):
1030 tokens
= tokenize
.generate_tokens(StringIO(sig_text
).readline
)
1036 def get_token(pos
=False):
1038 tok_type
, tok_string
, (srow
, scol
), (erow
, ecol
), line
= next(tokens
)
1039 except StopIteration:
1040 return tokenize
.ENDMARKER
, ''
1042 return tok_type
, tok_string
, (srow
, scol
), (erow
, ecol
)
1044 return tok_type
, tok_string
1047 tok_type
, tok_string
= get_token()
1048 if tok_type
== tokenize
.ENDMARKER
:
1050 if tok_type
== tokenize
.OP
and (tok_string
== '*' or tok_string
== '**'):
1051 var_arg_type
= tok_string
1052 tok_type
, tok_string
= get_token()
1053 if tok_type
!= tokenize
.NAME
:
1054 raise TemplateError('Invalid signature: (%s)' % sig_text
,
1055 position
=pos
, name
=name
)
1056 var_name
= tok_string
1057 tok_type
, tok_string
= get_token()
1058 if tok_type
== tokenize
.ENDMARKER
or (tok_type
== tokenize
.OP
and tok_string
== ','):
1059 if var_arg_type
== '*':
1061 elif var_arg_type
== '**':
1064 sig_args
.append(var_name
)
1065 if tok_type
== tokenize
.ENDMARKER
:
1068 if var_arg_type
is not None:
1069 raise TemplateError('Invalid signature: (%s)' % sig_text
,
1070 position
=pos
, name
=name
)
1071 if tok_type
== tokenize
.OP
and tok_string
== '=':
1075 start_pos
= end_pos
= None
1078 tok_type
, tok_string
, s
, e
= get_token(True)
1079 if start_pos
is None:
1082 if tok_type
== tokenize
.ENDMARKER
and nest_count
:
1083 raise TemplateError('Invalid signature: (%s)' % sig_text
,
1084 position
=pos
, name
=name
)
1085 if (not nest_count
and
1086 (tok_type
== tokenize
.ENDMARKER
or (tok_type
== tokenize
.OP
and tok_string
== ','))):
1087 default_expr
= isolate_expression(sig_text
, start_pos
, end_pos
)
1088 defaults
[var_name
] = default_expr
1089 sig_args
.append(var_name
)
1091 parts
.append((tok_type
, tok_string
))
1092 if nest_count
and tok_type
== tokenize
.OP
and tok_string
== nest_type
:
1094 elif nest_count
and tok_type
== tokenize
.OP
and tok_string
== unnest_type
:
1097 nest_type
= unnest_type
= None
1098 elif not nest_count
and tok_type
== tokenize
.OP
and tok_string
in ('(', '[', '{'):
1099 nest_type
= tok_string
1101 unnest_type
= {'(': ')', '[': ']', '{': '}'}[nest_type
]
1102 return sig_args
, var_arg
, var_kw
, defaults
1105 def isolate_expression(string
, start_pos
, end_pos
):
1106 srow
, scol
= start_pos
1108 erow
, ecol
= end_pos
1110 lines
= string
.splitlines(True)
1112 return lines
[srow
][scol
:ecol
]
1113 parts
= [lines
[srow
][scol
:]]
1114 parts
.extend(lines
[srow
+1:erow
])
1115 if erow
< len(lines
):
1116 # It'll sometimes give (end_row_past_finish, 0)
1117 parts
.append(lines
[erow
][:ecol
])
1118 return ''.join(parts
)
1120 _fill_command_usage
= """\
1121 %prog [OPTIONS] TEMPLATE arg=value
1123 Use py:arg=value to set a Python value; otherwise all values are
1128 def fill_command(args
=None):
1131 import pkg_resources
1135 dist
= pkg_resources
.get_distribution('Paste')
1136 parser
= optparse
.OptionParser(
1137 version
=coerce_text(dist
),
1138 usage
=_fill_command_usage
)
1143 help="File to write output to (default stdout)")
1147 action
='store_true',
1148 help="Use HTML style filling (including automatic HTML quoting)")
1152 action
='store_true',
1153 help="Put the environment in as top-level variables")
1154 options
, args
= parser
.parse_args(args
)
1156 print('You must give a template filename')
1158 template_name
= args
[0]
1162 vars.update(os
.environ
)
1164 if '=' not in value
:
1165 print('Bad argument: %r' % value
)
1167 name
, value
= value
.split('=', 1)
1168 if name
.startswith('py:'):
1172 if template_name
== '-':
1173 template_content
= sys
.stdin
.read()
1174 template_name
= '<stdin>'
1176 f
= open(template_name
, 'rb')
1177 template_content
= f
.read()
1179 if options
.use_html
:
1180 TemplateClass
= HTMLTemplate
1182 TemplateClass
= Template
1183 template
= TemplateClass(template_content
, name
=template_name
)
1184 result
= template
.substitute(vars)
1186 f
= open(options
.output
, 'wb')
1190 sys
.stdout
.write(result
)
1192 if __name__
== '__main__':