Exceptions

Exception objects can not be created directly by UL4 templates, but UL4 templates can work with exceptions and access their attributes. The function isexception returns True if the argument is an exception object and exception objects have an attribute context that exposed the __cause__ or __context__ attribute of the Python exception object.

Exceptions that happen in UL4 templates use exception chaining to add information about the location of the error while the exception bubbles up the Python call stack. So the exception will be e.g. a TypeError object and its __cause__ attribute (which is accessible as the UL4 attribute context) specifies the immediate location inside the UL4 source code where the exception happened (and its __cause__ is the location that called that one etc.). So if we have the following UL4 template:

<?def x(i)?>
   Print: <?print 1/i?>
   Render: <?render x(i-1)?>
<?end def?>
Initial render: <?render x(3)?>

Calling the template will result in a ZeroDivisionError exception. We can format a nice UL4 stacktrace (in HTML) for this exception with the following UL4 code:

<?def frame(exc)?>
   <?if exc.context?>
      <?render frame(exc.context)?>
   <?end if?>
   <?if exc.location?>
      <li>
         <p>
            <b><?printx type(exc)?></b>
            in template <b><?printx exc.location.template.name?></b>
            : line <?printx exc.location.line?>
            ; col <?printx exc.location.col?>
         </p>
         <p>
            <?print exc.location.sourceprefix?>
            <b><?print exc.location.source?></b>
            <?print exc.location.sourcesuffix?>
         </p>
      </li>
   <?else?>
      <li>
         <p>
            <b><?printx type(exc).__name__?></b><?if str(exc)?>: <?print str(exc)?><?end if?>
         </p>
      </li>
   <?end if?>
<?end def?>

<ul>
   <?render frame(exc)?>
</ul>