Issue32317
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2017-12-13 23:27 by Garrett Berg, last changed 2022-04-11 14:58 by admin. This issue is now closed.
| Messages (4) | |||
|---|---|---|---|
| msg308264 - (view) | Author: Garrett Berg (Garrett Berg) * | Date: 2017-12-13 23:27 | |
# Summary
In python (2 or 3) it should always be valid to have a bare *raise* statement in an exception block.
try:
do_something()
except SomeException:
do_cleanup()
raise # always reraises SomeException with original stack trace
You can see this in the python exception documentation:
https://docs.python.org/2.7/tutorial/errors.html#raising-exceptions
# Reproduction of Failure of invariants
Example python file where this doesn't work:
from __future__ import print_function
import sys
def doclear():
"""This should basically be a noop"""
sys.exc_clear()
try:
1/0
except ZeroDivisionError:
exc = sys.exc_info()[0]
print("first exc:", exc)
assert exc is ZeroDivisionError
try:
1/0
except ZeroDivisionError:
exc = sys.exc_info()[0]
print("second exc (before doclear):", exc)
doclear()
exc = sys.exc_info()[0]
print("second exc (after doclear):", exc)
assert sys.exc_info()[0] is ZeroDivisionError # fails
Running in python2.7 gives:
first exc: <type 'exceptions.ZeroDivisionError'>
second exc (before doclear): <type 'exceptions.ZeroDivisionError'>
second exc (after doclear): None
Traceback (most recent call last):
File "doclear.py", line 23, in <module>
assert sys.exc_info()[0] is ZeroDivisionError # fails
AssertionError
# Conclusion
There seems to be a bug in python 2.7 where it doesn't follow it's own spec.
[sys.exc_clear()](https://docs.python.org/2/library/sys.html#sys.exc_clear)
states:
> This function clears all information relating to the current or last
> exception that occurred in the current thread. After calling this function,
> exc_info() will return three None values until another exception is raised in
> the current thread or *the execution stack returns to a frame where another
> exception is being handled*.
The above code is clear example where sys.exc_clear() is changing the value of
sys.exc_info() *in a different stack frame*, which is against what is stated in
the above docs.
This bug makes it impossible to reraise with a bare raise statement when doing
cleanup that could use sys.exc_clear(). In other words, calling into functions
can *change your local state* with regards to what exception is being handled.
|
|||
| msg308265 - (view) | Author: Garrett Berg (Garrett Berg) * | Date: 2017-12-13 23:29 | |
I forgot to post this:
python --version
Python 2.7.14
|
|||
| msg308269 - (view) | Author: Garrett Berg (Garrett Berg) * | Date: 2017-12-14 00:06 | |
I found a workaround, and probably the reason this has not been detected before:
import sys
def doclear2()
try:
1/0
except ZeroDivisionError:
sys.exc_clear()
try:
1/0
except ZeroDivisionError:
exc = sys.exc_info()[0]
print("third exc (before doclear):", exc)
doclear2()
exc = sys.exc_info()[0]
print("third exc (after doclear):", exc)
assert sys.exc_info()[0] is ZeroDivisionError
The above passes. It seems that being inside a local exception block allows for the spec to hold, only the "local exception" is cleared.
I still think this is a pretty major bug, but it is good to know what the workaround/reason it's "been working" is.
|
|||
| msg381634 - (view) | Author: Irit Katriel (iritkatriel) * ![]() |
Date: 2020-11-22 20:39 | |
sys.exc_clear was removed in Python 3: https://docs.python.org/3/whatsnew/3.0.html#index-22 |
|||
| History | |||
|---|---|---|---|
| Date | User | Action | Args |
| 2022-04-11 14:58:55 | admin | set | github: 76498 |
| 2020-11-22 20:39:24 | iritkatriel | set | status: open -> closed nosy: + iritkatriel messages: + msg381634 resolution: out of date stage: resolved |
| 2017-12-14 00:06:27 | Garrett Berg | set | messages: + msg308269 |
| 2017-12-13 23:29:50 | Garrett Berg | set | messages: + msg308265 |
| 2017-12-13 23:27:44 | Garrett Berg | create | |
