a4e9bd
Index: Paste-1.7.4/paste/util/looper/__init__.py
a4e9bd
===================================================================
a4e9bd
--- /dev/null
a4e9bd
+++ Paste-1.7.4/paste/util/looper/__init__.py
a4e9bd
@@ -0,0 +1,4 @@
a4e9bd
+try:
a4e9bd
+    from tempita._looper import *
a4e9bd
+except ImportError:
a4e9bd
+    from _looper import *
a4e9bd
Index: Paste-1.7.4/paste/util/looper/_looper.py
a4e9bd
===================================================================
a4e9bd
--- /dev/null
a4e9bd
+++ Paste-1.7.4/paste/util/looper/_looper.py
a4e9bd
@@ -0,0 +1,152 @@
a4e9bd
+"""
a4e9bd
+Helper for looping over sequences, particular in templates.
a4e9bd
+
a4e9bd
+Often in a loop in a template it's handy to know what's next up,
a4e9bd
+previously up, if this is the first or last item in the sequence, etc.
a4e9bd
+These can be awkward to manage in a normal Python loop, but using the
a4e9bd
+looper you can get a better sense of the context.  Use like::
a4e9bd
+
a4e9bd
+    >>> for loop, item in looper(['a', 'b', 'c']):
a4e9bd
+    ...     print loop.number, item
a4e9bd
+    ...     if not loop.last:
a4e9bd
+    ...         print '---'
a4e9bd
+    1 a
a4e9bd
+    ---
a4e9bd
+    2 b
a4e9bd
+    ---
a4e9bd
+    3 c
a4e9bd
+
a4e9bd
+"""
a4e9bd
+
a4e9bd
+__all__ = ['looper']
a4e9bd
+
a4e9bd
+class looper(object):
a4e9bd
+    """
a4e9bd
+    Helper for looping (particularly in templates)
a4e9bd
+    
a4e9bd
+    Use this like::
a4e9bd
+    
a4e9bd
+        for loop, item in looper(seq):
a4e9bd
+            if loop.first:
a4e9bd
+                ...
a4e9bd
+    """
a4e9bd
+
a4e9bd
+    def __init__(self, seq):
a4e9bd
+        self.seq = seq
a4e9bd
+
a4e9bd
+    def __iter__(self):
a4e9bd
+        return looper_iter(self.seq)
a4e9bd
+
a4e9bd
+    def __repr__(self):
a4e9bd
+        return '<%s for %r>' % (
a4e9bd
+            self.__class__.__name__, self.seq)
a4e9bd
+
a4e9bd
+class looper_iter(object):
a4e9bd
+
a4e9bd
+    def __init__(self, seq):
a4e9bd
+        self.seq = list(seq)
a4e9bd
+        self.pos = 0
a4e9bd
+
a4e9bd
+    def __iter__(self):
a4e9bd
+        return self
a4e9bd
+
a4e9bd
+    def next(self):
a4e9bd
+        if self.pos >= len(self.seq):
a4e9bd
+            raise StopIteration
a4e9bd
+        result = loop_pos(self.seq, self.pos), self.seq[self.pos]
a4e9bd
+        self.pos += 1
a4e9bd
+        return result
a4e9bd
+
a4e9bd
+class loop_pos(object):
a4e9bd
+
a4e9bd
+    def __init__(self, seq, pos):
a4e9bd
+        self.seq = seq
a4e9bd
+        self.pos = pos
a4e9bd
+
a4e9bd
+    def __repr__(self):
a4e9bd
+        return '<loop pos=%r at %r>' % (
a4e9bd
+            self.seq[pos], pos)
a4e9bd
+
a4e9bd
+    def index(self):
a4e9bd
+        return self.pos
a4e9bd
+    index = property(index)
a4e9bd
+
a4e9bd
+    def number(self):
a4e9bd
+        return self.pos + 1
a4e9bd
+    number = property(number)
a4e9bd
+
a4e9bd
+    def item(self):
a4e9bd
+        return self.seq[self.pos]
a4e9bd
+    item = property(item)
a4e9bd
+
a4e9bd
+    def next(self):
a4e9bd
+        try:
a4e9bd
+            return self.seq[self.pos+1]
a4e9bd
+        except IndexError:
a4e9bd
+            return None
a4e9bd
+    next = property(next)
a4e9bd
+
a4e9bd
+    def previous(self):
a4e9bd
+        if self.pos == 0:
a4e9bd
+            return None
a4e9bd
+        return self.seq[self.pos-1]
a4e9bd
+    previous = property(previous)
a4e9bd
+
a4e9bd
+    def odd(self):
a4e9bd
+        return not self.pos % 2
a4e9bd
+    odd = property(odd)
a4e9bd
+
a4e9bd
+    def even(self):
a4e9bd
+        return self.pos % 2
a4e9bd
+    even = property(even)
a4e9bd
+
a4e9bd
+    def first(self):
a4e9bd
+        return self.pos == 0
a4e9bd
+    first = property(first)
a4e9bd
+
a4e9bd
+    def last(self):
a4e9bd
+        return self.pos == len(self.seq)-1
a4e9bd
+    last = property(last)
a4e9bd
+
a4e9bd
+    def length(self):
a4e9bd
+        return len(self.seq)
a4e9bd
+    length = property(length)
a4e9bd
+
a4e9bd
+    def first_group(self, getter=None):
a4e9bd
+        """
a4e9bd
+        Returns true if this item is the start of a new group,
a4e9bd
+        where groups mean that some attribute has changed.  The getter
a4e9bd
+        can be None (the item itself changes), an attribute name like
a4e9bd
+        ``'.attr'``, a function, or a dict key or list index.
a4e9bd
+        """
a4e9bd
+        if self.first:
a4e9bd
+            return True
a4e9bd
+        return self._compare_group(self.item, self.previous, getter)
a4e9bd
+
a4e9bd
+    def last_group(self, getter=None):
a4e9bd
+        """
a4e9bd
+        Returns true if this item is the end of a new group,
a4e9bd
+        where groups mean that some attribute has changed.  The getter
a4e9bd
+        can be None (the item itself changes), an attribute name like
a4e9bd
+        ``'.attr'``, a function, or a dict key or list index.
a4e9bd
+        """
a4e9bd
+        if self.last:
a4e9bd
+            return True
a4e9bd
+        return self._compare_group(self.item, self.next, getter)
a4e9bd
+
a4e9bd
+    def _compare_group(self, item, other, getter):
a4e9bd
+        if getter is None:
a4e9bd
+            return item != other
a4e9bd
+        elif (isinstance(getter, basestring)
a4e9bd
+              and getter.startswith('.')):
a4e9bd
+            getter = getter[1:]
a4e9bd
+            if getter.endswith('()'):
a4e9bd
+                getter = getter[:-2]
a4e9bd
+                return getattr(item, getter)() != getattr(other, getter)()
a4e9bd
+            else:
a4e9bd
+                return getattr(item, getter) != getattr(other, getter)
a4e9bd
+        elif callable(getter):
a4e9bd
+            return getter(item) != getter(other)
a4e9bd
+        else:
a4e9bd
+            return item[getter] != other[getter]
a4e9bd
+    
a4e9bd
Index: Paste-1.7.4/paste/util/looper.py
a4e9bd
===================================================================
a4e9bd
--- Paste-1.7.4.orig/paste/util/looper.py
a4e9bd
+++ /dev/null
a4e9bd
@@ -1,152 +0,0 @@
a4e9bd
-"""
a4e9bd
-Helper for looping over sequences, particular in templates.
a4e9bd
-
a4e9bd
-Often in a loop in a template it's handy to know what's next up,
a4e9bd
-previously up, if this is the first or last item in the sequence, etc.
a4e9bd
-These can be awkward to manage in a normal Python loop, but using the
a4e9bd
-looper you can get a better sense of the context.  Use like::
a4e9bd
-
a4e9bd
-    >>> for loop, item in looper(['a', 'b', 'c']):
a4e9bd
-    ...     print loop.number, item
a4e9bd
-    ...     if not loop.last:
a4e9bd
-    ...         print '---'
a4e9bd
-    1 a
a4e9bd
-    ---
a4e9bd
-    2 b
a4e9bd
-    ---
a4e9bd
-    3 c
a4e9bd
-
a4e9bd
-"""
a4e9bd
-
a4e9bd
-__all__ = ['looper']
a4e9bd
-
a4e9bd
-class looper(object):
a4e9bd
-    """
a4e9bd
-    Helper for looping (particularly in templates)
a4e9bd
-    
a4e9bd
-    Use this like::
a4e9bd
-    
a4e9bd
-        for loop, item in looper(seq):
a4e9bd
-            if loop.first:
a4e9bd
-                ...
a4e9bd
-    """
a4e9bd
-
a4e9bd
-    def __init__(self, seq):
a4e9bd
-        self.seq = seq
a4e9bd
-
a4e9bd
-    def __iter__(self):
a4e9bd
-        return looper_iter(self.seq)
a4e9bd
-
a4e9bd
-    def __repr__(self):
a4e9bd
-        return '<%s for %r>' % (
a4e9bd
-            self.__class__.__name__, self.seq)
a4e9bd
-
a4e9bd
-class looper_iter(object):
a4e9bd
-
a4e9bd
-    def __init__(self, seq):
a4e9bd
-        self.seq = list(seq)
a4e9bd
-        self.pos = 0
a4e9bd
-
a4e9bd
-    def __iter__(self):
a4e9bd
-        return self
a4e9bd
-
a4e9bd
-    def next(self):
a4e9bd
-        if self.pos >= len(self.seq):
a4e9bd
-            raise StopIteration
a4e9bd
-        result = loop_pos(self.seq, self.pos), self.seq[self.pos]
a4e9bd
-        self.pos += 1
a4e9bd
-        return result
a4e9bd
-
a4e9bd
-class loop_pos(object):
a4e9bd
-
a4e9bd
-    def __init__(self, seq, pos):
a4e9bd
-        self.seq = seq
a4e9bd
-        self.pos = pos
a4e9bd
-
a4e9bd
-    def __repr__(self):
a4e9bd
-        return '<loop pos=%r at %r>' % (
a4e9bd
-            self.seq[pos], pos)
a4e9bd
-
a4e9bd
-    def index(self):
a4e9bd
-        return self.pos
a4e9bd
-    index = property(index)
a4e9bd
-
a4e9bd
-    def number(self):
a4e9bd
-        return self.pos + 1
a4e9bd
-    number = property(number)
a4e9bd
-
a4e9bd
-    def item(self):
a4e9bd
-        return self.seq[self.pos]
a4e9bd
-    item = property(item)
a4e9bd
-
a4e9bd
-    def next(self):
a4e9bd
-        try:
a4e9bd
-            return self.seq[self.pos+1]
a4e9bd
-        except IndexError:
a4e9bd
-            return None
a4e9bd
-    next = property(next)
a4e9bd
-
a4e9bd
-    def previous(self):
a4e9bd
-        if self.pos == 0:
a4e9bd
-            return None
a4e9bd
-        return self.seq[self.pos-1]
a4e9bd
-    previous = property(previous)
a4e9bd
-
a4e9bd
-    def odd(self):
a4e9bd
-        return not self.pos % 2
a4e9bd
-    odd = property(odd)
a4e9bd
-
a4e9bd
-    def even(self):
a4e9bd
-        return self.pos % 2
a4e9bd
-    even = property(even)
a4e9bd
-
a4e9bd
-    def first(self):
a4e9bd
-        return self.pos == 0
a4e9bd
-    first = property(first)
a4e9bd
-
a4e9bd
-    def last(self):
a4e9bd
-        return self.pos == len(self.seq)-1
a4e9bd
-    last = property(last)
a4e9bd
-
a4e9bd
-    def length(self):
a4e9bd
-        return len(self.seq)
a4e9bd
-    length = property(length)
a4e9bd
-
a4e9bd
-    def first_group(self, getter=None):
a4e9bd
-        """
a4e9bd
-        Returns true if this item is the start of a new group,
a4e9bd
-        where groups mean that some attribute has changed.  The getter
a4e9bd
-        can be None (the item itself changes), an attribute name like
a4e9bd
-        ``'.attr'``, a function, or a dict key or list index.
a4e9bd
-        """
a4e9bd
-        if self.first:
a4e9bd
-            return True
a4e9bd
-        return self._compare_group(self.item, self.previous, getter)
a4e9bd
-
a4e9bd
-    def last_group(self, getter=None):
a4e9bd
-        """
a4e9bd
-        Returns true if this item is the end of a new group,
a4e9bd
-        where groups mean that some attribute has changed.  The getter
a4e9bd
-        can be None (the item itself changes), an attribute name like
a4e9bd
-        ``'.attr'``, a function, or a dict key or list index.
a4e9bd
-        """
a4e9bd
-        if self.last:
a4e9bd
-            return True
a4e9bd
-        return self._compare_group(self.item, self.next, getter)
a4e9bd
-
a4e9bd
-    def _compare_group(self, item, other, getter):
a4e9bd
-        if getter is None:
a4e9bd
-            return item != other
a4e9bd
-        elif (isinstance(getter, basestring)
a4e9bd
-              and getter.startswith('.')):
a4e9bd
-            getter = getter[1:]
a4e9bd
-            if getter.endswith('()'):
a4e9bd
-                getter = getter[:-2]
a4e9bd
-                return getattr(item, getter)() != getattr(other, getter)()
a4e9bd
-            else:
a4e9bd
-                return getattr(item, getter) != getattr(other, getter)
a4e9bd
-        elif callable(getter):
a4e9bd
-            return getter(item) != getter(other)
a4e9bd
-        else:
a4e9bd
-            return item[getter] != other[getter]
a4e9bd
-    
a4e9bd
Index: Paste-1.7.4/paste/util/template/__init__.py
a4e9bd
===================================================================
a4e9bd
--- /dev/null
a4e9bd
+++ Paste-1.7.4/paste/util/template/__init__.py
a4e9bd
@@ -0,0 +1,6 @@
a4e9bd
+try:
a4e9bd
+    from tempita import *
a4e9bd
+    from tempita import paste_script_template_renderer
a4e9bd
+except ImportError:
a4e9bd
+    from _template import *
a4e9bd
+    from _template import paste_script_template_renderer
a4e9bd
Index: Paste-1.7.4/paste/util/template/_template.py
a4e9bd
===================================================================
a4e9bd
--- /dev/null
a4e9bd
+++ Paste-1.7.4/paste/util/template/_template.py
a4e9bd
@@ -0,0 +1,758 @@
a4e9bd
+"""
a4e9bd
+A small templating language
a4e9bd
+
a4e9bd
+This implements a small templating language for use internally in
a4e9bd
+Paste and Paste Script.  This language implements if/elif/else,
a4e9bd
+for/continue/break, expressions, and blocks of Python code.  The
a4e9bd
+syntax is::
a4e9bd
+
a4e9bd
+  {{any expression (function calls etc)}}
a4e9bd
+  {{any expression | filter}}
a4e9bd
+  {{for x in y}}...{{endfor}}
a4e9bd
+  {{if x}}x{{elif y}}y{{else}}z{{endif}}
a4e9bd
+  {{py:x=1}}
a4e9bd
+  {{py:
a4e9bd
+  def foo(bar):
a4e9bd
+      return 'baz'
a4e9bd
+  }}
a4e9bd
+  {{default var = default_value}}
a4e9bd
+  {{# comment}}
a4e9bd
+
a4e9bd
+You use this with the ``Template`` class or the ``sub`` shortcut.
a4e9bd
+The ``Template`` class takes the template string and the name of
a4e9bd
+the template (for errors) and a default namespace.  Then (like
a4e9bd
+``string.Template``) you can call the ``tmpl.substitute(**kw)``
a4e9bd
+method to make a substitution (or ``tmpl.substitute(a_dict)``).
a4e9bd
+
a4e9bd
+``sub(content, **kw)`` substitutes the template immediately.  You
a4e9bd
+can use ``__name='tmpl.html'`` to set the name of the template.
a4e9bd
+
a4e9bd
+If there are syntax errors ``TemplateError`` will be raised.
a4e9bd
+"""
a4e9bd
+
a4e9bd
+import re
a4e9bd
+import sys
a4e9bd
+import cgi
a4e9bd
+import urllib
a4e9bd
+from paste.util.looper import looper
a4e9bd
+
a4e9bd
+__all__ = ['TemplateError', 'Template', 'sub', 'HTMLTemplate',
a4e9bd
+           'sub_html', 'html', 'bunch']
a4e9bd
+
a4e9bd
+token_re = re.compile(r'\{\{|\}\}')
a4e9bd
+in_re = re.compile(r'\s+in\s+')
a4e9bd
+var_re = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
a4e9bd
+
a4e9bd
+class TemplateError(Exception):
a4e9bd
+    """Exception raised while parsing a template
a4e9bd
+    """
a4e9bd
+
a4e9bd
+    def __init__(self, message, position, name=None):
a4e9bd
+        self.message = message
a4e9bd
+        self.position = position
a4e9bd
+        self.name = name
a4e9bd
+
a4e9bd
+    def __str__(self):
a4e9bd
+        msg = '%s at line %s column %s' % (
a4e9bd
+            self.message, self.position[0], self.position[1])
a4e9bd
+        if self.name:
a4e9bd
+            msg += ' in %s' % self.name
a4e9bd
+        return msg
a4e9bd
+
a4e9bd
+class _TemplateContinue(Exception):
a4e9bd
+    pass
a4e9bd
+
a4e9bd
+class _TemplateBreak(Exception):
a4e9bd
+    pass
a4e9bd
+
a4e9bd
+class Template(object):
a4e9bd
+
a4e9bd
+    default_namespace = {
a4e9bd
+        'start_braces': '{{',
a4e9bd
+        'end_braces': '}}',
a4e9bd
+        'looper': looper,
a4e9bd
+        }
a4e9bd
+
a4e9bd
+    default_encoding = 'utf8'
a4e9bd
+
a4e9bd
+    def __init__(self, content, name=None, namespace=None):
a4e9bd
+        self.content = content
a4e9bd
+        self._unicode = isinstance(content, unicode)
a4e9bd
+        self.name = name
a4e9bd
+        self._parsed = parse(content, name=name)
a4e9bd
+        if namespace is None:
a4e9bd
+            namespace = {}
a4e9bd
+        self.namespace = namespace
a4e9bd
+
a4e9bd
+    def from_filename(cls, filename, namespace=None, encoding=None):
a4e9bd
+        f = open(filename, 'rb')
a4e9bd
+        c = f.read()
a4e9bd
+        f.close()
a4e9bd
+        if encoding:
a4e9bd
+            c = c.decode(encoding)
a4e9bd
+        return cls(content=c, name=filename, namespace=namespace)
a4e9bd
+
a4e9bd
+    from_filename = classmethod(from_filename)
a4e9bd
+
a4e9bd
+    def __repr__(self):
a4e9bd
+        return '<%s %s name=%r>' % (
a4e9bd
+            self.__class__.__name__,
a4e9bd
+            hex(id(self))[2:], self.name)
a4e9bd
+
a4e9bd
+    def substitute(self, *args, **kw):
a4e9bd
+        if args:
a4e9bd
+            if kw:
a4e9bd
+                raise TypeError(
a4e9bd
+                    "You can only give positional *or* keyword arguments")
a4e9bd
+            if len(args) > 1:
a4e9bd
+                raise TypeError(
a4e9bd
+                    "You can only give on positional argument")
a4e9bd
+            kw = args[0]
a4e9bd
+        ns = self.default_namespace.copy()
a4e9bd
+        ns.update(self.namespace)
a4e9bd
+        ns.update(kw)
a4e9bd
+        result = self._interpret(ns)
a4e9bd
+        return result
a4e9bd
+
a4e9bd
+    def _interpret(self, ns):
a4e9bd
+        __traceback_hide__ = True
a4e9bd
+        parts = []
a4e9bd
+        self._interpret_codes(self._parsed, ns, out=parts)
a4e9bd
+        return ''.join(parts)
a4e9bd
+
a4e9bd
+    def _interpret_codes(self, codes, ns, out):
a4e9bd
+        __traceback_hide__ = True
a4e9bd
+        for item in codes:
a4e9bd
+            if isinstance(item, basestring):
a4e9bd
+                out.append(item)
a4e9bd
+            else:
a4e9bd
+                self._interpret_code(item, ns, out)
a4e9bd
+
a4e9bd
+    def _interpret_code(self, code, ns, out):
a4e9bd
+        __traceback_hide__ = True
a4e9bd
+        name, pos = code[0], code[1]
a4e9bd
+        if name == 'py':
a4e9bd
+            self._exec(code[2], ns, pos)
a4e9bd
+        elif name == 'continue':
a4e9bd
+            raise _TemplateContinue()
a4e9bd
+        elif name == 'break':
a4e9bd
+            raise _TemplateBreak()
a4e9bd
+        elif name == 'for':
a4e9bd
+            vars, expr, content = code[2], code[3], code[4]
a4e9bd
+            expr = self._eval(expr, ns, pos)
a4e9bd
+            self._interpret_for(vars, expr, content, ns, out)
a4e9bd
+        elif name == 'cond':
a4e9bd
+            parts = code[2:]
a4e9bd
+            self._interpret_if(parts, ns, out)
a4e9bd
+        elif name == 'expr':
a4e9bd
+            parts = code[2].split('|')
a4e9bd
+            base = self._eval(parts[0], ns, pos)
a4e9bd
+            for part in parts[1:]:
a4e9bd
+                func = self._eval(part, ns, pos)
a4e9bd
+                base = func(base)
a4e9bd
+            out.append(self._repr(base, pos))
a4e9bd
+        elif name == 'default':
a4e9bd
+            var, expr = code[2], code[3]
a4e9bd
+            if var not in ns:
a4e9bd
+                result = self._eval(expr, ns, pos)
a4e9bd
+                ns[var] = result
a4e9bd
+        elif name == 'comment':
a4e9bd
+            return
a4e9bd
+        else:
a4e9bd
+            assert 0, "Unknown code: %r" % name
a4e9bd
+
a4e9bd
+    def _interpret_for(self, vars, expr, content, ns, out):
a4e9bd
+        __traceback_hide__ = True
a4e9bd
+        for item in expr:
a4e9bd
+            if len(vars) == 1:
a4e9bd
+                ns[vars[0]] = item
a4e9bd
+            else:
a4e9bd
+                if len(vars) != len(item):
a4e9bd
+                    raise ValueError(
a4e9bd
+                        'Need %i items to unpack (got %i items)'
a4e9bd
+                        % (len(vars), len(item)))
a4e9bd
+                for name, value in zip(vars, item):
a4e9bd
+                    ns[name] = value
a4e9bd
+            try:
a4e9bd
+                self._interpret_codes(content, ns, out)
a4e9bd
+            except _TemplateContinue:
a4e9bd
+                continue
a4e9bd
+            except _TemplateBreak:
a4e9bd
+                break
a4e9bd
+
a4e9bd
+    def _interpret_if(self, parts, ns, out):
a4e9bd
+        __traceback_hide__ = True
a4e9bd
+        # @@: if/else/else gets through
a4e9bd
+        for part in parts:
a4e9bd
+            assert not isinstance(part, basestring)
a4e9bd
+            name, pos = part[0], part[1]
a4e9bd
+            if name == 'else':
a4e9bd
+                result = True
a4e9bd
+            else:
a4e9bd
+                result = self._eval(part[2], ns, pos)
a4e9bd
+            if result:
a4e9bd
+                self._interpret_codes(part[3], ns, out)
a4e9bd
+                break
a4e9bd
+
a4e9bd
+    def _eval(self, code, ns, pos):
a4e9bd
+        __traceback_hide__ = True
a4e9bd
+        try:
a4e9bd
+            value = eval(code, ns)
a4e9bd
+            return value
a4e9bd
+        except:
a4e9bd
+            exc_info = sys.exc_info()
a4e9bd
+            e = exc_info[1]
a4e9bd
+            if getattr(e, 'args'):
a4e9bd
+                arg0 = e.args[0]
a4e9bd
+            else:
a4e9bd
+                arg0 = str(e)
a4e9bd
+            e.args = (self._add_line_info(arg0, pos),)
a4e9bd
+            raise exc_info[0], e, exc_info[2]
a4e9bd
+
a4e9bd
+    def _exec(self, code, ns, pos):
a4e9bd
+        __traceback_hide__ = True
a4e9bd
+        try:
a4e9bd
+            exec code in ns
a4e9bd
+        except:
a4e9bd
+            exc_info = sys.exc_info()
a4e9bd
+            e = exc_info[1]
a4e9bd
+            e.args = (self._add_line_info(e.args[0], pos),)
a4e9bd
+            raise exc_info[0], e, exc_info[2]
a4e9bd
+
a4e9bd
+    def _repr(self, value, pos):
a4e9bd
+        __traceback_hide__ = True
a4e9bd
+        try:
a4e9bd
+            if value is None:
a4e9bd
+                return ''
a4e9bd
+            if self._unicode:
a4e9bd
+                try:
a4e9bd
+                    value = unicode(value)
a4e9bd
+                except UnicodeDecodeError:
a4e9bd
+                    value = str(value)
a4e9bd
+            else:
a4e9bd
+                value = str(value)
a4e9bd
+        except:
a4e9bd
+            exc_info = sys.exc_info()
a4e9bd
+            e = exc_info[1]
a4e9bd
+            e.args = (self._add_line_info(e.args[0], pos),)
a4e9bd
+            raise exc_info[0], e, exc_info[2]
a4e9bd
+        else:
a4e9bd
+            if self._unicode and isinstance(value, str):
a4e9bd
+                if not self.decode_encoding:
a4e9bd
+                    raise UnicodeDecodeError(
a4e9bd
+                        'Cannot decode str value %r into unicode '
a4e9bd
+                        '(no default_encoding provided)' % value)
a4e9bd
+                value = value.decode(self.default_encoding)
a4e9bd
+            elif not self._unicode and isinstance(value, unicode):
a4e9bd
+                if not self.decode_encoding:
a4e9bd
+                    raise UnicodeEncodeError(
a4e9bd
+                        'Cannot encode unicode value %r into str '
a4e9bd
+                        '(no default_encoding provided)' % value)
a4e9bd
+                value = value.encode(self.default_encoding)
a4e9bd
+            return value
a4e9bd
+        
a4e9bd
+
a4e9bd
+    def _add_line_info(self, msg, pos):
a4e9bd
+        msg = "%s at line %s column %s" % (
a4e9bd
+            msg, pos[0], pos[1])
a4e9bd
+        if self.name:
a4e9bd
+            msg += " in file %s" % self.name
a4e9bd
+        return msg
a4e9bd
+
a4e9bd
+def sub(content, **kw):
a4e9bd
+    name = kw.get('__name')
a4e9bd
+    tmpl = Template(content, name=name)
a4e9bd
+    return tmpl.substitute(kw)
a4e9bd
+    return result
a4e9bd
+
a4e9bd
+def paste_script_template_renderer(content, vars, filename=None):
a4e9bd
+    tmpl = Template(content, name=filename)
a4e9bd
+    return tmpl.substitute(vars)
a4e9bd
+
a4e9bd
+class bunch(dict):
a4e9bd
+
a4e9bd
+    def __init__(self, **kw):
a4e9bd
+        for name, value in kw.items():
a4e9bd
+            setattr(self, name, value)
a4e9bd
+
a4e9bd
+    def __setattr__(self, name, value):
a4e9bd
+        self[name] = value
a4e9bd
+
a4e9bd
+    def __getattr__(self, name):
a4e9bd
+        try:
a4e9bd
+            return self[name]
a4e9bd
+        except KeyError:
a4e9bd
+            raise AttributeError(name)
a4e9bd
+
a4e9bd
+    def __getitem__(self, key):
a4e9bd
+        if 'default' in self:
a4e9bd
+            try:
a4e9bd
+                return dict.__getitem__(self, key)
a4e9bd
+            except KeyError:
a4e9bd
+                return dict.__getitem__(self, 'default')
a4e9bd
+        else:
a4e9bd
+            return dict.__getitem__(self, key)
a4e9bd
+
a4e9bd
+    def __repr__(self):
a4e9bd
+        items = [
a4e9bd
+            (k, v) for k, v in self.items()]
a4e9bd
+        items.sort()
a4e9bd
+        return '<%s %s>' % (
a4e9bd
+            self.__class__.__name__,
a4e9bd
+            ' '.join(['%s=%r' % (k, v) for k, v in items]))
a4e9bd
+
a4e9bd
+############################################################
a4e9bd
+## HTML Templating
a4e9bd
+############################################################
a4e9bd
+
a4e9bd
+class html(object):
a4e9bd
+    def __init__(self, value):
a4e9bd
+        self.value = value
a4e9bd
+    def __str__(self):
a4e9bd
+        return self.value
a4e9bd
+    def __repr__(self):
a4e9bd
+        return '<%s %r>' % (
a4e9bd
+            self.__class__.__name__, self.value)
a4e9bd
+
a4e9bd
+def html_quote(value):
a4e9bd
+    if value is None:
a4e9bd
+        return ''
a4e9bd
+    if not isinstance(value, basestring):
a4e9bd
+        if hasattr(value, '__unicode__'):
a4e9bd
+            value = unicode(value)
a4e9bd
+        else:
a4e9bd
+            value = str(value)
a4e9bd
+    value = cgi.escape(value, 1)
a4e9bd
+    if isinstance(value, unicode):
a4e9bd
+        value = value.encode('ascii', 'xmlcharrefreplace')
a4e9bd
+    return value
a4e9bd
+
a4e9bd
+def url(v):
a4e9bd
+    if not isinstance(v, basestring):
a4e9bd
+        if hasattr(v, '__unicode__'):
a4e9bd
+            v = unicode(v)
a4e9bd
+        else:
a4e9bd
+            v = str(v)
a4e9bd
+    if isinstance(v, unicode):
a4e9bd
+        v = v.encode('utf8')
a4e9bd
+    return urllib.quote(v)
a4e9bd
+
a4e9bd
+def attr(**kw):
a4e9bd
+    kw = kw.items()
a4e9bd
+    kw.sort()
a4e9bd
+    parts = []
a4e9bd
+    for name, value in kw:
a4e9bd
+        if value is None:
a4e9bd
+            continue
a4e9bd
+        if name.endswith('_'):
a4e9bd
+            name = name[:-1]
a4e9bd
+        parts.append('%s="%s"' % (html_quote(name), html_quote(value)))
a4e9bd
+    return html(' '.join(parts))
a4e9bd
+
a4e9bd
+class HTMLTemplate(Template):
a4e9bd
+
a4e9bd
+    default_namespace = Template.default_namespace.copy()
a4e9bd
+    default_namespace.update(dict(
a4e9bd
+        html=html,
a4e9bd
+        attr=attr,
a4e9bd
+        url=url,
a4e9bd
+        ))
a4e9bd
+
a4e9bd
+    def _repr(self, value, pos):
a4e9bd
+        plain = Template._repr(self, value, pos)
a4e9bd
+        if isinstance(value, html):
a4e9bd
+            return plain
a4e9bd
+        else:
a4e9bd
+            return html_quote(plain)
a4e9bd
+
a4e9bd
+def sub_html(content, **kw):
a4e9bd
+    name = kw.get('__name')
a4e9bd
+    tmpl = HTMLTemplate(content, name=name)
a4e9bd
+    return tmpl.substitute(kw)
a4e9bd
+    return result
a4e9bd
+
a4e9bd
+
a4e9bd
+############################################################
a4e9bd
+## Lexing and Parsing
a4e9bd
+############################################################
a4e9bd
+
a4e9bd
+def lex(s, name=None, trim_whitespace=True):
a4e9bd
+    """
a4e9bd
+    Lex a string into chunks:
a4e9bd
+
a4e9bd
+        >>> lex('hey')
a4e9bd
+        ['hey']
a4e9bd
+        >>> lex('hey {{you}}')
a4e9bd
+        ['hey ', ('you', (1, 7))]
a4e9bd
+        >>> lex('hey {{')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: No }} to finish last expression at line 1 column 7
a4e9bd
+        >>> lex('hey }}')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: }} outside expression at line 1 column 7
a4e9bd
+        >>> lex('hey {{ {{')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: {{ inside expression at line 1 column 10
a4e9bd
+
a4e9bd
+    """
a4e9bd
+    in_expr = False
a4e9bd
+    chunks = []
a4e9bd
+    last = 0
a4e9bd
+    last_pos = (1, 1)
a4e9bd
+    for match in token_re.finditer(s):
a4e9bd
+        expr = match.group(0)
a4e9bd
+        pos = find_position(s, match.end())
a4e9bd
+        if expr == '{{' and in_expr:
a4e9bd
+            raise TemplateError('{{ inside expression', position=pos,
a4e9bd
+                                name=name)
a4e9bd
+        elif expr == '}}' and not in_expr:
a4e9bd
+            raise TemplateError('}} outside expression', position=pos,
a4e9bd
+                                name=name)
a4e9bd
+        if expr == '{{':
a4e9bd
+            part = s[last:match.start()]
a4e9bd
+            if part:
a4e9bd
+                chunks.append(part)
a4e9bd
+            in_expr = True
a4e9bd
+        else:
a4e9bd
+            chunks.append((s[last:match.start()], last_pos))
a4e9bd
+            in_expr = False
a4e9bd
+        last = match.end()
a4e9bd
+        last_pos = pos
a4e9bd
+    if in_expr:
a4e9bd
+        raise TemplateError('No }} to finish last expression',
a4e9bd
+                            name=name, position=last_pos)
a4e9bd
+    part = s[last:]
a4e9bd
+    if part:
a4e9bd
+        chunks.append(part)
a4e9bd
+    if trim_whitespace:
a4e9bd
+        chunks = trim_lex(chunks)
a4e9bd
+    return chunks
a4e9bd
+
a4e9bd
+statement_re = re.compile(r'^(?:if |elif |else |for |py:)')
a4e9bd
+single_statements = ['endif', 'endfor', 'continue', 'break']
a4e9bd
+trail_whitespace_re = re.compile(r'\n[\t ]*$')
a4e9bd
+lead_whitespace_re = re.compile(r'^[\t ]*\n')
a4e9bd
+
a4e9bd
+def trim_lex(tokens):
a4e9bd
+    r"""
a4e9bd
+    Takes a lexed set of tokens, and removes whitespace when there is
a4e9bd
+    a directive on a line by itself:
a4e9bd
+
a4e9bd
+       >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False)
a4e9bd
+       >>> tokens
a4e9bd
+       [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny']
a4e9bd
+       >>> trim_lex(tokens)
a4e9bd
+       [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y']
a4e9bd
+    """
a4e9bd
+    for i in range(len(tokens)):
a4e9bd
+        current = tokens[i]
a4e9bd
+        if isinstance(tokens[i], basestring):
a4e9bd
+            # we don't trim this
a4e9bd
+            continue
a4e9bd
+        item = current[0]
a4e9bd
+        if not statement_re.search(item) and item not in single_statements:
a4e9bd
+            continue
a4e9bd
+        if not i:
a4e9bd
+            prev = ''
a4e9bd
+        else:
a4e9bd
+            prev = tokens[i-1]
a4e9bd
+        if i+1 >= len(tokens):
a4e9bd
+            next = ''
a4e9bd
+        else:
a4e9bd
+            next = tokens[i+1]
a4e9bd
+        if (not isinstance(next, basestring)
a4e9bd
+            or not isinstance(prev, basestring)):
a4e9bd
+            continue
a4e9bd
+        if ((not prev or trail_whitespace_re.search(prev))
a4e9bd
+            and (not next or lead_whitespace_re.search(next))):
a4e9bd
+            if prev:
a4e9bd
+                m = trail_whitespace_re.search(prev)
a4e9bd
+                # +1 to leave the leading \n on:
a4e9bd
+                prev = prev[:m.start()+1]
a4e9bd
+                tokens[i-1] = prev
a4e9bd
+            if next:
a4e9bd
+                m = lead_whitespace_re.search(next)
a4e9bd
+                next = next[m.end():]
a4e9bd
+                tokens[i+1] = next
a4e9bd
+    return tokens
a4e9bd
+        
a4e9bd
+
a4e9bd
+def find_position(string, index):
a4e9bd
+    """Given a string and index, return (line, column)"""
a4e9bd
+    leading = string[:index].splitlines()
a4e9bd
+    return (len(leading), len(leading[-1])+1)
a4e9bd
+
a4e9bd
+def parse(s, name=None):
a4e9bd
+    r"""
a4e9bd
+    Parses a string into a kind of AST
a4e9bd
+
a4e9bd
+        >>> parse('{{x}}')
a4e9bd
+        [('expr', (1, 3), 'x')]
a4e9bd
+        >>> parse('foo')
a4e9bd
+        ['foo']
a4e9bd
+        >>> parse('{{if x}}test{{endif}}')
a4e9bd
+        [('cond', (1, 3), ('if', (1, 3), 'x', ['test']))]
a4e9bd
+        >>> parse('series->{{for x in y}}x={{x}}{{endfor}}')
a4e9bd
+        ['series->', ('for', (1, 11), ('x',), 'y', ['x=', ('expr', (1, 27), 'x')])]
a4e9bd
+        >>> parse('{{for x, y in z:}}{{continue}}{{endfor}}')
a4e9bd
+        [('for', (1, 3), ('x', 'y'), 'z', [('continue', (1, 21))])]
a4e9bd
+        >>> parse('{{py:x=1}}')
a4e9bd
+        [('py', (1, 3), 'x=1')]
a4e9bd
+        >>> parse('{{if x}}a{{elif y}}b{{else}}c{{endif}}')
a4e9bd
+        [('cond', (1, 3), ('if', (1, 3), 'x', ['a']), ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))]
a4e9bd
+
a4e9bd
+    Some exceptions::
a4e9bd
+        
a4e9bd
+        >>> parse('{{continue}}')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: continue outside of for loop at line 1 column 3
a4e9bd
+        >>> parse('{{if x}}foo')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: No {{endif}} at line 1 column 3
a4e9bd
+        >>> parse('{{else}}')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: else outside of an if block at line 1 column 3
a4e9bd
+        >>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: Unexpected endif at line 1 column 25
a4e9bd
+        >>> parse('{{if}}{{endif}}')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: if with no expression at line 1 column 3
a4e9bd
+        >>> parse('{{for x y}}{{endfor}}')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
a4e9bd
+        >>> parse('{{py:x=1\ny=2}}')
a4e9bd
+        Traceback (most recent call last):
a4e9bd
+            ...
a4e9bd
+        TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
a4e9bd
+    """
a4e9bd
+    tokens = lex(s, name=name)
a4e9bd
+    result = []
a4e9bd
+    while tokens:
a4e9bd
+        next, tokens = parse_expr(tokens, name)
a4e9bd
+        result.append(next)
a4e9bd
+    return result
a4e9bd
+
a4e9bd
+def parse_expr(tokens, name, context=()):
a4e9bd
+    if isinstance(tokens[0], basestring):
a4e9bd
+        return tokens[0], tokens[1:]
a4e9bd
+    expr, pos = tokens[0]
a4e9bd
+    expr = expr.strip()
a4e9bd
+    if expr.startswith('py:'):
a4e9bd
+        expr = expr[3:].lstrip(' \t')
a4e9bd
+        if expr.startswith('\n'):
a4e9bd
+            expr = expr[1:]
a4e9bd
+        else:
a4e9bd
+            if '\n' in expr:
a4e9bd
+                raise TemplateError(
a4e9bd
+                    'Multi-line py blocks must start with a newline',
a4e9bd
+                    position=pos, name=name)
a4e9bd
+        return ('py', pos, expr), tokens[1:]
a4e9bd
+    elif expr in ('continue', 'break'):
a4e9bd
+        if 'for' not in context:
a4e9bd
+            raise TemplateError(
a4e9bd
+                'continue outside of for loop',
a4e9bd
+                position=pos, name=name)
a4e9bd
+        return (expr, pos), tokens[1:]
a4e9bd
+    elif expr.startswith('if '):
a4e9bd
+        return parse_cond(tokens, name, context)
a4e9bd
+    elif (expr.startswith('elif ')
a4e9bd
+          or expr == 'else'):
a4e9bd
+        raise TemplateError(
a4e9bd
+            '%s outside of an if block' % expr.split()[0],
a4e9bd
+            position=pos, name=name)
a4e9bd
+    elif expr in ('if', 'elif', 'for'):
a4e9bd
+        raise TemplateError(
a4e9bd
+            '%s with no expression' % expr,
a4e9bd
+            position=pos, name=name)
a4e9bd
+    elif expr in ('endif', 'endfor'):
a4e9bd
+        raise TemplateError(
a4e9bd
+            'Unexpected %s' % expr,
a4e9bd
+            position=pos, name=name)
a4e9bd
+    elif expr.startswith('for '):
a4e9bd
+        return parse_for(tokens, name, context)
a4e9bd
+    elif expr.startswith('default '):
a4e9bd
+        return parse_default(tokens, name, context)
a4e9bd
+    elif expr.startswith('#'):
a4e9bd
+        return ('comment', pos, tokens[0][0]), tokens[1:]
a4e9bd
+    return ('expr', pos, tokens[0][0]), tokens[1:]
a4e9bd
+
a4e9bd
+def parse_cond(tokens, name, context):
a4e9bd
+    start = tokens[0][1]
a4e9bd
+    pieces = []
a4e9bd
+    context = context + ('if',)
a4e9bd
+    while 1:
a4e9bd
+        if not tokens:
a4e9bd
+            raise TemplateError(
a4e9bd
+                'Missing {{endif}}',
a4e9bd
+                position=start, name=name)
a4e9bd
+        if (isinstance(tokens[0], tuple)
a4e9bd
+            and tokens[0][0] == 'endif'):
a4e9bd
+            return ('cond', start) + tuple(pieces), tokens[1:]
a4e9bd
+        next, tokens = parse_one_cond(tokens, name, context)
a4e9bd
+        pieces.append(next)
a4e9bd
+
a4e9bd
+def parse_one_cond(tokens, name, context):
a4e9bd
+    (first, pos), tokens = tokens[0], tokens[1:]
a4e9bd
+    content = []
a4e9bd
+    if first.endswith(':'):
a4e9bd
+        first = first[:-1]
a4e9bd
+    if first.startswith('if '):
a4e9bd
+        part = ('if', pos, first[3:].lstrip(), content)
a4e9bd
+    elif first.startswith('elif '):
a4e9bd
+        part = ('elif', pos, first[5:].lstrip(), content)
a4e9bd
+    elif first == 'else':
a4e9bd
+        part = ('else', pos, None, content)
a4e9bd
+    else:
a4e9bd
+        assert 0, "Unexpected token %r at %s" % (first, pos)
a4e9bd
+    while 1:
a4e9bd
+        if not tokens:
a4e9bd
+            raise TemplateError(
a4e9bd
+                'No {{endif}}',
a4e9bd
+                position=pos, name=name)
a4e9bd
+        if (isinstance(tokens[0], tuple)
a4e9bd
+            and (tokens[0][0] == 'endif'
a4e9bd
+                 or tokens[0][0].startswith('elif ')
a4e9bd
+                 or tokens[0][0] == 'else')):
a4e9bd
+            return part, tokens
a4e9bd
+        next, tokens = parse_expr(tokens, name, context)
a4e9bd
+        content.append(next)
a4e9bd
+        
a4e9bd
+def parse_for(tokens, name, context):
a4e9bd
+    first, pos = tokens[0]
a4e9bd
+    tokens = tokens[1:]
a4e9bd
+    context = ('for',) + context
a4e9bd
+    content = []
a4e9bd
+    assert first.startswith('for ')
a4e9bd
+    if first.endswith(':'):
a4e9bd
+        first = first[:-1]
a4e9bd
+    first = first[3:].strip()
a4e9bd
+    match = in_re.search(first)
a4e9bd
+    if not match:
a4e9bd
+        raise TemplateError(
a4e9bd
+            'Bad for (no "in") in %r' % first,
a4e9bd
+            position=pos, name=name)
a4e9bd
+    vars = first[:match.start()]
a4e9bd
+    if '(' in vars:
a4e9bd
+        raise TemplateError(
a4e9bd
+            'You cannot have () in the variable section of a for loop (%r)'
a4e9bd
+            % vars, position=pos, name=name)
a4e9bd
+    vars = tuple([
a4e9bd
+        v.strip() for v in first[:match.start()].split(',')
a4e9bd
+        if v.strip()])
a4e9bd
+    expr = first[match.end():]
a4e9bd
+    while 1:
a4e9bd
+        if not tokens:
a4e9bd
+            raise TemplateError(
a4e9bd
+                'No {{endfor}}',
a4e9bd
+                position=pos, name=name)
a4e9bd
+        if (isinstance(tokens[0], tuple)
a4e9bd
+            and tokens[0][0] == 'endfor'):
a4e9bd
+            return ('for', pos, vars, expr, content), tokens[1:]
a4e9bd
+        next, tokens = parse_expr(tokens, name, context)
a4e9bd
+        content.append(next)
a4e9bd
+
a4e9bd
+def parse_default(tokens, name, context):
a4e9bd
+    first, pos = tokens[0]
a4e9bd
+    assert first.startswith('default ')
a4e9bd
+    first = first.split(None, 1)[1]
a4e9bd
+    parts = first.split('=', 1)
a4e9bd
+    if len(parts) == 1:
a4e9bd
+        raise TemplateError(
a4e9bd
+            "Expression must be {{default var=value}}; no = found in %r" % first,
a4e9bd
+            position=pos, name=name)
a4e9bd
+    var = parts[0].strip()
a4e9bd
+    if ',' in var:
a4e9bd
+        raise TemplateError(
a4e9bd
+            "{{default x, y = ...}} is not supported",
a4e9bd
+            position=pos, name=name)
a4e9bd
+    if not var_re.search(var):
a4e9bd
+        raise TemplateError(
a4e9bd
+            "Not a valid variable name for {{default}}: %r"
a4e9bd
+            % var, position=pos, name=name)
a4e9bd
+    expr = parts[1].strip()
a4e9bd
+    return ('default', pos, var, expr), tokens[1:]
a4e9bd
+
a4e9bd
+_fill_command_usage = """\
a4e9bd
+%prog [OPTIONS] TEMPLATE arg=value
a4e9bd
+
a4e9bd
+Use py:arg=value to set a Python value; otherwise all values are
a4e9bd
+strings.
a4e9bd
+"""
a4e9bd
+
a4e9bd
+def fill_command(args=None):
a4e9bd
+    import sys, optparse, pkg_resources, os
a4e9bd
+    if args is None:
a4e9bd
+        args = sys.argv[1:]
a4e9bd
+    dist = pkg_resources.get_distribution('Paste')
a4e9bd
+    parser = optparse.OptionParser(
a4e9bd
+        version=str(dist),
a4e9bd
+        usage=_fill_command_usage)
a4e9bd
+    parser.add_option(
a4e9bd
+        '-o', '--output',
a4e9bd
+        dest='output',
a4e9bd
+        metavar="FILENAME",
a4e9bd
+        help="File to write output to (default stdout)")
a4e9bd
+    parser.add_option(
a4e9bd
+        '--html',
a4e9bd
+        dest='use_html',
a4e9bd
+        action='store_true',
a4e9bd
+        help="Use HTML style filling (including automatic HTML quoting)")
a4e9bd
+    parser.add_option(
a4e9bd
+        '--env',
a4e9bd
+        dest='use_env',
a4e9bd
+        action='store_true',
a4e9bd
+        help="Put the environment in as top-level variables")
a4e9bd
+    options, args = parser.parse_args(args)
a4e9bd
+    if len(args) < 1:
a4e9bd
+        print 'You must give a template filename'
a4e9bd
+        print dir(parser)
a4e9bd
+        assert 0
a4e9bd
+    template_name = args[0]
a4e9bd
+    args = args[1:]
a4e9bd
+    vars = {}
a4e9bd
+    if options.use_env:
a4e9bd
+        vars.update(os.environ)
a4e9bd
+    for value in args:
a4e9bd
+        if '=' not in value:
a4e9bd
+            print 'Bad argument: %r' % value
a4e9bd
+            sys.exit(2)
a4e9bd
+        name, value = value.split('=', 1)
a4e9bd
+        if name.startswith('py:'):
a4e9bd
+            name = name[:3]
a4e9bd
+            value = eval(value)
a4e9bd
+        vars[name] = value
a4e9bd
+    if template_name == '-':
a4e9bd
+        template_content = sys.stdin.read()
a4e9bd
+        template_name = '<stdin>'
a4e9bd
+    else:
a4e9bd
+        f = open(template_name, 'rb')
a4e9bd
+        template_content = f.read()
a4e9bd
+        f.close()
a4e9bd
+    if options.use_html:
a4e9bd
+        TemplateClass = HTMLTemplate
a4e9bd
+    else:
a4e9bd
+        TemplateClass = Template
a4e9bd
+    template = TemplateClass(template_content, name=template_name)
a4e9bd
+    result = template.substitute(vars)
a4e9bd
+    if options.output:
a4e9bd
+        f = open(options.output, 'wb')
a4e9bd
+        f.write(result)
a4e9bd
+        f.close()
a4e9bd
+    else:
a4e9bd
+        sys.stdout.write(result)
a4e9bd
+
a4e9bd
+if __name__ == '__main__':
a4e9bd
+    from paste.util.template import fill_command
a4e9bd
+    fill_command()
a4e9bd
+        
a4e9bd
+    
a4e9bd
Index: Paste-1.7.4/paste/util/template.py
a4e9bd
===================================================================
a4e9bd
--- Paste-1.7.4.orig/paste/util/template.py
a4e9bd
+++ /dev/null
a4e9bd
@@ -1,758 +0,0 @@
a4e9bd
-"""
a4e9bd
-A small templating language
a4e9bd
-
a4e9bd
-This implements a small templating language for use internally in
a4e9bd
-Paste and Paste Script.  This language implements if/elif/else,
a4e9bd
-for/continue/break, expressions, and blocks of Python code.  The
a4e9bd
-syntax is::
a4e9bd
-
a4e9bd
-  {{any expression (function calls etc)}}
a4e9bd
-  {{any expression | filter}}
a4e9bd
-  {{for x in y}}...{{endfor}}
a4e9bd
-  {{if x}}x{{elif y}}y{{else}}z{{endif}}
a4e9bd
-  {{py:x=1}}
a4e9bd
-  {{py:
a4e9bd
-  def foo(bar):
a4e9bd
-      return 'baz'
a4e9bd
-  }}
a4e9bd
-  {{default var = default_value}}
a4e9bd
-  {{# comment}}
a4e9bd
-
a4e9bd
-You use this with the ``Template`` class or the ``sub`` shortcut.
a4e9bd
-The ``Template`` class takes the template string and the name of
a4e9bd
-the template (for errors) and a default namespace.  Then (like
a4e9bd
-``string.Template``) you can call the ``tmpl.substitute(**kw)``
a4e9bd
-method to make a substitution (or ``tmpl.substitute(a_dict)``).
a4e9bd
-
a4e9bd
-``sub(content, **kw)`` substitutes the template immediately.  You
a4e9bd
-can use ``__name='tmpl.html'`` to set the name of the template.
a4e9bd
-
a4e9bd
-If there are syntax errors ``TemplateError`` will be raised.
a4e9bd
-"""
a4e9bd
-
a4e9bd
-import re
a4e9bd
-import sys
a4e9bd
-import cgi
a4e9bd
-import urllib
a4e9bd
-from paste.util.looper import looper
a4e9bd
-
a4e9bd
-__all__ = ['TemplateError', 'Template', 'sub', 'HTMLTemplate',
a4e9bd
-           'sub_html', 'html', 'bunch']
a4e9bd
-
a4e9bd
-token_re = re.compile(r'\{\{|\}\}')
a4e9bd
-in_re = re.compile(r'\s+in\s+')
a4e9bd
-var_re = re.compile(r'^[a-z_][a-z0-9_]*$', re.I)
a4e9bd
-
a4e9bd
-class TemplateError(Exception):
a4e9bd
-    """Exception raised while parsing a template
a4e9bd
-    """
a4e9bd
-
a4e9bd
-    def __init__(self, message, position, name=None):
a4e9bd
-        self.message = message
a4e9bd
-        self.position = position
a4e9bd
-        self.name = name
a4e9bd
-
a4e9bd
-    def __str__(self):
a4e9bd
-        msg = '%s at line %s column %s' % (
a4e9bd
-            self.message, self.position[0], self.position[1])
a4e9bd
-        if self.name:
a4e9bd
-            msg += ' in %s' % self.name
a4e9bd
-        return msg
a4e9bd
-
a4e9bd
-class _TemplateContinue(Exception):
a4e9bd
-    pass
a4e9bd
-
a4e9bd
-class _TemplateBreak(Exception):
a4e9bd
-    pass
a4e9bd
-
a4e9bd
-class Template(object):
a4e9bd
-
a4e9bd
-    default_namespace = {
a4e9bd
-        'start_braces': '{{',
a4e9bd
-        'end_braces': '}}',
a4e9bd
-        'looper': looper,
a4e9bd
-        }
a4e9bd
-
a4e9bd
-    default_encoding = 'utf8'
a4e9bd
-
a4e9bd
-    def __init__(self, content, name=None, namespace=None):
a4e9bd
-        self.content = content
a4e9bd
-        self._unicode = isinstance(content, unicode)
a4e9bd
-        self.name = name
a4e9bd
-        self._parsed = parse(content, name=name)
a4e9bd
-        if namespace is None:
a4e9bd
-            namespace = {}
a4e9bd
-        self.namespace = namespace
a4e9bd
-
a4e9bd
-    def from_filename(cls, filename, namespace=None, encoding=None):
a4e9bd
-        f = open(filename, 'rb')
a4e9bd
-        c = f.read()
a4e9bd
-        f.close()
a4e9bd
-        if encoding:
a4e9bd
-            c = c.decode(encoding)
a4e9bd
-        return cls(content=c, name=filename, namespace=namespace)
a4e9bd
-
a4e9bd
-    from_filename = classmethod(from_filename)
a4e9bd
-
a4e9bd
-    def __repr__(self):
a4e9bd
-        return '<%s %s name=%r>' % (
a4e9bd
-            self.__class__.__name__,
a4e9bd
-            hex(id(self))[2:], self.name)
a4e9bd
-
a4e9bd
-    def substitute(self, *args, **kw):
a4e9bd
-        if args:
a4e9bd
-            if kw:
a4e9bd
-                raise TypeError(
a4e9bd
-                    "You can only give positional *or* keyword arguments")
a4e9bd
-            if len(args) > 1:
a4e9bd
-                raise TypeError(
a4e9bd
-                    "You can only give on positional argument")
a4e9bd
-            kw = args[0]
a4e9bd
-        ns = self.default_namespace.copy()
a4e9bd
-        ns.update(self.namespace)
a4e9bd
-        ns.update(kw)
a4e9bd
-        result = self._interpret(ns)
a4e9bd
-        return result
a4e9bd
-
a4e9bd
-    def _interpret(self, ns):
a4e9bd
-        __traceback_hide__ = True
a4e9bd
-        parts = []
a4e9bd
-        self._interpret_codes(self._parsed, ns, out=parts)
a4e9bd
-        return ''.join(parts)
a4e9bd
-
a4e9bd
-    def _interpret_codes(self, codes, ns, out):
a4e9bd
-        __traceback_hide__ = True
a4e9bd
-        for item in codes:
a4e9bd
-            if isinstance(item, basestring):
a4e9bd
-                out.append(item)
a4e9bd
-            else:
a4e9bd
-                self._interpret_code(item, ns, out)
a4e9bd
-
a4e9bd
-    def _interpret_code(self, code, ns, out):
a4e9bd
-        __traceback_hide__ = True
a4e9bd
-        name, pos = code[0], code[1]
a4e9bd
-        if name == 'py':
a4e9bd
-            self._exec(code[2], ns, pos)
a4e9bd
-        elif name == 'continue':
a4e9bd
-            raise _TemplateContinue()
a4e9bd
-        elif name == 'break':
a4e9bd
-            raise _TemplateBreak()
a4e9bd
-        elif name == 'for':
a4e9bd
-            vars, expr, content = code[2], code[3], code[4]
a4e9bd
-            expr = self._eval(expr, ns, pos)
a4e9bd
-            self._interpret_for(vars, expr, content, ns, out)
a4e9bd
-        elif name == 'cond':
a4e9bd
-            parts = code[2:]
a4e9bd
-            self._interpret_if(parts, ns, out)
a4e9bd
-        elif name == 'expr':
a4e9bd
-            parts = code[2].split('|')
a4e9bd
-            base = self._eval(parts[0], ns, pos)
a4e9bd
-            for part in parts[1:]:
a4e9bd
-                func = self._eval(part, ns, pos)
a4e9bd
-                base = func(base)
a4e9bd
-            out.append(self._repr(base, pos))
a4e9bd
-        elif name == 'default':
a4e9bd
-            var, expr = code[2], code[3]
a4e9bd
-            if var not in ns:
a4e9bd
-                result = self._eval(expr, ns, pos)
a4e9bd
-                ns[var] = result
a4e9bd
-        elif name == 'comment':
a4e9bd
-            return
a4e9bd
-        else:
a4e9bd
-            assert 0, "Unknown code: %r" % name
a4e9bd
-
a4e9bd
-    def _interpret_for(self, vars, expr, content, ns, out):
a4e9bd
-        __traceback_hide__ = True
a4e9bd
-        for item in expr:
a4e9bd
-            if len(vars) == 1:
a4e9bd
-                ns[vars[0]] = item
a4e9bd
-            else:
a4e9bd
-                if len(vars) != len(item):
a4e9bd
-                    raise ValueError(
a4e9bd
-                        'Need %i items to unpack (got %i items)'
a4e9bd
-                        % (len(vars), len(item)))
a4e9bd
-                for name, value in zip(vars, item):
a4e9bd
-                    ns[name] = value
a4e9bd
-            try:
a4e9bd
-                self._interpret_codes(content, ns, out)
a4e9bd
-            except _TemplateContinue:
a4e9bd
-                continue
a4e9bd
-            except _TemplateBreak:
a4e9bd
-                break
a4e9bd
-
a4e9bd
-    def _interpret_if(self, parts, ns, out):
a4e9bd
-        __traceback_hide__ = True
a4e9bd
-        # @@: if/else/else gets through
a4e9bd
-        for part in parts:
a4e9bd
-            assert not isinstance(part, basestring)
a4e9bd
-            name, pos = part[0], part[1]
a4e9bd
-            if name == 'else':
a4e9bd
-                result = True
a4e9bd
-            else:
a4e9bd
-                result = self._eval(part[2], ns, pos)
a4e9bd
-            if result:
a4e9bd
-                self._interpret_codes(part[3], ns, out)
a4e9bd
-                break
a4e9bd
-
a4e9bd
-    def _eval(self, code, ns, pos):
a4e9bd
-        __traceback_hide__ = True
a4e9bd
-        try:
a4e9bd
-            value = eval(code, ns)
a4e9bd
-            return value
a4e9bd
-        except:
a4e9bd
-            exc_info = sys.exc_info()
a4e9bd
-            e = exc_info[1]
a4e9bd
-            if getattr(e, 'args'):
a4e9bd
-                arg0 = e.args[0]
a4e9bd
-            else:
a4e9bd
-                arg0 = str(e)
a4e9bd
-            e.args = (self._add_line_info(arg0, pos),)
a4e9bd
-            raise exc_info[0], e, exc_info[2]
a4e9bd
-
a4e9bd
-    def _exec(self, code, ns, pos):
a4e9bd
-        __traceback_hide__ = True
a4e9bd
-        try:
a4e9bd
-            exec code in ns
a4e9bd
-        except:
a4e9bd
-            exc_info = sys.exc_info()
a4e9bd
-            e = exc_info[1]
a4e9bd
-            e.args = (self._add_line_info(e.args[0], pos),)
a4e9bd
-            raise exc_info[0], e, exc_info[2]
a4e9bd
-
a4e9bd
-    def _repr(self, value, pos):
a4e9bd
-        __traceback_hide__ = True
a4e9bd
-        try:
a4e9bd
-            if value is None:
a4e9bd
-                return ''
a4e9bd
-            if self._unicode:
a4e9bd
-                try:
a4e9bd
-                    value = unicode(value)
a4e9bd
-                except UnicodeDecodeError:
a4e9bd
-                    value = str(value)
a4e9bd
-            else:
a4e9bd
-                value = str(value)
a4e9bd
-        except:
a4e9bd
-            exc_info = sys.exc_info()
a4e9bd
-            e = exc_info[1]
a4e9bd
-            e.args = (self._add_line_info(e.args[0], pos),)
a4e9bd
-            raise exc_info[0], e, exc_info[2]
a4e9bd
-        else:
a4e9bd
-            if self._unicode and isinstance(value, str):
a4e9bd
-                if not self.decode_encoding:
a4e9bd
-                    raise UnicodeDecodeError(
a4e9bd
-                        'Cannot decode str value %r into unicode '
a4e9bd
-                        '(no default_encoding provided)' % value)
a4e9bd
-                value = value.decode(self.default_encoding)
a4e9bd
-            elif not self._unicode and isinstance(value, unicode):
a4e9bd
-                if not self.decode_encoding:
a4e9bd
-                    raise UnicodeEncodeError(
a4e9bd
-                        'Cannot encode unicode value %r into str '
a4e9bd
-                        '(no default_encoding provided)' % value)
a4e9bd
-                value = value.encode(self.default_encoding)
a4e9bd
-            return value
a4e9bd
-        
a4e9bd
-
a4e9bd
-    def _add_line_info(self, msg, pos):
a4e9bd
-        msg = "%s at line %s column %s" % (
a4e9bd
-            msg, pos[0], pos[1])
a4e9bd
-        if self.name:
a4e9bd
-            msg += " in file %s" % self.name
a4e9bd
-        return msg
a4e9bd
-
a4e9bd
-def sub(content, **kw):
a4e9bd
-    name = kw.get('__name')
a4e9bd
-    tmpl = Template(content, name=name)
a4e9bd
-    return tmpl.substitute(kw)
a4e9bd
-    return result
a4e9bd
-
a4e9bd
-def paste_script_template_renderer(content, vars, filename=None):
a4e9bd
-    tmpl = Template(content, name=filename)
a4e9bd
-    return tmpl.substitute(vars)
a4e9bd
-
a4e9bd
-class bunch(dict):
a4e9bd
-
a4e9bd
-    def __init__(self, **kw):
a4e9bd
-        for name, value in kw.items():
a4e9bd
-            setattr(self, name, value)
a4e9bd
-
a4e9bd
-    def __setattr__(self, name, value):
a4e9bd
-        self[name] = value
a4e9bd
-
a4e9bd
-    def __getattr__(self, name):
a4e9bd
-        try:
a4e9bd
-            return self[name]
a4e9bd
-        except KeyError:
a4e9bd
-            raise AttributeError(name)
a4e9bd
-
a4e9bd
-    def __getitem__(self, key):
a4e9bd
-        if 'default' in self:
a4e9bd
-            try:
a4e9bd
-                return dict.__getitem__(self, key)
a4e9bd
-            except KeyError:
a4e9bd
-                return dict.__getitem__(self, 'default')
a4e9bd
-        else:
a4e9bd
-            return dict.__getitem__(self, key)
a4e9bd
-
a4e9bd
-    def __repr__(self):
a4e9bd
-        items = [
a4e9bd
-            (k, v) for k, v in self.items()]
a4e9bd
-        items.sort()
a4e9bd
-        return '<%s %s>' % (
a4e9bd
-            self.__class__.__name__,
a4e9bd
-            ' '.join(['%s=%r' % (k, v) for k, v in items]))
a4e9bd
-
a4e9bd
-############################################################
a4e9bd
-## HTML Templating
a4e9bd
-############################################################
a4e9bd
-
a4e9bd
-class html(object):
a4e9bd
-    def __init__(self, value):
a4e9bd
-        self.value = value
a4e9bd
-    def __str__(self):
a4e9bd
-        return self.value
a4e9bd
-    def __repr__(self):
a4e9bd
-        return '<%s %r>' % (
a4e9bd
-            self.__class__.__name__, self.value)
a4e9bd
-
a4e9bd
-def html_quote(value):
a4e9bd
-    if value is None:
a4e9bd
-        return ''
a4e9bd
-    if not isinstance(value, basestring):
a4e9bd
-        if hasattr(value, '__unicode__'):
a4e9bd
-            value = unicode(value)
a4e9bd
-        else:
a4e9bd
-            value = str(value)
a4e9bd
-    value = cgi.escape(value, 1)
a4e9bd
-    if isinstance(value, unicode):
a4e9bd
-        value = value.encode('ascii', 'xmlcharrefreplace')
a4e9bd
-    return value
a4e9bd
-
a4e9bd
-def url(v):
a4e9bd
-    if not isinstance(v, basestring):
a4e9bd
-        if hasattr(v, '__unicode__'):
a4e9bd
-            v = unicode(v)
a4e9bd
-        else:
a4e9bd
-            v = str(v)
a4e9bd
-    if isinstance(v, unicode):
a4e9bd
-        v = v.encode('utf8')
a4e9bd
-    return urllib.quote(v)
a4e9bd
-
a4e9bd
-def attr(**kw):
a4e9bd
-    kw = kw.items()
a4e9bd
-    kw.sort()
a4e9bd
-    parts = []
a4e9bd
-    for name, value in kw:
a4e9bd
-        if value is None:
a4e9bd
-            continue
a4e9bd
-        if name.endswith('_'):
a4e9bd
-            name = name[:-1]
a4e9bd
-        parts.append('%s="%s"' % (html_quote(name), html_quote(value)))
a4e9bd
-    return html(' '.join(parts))
a4e9bd
-
a4e9bd
-class HTMLTemplate(Template):
a4e9bd
-
a4e9bd
-    default_namespace = Template.default_namespace.copy()
a4e9bd
-    default_namespace.update(dict(
a4e9bd
-        html=html,
a4e9bd
-        attr=attr,
a4e9bd
-        url=url,
a4e9bd
-        ))
a4e9bd
-
a4e9bd
-    def _repr(self, value, pos):
a4e9bd
-        plain = Template._repr(self, value, pos)
a4e9bd
-        if isinstance(value, html):
a4e9bd
-            return plain
a4e9bd
-        else:
a4e9bd
-            return html_quote(plain)
a4e9bd
-
a4e9bd
-def sub_html(content, **kw):
a4e9bd
-    name = kw.get('__name')
a4e9bd
-    tmpl = HTMLTemplate(content, name=name)
a4e9bd
-    return tmpl.substitute(kw)
a4e9bd
-    return result
a4e9bd
-
a4e9bd
-
a4e9bd
-############################################################
a4e9bd
-## Lexing and Parsing
a4e9bd
-############################################################
a4e9bd
-
a4e9bd
-def lex(s, name=None, trim_whitespace=True):
a4e9bd
-    """
a4e9bd
-    Lex a string into chunks:
a4e9bd
-
a4e9bd
-        >>> lex('hey')
a4e9bd
-        ['hey']
a4e9bd
-        >>> lex('hey {{you}}')
a4e9bd
-        ['hey ', ('you', (1, 7))]
a4e9bd
-        >>> lex('hey {{')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: No }} to finish last expression at line 1 column 7
a4e9bd
-        >>> lex('hey }}')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: }} outside expression at line 1 column 7
a4e9bd
-        >>> lex('hey {{ {{')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: {{ inside expression at line 1 column 10
a4e9bd
-
a4e9bd
-    """
a4e9bd
-    in_expr = False
a4e9bd
-    chunks = []
a4e9bd
-    last = 0
a4e9bd
-    last_pos = (1, 1)
a4e9bd
-    for match in token_re.finditer(s):
a4e9bd
-        expr = match.group(0)
a4e9bd
-        pos = find_position(s, match.end())
a4e9bd
-        if expr == '{{' and in_expr:
a4e9bd
-            raise TemplateError('{{ inside expression', position=pos,
a4e9bd
-                                name=name)
a4e9bd
-        elif expr == '}}' and not in_expr:
a4e9bd
-            raise TemplateError('}} outside expression', position=pos,
a4e9bd
-                                name=name)
a4e9bd
-        if expr == '{{':
a4e9bd
-            part = s[last:match.start()]
a4e9bd
-            if part:
a4e9bd
-                chunks.append(part)
a4e9bd
-            in_expr = True
a4e9bd
-        else:
a4e9bd
-            chunks.append((s[last:match.start()], last_pos))
a4e9bd
-            in_expr = False
a4e9bd
-        last = match.end()
a4e9bd
-        last_pos = pos
a4e9bd
-    if in_expr:
a4e9bd
-        raise TemplateError('No }} to finish last expression',
a4e9bd
-                            name=name, position=last_pos)
a4e9bd
-    part = s[last:]
a4e9bd
-    if part:
a4e9bd
-        chunks.append(part)
a4e9bd
-    if trim_whitespace:
a4e9bd
-        chunks = trim_lex(chunks)
a4e9bd
-    return chunks
a4e9bd
-
a4e9bd
-statement_re = re.compile(r'^(?:if |elif |else |for |py:)')
a4e9bd
-single_statements = ['endif', 'endfor', 'continue', 'break']
a4e9bd
-trail_whitespace_re = re.compile(r'\n[\t ]*$')
a4e9bd
-lead_whitespace_re = re.compile(r'^[\t ]*\n')
a4e9bd
-
a4e9bd
-def trim_lex(tokens):
a4e9bd
-    r"""
a4e9bd
-    Takes a lexed set of tokens, and removes whitespace when there is
a4e9bd
-    a directive on a line by itself:
a4e9bd
-
a4e9bd
-       >>> tokens = lex('{{if x}}\nx\n{{endif}}\ny', trim_whitespace=False)
a4e9bd
-       >>> tokens
a4e9bd
-       [('if x', (1, 3)), '\nx\n', ('endif', (3, 3)), '\ny']
a4e9bd
-       >>> trim_lex(tokens)
a4e9bd
-       [('if x', (1, 3)), 'x\n', ('endif', (3, 3)), 'y']
a4e9bd
-    """
a4e9bd
-    for i in range(len(tokens)):
a4e9bd
-        current = tokens[i]
a4e9bd
-        if isinstance(tokens[i], basestring):
a4e9bd
-            # we don't trim this
a4e9bd
-            continue
a4e9bd
-        item = current[0]
a4e9bd
-        if not statement_re.search(item) and item not in single_statements:
a4e9bd
-            continue
a4e9bd
-        if not i:
a4e9bd
-            prev = ''
a4e9bd
-        else:
a4e9bd
-            prev = tokens[i-1]
a4e9bd
-        if i+1 >= len(tokens):
a4e9bd
-            next = ''
a4e9bd
-        else:
a4e9bd
-            next = tokens[i+1]
a4e9bd
-        if (not isinstance(next, basestring)
a4e9bd
-            or not isinstance(prev, basestring)):
a4e9bd
-            continue
a4e9bd
-        if ((not prev or trail_whitespace_re.search(prev))
a4e9bd
-            and (not next or lead_whitespace_re.search(next))):
a4e9bd
-            if prev:
a4e9bd
-                m = trail_whitespace_re.search(prev)
a4e9bd
-                # +1 to leave the leading \n on:
a4e9bd
-                prev = prev[:m.start()+1]
a4e9bd
-                tokens[i-1] = prev
a4e9bd
-            if next:
a4e9bd
-                m = lead_whitespace_re.search(next)
a4e9bd
-                next = next[m.end():]
a4e9bd
-                tokens[i+1] = next
a4e9bd
-    return tokens
a4e9bd
-        
a4e9bd
-
a4e9bd
-def find_position(string, index):
a4e9bd
-    """Given a string and index, return (line, column)"""
a4e9bd
-    leading = string[:index].splitlines()
a4e9bd
-    return (len(leading), len(leading[-1])+1)
a4e9bd
-
a4e9bd
-def parse(s, name=None):
a4e9bd
-    r"""
a4e9bd
-    Parses a string into a kind of AST
a4e9bd
-
a4e9bd
-        >>> parse('{{x}}')
a4e9bd
-        [('expr', (1, 3), 'x')]
a4e9bd
-        >>> parse('foo')
a4e9bd
-        ['foo']
a4e9bd
-        >>> parse('{{if x}}test{{endif}}')
a4e9bd
-        [('cond', (1, 3), ('if', (1, 3), 'x', ['test']))]
a4e9bd
-        >>> parse('series->{{for x in y}}x={{x}}{{endfor}}')
a4e9bd
-        ['series->', ('for', (1, 11), ('x',), 'y', ['x=', ('expr', (1, 27), 'x')])]
a4e9bd
-        >>> parse('{{for x, y in z:}}{{continue}}{{endfor}}')
a4e9bd
-        [('for', (1, 3), ('x', 'y'), 'z', [('continue', (1, 21))])]
a4e9bd
-        >>> parse('{{py:x=1}}')
a4e9bd
-        [('py', (1, 3), 'x=1')]
a4e9bd
-        >>> parse('{{if x}}a{{elif y}}b{{else}}c{{endif}}')
a4e9bd
-        [('cond', (1, 3), ('if', (1, 3), 'x', ['a']), ('elif', (1, 12), 'y', ['b']), ('else', (1, 23), None, ['c']))]
a4e9bd
-
a4e9bd
-    Some exceptions::
a4e9bd
-        
a4e9bd
-        >>> parse('{{continue}}')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: continue outside of for loop at line 1 column 3
a4e9bd
-        >>> parse('{{if x}}foo')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: No {{endif}} at line 1 column 3
a4e9bd
-        >>> parse('{{else}}')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: else outside of an if block at line 1 column 3
a4e9bd
-        >>> parse('{{if x}}{{for x in y}}{{endif}}{{endfor}}')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: Unexpected endif at line 1 column 25
a4e9bd
-        >>> parse('{{if}}{{endif}}')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: if with no expression at line 1 column 3
a4e9bd
-        >>> parse('{{for x y}}{{endfor}}')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: Bad for (no "in") in 'x y' at line 1 column 3
a4e9bd
-        >>> parse('{{py:x=1\ny=2}}')
a4e9bd
-        Traceback (most recent call last):
a4e9bd
-            ...
a4e9bd
-        TemplateError: Multi-line py blocks must start with a newline at line 1 column 3
a4e9bd
-    """
a4e9bd
-    tokens = lex(s, name=name)
a4e9bd
-    result = []
a4e9bd
-    while tokens:
a4e9bd
-        next, tokens = parse_expr(tokens, name)
a4e9bd
-        result.append(next)
a4e9bd
-    return result
a4e9bd
-
a4e9bd
-def parse_expr(tokens, name, context=()):
a4e9bd
-    if isinstance(tokens[0], basestring):
a4e9bd
-        return tokens[0], tokens[1:]
a4e9bd
-    expr, pos = tokens[0]
a4e9bd
-    expr = expr.strip()
a4e9bd
-    if expr.startswith('py:'):
a4e9bd
-        expr = expr[3:].lstrip(' \t')
a4e9bd
-        if expr.startswith('\n'):
a4e9bd
-            expr = expr[1:]
a4e9bd
-        else:
a4e9bd
-            if '\n' in expr:
a4e9bd
-                raise TemplateError(
a4e9bd
-                    'Multi-line py blocks must start with a newline',
a4e9bd
-                    position=pos, name=name)
a4e9bd
-        return ('py', pos, expr), tokens[1:]
a4e9bd
-    elif expr in ('continue', 'break'):
a4e9bd
-        if 'for' not in context:
a4e9bd
-            raise TemplateError(
a4e9bd
-                'continue outside of for loop',
a4e9bd
-                position=pos, name=name)
a4e9bd
-        return (expr, pos), tokens[1:]
a4e9bd
-    elif expr.startswith('if '):
a4e9bd
-        return parse_cond(tokens, name, context)
a4e9bd
-    elif (expr.startswith('elif ')
a4e9bd
-          or expr == 'else'):
a4e9bd
-        raise TemplateError(
a4e9bd
-            '%s outside of an if block' % expr.split()[0],
a4e9bd
-            position=pos, name=name)
a4e9bd
-    elif expr in ('if', 'elif', 'for'):
a4e9bd
-        raise TemplateError(
a4e9bd
-            '%s with no expression' % expr,
a4e9bd
-            position=pos, name=name)
a4e9bd
-    elif expr in ('endif', 'endfor'):
a4e9bd
-        raise TemplateError(
a4e9bd
-            'Unexpected %s' % expr,
a4e9bd
-            position=pos, name=name)
a4e9bd
-    elif expr.startswith('for '):
a4e9bd
-        return parse_for(tokens, name, context)
a4e9bd
-    elif expr.startswith('default '):
a4e9bd
-        return parse_default(tokens, name, context)
a4e9bd
-    elif expr.startswith('#'):
a4e9bd
-        return ('comment', pos, tokens[0][0]), tokens[1:]
a4e9bd
-    return ('expr', pos, tokens[0][0]), tokens[1:]
a4e9bd
-
a4e9bd
-def parse_cond(tokens, name, context):
a4e9bd
-    start = tokens[0][1]
a4e9bd
-    pieces = []
a4e9bd
-    context = context + ('if',)
a4e9bd
-    while 1:
a4e9bd
-        if not tokens:
a4e9bd
-            raise TemplateError(
a4e9bd
-                'Missing {{endif}}',
a4e9bd
-                position=start, name=name)
a4e9bd
-        if (isinstance(tokens[0], tuple)
a4e9bd
-            and tokens[0][0] == 'endif'):
a4e9bd
-            return ('cond', start) + tuple(pieces), tokens[1:]
a4e9bd
-        next, tokens = parse_one_cond(tokens, name, context)
a4e9bd
-        pieces.append(next)
a4e9bd
-
a4e9bd
-def parse_one_cond(tokens, name, context):
a4e9bd
-    (first, pos), tokens = tokens[0], tokens[1:]
a4e9bd
-    content = []
a4e9bd
-    if first.endswith(':'):
a4e9bd
-        first = first[:-1]
a4e9bd
-    if first.startswith('if '):
a4e9bd
-        part = ('if', pos, first[3:].lstrip(), content)
a4e9bd
-    elif first.startswith('elif '):
a4e9bd
-        part = ('elif', pos, first[5:].lstrip(), content)
a4e9bd
-    elif first == 'else':
a4e9bd
-        part = ('else', pos, None, content)
a4e9bd
-    else:
a4e9bd
-        assert 0, "Unexpected token %r at %s" % (first, pos)
a4e9bd
-    while 1:
a4e9bd
-        if not tokens:
a4e9bd
-            raise TemplateError(
a4e9bd
-                'No {{endif}}',
a4e9bd
-                position=pos, name=name)
a4e9bd
-        if (isinstance(tokens[0], tuple)
a4e9bd
-            and (tokens[0][0] == 'endif'
a4e9bd
-                 or tokens[0][0].startswith('elif ')
a4e9bd
-                 or tokens[0][0] == 'else')):
a4e9bd
-            return part, tokens
a4e9bd
-        next, tokens = parse_expr(tokens, name, context)
a4e9bd
-        content.append(next)
a4e9bd
-        
a4e9bd
-def parse_for(tokens, name, context):
a4e9bd
-    first, pos = tokens[0]
a4e9bd
-    tokens = tokens[1:]
a4e9bd
-    context = ('for',) + context
a4e9bd
-    content = []
a4e9bd
-    assert first.startswith('for ')
a4e9bd
-    if first.endswith(':'):
a4e9bd
-        first = first[:-1]
a4e9bd
-    first = first[3:].strip()
a4e9bd
-    match = in_re.search(first)
a4e9bd
-    if not match:
a4e9bd
-        raise TemplateError(
a4e9bd
-            'Bad for (no "in") in %r' % first,
a4e9bd
-            position=pos, name=name)
a4e9bd
-    vars = first[:match.start()]
a4e9bd
-    if '(' in vars:
a4e9bd
-        raise TemplateError(
a4e9bd
-            'You cannot have () in the variable section of a for loop (%r)'
a4e9bd
-            % vars, position=pos, name=name)
a4e9bd
-    vars = tuple([
a4e9bd
-        v.strip() for v in first[:match.start()].split(',')
a4e9bd
-        if v.strip()])
a4e9bd
-    expr = first[match.end():]
a4e9bd
-    while 1:
a4e9bd
-        if not tokens:
a4e9bd
-            raise TemplateError(
a4e9bd
-                'No {{endfor}}',
a4e9bd
-                position=pos, name=name)
a4e9bd
-        if (isinstance(tokens[0], tuple)
a4e9bd
-            and tokens[0][0] == 'endfor'):
a4e9bd
-            return ('for', pos, vars, expr, content), tokens[1:]
a4e9bd
-        next, tokens = parse_expr(tokens, name, context)
a4e9bd
-        content.append(next)
a4e9bd
-
a4e9bd
-def parse_default(tokens, name, context):
a4e9bd
-    first, pos = tokens[0]
a4e9bd
-    assert first.startswith('default ')
a4e9bd
-    first = first.split(None, 1)[1]
a4e9bd
-    parts = first.split('=', 1)
a4e9bd
-    if len(parts) == 1:
a4e9bd
-        raise TemplateError(
a4e9bd
-            "Expression must be {{default var=value}}; no = found in %r" % first,
a4e9bd
-            position=pos, name=name)
a4e9bd
-    var = parts[0].strip()
a4e9bd
-    if ',' in var:
a4e9bd
-        raise TemplateError(
a4e9bd
-            "{{default x, y = ...}} is not supported",
a4e9bd
-            position=pos, name=name)
a4e9bd
-    if not var_re.search(var):
a4e9bd
-        raise TemplateError(
a4e9bd
-            "Not a valid variable name for {{default}}: %r"
a4e9bd
-            % var, position=pos, name=name)
a4e9bd
-    expr = parts[1].strip()
a4e9bd
-    return ('default', pos, var, expr), tokens[1:]
a4e9bd
-
a4e9bd
-_fill_command_usage = """\
a4e9bd
-%prog [OPTIONS] TEMPLATE arg=value
a4e9bd
-
a4e9bd
-Use py:arg=value to set a Python value; otherwise all values are
a4e9bd
-strings.
a4e9bd
-"""
a4e9bd
-
a4e9bd
-def fill_command(args=None):
a4e9bd
-    import sys, optparse, pkg_resources, os
a4e9bd
-    if args is None:
a4e9bd
-        args = sys.argv[1:]
a4e9bd
-    dist = pkg_resources.get_distribution('Paste')
a4e9bd
-    parser = optparse.OptionParser(
a4e9bd
-        version=str(dist),
a4e9bd
-        usage=_fill_command_usage)
a4e9bd
-    parser.add_option(
a4e9bd
-        '-o', '--output',
a4e9bd
-        dest='output',
a4e9bd
-        metavar="FILENAME",
a4e9bd
-        help="File to write output to (default stdout)")
a4e9bd
-    parser.add_option(
a4e9bd
-        '--html',
a4e9bd
-        dest='use_html',
a4e9bd
-        action='store_true',
a4e9bd
-        help="Use HTML style filling (including automatic HTML quoting)")
a4e9bd
-    parser.add_option(
a4e9bd
-        '--env',
a4e9bd
-        dest='use_env',
a4e9bd
-        action='store_true',
a4e9bd
-        help="Put the environment in as top-level variables")
a4e9bd
-    options, args = parser.parse_args(args)
a4e9bd
-    if len(args) < 1:
a4e9bd
-        print 'You must give a template filename'
a4e9bd
-        print dir(parser)
a4e9bd
-        assert 0
a4e9bd
-    template_name = args[0]
a4e9bd
-    args = args[1:]
a4e9bd
-    vars = {}
a4e9bd
-    if options.use_env:
a4e9bd
-        vars.update(os.environ)
a4e9bd
-    for value in args:
a4e9bd
-        if '=' not in value:
a4e9bd
-            print 'Bad argument: %r' % value
a4e9bd
-            sys.exit(2)
a4e9bd
-        name, value = value.split('=', 1)
a4e9bd
-        if name.startswith('py:'):
a4e9bd
-            name = name[:3]
a4e9bd
-            value = eval(value)
a4e9bd
-        vars[name] = value
a4e9bd
-    if template_name == '-':
a4e9bd
-        template_content = sys.stdin.read()
a4e9bd
-        template_name = '<stdin>'
a4e9bd
-    else:
a4e9bd
-        f = open(template_name, 'rb')
a4e9bd
-        template_content = f.read()
a4e9bd
-        f.close()
a4e9bd
-    if options.use_html:
a4e9bd
-        TemplateClass = HTMLTemplate
a4e9bd
-    else:
a4e9bd
-        TemplateClass = Template
a4e9bd
-    template = TemplateClass(template_content, name=template_name)
a4e9bd
-    result = template.substitute(vars)
a4e9bd
-    if options.output:
a4e9bd
-        f = open(options.output, 'wb')
a4e9bd
-        f.write(result)
a4e9bd
-        f.close()
a4e9bd
-    else:
a4e9bd
-        sys.stdout.write(result)
a4e9bd
-
a4e9bd
-if __name__ == '__main__':
a4e9bd
-    from paste.util.template import fill_command
a4e9bd
-    fill_command()
a4e9bd
-        
a4e9bd
-