A Stacktrace Puzzle
bugsink.com
bugsink.com
That said:
* Would printing the trace as a tree actually clarify anything?
* Good code almost always minimizes the amount of code in an exception handler.
* `raise_exception()` is generally bad practice; consider `raise get_exception()` if you need something complicated (among others, this allows choice of `from`)
* `raise ... from None` is useful since it's often the most recent stacktrace that's actually relevant.
* `raise ... from e` likely implies that it is the older stacktrace that's most relevant; ideally this would just be a single annotation on a particular frame "exception was replaced here".
python is the only one doing this weird crap
`raise_exception()` in the example is just to ensure a trivial example which still has a stacktrace in the handler (this is not uncommon in reality, though in reality the called function is not typically called with the goal of triggering yet another example)
Could you give an example of what this tree would look like? A stack is entirely linear.
I've never had a problem with Python's stack traces. Certainly better than Java, where the paradigms rely on a ridiculous amount of introspection, reflection, and abstraction, so your stack traces end up being a hundred lines long, with half of them being ".invoke()".
In the long stack trace example, the first two traces start at the same line 277 which, I think, solves part of the problem you've posed. I need to look at the source code for the rest of the stack trace.
Traceback (most recent call last):
File "requests\packages\urllib3\contrib\pyopenssl.py", line 277, in recv_into
return self.connection.recv_into(*args, \*kwargs)
File "OpenSSL\SSL.py", line 1335, in recv_into
self._raise_ssl_error(self._ssl, result)
File "OpenSSL\SSL.py", line 1149, in _raise_ssl_error
raise WantReadError()
OpenSSL.SSL.WantReadError
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "requests\packages\urllib3\contrib\pyopenssl.py", line 277, in recv_into
return self.connection.recv_into(*args, \*kwargs)
File "OpenSSL\SSL.py", line 1335, in recv_into
self._raise_ssl_error(self._ssl, result)
File "OpenSSL\SSL.py", line 1166, in _raise_ssl_error
raise SysCallError(errno, errorcode.get(errno))
OpenSSL.SSL.SysCallError: (10054, 'WSAECONNRESET')*It feels like OP is trying to infer something aboub the source code structure which isn’t to be expected. The second “simple example” is only shown in the same order in stacktrace and code because OP ordered the functions by execution order, but that’s not a given for an actual code base, so you’ll always want to see the context of the exception thrown, and if the exception happened during handling another then you’ll see the full context.
What’s the supposed puzzle here?
My problem is that information is removed from the stacktrace of the first exception, and that this information cannot be constructed by looking at the stacktrace. This makes it so that the first exception's "life story" begins in the middle.
> because OP ordered the functions by execution order
That's coincidental.
I'm arguing that, in the simple example, the execution order can be inferred from the stacktrace alone. Simply by reading top to bottom. This is not true for the first example.
Is python automatically chaining the exceptions itself in a non-explicit manner?
Edit: found the PEP: https://peps.python.org/pep-3134/
You can also disable this behavior using the `from None` syntax.
https://docs.python.org/3/tutorial/errors.html#exception-cha...
If your exception handler throws another exception, then knowing what caused the original exception is really good to know and greatly helps debugging, especially if your code is calling the handling code incorrectly. Without chaining, the root cause of the second exception would be hidden.
If you find the chained exception output confusing, that's literally a skill issue. It's giving you invaluable information. Learn what it's telling you and you can fix bugs far more easily.