Changeset 3527:6a07195394ff in livinglogic.python.xist

Show
Ignore:
Timestamp:
07/18/08 23:22:00 (11 years ago)
Author:
Walter Doerwald <walter@…>
Branch:
default
Message:

Rewrite exception handing, so that each template render call is wrapped in an exception.

Exception formatting then gives a nice trace of the nested calls in <?render?> tags.

Files:
3 modified

Legend:

Unmodified
Added
Removed
  • NEWS.rst

    r3526 r3527  
    2626    the additional inputs will be output too). :meth:`ll.make.Project.findpaths` 
    2727    has been fixed to work with non-:class:`ll.make.Action` inputs. (This means 
    28     that now you *have* to pass real action to :meth:`findpaths`). 
     28    that now you *have* to pass a real registered target action to 
     29    :meth:`findpaths` not just its key). 
    2930 
    3031*   The missing processing instruction :class:`render` has been added to 
    3132    :mod:`ll.xist.ns.ul4`. 
     33 
     34*   Exception handling in UL4 has been rewritten to allow proper error reporting 
     35    when calling nested templates. 
    3236 
    3337 
  • src/ll/ul4c.py

    r3521 r3527  
    8181    def tag(self): 
    8282        return self.source[self.starttag:self.endtag] 
     83 
     84    def __repr__(self): 
     85        return "<%s.%s %s at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self, id(self)) 
    8386 
    8487    def __str__(self): 
     
    99102class Error(Exception): 
    100103    """ 
    101     base class of all exceptions. 
    102     """ 
    103     def __init__(self, exception=None): 
    104         self.location = None 
    105         self.exception = exception 
     104    Exception class that wraps another exception and provides a location. 
     105    """ 
     106    def __init__(self, location, cause): 
     107        self.location = location 
     108        self.cause = cause 
     109 
     110    def __repr__(self): 
     111        return "<%s.%s in %s at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.location, id(self)) 
    106112 
    107113    def __str__(self): 
    108         return self.format(str(self.exception) if self.exception is not None else "error") 
    109  
    110     def decorate(self, location): 
    111         self.location = location 
    112         return self 
    113  
    114     def format(self, message): 
    115         if self.exception is not None: 
    116             name = self.exception.__class__.__name__ 
    117             module = self.exception.__class__.__module__ 
    118             if module != "exceptions": 
    119                 name = "%s.%s" % (module, name) 
    120             if self.location is not None: 
    121                 return "%s in %s: %s" % (name, self.location, message) 
    122             else: 
    123                 return "%s: %s" % (name, message) 
    124         else: 
    125             if self.location is not None: 
    126                 return "in %s: %s" % (self.location, message) 
    127             else: 
    128                 return message 
    129  
    130  
    131 class LexicalError(Error): 
     114        path = [] 
     115 
     116        exc = self 
     117        while isinstance(exc, Error): 
     118            path.append(str(exc.location)) 
     119            exc = exc.cause 
     120        name = exc.__class__.__name__ 
     121        module = exc.__class__.__module__ 
     122        if module != "exceptions": 
     123            name = "%s.%s" % (module, name) 
     124        return "%s in %s: %s" % (name, ": in ".join(path), exc) 
     125 
     126 
     127class LexicalError(Exception): 
    132128    def __init__(self, start, end, input): 
    133         Error.__init__(self) 
    134129        self.start = start 
    135130        self.end = end 
     
    137132 
    138133    def __str__(self): 
    139         return self.format("Unmatched input %r" % self.input) 
    140  
    141  
    142 class SyntaxError(Error): 
     134        return "Unmatched input %r" % self.input 
     135 
     136 
     137class SyntaxError(Exception): 
    143138    def __init__(self, token): 
    144         Error.__init__(self) 
    145139        self.token = token 
    146140 
    147141    def __str__(self): 
    148         return self.format("Lexical error near %r" % str(self.token)) 
    149  
    150  
    151 class UnterminatedStringError(Error): 
     142        return "Lexical error near %r" % str(self.token) 
     143 
     144 
     145class UnterminatedStringError(Exception): 
    152146    """ 
    153147    Exception that is raised by the parser when a string constant is not 
     
    155149    """ 
    156150    def __str__(self): 
    157         return self.format("Unterminated string") 
    158  
    159  
    160 class BlockError(Error): 
     151        return "Unterminated string" 
     152 
     153 
     154class BlockError(Exception): 
    161155    """ 
    162156    Exception that is raised by the compiler when an illegal block structure is 
     
    165159 
    166160    def __init__(self, message): 
    167         Error.__init__(self) 
    168161        self.message = message 
    169162 
    170163    def __str__(self): 
    171         return self.format(self.message) 
    172  
    173  
    174 class UnknownFunctionError(Error): 
     164        return self.message 
     165 
     166 
     167class UnknownFunctionError(Exception): 
    175168    """ 
    176169    Exception that is raised by the renderer if the function to be executed by 
     
    180173 
    181174    def __init__(self, funcname): 
    182         Error.__init__(self) 
    183175        self.funcname = funcname 
    184176 
    185177    def __str__(self): 
    186         return self.format("function %r unknown" % self.funcname) 
    187  
    188  
    189 class UnknownMethodError(Error): 
     178        return "function %r unknown" % self.funcname 
     179 
     180 
     181class UnknownMethodError(Exception): 
    190182    """ 
    191183    Exception that is raised by the renderer if the method to be executed by the 
     
    195187 
    196188    def __init__(self, methname): 
    197         Error.__init__(self) 
    198189        self.methname = methname 
    199190 
    200191    def __str__(self): 
    201         return self.format("method %r unknown" % self.methname) 
    202  
    203  
    204 class UnknownOpcodeError(Error): 
     192        return "method %r unknown" % self.methname 
     193 
     194 
     195class UnknownOpcodeError(Exception): 
    205196    """ 
    206197    Exception that is raised when an unknown opcode is encountered by the renderer. 
     
    208199 
    209200    def __init__(self, opcode): 
    210         Error.__init__(self) 
    211201        self.opcode = opcode 
    212202 
    213203    def __str__(self): 
    214         return self.format("opcode %r unknown" % self.opcode) 
    215  
    216  
    217 class OutOfRegistersError(Error): 
     204        return "opcode %r unknown" % self.opcode 
     205 
     206 
     207class OutOfRegistersError(Exception): 
    218208    """ 
    219209    Exception that is raised by the compiler when there are no more free 
     
    222212 
    223213    def __str__(self): 
    224         return self.format("out of registers") 
     214        return "out of registers" 
    225215 
    226216 
     
    10221012                    raise UnknownOpcodeError(opcode.code) 
    10231013                lastopcode = opcode.code 
    1024         except Error, exc: 
    1025             exc.decorate(opcode.location) 
    1026             raise 
    10271014        except Exception, exc: 
    1028             raise Error(exc).decorate(opcode.location) 
     1015            raise Error(opcode.location, exc) 
    10291016        indent -= 1 
    10301017        buildloc = "ul4c.Location(source, *locations[sys.exc_info()[2].tb_lineno-startline])" 
    1031         _code("except ul4c.Error, exc:") 
    1032         indent += 1 
    1033         _code("exc.decorate(%s)" % buildloc) 
    1034         _code("raise") 
    1035         indent -= 1 
    10361018        _code("except Exception, exc:") 
    10371019        indent += 1 
    1038         _code("raise ul4c.Error(exc).decorate(%s)" % buildloc) 
     1020        _code("raise ul4c.Error(%s, exc)" % buildloc) 
    10391021        return "\n".join(output) 
    10401022 
    10411023    def pythonfunction(self): 
    10421024        """ 
    1043         Return a Python generator that can be called to render the template. 
    1044         The argument signature of the function will be 
    1045         ``templates={}, **variables``. 
     1025        Return a Python generator that can be called to render the template. The 
     1026        argument signature of the function will be ``templates={}, **variables``. 
    10461027        """ 
    10471028        if self._pythonfunction is None: 
     
    12251206                else: # Can't happen 
    12261207                    raise ValueError("unknown tag %r" % location.type) 
    1227             except Error, exc: 
    1228                 exc.decorate(location) 
    1229                 raise 
    12301208            except Exception, exc: 
    1231                 raise Error(exc).decorate(location) 
     1209                raise Error(location, exc) 
    12321210            finally: 
    12331211                del self.location 
    12341212        if stack: 
    1235             raise BlockError("block unclosed").decorate(stack[-1][1]) 
     1213            raise Error(stack[-1][1], BlockError("block unclosed")) 
    12361214 
    12371215    def __str__(self): 
     
    17301708            if self.mode != "default": 
    17311709                raise UnterminatedStringError() 
    1732         except Error, exc: 
    1733             exc.decorate(location) 
    1734             raise 
    17351710        except Exception, exc: 
    1736             raise Error(exc).decorate(location) 
     1711            raise Error(location, exc) 
    17371712        return self.rv 
    17381713 
     
    18861861            ast = self.parse(self.scanner.tokenize(location)) 
    18871862            return ast.compile(template) 
    1888         except Error, exc: 
    1889             exc.decorate(location) 
    1890             raise 
    18911863        except Exception, exc: 
    1892             raise Error(exc).decorate(location) 
     1864            raise Error(location, exc) 
    18931865        finally: 
    18941866            del template.registers 
  • test/test_ul4.py

    r3521 r3527  
    886886def test_parse(): 
    887887    check('42', '<?print data.Noner?>', data=dict(Noner=42)) 
     888 
     889 
     890def test_nested_exceptions(): 
     891    tmpl1 = ul4c.compile("<?print 2*x?>") 
     892    tmpl2 = ul4c.compile("<?render tmpl1(x=x)?>") 
     893    tmpl3 = ul4c.compile("<?render tmpl2(x=x)?>") 
     894 
     895    checkrunerror(r"TypeError .*render tmpl3.*render tmpl2.*render tmpl1.*print 2.*unsupported operand type", "<?render tmpl3(x=x)?>", dict(tmpl1=tmpl1, tmpl2=tmpl2, tmpl3=tmpl3), x=None)