You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Oct 23, 2023. It is now read-only.
Hello! I'm a maintainer of Trio, an async library for Python. (Think of it as an alternative to asyncio.) We just had someone try to use it and raven together, and they ran into some issues: python-trio/trio#469
I'm not sure what the best way to fix these is, and it might require changes on both sides, so I figured it was worth starting a conversation. There are a few different things.
Raven crashes when trying to process Trio program stack frames
Trio, for extremely annoying reasons related to limitations in the Python interpreter and control-C handling, has to store some metadata inside magic invisible local variables that it injects into some user frames [1]. In order to make sure that we don't collide with actual user local variables, we do this by storing an entry in the frame.f_locals dict with a unique sentinel object as its key, i.e., it's a local variable but its name is not a string. Yeah, Python allows this – who knew, right? Well... apparently raven didn't :-). It makes raven crash. On the one hand, trio's behavior here is The Right Thing, and raven might want to be robust against this in general, given that Python does allow it. OTOH, if raven is breaking then probably other things will also break, and maybe I should just hold my nose and name the variable __trio_internal_value_please_ignore_this_and_dont_name_any_variables_like_this_awoiehiuawf or whatever.
Trio's extensions to Python's extension semantics cause problems with/for Raven
This is more interesting. Trio is a concurrency library, and one of the fun advantages of concurrent programs over sequential programs is that in a concurrency library, you can have multiple things crash at the same time. Most (all other?) concurrency libraries for Python handle this by throwing some exceptions on the floor and hoping for the best. (I guess you of all projects are aware of this!) Trio doesn't throw exceptions on the floor; instead it arranges its tasks in a tree, and if one task doesn't handle an exception it flows up into its parent task, etc., until it eventually hits the root of the whole program. But this means that you have to handle the case where two exceptions are propagating independently and bump into each other. Python... doesn't really have any way to handle this, but we refuse to throw exceptions on the floor, so we're stuck hacking around Python's limitations.
Basically the way we do this is with a MultiError class, which is an Exception class that acts as a container for other exceptions. It's actually fairly sophisticated: you can have MultiErrors nested inside each other, and the nested structure represents the nested structure of the task tree, we have mechanisms to catch "part of" a MultiError and trio knows how to fix up tracebacks afterwards, we can print tracebacks in a nice way where we first show the path that each exception took independently, then as they join up we switch to printing the common traceback together, etc.
One issue this causes is that trio and raven both want to take over sys.excepthook. They're both written carefully, but since trio actually wants to replace the default handling of tracebacks, it can't do raven's trick of falling back on the regular hook; instead, if it detects that some other library has been missing with sys.excepthook, then it prints a warning and skips installing its hook. End result: if you import raven, trio, then exception printing is broken. import trio, raven OTOH works fine. It'd be nice if we could coordinate to make this work both ways. Any ideas?
The other deeper issue is obviously raven has no idea about these special MultiError objects, so even after we get excepthook to run, it won't actually be able to report proper traceback information. Ideally raven should be peeking inside to gather up the full traceback tree, especially since the topmost MultiError's traceback will usually be the boring stuff you already knew like "oh the error happened underneath main that's nice good to know", and it's the embedded exceptions that have the information on the frames where the errors actually occurred.
Just a quick comment about storing integers in f_locals: is not a great idea because it requires that the dict representation allows non string keys which is normally not the case.
@mitsuhiko You mean, you lose the optimization for dicts where all the keys are strings? I guess that's true, but I don't think it matters – the f_locals dict isn't used at all during normal execution.
I ran into the LOCALS_KEY_KI_PROTECTION_ENABLED :/ I thought maybe we can adapt the JSON encoder to deal with this, but it doesn't seem possible to change it's behaviour with any default types (in this case, a dict).
Hello! I'm a maintainer of Trio, an async library for Python. (Think of it as an alternative to asyncio.) We just had someone try to use it and raven together, and they ran into some issues: python-trio/trio#469
I'm not sure what the best way to fix these is, and it might require changes on both sides, so I figured it was worth starting a conversation. There are a few different things.
Raven crashes when trying to process Trio program stack frames
Trio, for extremely annoying reasons related to limitations in the Python interpreter and control-C handling, has to store some metadata inside magic invisible local variables that it injects into some user frames [1]. In order to make sure that we don't collide with actual user local variables, we do this by storing an entry in the
frame.f_locals
dict with a unique sentinel object as its key, i.e., it's a local variable but its name is not a string. Yeah, Python allows this – who knew, right? Well... apparently raven didn't :-). It makes raven crash. On the one hand, trio's behavior here is The Right Thing, and raven might want to be robust against this in general, given that Python does allow it. OTOH, if raven is breaking then probably other things will also break, and maybe I should just hold my nose and name the variable__trio_internal_value_please_ignore_this_and_dont_name_any_variables_like_this_awoiehiuawf
or whatever.Trio's extensions to Python's extension semantics cause problems with/for Raven
This is more interesting. Trio is a concurrency library, and one of the fun advantages of concurrent programs over sequential programs is that in a concurrency library, you can have multiple things crash at the same time. Most (all other?) concurrency libraries for Python handle this by throwing some exceptions on the floor and hoping for the best. (I guess you of all projects are aware of this!) Trio doesn't throw exceptions on the floor; instead it arranges its tasks in a tree, and if one task doesn't handle an exception it flows up into its parent task, etc., until it eventually hits the root of the whole program. But this means that you have to handle the case where two exceptions are propagating independently and bump into each other. Python... doesn't really have any way to handle this, but we refuse to throw exceptions on the floor, so we're stuck hacking around Python's limitations.
Basically the way we do this is with a
MultiError
class, which is anException
class that acts as a container for other exceptions. It's actually fairly sophisticated: you can haveMultiError
s nested inside each other, and the nested structure represents the nested structure of the task tree, we have mechanisms to catch "part of" aMultiError
and trio knows how to fix up tracebacks afterwards, we can print tracebacks in a nice way where we first show the path that each exception took independently, then as they join up we switch to printing the common traceback together, etc.There's a simple example here to give you the idea.
Of course, raven has no idea about any of this.
One issue this causes is that trio and raven both want to take over
sys.excepthook
. They're both written carefully, but since trio actually wants to replace the default handling of tracebacks, it can't do raven's trick of falling back on the regular hook; instead, if it detects that some other library has been missing withsys.excepthook
, then it prints a warning and skips installing its hook. End result: if youimport raven, trio
, then exception printing is broken.import trio, raven
OTOH works fine. It'd be nice if we could coordinate to make this work both ways. Any ideas?The other deeper issue is obviously raven has no idea about these special
MultiError
objects, so even after we getexcepthook
to run, it won't actually be able to report proper traceback information. Ideally raven should be peeking inside to gather up the full traceback tree, especially since the topmostMultiError
's traceback will usually be the boring stuff you already knew like "oh the error happened underneathmain
that's nice good to know", and it's the embedded exceptions that have the information on the frames where the errors actually occurred.I'd love to hear any thoughts here.
[1] If you really want to know the details, there's an exhaustive/exhausting blog post here, and bpo-12857 is the upstream issue that I'm hoping we might be able to fix for 3.8. But in the mean time we're stuck with this.
The text was updated successfully, but these errors were encountered: