Draft Processor Exception Handling - #173
ianjosephwilson wants to merge 4 commits into
Conversation
|
@davepeck Excluding what was removed I think this is the last (!) part of #145. After this .. world wide fame and acclaim. You might have to run it to see the notes/chained exceptions to get a feel for it. There are a lot of small decisions we might still need to make. Maybe I can try to comment on the PR and point those out. |
richly deserved |
| iter_index=iter_index, | ||
| values_index=values_index, | ||
| ) | ||
| for iter_index, v in enumerate(value) |
There was a problem hiding this comment.
A lot of the exception handling only affects what happens after an exception has occurred but to track which value in a sequence actually failed here we need to know the index so we wrap it in enumerate. Depending on how we handle error reporting this could help us better track down the exact location of the error and fits in with the other tracking concepts but does add overhead (minimal though?).
| tattrs = _resolve_t_attrs(attrs, template.interpolations) | ||
| except ProcessingError: # @TODO: Is there a native way to guard this? | ||
| raise | ||
| except Exception as e: |
There was a problem hiding this comment.
When an exception "breaks through" in the processor the traceback is almost gibberish. So we just catch everything here and then chain it with from e. The chaining prevents us from masking/shadowing the original exception but still allows us to add more contextual info for error messages.
There was a problem hiding this comment.
We try to keep the wrapping as close to the user code as possible so 1. we don't also chain our own bugs as if they were user bugs and 2. we can provide better contextual information about where the exception took place (ie. instead of in the Processor at line XYZ it is in attributes of component {LoginForm} in the template at line 3 offset 15 or whatever).
| e.last_tnode = None | ||
| e.values_index = None | ||
| e.iter_index = None | ||
| raise |
There was a problem hiding this comment.
Once we have wrapped an exception or started an exception we try to keep the SAME exception so we don't lose the traceback. This means we use it as a scratchpad to push/pop info as we go in and out of templates reversing our descent back up to the original html() call. We keep that info in a list. This has a few weird quirks but seems to work, like keeping the innermost tnode where the exception occurred.
| except ProcessingError as e: | ||
| if e.last_tnode is None: | ||
| e.last_tnode = tnode | ||
| raise |
There was a problem hiding this comment.
This is subtle but we check if we set a tnode or not so that we only set this tnode one time getting the "innermost tnode". If we just set it everytime we'd overwrite that tnode as we ascending into each parent. We also don't want to pass the tnode down into every single leaf method, so we don't have the innermost tnode at the first raise, so we have to catch the exception on the way back up and tag the tnode on it so we can pull it off again once we are at the root of the template.
| else: | ||
| starttag_pos_msg = "unknown location" # source_pos is optional right now | ||
|
|
||
| e.add_note(f"Error occurred at {starttag_repr} at {starttag_pos_msg}.") |
There was a problem hiding this comment.
One sort of confusing issue is how/if at all do we identify a template to the user of tdom? Templates can be "anonymous", ie. Template(""), which is useless for tracking but is there a way to get "identity" information from a static t-string? Sort of like using inspect(python_func) to find the code and the file name, etc.? I think I've asked this before but I don't remember what the answer was. We'd like to say "Error occurred at ... at ... in Template {TEMPLATE IDENTITY}".
|
@davepeck I added some notes. Let me know what you think whenever you get a chance. |
ProcessingError(s)TNodetemplateandTTree(contains the templateTNode root) to the exceptionchainthem withraise ProcessingError() from eTemplateProcessor.process()we should have a singleProcessingErrorthat has a stack of error states that go down until we reach the lowest levelTemplatewhere an error first occurred so we could print something LIKE this (TBD):