Skip to content

Make {{ obj.bad_attr }} preserve the AttributeError's traceback #2103

@encukou

Description

@encukou

Environment.getattr() and Environment.getitem() discard errors
from (TypeError, LookupError, AttributeError) and return Undefined instead.

When the Undefined raises UndefinedError, the original exception,
and its traceback, is lost. This makes debugging hard.

My use case

The CPython buildbot release dashboard
uses lazily computed attributes (with cached_property).
In other words, “business logic” is driven by the template rendering.
This avoids additional logic to pre-determine which data the template will need.
Unfortunately, any AttributeError that occurs deep in application code bubbles
up and is replaced by a traceback-less UndefinedError.

Possible workarounds outside Jinja

A hacky workaround is a decorator that replaces AttributeError by a different kind of error.
(The linked code doesn't use it -- I apply it only when debugging.)

It is also possible to subclass Undefined to preserve the exception:

class StrictUndefinedWithCause(jinja2.StrictUndefined):
    __slots__ = ['_original_exception']
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._original_exception = sys.exception()

    def _fail_with_undefined_error(self, *args, **kwargs):
        try:
            super()._fail_with_undefined_error(*args, **kwargs)
        except BaseException as exc:
            raise exc from self._original_exception

This would work well if Environment.getitem and Environment.getattr
always called their self.undefined(...) inside except blocks.
That's not always the case -- and that's the main reason I can't find
a good solution without changes in Jinja.

I suggest that Jinja itself adds the relevant tracebacks when it raises
UndefinedError, and adds tests to ensure it does that.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions