Changeset 3787:530e451465fe in livinglogic.python.xist

Show
Ignore:
Timestamp:
06/02/09 18:16:29 (10 years ago)
Author:
Walter Doerwald <walter@…>
Parents:
3784:41a00830261e (diff), 3786:56fb60cd4cef (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Branch:
default
Message:

Merged in 3.6.5.

Files:
1 removed
7 modified

Legend:

Unmodified
Added
Removed
  • .hgtags

    r3722 r3787  
    9797d9a14e80684295d5906ef3bdfdd09ea665b25e77 rel-3-6-3 
    989897cae619a257dba4892c72d36d100d3fdd65fa99 rel-3-6-4 
     993aa5a5d89287166b52a746e030431a2a068fbc7c rel-3-6-5 
  • .hgtags

    r3786 r3787  
    96965fb0da6822fdbaae3677a3c48d5352096b96ab1a rel-3-6-2 
    9797d9a14e80684295d5906ef3bdfdd09ea665b25e77 rel-3-6-3 
     9897cae619a257dba4892c72d36d100d3fdd65fa99 rel-3-6-4 
    98993aa5a5d89287166b52a746e030431a2a068fbc7c rel-3-6-5 
  • NEWS.rst

    r3784 r3787  
    4646*   Fixed a bug in the remote :meth:`stat` method for ssh URLs (it seems that 
    4747    the :class:`posix.stat_result` tuple objects can no longer be pickled). 
     48 
     49 
     50Changes in 3.6.5 (released 06/02/2009) 
     51-------------------------------------- 
     52 
     53*   Fix UL4 templates that produce no output: As the generated Python sourcecode 
     54    didn't contain any ``yield`` statements, the resulting function was an 
     55    ordinary function instead of a generator. 
    4856 
    4957 
  • NEWS.rst

    r3785 r3787  
     1Changes in 3.7 (released ??/??/2009) 
     2------------------------------------ 
     3 
     4*   In UL4 templates it's now possible to define locale templates via 
     5    ``<?def tmpl?>templatecode<?end def?>``. 
     6 
     7*   Python 2.6 is required now. 
     8 
     9*   :mod:`ll.make` has a new Action class: :class:`ObjectAction` that simply 
     10    returns an existing object. 
     11 
     12*   The following classes have been removed from :mod:`ll.make`: 
     13    :class:`EncodeAction`, :class:`DecodeAction`, :class:`EvalAction`, 
     14    :class:`GZipAction`, :class:`GUnzipAction`, 
     15    :class:`JavascriptMinifyAction`, :class:`XISTBytesAction`, 
     16    :class:`XISTStringAction`, :class:`JoinAction`, :class:`UnpickleAction`, 
     17    :class:`PickleAction`, :class:`TOXICAction`, :class:`TOXICPrettifyAction`, 
     18    :class:`SplatAction`, :class:`UL4CompileAction`, :class:`UL4RenderAction`, 
     19    :class:`UL4DumpAction`, :class:`UL4LoadAction`, :class:`XISTTextAction` and 
     20    :class:`XISTConvertAction`. All of these actions can be executed by using 
     21    :class:`CallAction` or :class:`CallAttrAction`. 
     22 
     23*   :class:`ll.make.PipeAction` has been renamed to :class:`TransformAction`. 
     24 
     25*   The new :class:`ll.make.PipeAction` pipes the input through an external 
     26    command. 
     27 
     28*   :class:`ll.make.FileAction` now automatically wraps the :var:`key` argument 
     29    into an :class:`URL` object. 
     30 
     31*   :class:`ll.make.FileAction` has two new methods :meth:`chmod` and 
     32    :meth:`chown` that return a :class:`ModeAction` and :class:`OwnerAction` 
     33    for modifying the file created by the :class:`FileAction`. 
     34 
     35*   The division operator is no longer implemented for :class:`Action` objects 
     36    in :mod:`ll.make`. 
     37 
     38*   Two new UL4 functions have been added: ``float`` and ``iscolor``. 
     39 
     40*   Two new scripts have been added: ``uls`` can be used to list any directory 
     41    given as an URL. ``ucat`` can be used to output any file or directory. 
     42 
     43*   The script ``ucp`` now changes the user and group only if a user or group is 
     44    given. 
     45 
     46*   Fixed a bug in the remote :meth:`stat` method for ssh URLs (it seems that 
     47    the :class:`posix.stat_result` tuple objects can no longer be pickled). 
     48 
     49 
    150Changes in 3.6.5 (released 06/02/2009) 
    251-------------------------------------- 
  • setup.py

    r3785 r3787  
    2020'object oriented XSLT'. 
    2121 
    22 XIST also includes the following modules: 
    23  
    24 *   :mod:`ll.astyle` can be used for colored terminal output (via ANSI escape 
    25     sequences). 
    26  
    27 *   :mod:`ll.color` provides classes and functions for handling RGB color values. 
    28     This includes the ability to convert between different color models 
    29     (RGB, HSV, HLS) as well as to and from CSS format, and several functions 
    30     for modifying and mixing colors. 
     22XIST also includes the following modules and packages: 
    3123 
    3224*   :mod:`ll.make` is an object oriented make replacement. Like make it allows 
     
    3426    when files don't exist or are out of date with respect to one 
    3527    of their sources. But unlike make you can do this in a object oriented 
    36     way and targets are not only limited to files, but you can implement 
    37     e.g. dependencies on database records. 
    38  
    39 *   :mod:`ll.misc` provides several small utility functions and classes. 
    40  
    41 *   :mod:`ll.sisyphus` provides classes for running Python scripts as cron jobs. 
    42  
    43 *   :mod:`ll.daemon` can be used on UNIX to fork a daemon process. 
     28    way and targets are not only limited to files. 
    4429 
    4530*   :mod:`ll.url` provides classes for parsing and constructing RFC 2396 
    4631    compliant URLs. 
     32 
     33*   :mod:`ll.orasql` provides utilities for working with cx_Oracle_: 
     34 
     35    -   It allows calling functions and procedures with keyword arguments. 
     36 
     37    -   Query results will be put into Record objects, where database fields 
     38        are accessible as object attributes. 
     39 
     40    -   The :class:`Connection` class provides methods for iterating through the 
     41        database metadata. 
     42 
     43    -   Importing the modules adds support for URLs with the scheme ``oracle`` to 
     44        :mod:`ll.url`. 
     45 
     46    .. _cx_Oracle: http://cx-oracle.sourceforge.net/ 
    4747 
    4848*   :mod:`ll.ul4c` is compiler for a templating language with similar capabilities 
     
    5353 
    5454    __ http://www.djangoproject.com/documentation/templates/ 
     55 
     56*   :mod:`ll.astyle` can be used for colored terminal output (via ANSI escape 
     57    sequences). 
     58 
     59*   :mod:`ll.color` provides classes and functions for handling RGB color values. 
     60    This includes the ability to convert between different color models 
     61    (RGB, HSV, HLS) as well as to and from CSS format, and several functions 
     62    for modifying and mixing colors. 
     63 
     64*   :mod:`ll.misc` provides several small utility functions and classes. 
     65 
     66*   :mod:`ll.sisyphus` provides classes for running Python scripts as cron jobs. 
     67 
     68*   :mod:`ll.daemon` can be used on UNIX to fork a daemon process. 
    5569 
    5670*   :mod:`ll.xml_codec` contains a complete codec for encoding and decoding XML. 
     
    101115# TOXIC 
    102116Topic :: Database 
     117 
     118# orasql 
     119Topic :: Database 
    103120""" 
    104121 
     
    116133 
    117134# color 
     135color 
    118136RGB 
    119137HSV 
     
    121139HLS 
    122140CSS 
    123 red 
    124 green 
    125 blue 
    126 hue 
    127 saturation 
    128 value 
    129 brightness 
    130 luminance 
    131141 
    132142# make 
     
    154164XML 
    155165codec 
    156 decoding 
    157166 
    158167# XIST 
     
    176185processing instruction 
    177186PI 
    178 embed 
    179187 
    180188# ul4 
    181189template 
     190templating language 
     191 
     192# orasql 
     193database 
     194Oracle 
     195cx_Oracle 
     196record 
     197procedure 
     198schema 
    182199""" 
    183200 
     
    201218args = dict( 
    202219    name="ll-xist", 
    203     version="3.6.5", 
    204     description="Extensible HTML/XML generator, cross-platform templating language and various other tools", 
     220    version="3.7", 
     221    description="Extensible HTML/XML generator, cross-platform templating language, Oracle utilities and various other tools", 
    205222    long_description=descr, 
    206223    author="Walter Doerwald", 
     
    212229    keywords=", ".join(sorted(set(k.strip() for k in KEYWORDS.strip().splitlines() if k.strip() and not k.strip().startswith("#")))), 
    213230    package_dir={"": "src"}, 
    214     packages=["ll", "ll.scripts", "ll.xist", "ll.xist.ns", "ll.xist.scripts"], 
     231    packages=["ll", "ll.scripts", "ll.xist", "ll.xist.ns", "ll.xist.scripts", "ll.orasql", "ll.orasql.scripts"], 
    215232    ext_modules=[ 
    216233        tools.Extension("ll._url", ["src/ll/_url.c"]), 
     
    222239    entry_points=dict( 
    223240        console_scripts=[ 
     241            "uls = ll.scripts.uls:main", 
    224242            "ucp = ll.scripts.ucp:main", 
     243            "ucat = ll.scripts.ucat:main", 
    225244            "db2ul4 = ll.scripts.db2ul4:main", 
    226245            "dtd2xsc = ll.xist.scripts.dtd2xsc:main", 
     
    228247            "doc2txt = ll.xist.scripts.doc2txt:main", 
    229248            "xml2xsc = ll.xist.scripts.xml2xsc:main", 
     249            "oracreate = ll.orasql.scripts.oracreate:main [oracle]", 
     250            "oradrop = ll.orasql.scripts.oradrop:main [oracle]", 
     251            "oradelete = ll.orasql.scripts.oradelete:main [oracle]", 
     252            "oradiff = ll.orasql.scripts.oradiff:main [oracle]", 
     253            "oramerge = ll.orasql.scripts.oramerge:main [oracle]", 
     254            "oragrant = ll.orasql.scripts.oragrant:main [oracle]", 
     255            "orafind = ll.orasql.scripts.orafind:main [oracle]", 
    230256        ] 
    231257    ), 
    232258    scripts=[ 
     259        "scripts/uls.py", 
    233260        "scripts/ucp.py", 
     261        "scripts/ucat.py", 
    234262        "scripts/db2ul4.py", 
    235263        "scripts/dtd2xsc.py", 
     
    237265        "scripts/doc2txt.py", 
    238266        "scripts/xml2xsc.py", 
     267        "scripts/oracreate.py", 
     268        "scripts/oradrop.py", 
     269        "scripts/oradelete.py", 
     270        "scripts/oradiff.py", 
     271        "scripts/oramerge.py", 
     272        "scripts/oragrant.py", 
     273        "scripts/orafind.py", 
    239274    ], 
    240275    install_requires=[ 
    241276        "cssutils == 0.9.5.1", 
    242277    ], 
     278    extras_require = { 
     279        "oracle":  ["cx_Oracle >= 5.0.1"], 
     280    }, 
    243281    namespace_packages=["ll"], 
    244282    zip_safe=False, 
     283    dependency_links=[ 
     284        "http://sourceforge.net/project/showfiles.php?group_id=84168", # cx_Oracle 
     285    ], 
    245286) 
    246287 
  • src/ll/ul4c.py

    r3780 r3787  
    11281128        self._pythonsource_line(self.lastlocation, "try:") 
    11291129        self.indent += 1 
     1130        # Make sure that the resulting code is a generator even if the byte codes produce no yield statement 
     1131        self._pythonsource_line(self.lastlocation, "if 0: yield ''") 
    11301132        try: 
    11311133            for opcode in self.opcodes: 
  • src/ll/ul4c.py

    r3785 r3787  
    5353 
    5454        :var:`type` 
    55             The tag type (i.e. ``"for"``, ``"if"``, etc.) 
     55            The tag type (i.e. ``"for"``, ``"if"``, etc. or ``None`` for literal 
     56            text) 
    5657 
    5758        :var:`starttag` 
     
    105106    Exception class that wraps another exception and provides a location. 
    106107    """ 
    107     def __init__(self, location, cause): 
     108    def __init__(self, location): 
    108109        self.location = location 
    109         self.cause = cause 
     110        self.__cause__ = None 
    110111 
    111112    def __repr__(self): 
     
    119120            if not path or path[-1] is not exc.location: 
    120121                path.append(exc.location) 
    121             exc = exc.cause 
     122            exc = exc.__cause__ 
    122123        name = exc.__class__.__name__ 
    123124        module = exc.__class__.__module__ 
    124125        if module != "exceptions": 
    125126            name = "%s.%s" % (module, name) 
    126         return "%s %s %s" % (name, "".join("in %s:" % location for location in path), exc) 
     127        return "%s %s %s" % (name, " ".join("in %s:" % location for location in path), exc) 
    127128 
    128129 
     
    449450        :attr:`r2` (which must be a dictionary) will be passed to the template as 
    450451        the variable dictionary. 
     452 
     453    ``"def"`` 
     454        Begin the definition of a local template. The template will be stored in 
     455        the variable named :attr:`arg`. 
     456 
     457    ``"enddef"`` 
     458        End the definition of a local template. 
     459 
    451460    """ 
    452461    __slots__ = ("code", "r1", "r2", "r3", "r4", "r5", "arg", "location", "jump") 
     
    607616        elif self.code == "render": 
    608617            return "render r%r(r%r)" % (self.r1, self.r2) 
     618        elif self.code == "def": 
     619            return "def %s(vars)" % self.arg 
     620        elif self.code == "enddef": 
     621            return "endfor" 
    609622        else: 
    610623            raise UnknownOpcodeError(self.code) 
     
    624637    is a generator) or :meth:`renders` (which returns a string). 
    625638    """ 
    626     version = "10" 
     639    version = "11" 
    627640 
    628641    def __init__(self): 
     
    633646        # The following is used for converting the opcodes back to executable Python code 
    634647        self._pythonfunction = None 
     648        # Stack for currently open def opcodes 
     649        self.defs = [] 
    635650 
    636651    @classmethod 
     
    793808        return "".join(self.iterdump()) 
    794809 
     810    def _pythonsource_line(self, location, line): 
     811        self.lines.append("%s%s" % ("\t"*self.indent, line)) 
     812        if self.lastlocation is not location or not self.locations: 
     813            self.locations.append((location.type, location.starttag, location.endtag, location.startcode, location.endcode)) 
     814            self.lastlocation = location 
     815        self.lines2locs.append(len(self.locations)-1) 
     816 
    795817    def _pythonsource_dispatch_None(self, opcode): 
    796         self.lines.append("%syield %r" % (self.indent, opcode.location.code)) 
    797  
     818        self._pythonsource_line(opcode.location, "yield %r" % opcode.location.code) 
    798819    def _pythonsource_dispatch_loadstr(self, opcode): 
    799         self.lines.append("%sreg%d = %r" % (self.indent, opcode.r1, opcode.arg)) 
    800  
     820        self._pythonsource_line(opcode.location, "r%d = %r" % (opcode.r1, opcode.arg)) 
    801821    def _pythonsource_dispatch_loadint(self, opcode): 
    802         self.lines.append("%sreg%d = %s" % (self.indent, opcode.r1, opcode.arg)) 
    803  
     822        self._pythonsource_line(opcode.location, "r%d = %s" % (opcode.r1, opcode.arg)) 
    804823    def _pythonsource_dispatch_loadfloat(self, opcode): 
    805         self.lines.append("%sreg%d = %s" % (self.indent, opcode.r1, opcode.arg)) 
    806  
     824        self._pythonsource_line(opcode.location, "r%d = %s" % (opcode.r1, opcode.arg)) 
    807825    def _pythonsource_dispatch_loadnone(self, opcode): 
    808         self.lines.append("%sreg%d = None" % (self.indent, opcode.r1)) 
    809  
     826        self._pythonsource_line(opcode.location, "r%d = None" % opcode.r1) 
    810827    def _pythonsource_dispatch_loadfalse(self, opcode): 
    811         self.lines.append("%sreg%d = False" % (self.indent, opcode.r1)) 
    812  
     828        self._pythonsource_line(opcode.location, "r%d = False" % opcode.r1) 
    813829    def _pythonsource_dispatch_loadtrue(self, opcode): 
    814         self.lines.append("%sreg%d = True" % (self.indent, opcode.r1)) 
    815  
     830        self._pythonsource_line(opcode.location, "r%d = True" % opcode.r1) 
    816831    def _pythonsource_dispatch_loaddate(self, opcode): 
    817         self.lines.append("%sreg%d = datetime.datetime(%s)" % (self.indent, opcode.r1, ", ".join(str(int(p)) for p in datesplitter.split(opcode.arg)))) 
    818  
     832        self._pythonsource_line(opcode.location, "r%d = datetime.datetime(%s)" % (opcode.r1, ", ".join(str(int(p)) for p in datesplitter.split(opcode.arg)))) 
    819833    def _pythonsource_dispatch_loadcolor(self, opcode): 
    820         self.lines.append("%sreg%d = color.Color(0x%s, 0x%s, 0x%s, 0x%s)" % (self.indent, opcode.r1, opcode.arg[:2], opcode.arg[2:4], opcode.arg[4:6], opcode.arg[6:])) 
    821  
     834        self._pythonsource_line(opcode.location, "r%d = color.Color(0x%s, 0x%s, 0x%s, 0x%s)" % (opcode.r1, opcode.arg[:2], opcode.arg[2:4], opcode.arg[4:6], opcode.arg[6:])) 
    822835    def _pythonsource_dispatch_buildlist(self, opcode): 
    823         self.lines.append("%sreg%d = []" % (self.indent, opcode.r1)) 
    824  
     836        self._pythonsource_line(opcode.location, "r%d = []" % opcode.r1) 
    825837    def _pythonsource_dispatch_builddict(self, opcode): 
    826         self.lines.append("%sreg%d = {}" % (self.indent, opcode.r1)) 
    827  
     838        self._pythonsource_line(opcode.location, "r%d = {}" % opcode.r1) 
    828839    def _pythonsource_dispatch_addlist(self, opcode): 
    829         self.lines.append("%sreg%d.append(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    830  
     840        self._pythonsource_line(opcode.location, "r%d.append(r%d)" % (opcode.r1, opcode.r2)) 
    831841    def _pythonsource_dispatch_adddict(self, opcode): 
    832         self.lines.append("%sreg%d[reg%d] = reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    833  
     842        self._pythonsource_line(opcode.location, "r%d[r%d] = r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    834843    def _pythonsource_dispatch_updatedict(self, opcode): 
    835         self.lines.append("%sreg%d.update(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    836  
     844        self._pythonsource_line(opcode.location, "r%d.update(r%d)" % (opcode.r1, opcode.r2)) 
    837845    def _pythonsource_dispatch_loadvar(self, opcode): 
    838         self.lines.append("%sreg%d = variables[%r]" % (self.indent, opcode.r1, opcode.arg)) 
    839  
     846        self._pythonsource_line(opcode.location, "r%d = variables[%r]" % (opcode.r1, opcode.arg)) 
    840847    def _pythonsource_dispatch_storevar(self, opcode): 
    841         self.lines.append("%svariables[%r] = reg%d" % (self.indent, opcode.arg, opcode.r1)) 
    842  
     848        self._pythonsource_line(opcode.location, "variables[%r] = r%d" % (opcode.arg, opcode.r1)) 
    843849    def _pythonsource_dispatch_addvar(self, opcode): 
    844         self.lines.append("%svariables[%r] += reg%d" % (self.indent, opcode.arg, opcode.r1)) 
    845  
     850        self._pythonsource_line(opcode.location, "variables[%r] += r%d" % (opcode.arg, opcode.r1)) 
    846851    def _pythonsource_dispatch_subvar(self, opcode): 
    847         self.lines.append("%svariables[%r] -= reg%d" % (self.indent, opcode.arg, opcode.r1)) 
    848  
     852        self._pythonsource_line(opcode.location, "variables[%r] -= r%d" % (opcode.arg, opcode.r1)) 
    849853    def _pythonsource_dispatch_mulvar(self, opcode): 
    850         self.lines.append("%svariables[%r] *= reg%d" % (self.indent, opcode.arg, opcode.r1)) 
    851  
     854        self._pythonsource_line(opcode.location, "variables[%r] *= r%d" % (opcode.arg, opcode.r1)) 
    852855    def _pythonsource_dispatch_truedivvar(self, opcode): 
    853         self.lines.append("%svariables[%r] /= reg%d" % (self.indent, opcode.arg, opcode.r1)) 
    854  
     856        self._pythonsource_line(opcode.location, "variables[%r] /= r%d" % (opcode.arg, opcode.r1)) 
    855857    def _pythonsource_dispatch_floordivvar(self, opcode): 
    856         self.lines.append("%svariables[%r] //= reg%d" % (self.indent, opcode.arg, opcode.r1)) 
    857  
     858        self._pythonsource_line(opcode.location, "variables[%r] //= r%d" % (opcode.arg, opcode.r1)) 
    858859    def _pythonsource_dispatch_modvar(self, opcode): 
    859         self.lines.append("%svariables[%r] %%= reg%d" % (self.indent, opcode.arg, opcode.r1)) 
    860  
     860        self._pythonsource_line(opcode.location, "variables[%r] %%= r%d" % (opcode.arg, opcode.r1)) 
    861861    def _pythonsource_dispatch_delvar(self, opcode): 
    862         self.lines.append("%sdel variables[%r]" % (self.indent, opcode.arg)) 
    863  
     862        self._pythonsource_line(opcode.location, "del variables[%r]" % opcode.arg) 
    864863    def _pythonsource_dispatch_getattr(self, opcode): 
    865         self.lines.append("%sreg%d = reg%d[%r]" % (self.indent, opcode.r1, opcode.r2, opcode.arg)) 
    866  
     864        self._pythonsource_line(opcode.location, "r%d = r%d[%r]" % (opcode.r1, opcode.r2, opcode.arg)) 
    867865    def _pythonsource_dispatch_getitem(self, opcode): 
    868         self.lines.append("%sreg%d = reg%d[reg%d]" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    869  
     866        self._pythonsource_line(opcode.location, "r%d = r%d[r%d]" % (opcode.r1, opcode.r2, opcode.r3)) 
    870867    def _pythonsource_dispatch_getslice12(self, opcode): 
    871         self.lines.append("%sreg%d = reg%d[reg%d:reg%d]" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    872  
     868        self._pythonsource_line(opcode.location, "r%d = r%d[r%d:r%d]" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    873869    def _pythonsource_dispatch_getslice1(self, opcode): 
    874         self.lines.append("%sreg%d = reg%d[reg%d:]" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    875  
     870        self._pythonsource_line(opcode.location, "r%d = r%d[r%d:]" % (opcode.r1, opcode.r2, opcode.r3)) 
    876871    def _pythonsource_dispatch_getslice2(self, opcode): 
    877         self.lines.append("%sreg%d = reg%d[:reg%d]" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    878  
     872        self._pythonsource_line(opcode.location, "r%d = r%d[:r%d]" % (opcode.r1, opcode.r2, opcode.r3)) 
    879873    def _pythonsource_dispatch_print(self, opcode): 
    880         self.lines.append("%sif reg%d is not None: yield unicode(reg%d)" % (self.indent, opcode.r1, opcode.r1)) 
    881  
     874        self._pythonsource_line(opcode.location, "if r%d is not None: yield unicode(r%d)" % (opcode.r1, opcode.r1)) 
    882875    def _pythonsource_dispatch_printx(self, opcode): 
    883         self.lines.append("%sif reg%d is not None: yield xmlescape(unicode(reg%d))" % (self.indent, opcode.r1, opcode.r1)) 
    884  
     876        self._pythonsource_line(opcode.location, "if r%d is not None: yield xmlescape(unicode(r%d))" % (opcode.r1, opcode.r1)) 
    885877    def _pythonsource_dispatch_for(self, opcode): 
    886         self.lines.append("%sfor reg%d in reg%d:" % (self.indent, opcode.r1, opcode.r2)) 
    887         self.indent += "\t" 
    888  
     878        self._pythonsource_line(opcode.location, "for r%d in r%d:" % (opcode.r1, opcode.r2)) 
     879        self.indent += 1 
    889880    def _pythonsource_dispatch_endfor(self, opcode): 
    890881        # we don't have to check for empty loops here, as a ``<?for?>`` tag always generates at least one ``storevar`` opcode inside the loop 
    891         self.indent = self.indent[:-1] 
    892         self.lines.append("%s# end for" % self.indent) 
    893  
     882        self.indent -= 1 
    894883    def _pythonsource_dispatch_break(self, opcode): 
    895         self.lines.append("%sbreak" % self.indent) 
    896  
     884        self._pythonsource_line(opcode.location, "break") 
    897885    def _pythonsource_dispatch_continue(self, opcode): 
    898         self.lines.append("%scontinue" % self.indent) 
    899  
     886        self._pythonsource_line(opcode.location, "continue") 
    900887    def _pythonsource_dispatch_not(self, opcode): 
    901         self.lines.append("%sreg%d = not reg%d" % (self.indent, opcode.r1, opcode.r2)) 
    902  
     888        self._pythonsource_line(opcode.location, "r%d = not r%d" % (opcode.r1, opcode.r2)) 
    903889    def _pythonsource_dispatch_neg(self, opcode): 
    904         self.lines.append("%sreg%d = -reg%d" % (self.indent, opcode.r1, opcode.r2)) 
    905  
     890        self._pythonsource_line(opcode.location, "r%d = -r%d" % (opcode.r1, opcode.r2)) 
    906891    def _pythonsource_dispatch_contains(self, opcode): 
    907         self.lines.append("%sreg%d = reg%d in reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    908  
     892        self._pythonsource_line(opcode.location, "r%d = r%d in r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    909893    def _pythonsource_dispatch_notcontains(self, opcode): 
    910         self.lines.append("%sreg%d = reg%d not in reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    911  
     894        self._pythonsource_line(opcode.location, "r%d = r%d not in r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    912895    def _pythonsource_dispatch_eq(self, opcode): 
    913         self.lines.append("%sreg%d = reg%d == reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    914  
     896        self._pythonsource_line(opcode.location, "r%d = r%d == r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    915897    def _pythonsource_dispatch_ne(self, opcode): 
    916         self.lines.append("%sreg%d = reg%d != reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    917  
     898        self._pythonsource_line(opcode.location, "r%d = r%d != r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    918899    def _pythonsource_dispatch_lt(self, opcode): 
    919         self.lines.append("%sreg%d = reg%d < reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    920  
     900        self._pythonsource_line(opcode.location, "r%d = r%d < r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    921901    def _pythonsource_dispatch_le(self, opcode): 
    922         self.lines.append("%sreg%d = reg%d <= reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    923  
     902        self._pythonsource_line(opcode.location, "r%d = r%d <= r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    924903    def _pythonsource_dispatch_gt(self, opcode): 
    925         self.lines.append("%sreg%d = reg%d > reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    926  
     904        self._pythonsource_line(opcode.location, "r%d = r%d > r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    927905    def _pythonsource_dispatch_ge(self, opcode): 
    928         self.lines.append("%sreg%d = reg%d >= reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    929  
     906        self._pythonsource_line(opcode.location, "r%d = r%d >= r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    930907    def _pythonsource_dispatch_add(self, opcode): 
    931         self.lines.append("%sreg%d = reg%d + reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    932  
     908        self._pythonsource_line(opcode.location, "r%d = r%d + r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    933909    def _pythonsource_dispatch_sub(self, opcode): 
    934         self.lines.append("%sreg%d = reg%d - reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    935  
     910        self._pythonsource_line(opcode.location, "r%d = r%d - r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    936911    def _pythonsource_dispatch_mul(self, opcode): 
    937         self.lines.append("%sreg%d = reg%d * reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    938  
     912        self._pythonsource_line(opcode.location, "r%d = r%d * r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    939913    def _pythonsource_dispatch_floordiv(self, opcode): 
    940         self.lines.append("%sreg%d = reg%d // reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    941  
     914        self._pythonsource_line(opcode.location, "r%d = r%d // r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    942915    def _pythonsource_dispatch_truediv(self, opcode): 
    943         self.lines.append("%sreg%d = reg%d / reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    944  
     916        self._pythonsource_line(opcode.location, "r%d = r%d / r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    945917    def _pythonsource_dispatch_and(self, opcode): 
    946         self.lines.append("%sreg%d = reg%d and reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    947  
     918        self._pythonsource_line(opcode.location, "r%d = r%d and r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    948919    def _pythonsource_dispatch_or(self, opcode): 
    949         self.lines.append("%sreg%d = reg%d or reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    950  
     920        self._pythonsource_line(opcode.location, "r%d = r%d or r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    951921    def _pythonsource_dispatch_mod(self, opcode): 
    952         self.lines.append("%sreg%d = reg%d %% reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    953  
     922        self._pythonsource_line(opcode.location, "r%d = r%d %% r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    954923    def _pythonsource_dispatch_mod(self, opcode): 
    955         self.lines.append("%sreg%d = reg%d %% reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    956  
     924        self._pythonsource_line(opcode.location, "r%d = r%d %% r%d" % (opcode.r1, opcode.r2, opcode.r3)) 
    957925    def _pythonsource_dispatch_callfunc0(self, opcode): 
    958926        try: 
     
    960928        except AttributeError: 
    961929            raise UnknownFunctionError(opcode.arg) 
    962  
    963930    def _pythonsource_dispatch_callfunc1(self, opcode): 
    964931        try: 
     
    966933        except AttributeError: 
    967934            raise UnknownFunctionError(opcode.arg) 
    968  
    969935    def _pythonsource_dispatch_callfunc2(self, opcode): 
    970936        try: 
     
    972938        except AttributeError: 
    973939            raise UnknownFunctionError(opcode.arg) 
    974  
    975940    def _pythonsource_dispatch_callfunc3(self, opcode): 
    976941        try: 
     
    978943        except AttributeError: 
    979944            raise UnknownFunctionError(opcode.arg) 
    980  
    981945    def _pythonsource_dispatch_callfunc4(self, opcode): 
    982946        try: 
     
    984948        except AttributeError: 
    985949            raise UnknownFunctionError(opcode.arg) 
    986  
    987950    def _pythonsource_dispatch_callmeth0(self, opcode): 
    988951        if opcode.arg in ("split", "rsplit", "strip", "lstrip", "rstrip", "upper", "lower", "capitalize", "isoformat", "r", "g", "b", "a", "hls", "hlsa", "hsv", "hsva", "lum"): 
    989             self.lines.append("%sreg%d = reg%d.%s()" % (self.indent, opcode.r1, opcode.r2, opcode.arg)) 
     952            self._pythonsource_line(opcode.location, "r%d = r%d.%s()" % (opcode.r1, opcode.r2, opcode.arg)) 
    990953        elif opcode.arg == "items": 
    991             self.lines.append("%sreg%d = reg%d.iteritems()" % (self.indent, opcode.r1, opcode.r2)) 
     954            self._pythonsource_line(opcode.location, "r%d = r%d.iteritems()" % (opcode.r1, opcode.r2)) 
    992955        elif opcode.arg == "render": 
    993             self.lines.append('%sreg%d = "".join(reg%d())' % (self.indent, opcode.r1, opcode.r2)) 
     956            self._pythonsource_line(opcode.location, 'r%d = "".join(r%d())' % (opcode.r1, opcode.r2)) 
    994957        else: 
    995958            raise UnknownMethodError(opcode.arg) 
    996  
    997959    def _pythonsource_dispatch_callmeth1(self, opcode): 
    998960        if opcode.arg in ("split", "rsplit", "strip", "lstrip", "rstrip", "startswith", "endswith", "find", "get", "withlum", "witha"): 
    999             self.lines.append("%sreg%d = reg%d.%s(reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.arg, opcode.r3)) 
     961            self._pythonsource_line(opcode.location, "r%d = r%d.%s(r%d)" % (opcode.r1, opcode.r2, opcode.arg, opcode.r3)) 
    1000962        elif opcode.arg == "join": 
    1001             self.lines.append("%sreg%d = reg%d.join(unicode(x) for x in reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
     963            self._pythonsource_line(opcode.location, "r%d = r%d.join(unicode(x) for x in r%d)" % (opcode.r1, opcode.r2, opcode.r3)) 
    1002964        elif opcode.arg == "format": 
    1003             self.lines.append("%sreg%d = ul4c._format(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
     965            self._pythonsource_line(opcode.location, "r%d = r%d.__format__(r%d)" % (opcode.r1, opcode.r2, opcode.r3)) 
    1004966        else: 
    1005967            raise UnknownMethodError(opcode.arg) 
    1006  
    1007968    def _pythonsource_dispatch_callmeth2(self, opcode): 
    1008969        if opcode.arg in ("split", "rsplit", "find", "replace", "get"): 
    1009             self.lines.append("%sreg%d = reg%d.%s(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.arg, opcode.r3, opcode.r4)) 
     970            self._pythonsource_line(opcode.location, "r%d = r%d.%s(r%d, r%d)" % (opcode.r1, opcode.r2, opcode.arg, opcode.r3, opcode.r4)) 
    1010971        else: 
    1011972            raise UnknownMethodError(opcode.arg) 
    1012  
    1013973    def _pythonsource_dispatch_callmeth3(self, opcode): 
    1014974        if opcode.arg == "find": 
    1015             self.lines.append("%sreg%d = reg%d.%s(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.arg, opcode.r3, opcode.r4, opcode.r5)) 
     975            self._pythonsource_line(opcode.location, "r%d = r%d.%s(r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.arg, opcode.r3, opcode.r4, opcode.r5)) 
    1016976        else: 
    1017977            raise UnknownMethodError(opcode.arg) 
    1018  
    1019978    def _pythonsource_dispatch_callmethkw(self, opcode): 
    1020979        if opcode.arg == "render": 
    1021             self.lines.append('%sreg%d = "".join(reg%d(**dict((key.encode("utf-8"), value) for (key, value) in reg%d.iteritems())))' % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
     980            self._pythonsource_line(opcode.location, 'r%d = "".join(r%d(**dict((key.encode("utf-8"), value) for (key, value) in r%d.iteritems())))' % (opcode.r1, opcode.r2, opcode.r3)) 
    1022981        else: 
    1023982            raise UnknownMethodError(opcode.arg) 
    1024  
    1025983    def _pythonsource_dispatch_if(self, opcode): 
    1026         self.lines.append("%sif reg%d:" % (self.indent, opcode.r1)) 
    1027         self.indent += "\t" 
    1028  
     984        self._pythonsource_line(opcode.location, "if r%d:" % (opcode.r1)) 
     985        self.indent += 1 
    1029986    def _pythonsource_dispatch_else(self, opcode): 
    1030987        if self.lastopcode == "if": 
    1031988            self.lines[-1] += " pass" 
    1032         self.indent = self.indent[:-1] 
    1033         self.lines.append("%selse:" % self.indent) 
    1034         self.indent += "\t" 
    1035  
     989        self.indent -= 1 
     990        self._pythonsource_line(opcode.location, "else:") 
     991        self.indent += 1 
    1036992    def _pythonsource_dispatch_endif(self, opcode): 
    1037993        if self.lastopcode in ("if", "else"): 
    1038             self.lines[-1] += " pass" 
    1039         self.indent = self.indent[:-1] 
    1040         self.lines.append("%s# end if" % self.indent) 
    1041  
     994            lines[-1] += " pass" 
     995        self.indent -= 1 
     996    def _pythonsource_dispatch_def(self, opcode): 
     997        self._pythonsource_line(opcode.location, "def _(**variables):") 
     998        self.defs.append(opcode) 
     999        self.indent += 1 
     1000        self._pythonsource_line(opcode.location, 'variables = dict((key.decode("utf-8"), value) for (key, value) in variables.iteritems())') # FIXME: This can be dropped in Python 3.0 where strings are unicode 
     1001        self._pythonsource_line(opcode.location, "r0 = r1 = r2 = r3 = r4 = r5 = r6 = r7 = r8 = r9 = None") 
     1002        self._pythonsource_line(opcode.location, "try:") 
     1003        self.indent += 1 
     1004    def _pythonsource_dispatch_enddef(self, opcode): 
     1005        defopcode = self.defs.pop() 
     1006        self.indent -= 1 
     1007        self._pythonsource_line(opcode.location, "except Exception, exc:") 
     1008        self.indent += 1 
     1009        self._pythonsource_line(opcode.location, "newexc = ul4c.Error(ul4c.Location(source, *locations[lines2locs[sys.exc_info()[2].tb_lineno-startline]]))") # FIXME: Use ``raise ... from`` in Python 3.0 
     1010        self._pythonsource_line(opcode.location, "newexc.__cause__ = exc") 
     1011        self._pythonsource_line(opcode.location, "raise newexc") 
     1012        self.indent -= 2 
     1013        self._pythonsource_line(opcode.location, "variables[%r] = _" % defopcode.arg) 
    10421014    def _pythonsource_dispatch_render(self, opcode): 
    1043         self.lines.append('%sfor chunk in reg%d(**dict((key.encode("utf-8"), value) for (key, value) in reg%d.iteritems())): yield chunk' % (self.indent, opcode.r1, opcode.r2)) 
    1044  
     1015        self._pythonsource_line(opcode.location, 'for chunk in r%d(**dict((key.encode("utf-8"), value) for (key, value) in r%d.iteritems())): yield chunk' % (opcode.r1, opcode.r2)) 
    10451016    def _pythonsource_dispatch_callfunc0_now(self, opcode): 
    1046         self.lines.append("%sreg%d = datetime.datetime.now()" % (self.indent, opcode.r1)) 
    1047  
     1017        self._pythonsource_line(opcode.location, "r%d = datetime.datetime.now()" % opcode.r1) 
    10481018    def _pythonsource_dispatch_callfunc0_vars(self, opcode): 
    1049         self.lines.append("%sreg%d = variables" % (self.indent, opcode.r1)) 
    1050  
     1019        self._pythonsource_line(opcode.location, "r%d = variables" % opcode.r1) 
    10511020    def _pythonsource_dispatch_callfunc1_xmlescape(self, opcode): 
    1052         self.lines.append("%sreg%d = xmlescape(unicode(reg%d)) if reg%d is not None else u''" % (self.indent, opcode.r1, opcode.r2, opcode.r2)) 
    1053  
     1021        self._pythonsource_line(opcode.location, "r%d = xmlescape(unicode(r%d)) if r%d is not None else u''" % (opcode.r1, opcode.r2, opcode.r2)) 
    10541022    def _pythonsource_dispatch_callfunc1_csv(self, opcode): 
    1055         self.lines.append("%sreg%d = ul4c._csv(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1056  
     1023        self._pythonsource_line(opcode.location, "r%d = ul4c._csv(r%d)" % (opcode.r1, opcode.r2)) 
    10571024    def _pythonsource_dispatch_callfunc1_json(self, opcode): 
    1058         self.lines.append("%sreg%d = json.dumps(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1059  
     1025        self._pythonsource_line(opcode.location, "r%d = json.dumps(r%d)" % (opcode.r1, opcode.r2)) 
    10601026    def _pythonsource_dispatch_callfunc1_str(self, opcode): 
    1061         self.lines.append("%sreg%d = unicode(reg%d) if reg%d is not None else u''" % (self.indent, opcode.r1, opcode.r2, opcode.r2)) 
    1062  
     1027        self._pythonsource_line(opcode.location, "r%d = unicode(r%d) if r%d is not None else u''" % (opcode.r1, opcode.r2, opcode.r2)) 
    10631028    def _pythonsource_dispatch_callfunc1_int(self, opcode): 
    1064         self.lines.append("%sreg%d = int(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1065  
     1029        self._pythonsource_line(opcode.location, "r%d = int(r%d)" % (opcode.r1, opcode.r2)) 
     1030    def _pythonsource_dispatch_callfunc1_float(self, opcode): 
     1031        self._pythonsource_line(opcode.location, "r%d = float(r%d)" % (opcode.r1, opcode.r2)) 
    10661032    def _pythonsource_dispatch_callfunc1_bool(self, opcode): 
    1067         self.lines.append("%sreg%d = bool(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1068  
     1033        self._pythonsource_line(opcode.location, "r%d = bool(r%d)" % (opcode.r1, opcode.r2)) 
    10691034    def _pythonsource_dispatch_callfunc1_len(self, opcode): 
    1070         self.lines.append("%sreg%d = len(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1071  
     1035        self._pythonsource_line(opcode.location, "r%d = len(r%d)" % (opcode.r1, opcode.r2)) 
    10721036    def _pythonsource_dispatch_callfunc1_enumerate(self, opcode): 
    1073         self.lines.append("%sreg%d = enumerate(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1074  
     1037        self._pythonsource_line(opcode.location, "r%d = enumerate(r%d)" % (opcode.r1, opcode.r2)) 
    10751038    def _pythonsource_dispatch_callfunc1_isnone(self, opcode): 
    1076         self.lines.append("%sreg%d = reg%d is None" % (self.indent, opcode.r1, opcode.r2)) 
    1077  
     1039        self._pythonsource_line(opcode.location, "r%d = r%d is None" % (opcode.r1, opcode.r2)) 
    10781040    def _pythonsource_dispatch_callfunc1_isstr(self, opcode): 
    1079         self.lines.append("%sreg%d = isinstance(reg%d, basestring)" % (self.indent, opcode.r1, opcode.r2)) 
    1080  
     1041        self._pythonsource_line(opcode.location, "r%d = isinstance(r%d, basestring)" % (opcode.r1, opcode.r2)) 
    10811042    def _pythonsource_dispatch_callfunc1_isint(self, opcode): 
    1082         self.lines.append("%sreg%d = isinstance(reg%d, (int, long)) and not isinstance(reg%d, bool)" % (self.indent, opcode.r1, opcode.r2, opcode.r2)) 
    1083  
     1043        self._pythonsource_line(opcode.location, "r%d = isinstance(r%d, (int, long)) and not isinstance(r%d, bool)" % (opcode.r1, opcode.r2, opcode.r2)) 
    10841044    def _pythonsource_dispatch_callfunc1_isfloat(self, opcode): 
    1085         self.lines.append("%sreg%d = isinstance(reg%d, float)" % (self.indent, opcode.r1, opcode.r2)) 
    1086  
     1045        self._pythonsource_line(opcode.location, "r%d = isinstance(r%d, float)" % (opcode.r1, opcode.r2)) 
    10871046    def _pythonsource_dispatch_callfunc1_isbool(self, opcode): 
    1088         self.lines.append("%sreg%d = isinstance(reg%d, bool)" % (self.indent, opcode.r1, opcode.r2)) 
    1089  
     1047        self._pythonsource_line(opcode.location, "r%d = isinstance(r%d, bool)" % (opcode.r1, opcode.r2)) 
    10901048    def _pythonsource_dispatch_callfunc1_isdate(self, opcode): 
    1091         self.lines.append("%sreg%d = isinstance(reg%d, datetime.datetime)" % (self.indent, opcode.r1, opcode.r2)) 
    1092  
     1049        self._pythonsource_line(opcode.location, "r%d = isinstance(r%d, datetime.datetime)" % (opcode.r1, opcode.r2)) 
    10931050    def _pythonsource_dispatch_callfunc1_islist(self, opcode): 
    1094         self.lines.append("%sreg%d = isinstance(reg%d, (list, tuple))" % (self.indent, opcode.r1, opcode.r2)) 
    1095  
     1051        self._pythonsource_line(opcode.location, "r%d = isinstance(r%d, (list, tuple)) and not isinstance(r%d, color.Color)" % (opcode.r1, opcode.r2, opcode.r2)) 
    10961052    def _pythonsource_dispatch_callfunc1_isdict(self, opcode): 
    1097         self.lines.append("%sreg%d = isinstance(reg%d, dict)" % (self.indent, opcode.r1, opcode.r2)) 
    1098  
     1053        self._pythonsource_line(opcode.location, "r%d = isinstance(r%d, dict)" % (opcode.r1, opcode.r2)) 
    10991054    def _pythonsource_dispatch_callfunc1_istemplate(self, opcode): 
    1100         self.lines.append("%sreg%d = hasattr(reg%d, '__call__')" % (self.indent, opcode.r1, opcode.r2)) # this supports normal generators too 
    1101  
     1055        self._pythonsource_line(opcode.location, "r%d = hasattr(r%d, '__call__')" % (opcode.r1, opcode.r2)) # this supports normal generators too 
     1056    def _pythonsource_dispatch_callfunc1_iscolor(self, opcode): 
     1057        self._pythonsource_line(opcode.location, "r%d = isinstance(r%d, color.Color)" % (opcode.r1, opcode.r2)) 
    11021058    def _pythonsource_dispatch_callfunc1_repr(self, opcode): 
    1103         self.lines.append("%sreg%d = ul4c._repr(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1104  
     1059        self._pythonsource_line(opcode.location, "r%d = ul4c._repr(r%d)" % (opcode.r1, opcode.r2)) 
    11051060    def _pythonsource_dispatch_callfunc1_get(self, opcode): 
    1106         self.lines.append("%sreg%d = variables.get(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1107  
     1061        self._pythonsource_line(opcode.location, "r%d = variables.get(r%d)" % (opcode.r1, opcode.r2)) 
    11081062    def _pythonsource_dispatch_callfunc1_chr(self, opcode): 
    1109         self.lines.append("%sreg%d = unichr(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1110  
     1063        self._pythonsource_line(opcode.location, "r%d = unichr(r%d)" % (opcode.r1, opcode.r2)) 
    11111064    def _pythonsource_dispatch_callfunc1_ord(self, opcode): 
    1112         self.lines.append("%sreg%d = ord(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1113  
     1065        self._pythonsource_line(opcode.location, "r%d = ord(r%d)" % (opcode.r1, opcode.r2)) 
    11141066    def _pythonsource_dispatch_callfunc1_hex(self, opcode): 
    1115         self.lines.append("%sreg%d = hex(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1116  
     1067        self._pythonsource_line(opcode.location, "r%d = hex(r%d)" % (opcode.r1, opcode.r2)) 
    11171068    def _pythonsource_dispatch_callfunc1_oct(self, opcode): 
    1118         self.lines.append("%sreg%d = ul4c._oct(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1119  
     1069        self._pythonsource_line(opcode.location, "r%d = ul4c._oct(r%d)" % (opcode.r1, opcode.r2)) 
    11201070    def _pythonsource_dispatch_callfunc1_bin(self, opcode): 
    1121         self.lines.append("%sreg%d = ul4c._bin(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1122  
     1071        self._pythonsource_line(opcode.location, "r%d = bin(r%d)" % (opcode.r1, opcode.r2)) 
    11231072    def _pythonsource_dispatch_callfunc1_sorted(self, opcode): 
    1124         self.lines.append("%sreg%d = sorted(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1125  
     1073        self._pythonsource_line(opcode.location, "r%d = sorted(r%d)" % (opcode.r1, opcode.r2)) 
    11261074    def _pythonsource_dispatch_callfunc1_range(self, opcode): 
    1127         self.lines.append("%sreg%d = xrange(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1128  
     1075        self._pythonsource_line(opcode.location, "r%d = xrange(r%d)" % (opcode.r1, opcode.r2)) 
    11291076    def _pythonsource_dispatch_callfunc1_type(self, opcode): 
    1130         self.lines.append("%sreg%d = ul4c._type(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1131  
     1077        self._pythonsource_line(opcode.location, "r%d = ul4c._type(r%d)" % (opcode.r1, opcode.r2)) 
    11321078    def _pythonsource_dispatch_callfunc1_reversed(self, opcode): 
    1133         self.lines.append("%sreg%d = reversed(reg%d)" % (self.indent, opcode.r1, opcode.r2)) 
    1134  
     1079        self._pythonsource_line(opcode.location, "r%d = reversed(r%d)" % (opcode.r1, opcode.r2)) 
    11351080    def _pythonsource_dispatch_callfunc2_range(self, opcode): 
    1136         self.lines.append("%sreg%d = xrange(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    1137  
     1081        self._pythonsource_line(opcode.location, "r%d = xrange(r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3)) 
    11381082    def _pythonsource_dispatch_callfunc2_get(self, opcode): 
    1139         self.lines.append("%sreg%d = variables.get(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    1140  
     1083        self._pythonsource_line(opcode.location, "r%d = variables.get(r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3)) 
    11411084    def _pythonsource_dispatch_callfunc2_zip(self, opcode): 
    1142         self.lines.append("%sreg%d = itertools.izip(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    1143  
     1085        self._pythonsource_line(opcode.location, "r%d = itertools.izip(r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3)) 
    11441086    def _pythonsource_dispatch_callfunc2_int(self, opcode): 
    1145         self.lines.append("%sreg%d = int(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3)) 
    1146  
     1087        self._pythonsource_line(opcode.location, "r%d = int(r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3)) 
    11471088    def _pythonsource_dispatch_callfunc3_range(self, opcode): 
    1148         self.lines.append("%sreg%d = xrange(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    1149  
     1089        self._pythonsource_line(opcode.location, "r%d = xrange(r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    11501090    def _pythonsource_dispatch_callfunc3_zip(self, opcode): 
    1151         self.lines.append("%sreg%d = itertools.izip(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    1152  
     1091        self._pythonsource_line(opcode.location, "r%d = itertools.izip(r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    11531092    def _pythonsource_dispatch_callfunc3_rgb(self, opcode): 
    1154         self.lines.append("%sreg%d = color.Color.fromrgb(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    1155  
     1093        self._pythonsource_line(opcode.location, "r%d = color.Color.fromrgb(r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    11561094    def _pythonsource_dispatch_callfunc3_hls(self, opcode): 
    1157         self.lines.append("%sreg%d = color.Color.fromhls(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    1158  
     1095        self._pythonsource_line(opcode.location, "r%d = color.Color.fromhls(r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    11591096    def _pythonsource_dispatch_callfunc3_hsv(self, opcode): 
    1160         self.lines.append("%sreg%d = color.Color.fromhsv(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    1161  
     1097        self._pythonsource_line(opcode.location, "r%d = color.Color.fromhsv(r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4)) 
    11621098    def _pythonsource_dispatch_callfunc4_rgb(self, opcode): 
    1163         self.lines.append("%sreg%d = color.Color.fromrgb(reg%d, reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5)) 
    1164  
     1099        self._pythonsource_line(opcode.location, "r%d = color.Color.fromrgb(r%d, r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5)) 
    11651100    def _pythonsource_dispatch_callfunc4_hls(self, opcode): 
    1166         self.lines.append("%sreg%d = color.Color.fromhls(reg%d, reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5)) 
    1167  
     1101        self._pythonsource_line(opcode.location, "r%d = color.Color.fromhls(r%d, r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5)) 
    11681102    def _pythonsource_dispatch_callfunc4_hsv(self, opcode): 
    1169         self.lines.append("%sreg%d = color.Color.fromhsv(reg%d, reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5)) 
     1103        self._pythonsource_line(opcode.location, "r%d = color.Color.fromhsv(r%d, r%d, r%d, r%d)" % (opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5)) 
    11701104 
    11711105    def pythonsource(self, function=None): 
     
    11741108        the code will be wrapped in a function with this name. 
    11751109        """ 
    1176         self.indent = "" 
     1110 
     1111        self.indent = 0 
    11771112        self.lines = [] 
     1113        self.locations = [] 
     1114        self.lines2locs = [] 
     1115        self.lastopcode = None 
     1116        self.lastlocation = Location(self.source, None, 0, 0, 0, 0) 
    11781117 
    11791118        if function is not None: 
    1180             self.lines.append("%sdef %s(**variables):" % (self.indent, function)) 
    1181             self.indent += "\t" 
    1182         self.lines.append("%simport sys, datetime, itertools" % self.indent) 
    1183         self.lines.append("%stry:" % self.indent) 
    1184         self.indent += "\t" 
    1185         self.lines.append("%simport json" % self.indent) 
    1186         self.indent = self.indent[:-1] 
    1187         self.lines.append("%sexcept ImportError:" % self.indent) 
    1188         self.indent += "\t" 
    1189         self.lines.append("%stry:" % self.indent) 
    1190         self.indent += "\t" 
    1191         self.lines.append("%simport simplejson as json" % self.indent) 
    1192         self.indent = self.indent[:-1] 
    1193         self.lines.append("%sexcept ImportError:" % self.indent) 
    1194         self.indent += "\t" 
    1195         self.lines.append("%spass" % self.indent) 
    1196         self.indent = self.indent[:-2] 
    1197         self.lines.append("%sfrom ll.misc import xmlescape" % self.indent) 
    1198         self.lines.append("%sfrom ll import ul4c, color" % self.indent) 
    1199         self.lines.append("%ssource = %r" % (self.indent, self.source)) 
    1200         self.lines.append('%svariables = dict((key.decode("utf-8"), value) for (key, value) in variables.iteritems())' % self.indent) # FIXME: This can be dropped in Python 3.0 where strings are unicode 
    1201         # Make sure that the resulting code is a generator even if the byte codes produces no yield statement 
    1202         self.lines.append("%sif 0: yield ''" % self.indent) 
    1203         locations = [] 
    1204         lines2locs = [] 
    1205         index = -1 
    1206         for oc in self.opcodes: 
    1207             loc = (oc.location.type, oc.location.starttag, oc.location.endtag, oc.location.startcode, oc.location.endcode) 
    1208             if not locations or locations[-1] != loc: 
    1209                 locations.append(loc) 
    1210                 index += 1 
    1211             lines2locs.append(index) 
    1212         locations = tuple(locations) 
    1213         lines2locs = tuple(lines2locs) 
    1214         self.lines.append("%sreg0 = reg1 = reg2 = reg3 = reg4 = reg5 = reg6 = reg7 = reg8 = reg9 = None" % self.indent) 
    1215         self.lines.append("%stry:" % self.indent) 
    1216         self.indent += "\t" 
    1217         self.lines.append("%sstartline = sys._getframe().f_lineno+1" % self.indent) # The source line of the first opcode 
     1119            self._pythonsource_line(self.lastlocation, "def %s(**variables):" % function) 
     1120            self.indent += 1 
     1121            self.lines2locs = [] # We initialize startline one line below, which restarts the counter 
     1122        self._pythonsource_line(self.lastlocation, "import sys, datetime, itertools, json; from ll.misc import xmlescape; from ll import ul4c, color; startline = sys._getframe().f_lineno") # The line number of this line 
     1123        self._pythonsource_line(self.lastlocation, "__1__") 
     1124        self._pythonsource_line(self.lastlocation, "__2__") 
     1125        self._pythonsource_line(self.lastlocation, "source = %r" % self.source) 
     1126        self._pythonsource_line(self.lastlocation, 'variables = dict((key.decode("utf-8"), value) for (key, value) in variables.iteritems())') # FIXME: This can be dropped in Python 3.0 where strings are unicode 
     1127        self._pythonsource_line(self.lastlocation, "r0 = r1 = r2 = r3 = r4 = r5 = r6 = r7 = r8 = r9 = None") 
     1128        self._pythonsource_line(self.lastlocation, "try:") 
     1129        self.indent += 1 
     1130        # Make sure that the resulting code is a generator even if the byte codes produce no yield statement 
     1131        self._pythonsource_line(self.lastlocation, "if 0: yield ''") 
    12181132        try: 
    1219             self.lastopcode = None 
    12201133            for opcode in self.opcodes: 
    1221                 # The following code ensures that each opcode outputs exactly one source code line 
    1222                 # This makes it possible in case of an error to find out which opcode produced the error 
    12231134                try: 
    12241135                    getattr(self, "_pythonsource_dispatch_%s" % opcode.code)(opcode) 
     
    12281139        except Exception, exc: 
    12291140            raise #Error(opcode.location, exc) 
    1230         self.indent = self.indent[:-1] 
    1231         self.lines.append("%sexcept Exception, exc:" % self.indent) 
    1232         self.indent += "\t" 
    1233         self.lines.append("%slocations = %r" % (self.indent, locations)) 
    1234         self.lines.append("%slines2locs = %r" % (self.indent, lines2locs)) 
    1235         self.lines.append("%sraise ul4c.Error(ul4c.Location(source, *locations[lines2locs[sys.exc_info()[2].tb_lineno-startline]]), exc)" % self.indent) 
     1141        self.indent -= 1 
     1142        self._pythonsource_line(self.lastlocation, "except Exception, exc:") 
     1143        self.indent += 1 
     1144        self._pythonsource_line(self.lastlocation, "newexc = ul4c.Error(ul4c.Location(source, *locations[lines2locs[sys.exc_info()[2].tb_lineno-startline]]))") # FIXME: Use ``raise ... from`` in Python 3.0 
     1145        self._pythonsource_line(self.lastlocation, "newexc.__cause__ = exc") 
     1146        self._pythonsource_line(self.lastlocation, "raise newexc") 
     1147        locoffset = 1+int(self.lines[0].strip() != "__1__") 
     1148        self.lines[locoffset] = self.lines[locoffset].replace("__1__", "locations = %r" % (tuple(self.locations),)) 
     1149        self.lines[locoffset+1] = self.lines[locoffset+1].replace("__2__", "lines2locs = %r" % (tuple(self.lines2locs),)) 
    12361150        result = "\n".join(self.lines) 
     1151        del self.lastopcode 
     1152        del self.indent 
    12371153        del self.lines 
    1238         del self.indent 
     1154        del self.locations 
     1155        del self.lines2locs 
    12391156        return result 
    12401157 
     
    12771194        i = 0 
    12781195        for opcode in self.opcodes: 
    1279             if opcode.code in ("else", "endif", "endfor"): 
     1196            if opcode.code in ("else", "endif", "endfor", "enddef"): 
    12801197                i -= 1 
    1281             if opcode.code in ("endif", "endfor"): 
     1198            if opcode.code in ("endif", "endfor", "enddef"): 
    12821199                yield "%s}" % (i*indent) 
    1283             elif opcode.code in ("for", "if"): 
     1200            elif opcode.code in ("for", "if", "def"): 
    12841201                yield "%s%s {" % (i*indent, opcode) 
    12851202            elif opcode.code == "else": 
     
    12871204            else: 
    12881205                yield "%s%s" % (i*indent, opcode) 
    1289             if opcode.code in ("for", "if", "else"): 
     1206            if opcode.code in ("for", "if", "else", "def"): 
    12901207                i += 1 
    12911208 
     
    12981215        or non-tag text. It will be called by :meth:`_compile` internally. 
    12991216        """ 
    1300         pattern = u"%s(printx|print|code|for|if|elif|else|end|break|continue|render|note)(\s*((.|\\n)*?)\s*)?%s" % (re.escape(startdelim), re.escape(enddelim)) 
     1217        pattern = u"%s(printx|print|code|for|if|elif|else|end|break|continue|render|def|note)(\s*((.|\\n)*?)\s*)?%s" % (re.escape(startdelim), re.escape(enddelim)) 
    13011218        pos = 0 
    13021219        for match in re.finditer(pattern, source): 
     
    14011318                            if stack[-1][0] != "for": 
    14021319                                raise BlockError("endfor doesn't match any for") 
     1320                        elif code == "def": 
     1321                            if stack[-1][0] != "def": 
     1322                                raise BlockError("enddef doesn't match any def") 
    14031323                        else: 
    14041324                            raise BlockError("illegal end value %r" % code) 
     
    14071327                        for i in xrange(last[2]): 
    14081328                            self.opcode("endif") 
    1409                     else: # last[0] == "for": 
     1329                    elif last[0] == "for": 
    14101330                        self.opcode("endfor") 
     1331                    else: # last[0] == "def": 
     1332                        self.opcode("enddef") 
    14111333                elif location.type == "for": 
    14121334                    parsefor(self) 
     
    14221344                elif location.type == "render": 
    14231345                    parserender(self) 
     1346                elif location.type == "def": 
     1347                    self.opcode("def", arg=location.code) 
     1348                    stack.append(("def", location)) 
    14241349                else: # Can't happen 
    14251350                    raise ValueError("unknown tag %r" % location.type) 
    14261351            except Exception, exc: 
    1427                 raise Error(location, exc) 
     1352                newexc = Error(location) # FIXME: use ``raise ... from`` in Python 3.0 
     1353                newexc.__cause__ = exc 
     1354                raise newexc 
    14281355            finally: 
    14291356                del self.location 
    14301357        if stack: 
    1431             raise Error(stack[-1][1], BlockError("block unclosed")) 
     1358            newexc = Error(stack[-1][1]) # FIXME: use ``raise ... from`` in Python 3.0 
     1359            newexc.__cause__ = BlockError("block unclosed") 
     1360            raise newexc 
    14321361 
    14331362    def __str__(self): 
     
    19821911                raise UnterminatedStringError() 
    19831912        except Exception, exc: 
    1984             raise Error(location, exc) 
     1913            newexc = Error(location) # FIXME: use ``raise ... from`` in Python 3.0 
     1914            newexc.__cause__ = exc 
     1915            raise newexc 
    19851916        return self.rv 
    19861917 
     
    21522083            return ast.compile(template) 
    21532084        except Exception, exc: 
    2154             raise Error(location, exc) 
     2085            newexc = Error(location) # FIXME: Use ``raise ... from`` in Python 3.0 
     2086            newexc.__cause__ = exc 
     2087            raise newexc 
    21552088        finally: 
    21562089            del template.registers 
     
    25802513### Helper functions used at template runtime 
    25812514### 
    2582  
    2583 def _oct(value): 
    2584     """ 
    2585     Helper for the ``oct`` function. 
    2586     """ 
    2587     if value == 0: 
    2588         return "0o0" 
    2589     elif value < 0: 
    2590         return "-0o" + oct(value)[2:] 
    2591     else: 
    2592         return "0o" + oct(value)[1:] 
    2593  
    2594  
    2595 def _bin(value): 
    2596     """ 
    2597     Helper for the ``bin`` function. 
    2598     """ 
    2599     if value == 0: 
    2600         return "0b0" 
    2601     if value < 0: 
    2602         value = -value 
    2603         prefix = "-0b" 
    2604     else: 
    2605         prefix = "0b" 
    2606     v = [] 
    2607     while value: 
    2608         v.append(str(value&1)) 
    2609         value >>= 1 
    2610     return prefix+"".join(v)[::-1] 
    2611  
    2612  
    2613 def _format(obj, format): 
    2614     """ 
    2615     Helper for the ``format`` method. 
    2616     """ 
    2617     if isinstance(obj, datetime.datetime): 
    2618         if "%f" in format: 
    2619             format = format.replace("%f", "%06d" % obj.microsecond) # FIXME: This would replace "%%f", which is wrong (wait for Python 2.6) 
    2620         return obj.strftime(format.encode("utf-8")) # FIXME: We shouldn't have to encode the format string (wait for Python 3.0) 
    2621     elif obj is None or isinstance(obj, (int, long, float, str, unicode)): 
    2622         from ll import stringformat 
    2623         return stringformat.format_builtin_type(obj, format) 
    2624     else: 
    2625         return obj.format(format) # This will raise an ``AttributeError`` 
    2626  
    26272515 
    26282516def _repr(obj): 
     
    26552543 
    26562544 
     2545def _oct(value): 
     2546        """ 
     2547        Helper for the ``oct`` function. 
     2548        """ 
     2549        if value == 0: 
     2550            return "0o0" 
     2551        elif value < 0: 
     2552            return "-0o" + oct(value)[2:] 
     2553        else: 
     2554            return "0o" + oct(value)[1:] 
     2555 
     2556 
    26572557def _csv(obj): 
    26582558    """ 
     
    26922592    elif hasattr(obj, "__call__"): 
    26932593        return u"template" 
     2594    elif isinstance(obj, color.Color): 
     2595        return u"color" 
    26942596    return None