mirror of
https://github.com/python/cpython.git
synced 2025-10-03 13:45:29 +00:00
#3021: Antoine Pitrou's Lexical exception handlers
This commit is contained in:
parent
e8465f2b41
commit
eec3d71379
17 changed files with 410 additions and 303 deletions
|
@ -397,6 +397,14 @@ Miscellaneous opcodes.
|
||||||
denoting nested loops, try statements, and such.
|
denoting nested loops, try statements, and such.
|
||||||
|
|
||||||
|
|
||||||
|
.. opcode:: POP_EXCEPT ()
|
||||||
|
|
||||||
|
Removes one block from the block stack. The popped block must be an exception
|
||||||
|
handler block, as implicitly created when entering an except handler.
|
||||||
|
In addition to popping extraneous values from the frame stack, the
|
||||||
|
last three popped values are used to restore the exception state.
|
||||||
|
|
||||||
|
|
||||||
.. opcode:: END_FINALLY ()
|
.. opcode:: END_FINALLY ()
|
||||||
|
|
||||||
Terminates a :keyword:`finally` clause. The interpreter recalls whether the
|
Terminates a :keyword:`finally` clause. The interpreter recalls whether the
|
||||||
|
@ -412,24 +420,22 @@ Miscellaneous opcodes.
|
||||||
|
|
||||||
.. opcode:: WITH_CLEANUP ()
|
.. opcode:: WITH_CLEANUP ()
|
||||||
|
|
||||||
Cleans up the stack when a :keyword:`with` statement block exits. On top of
|
Cleans up the stack when a :keyword:`with` statement block exits. TOS is
|
||||||
the stack are 1--3 values indicating how/why the finally clause was entered:
|
the context manager's :meth:`__exit__` bound method. Below TOS are 1--3
|
||||||
|
values indicating how/why the finally clause was entered:
|
||||||
|
|
||||||
* TOP = ``None``
|
* SECOND = ``None``
|
||||||
* (TOP, SECOND) = (``WHY_{RETURN,CONTINUE}``), retval
|
* (SECOND, THIRD) = (``WHY_{RETURN,CONTINUE}``), retval
|
||||||
* TOP = ``WHY_*``; no retval below it
|
* SECOND = ``WHY_*``; no retval below it
|
||||||
* (TOP, SECOND, THIRD) = exc_info()
|
* (SECOND, THIRD, FOURTH) = exc_info()
|
||||||
|
|
||||||
Under them is EXIT, the context manager's :meth:`__exit__` bound method.
|
In the last case, ``TOS(SECOND, THIRD, FOURTH)`` is called, otherwise
|
||||||
|
``TOS(None, None, None)``. In addition, TOS is removed from the stack.
|
||||||
|
|
||||||
In the last case, ``EXIT(TOP, SECOND, THIRD)`` is called, otherwise
|
If the stack represents an exception, *and* the function call returns
|
||||||
``EXIT(None, None, None)``.
|
a 'true' value, this information is "zapped" and replaced with a single
|
||||||
|
``WHY_SILENCED`` to prevent ``END_FINALLY`` from re-raising the exception.
|
||||||
EXIT is removed from the stack, leaving the values above it in the same
|
(But non-local gotos will still be resumed.)
|
||||||
order. In addition, if the stack represents an exception, *and* the function
|
|
||||||
call returns a 'true' value, this information is "zapped", to prevent
|
|
||||||
``END_FINALLY`` from re-raising the exception. (But non-local gotos should
|
|
||||||
still be resumed.)
|
|
||||||
|
|
||||||
.. XXX explain the WHY stuff!
|
.. XXX explain the WHY stuff!
|
||||||
|
|
||||||
|
|
|
@ -94,17 +94,6 @@ attributes:
|
||||||
| | f_code | code object being |
|
| | f_code | code object being |
|
||||||
| | | executed in this frame |
|
| | | executed in this frame |
|
||||||
+-----------+-----------------+---------------------------+
|
+-----------+-----------------+---------------------------+
|
||||||
| | f_exc_traceback | traceback if raised in |
|
|
||||||
| | | this frame, or ``None`` |
|
|
||||||
+-----------+-----------------+---------------------------+
|
|
||||||
| | f_exc_type | exception type if raised |
|
|
||||||
| | | in this frame, or |
|
|
||||||
| | | ``None`` |
|
|
||||||
+-----------+-----------------+---------------------------+
|
|
||||||
| | f_exc_value | exception value if raised |
|
|
||||||
| | | in this frame, or |
|
|
||||||
| | | ``None`` |
|
|
||||||
+-----------+-----------------+---------------------------+
|
|
||||||
| | f_globals | global namespace seen by |
|
| | f_globals | global namespace seen by |
|
||||||
| | | this frame |
|
| | | this frame |
|
||||||
+-----------+-----------------+---------------------------+
|
+-----------+-----------------+---------------------------+
|
||||||
|
|
|
@ -136,8 +136,8 @@ always available.
|
||||||
frame is not handling an exception, the information is taken from the calling
|
frame is not handling an exception, the information is taken from the calling
|
||||||
stack frame, or its caller, and so on until a stack frame is found that is
|
stack frame, or its caller, and so on until a stack frame is found that is
|
||||||
handling an exception. Here, "handling an exception" is defined as "executing
|
handling an exception. Here, "handling an exception" is defined as "executing
|
||||||
or having executed an except clause." For any stack frame, only information
|
an except clause." For any stack frame, only information about the exception
|
||||||
about the most recently handled exception is accessible.
|
being currently handled is accessible.
|
||||||
|
|
||||||
.. index:: object: traceback
|
.. index:: object: traceback
|
||||||
|
|
||||||
|
|
|
@ -875,19 +875,14 @@ Internal types
|
||||||
|
|
||||||
.. index::
|
.. index::
|
||||||
single: f_trace (frame attribute)
|
single: f_trace (frame attribute)
|
||||||
single: f_exc_type (frame attribute)
|
|
||||||
single: f_exc_value (frame attribute)
|
|
||||||
single: f_exc_traceback (frame attribute)
|
|
||||||
single: f_lineno (frame attribute)
|
single: f_lineno (frame attribute)
|
||||||
|
|
||||||
Special writable attributes: :attr:`f_trace`, if not ``None``, is a function
|
Special writable attributes: :attr:`f_trace`, if not ``None``, is a function
|
||||||
called at the start of each source code line (this is used by the debugger);
|
called at the start of each source code line (this is used by the debugger);
|
||||||
:attr:`f_exc_type`, :attr:`f_exc_value`, :attr:`f_exc_traceback` represent the
|
:attr:`f_lineno` is the current line number of the frame --- writing to this
|
||||||
last exception raised in the parent frame provided another exception was ever
|
from within a trace function jumps to the given line (only for the bottom-most
|
||||||
raised in the current frame (in all other cases they are None); :attr:`f_lineno`
|
frame). A debugger can implement a Jump command (aka Set Next Statement)
|
||||||
is the current line number of the frame --- writing to this from within a trace
|
by writing to f_lineno.
|
||||||
function jumps to the given line (only for the bottom-most frame). A debugger
|
|
||||||
can implement a Jump command (aka Set Next Statement) by writing to f_lineno.
|
|
||||||
|
|
||||||
Traceback objects
|
Traceback objects
|
||||||
.. index::
|
.. index::
|
||||||
|
|
|
@ -27,13 +27,13 @@ typedef struct _frame {
|
||||||
PyObject **f_stacktop;
|
PyObject **f_stacktop;
|
||||||
PyObject *f_trace; /* Trace function */
|
PyObject *f_trace; /* Trace function */
|
||||||
|
|
||||||
/* If an exception is raised in this frame, the next three are used to
|
/* In a generator, we need to be able to swap between the exception
|
||||||
* record the exception info (if any) originally in the thread state. See
|
state inside the generator and the exception state of the calling
|
||||||
* comments before set_exc_info() -- it's not obvious.
|
frame (which shouldn't be impacted when the generator "yields"
|
||||||
* Invariant: if _type is NULL, then so are _value and _traceback.
|
from an except handler).
|
||||||
* Desired invariant: all three are NULL, or all three are non-NULL. That
|
These three fields exist exactly for that, and are unused for
|
||||||
* one isn't currently true, but "should be".
|
non-generator frames. See the SAVE_EXC_STATE and SWAP_EXC_STATE
|
||||||
*/
|
macros in ceval.c for details of their use. */
|
||||||
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
|
PyObject *f_exc_type, *f_exc_value, *f_exc_traceback;
|
||||||
|
|
||||||
PyThreadState *f_tstate;
|
PyThreadState *f_tstate;
|
||||||
|
|
|
@ -70,6 +70,7 @@ extern "C" {
|
||||||
#define YIELD_VALUE 86
|
#define YIELD_VALUE 86
|
||||||
#define POP_BLOCK 87
|
#define POP_BLOCK 87
|
||||||
#define END_FINALLY 88
|
#define END_FINALLY 88
|
||||||
|
#define POP_EXCEPT 89
|
||||||
|
|
||||||
#define HAVE_ARGUMENT 90 /* Opcodes from here have an argument: */
|
#define HAVE_ARGUMENT 90 /* Opcodes from here have an argument: */
|
||||||
|
|
||||||
|
@ -133,6 +134,13 @@ extern "C" {
|
||||||
#define EXTENDED_ARG 143
|
#define EXTENDED_ARG 143
|
||||||
|
|
||||||
|
|
||||||
|
/* EXCEPT_HANDLER is a special, implicit block type which is created when
|
||||||
|
entering an except handler. It is not an opcode but we define it here
|
||||||
|
as we want it to be available to both frameobject.c and ceval.c, while
|
||||||
|
remaining private.*/
|
||||||
|
#define EXCEPT_HANDLER 257
|
||||||
|
|
||||||
|
|
||||||
enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE,
|
enum cmp_op {PyCmp_LT=Py_LT, PyCmp_LE=Py_LE, PyCmp_EQ=Py_EQ, PyCmp_NE=Py_NE, PyCmp_GT=Py_GT, PyCmp_GE=Py_GE,
|
||||||
PyCmp_IN, PyCmp_NOT_IN, PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD};
|
PyCmp_IN, PyCmp_NOT_IN, PyCmp_IS, PyCmp_IS_NOT, PyCmp_EXC_MATCH, PyCmp_BAD};
|
||||||
|
|
||||||
|
|
|
@ -1242,10 +1242,9 @@ class DocTestRunner:
|
||||||
|
|
||||||
# The example raised an exception: check if it was expected.
|
# The example raised an exception: check if it was expected.
|
||||||
else:
|
else:
|
||||||
exc_info = sys.exc_info()
|
exc_msg = traceback.format_exception_only(*exception[:2])[-1]
|
||||||
exc_msg = traceback.format_exception_only(*exc_info[:2])[-1]
|
|
||||||
if not quiet:
|
if not quiet:
|
||||||
got += _exception_traceback(exc_info)
|
got += _exception_traceback(exception)
|
||||||
|
|
||||||
# If `example.exc_msg` is None, then we weren't expecting
|
# If `example.exc_msg` is None, then we weren't expecting
|
||||||
# an exception.
|
# an exception.
|
||||||
|
@ -1275,7 +1274,7 @@ class DocTestRunner:
|
||||||
elif outcome is BOOM:
|
elif outcome is BOOM:
|
||||||
if not quiet:
|
if not quiet:
|
||||||
self.report_unexpected_exception(out, test, example,
|
self.report_unexpected_exception(out, test, example,
|
||||||
exc_info)
|
exception)
|
||||||
failures += 1
|
failures += 1
|
||||||
else:
|
else:
|
||||||
assert False, ("unknown outcome", outcome)
|
assert False, ("unknown outcome", outcome)
|
||||||
|
|
|
@ -197,9 +197,6 @@ def isframe(object):
|
||||||
f_back next outer frame object (this frame's caller)
|
f_back next outer frame object (this frame's caller)
|
||||||
f_builtins built-in namespace seen by this frame
|
f_builtins built-in namespace seen by this frame
|
||||||
f_code code object being executed in this frame
|
f_code code object being executed in this frame
|
||||||
f_exc_traceback traceback if raised in this frame, or None
|
|
||||||
f_exc_type exception type if raised in this frame, or None
|
|
||||||
f_exc_value exception value if raised in this frame, or None
|
|
||||||
f_globals global namespace seen by this frame
|
f_globals global namespace seen by this frame
|
||||||
f_lasti index of last attempted instruction in bytecode
|
f_lasti index of last attempted instruction in bytecode
|
||||||
f_lineno current line number in Python source code
|
f_lineno current line number in Python source code
|
||||||
|
|
|
@ -105,6 +105,7 @@ def_op('MAKE_BYTES', 85)
|
||||||
def_op('YIELD_VALUE', 86)
|
def_op('YIELD_VALUE', 86)
|
||||||
def_op('POP_BLOCK', 87)
|
def_op('POP_BLOCK', 87)
|
||||||
def_op('END_FINALLY', 88)
|
def_op('END_FINALLY', 88)
|
||||||
|
def_op('POP_EXCEPT', 89)
|
||||||
|
|
||||||
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
|
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
|
||||||
|
|
||||||
|
|
|
@ -427,6 +427,7 @@ class ExceptionTests(unittest.TestCase):
|
||||||
local_ref = obj
|
local_ref = obj
|
||||||
raise MyException(obj)
|
raise MyException(obj)
|
||||||
|
|
||||||
|
# Qualified "except" with "as"
|
||||||
obj = MyObj()
|
obj = MyObj()
|
||||||
wr = weakref.ref(obj)
|
wr = weakref.ref(obj)
|
||||||
try:
|
try:
|
||||||
|
@ -437,6 +438,113 @@ class ExceptionTests(unittest.TestCase):
|
||||||
obj = wr()
|
obj = wr()
|
||||||
self.failUnless(obj is None, "%s" % obj)
|
self.failUnless(obj is None, "%s" % obj)
|
||||||
|
|
||||||
|
# Qualified "except" without "as"
|
||||||
|
obj = MyObj()
|
||||||
|
wr = weakref.ref(obj)
|
||||||
|
try:
|
||||||
|
inner_raising_func()
|
||||||
|
except MyException:
|
||||||
|
pass
|
||||||
|
obj = None
|
||||||
|
obj = wr()
|
||||||
|
self.failUnless(obj is None, "%s" % obj)
|
||||||
|
|
||||||
|
# Bare "except"
|
||||||
|
obj = MyObj()
|
||||||
|
wr = weakref.ref(obj)
|
||||||
|
try:
|
||||||
|
inner_raising_func()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
obj = None
|
||||||
|
obj = wr()
|
||||||
|
self.failUnless(obj is None, "%s" % obj)
|
||||||
|
|
||||||
|
# "except" with premature block leave
|
||||||
|
obj = MyObj()
|
||||||
|
wr = weakref.ref(obj)
|
||||||
|
for i in [0]:
|
||||||
|
try:
|
||||||
|
inner_raising_func()
|
||||||
|
except:
|
||||||
|
break
|
||||||
|
obj = None
|
||||||
|
obj = wr()
|
||||||
|
self.failUnless(obj is None, "%s" % obj)
|
||||||
|
|
||||||
|
# "except" block raising another exception
|
||||||
|
obj = MyObj()
|
||||||
|
wr = weakref.ref(obj)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
inner_raising_func()
|
||||||
|
except:
|
||||||
|
raise KeyError
|
||||||
|
except KeyError:
|
||||||
|
obj = None
|
||||||
|
obj = wr()
|
||||||
|
self.failUnless(obj is None, "%s" % obj)
|
||||||
|
|
||||||
|
# Some complicated construct
|
||||||
|
obj = MyObj()
|
||||||
|
wr = weakref.ref(obj)
|
||||||
|
try:
|
||||||
|
inner_raising_func()
|
||||||
|
except MyException:
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
raise
|
||||||
|
except MyException:
|
||||||
|
pass
|
||||||
|
obj = None
|
||||||
|
obj = wr()
|
||||||
|
self.failUnless(obj is None, "%s" % obj)
|
||||||
|
|
||||||
|
# Inside an exception-silencing "with" block
|
||||||
|
class Context:
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
def __exit__ (self, exc_type, exc_value, exc_tb):
|
||||||
|
return True
|
||||||
|
obj = MyObj()
|
||||||
|
wr = weakref.ref(obj)
|
||||||
|
with Context():
|
||||||
|
inner_raising_func()
|
||||||
|
obj = None
|
||||||
|
obj = wr()
|
||||||
|
self.failUnless(obj is None, "%s" % obj)
|
||||||
|
|
||||||
|
def test_generator_leaking(self):
|
||||||
|
# Test that generator exception state doesn't leak into the calling
|
||||||
|
# frame
|
||||||
|
def yield_raise():
|
||||||
|
try:
|
||||||
|
raise KeyError("caught")
|
||||||
|
except KeyError:
|
||||||
|
yield sys.exc_info()[0]
|
||||||
|
yield sys.exc_info()[0]
|
||||||
|
yield sys.exc_info()[0]
|
||||||
|
g = yield_raise()
|
||||||
|
self.assertEquals(next(g), KeyError)
|
||||||
|
self.assertEquals(sys.exc_info()[0], None)
|
||||||
|
self.assertEquals(next(g), KeyError)
|
||||||
|
self.assertEquals(sys.exc_info()[0], None)
|
||||||
|
self.assertEquals(next(g), None)
|
||||||
|
|
||||||
|
# Same test, but inside an exception handler
|
||||||
|
try:
|
||||||
|
raise TypeError("foo")
|
||||||
|
except TypeError:
|
||||||
|
g = yield_raise()
|
||||||
|
self.assertEquals(next(g), KeyError)
|
||||||
|
self.assertEquals(sys.exc_info()[0], TypeError)
|
||||||
|
self.assertEquals(next(g), KeyError)
|
||||||
|
self.assertEquals(sys.exc_info()[0], TypeError)
|
||||||
|
self.assertEquals(next(g), TypeError)
|
||||||
|
del g
|
||||||
|
self.assertEquals(sys.exc_info()[0], TypeError)
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
run_unittest(ExceptionTests)
|
run_unittest(ExceptionTests)
|
||||||
|
|
|
@ -16,6 +16,13 @@ def get_tb():
|
||||||
return sys.exc_info()[2]
|
return sys.exc_info()[2]
|
||||||
|
|
||||||
|
|
||||||
|
class Context:
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class TestRaise(unittest.TestCase):
|
class TestRaise(unittest.TestCase):
|
||||||
def test_invalid_reraise(self):
|
def test_invalid_reraise(self):
|
||||||
try:
|
try:
|
||||||
|
@ -37,6 +44,71 @@ class TestRaise(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
self.fail("No exception raised")
|
self.fail("No exception raised")
|
||||||
|
|
||||||
|
def test_except_reraise(self):
|
||||||
|
def reraise():
|
||||||
|
try:
|
||||||
|
raise TypeError("foo")
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
raise KeyError("caught")
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
self.assertRaises(TypeError, reraise)
|
||||||
|
|
||||||
|
def test_finally_reraise(self):
|
||||||
|
def reraise():
|
||||||
|
try:
|
||||||
|
raise TypeError("foo")
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
raise KeyError("caught")
|
||||||
|
finally:
|
||||||
|
raise
|
||||||
|
self.assertRaises(KeyError, reraise)
|
||||||
|
|
||||||
|
def test_nested_reraise(self):
|
||||||
|
def nested_reraise():
|
||||||
|
raise
|
||||||
|
def reraise():
|
||||||
|
try:
|
||||||
|
raise TypeError("foo")
|
||||||
|
except:
|
||||||
|
nested_reraise()
|
||||||
|
self.assertRaises(TypeError, reraise)
|
||||||
|
|
||||||
|
def test_with_reraise1(self):
|
||||||
|
def reraise():
|
||||||
|
try:
|
||||||
|
raise TypeError("foo")
|
||||||
|
except:
|
||||||
|
with Context():
|
||||||
|
pass
|
||||||
|
raise
|
||||||
|
self.assertRaises(TypeError, reraise)
|
||||||
|
|
||||||
|
def test_with_reraise2(self):
|
||||||
|
def reraise():
|
||||||
|
try:
|
||||||
|
raise TypeError("foo")
|
||||||
|
except:
|
||||||
|
with Context():
|
||||||
|
raise KeyError("caught")
|
||||||
|
raise
|
||||||
|
self.assertRaises(TypeError, reraise)
|
||||||
|
|
||||||
|
def test_yield_reraise(self):
|
||||||
|
def reraise():
|
||||||
|
try:
|
||||||
|
raise TypeError("foo")
|
||||||
|
except:
|
||||||
|
yield 1
|
||||||
|
raise
|
||||||
|
g = reraise()
|
||||||
|
next(g)
|
||||||
|
self.assertRaises(TypeError, lambda: next(g))
|
||||||
|
self.assertRaises(StopIteration, lambda: next(g))
|
||||||
|
|
||||||
def test_erroneous_exception(self):
|
def test_erroneous_exception(self):
|
||||||
class MyException(Exception):
|
class MyException(Exception):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -158,6 +230,5 @@ class TestRemovedFunctionality(unittest.TestCase):
|
||||||
def test_main():
|
def test_main():
|
||||||
support.run_unittest(__name__)
|
support.run_unittest(__name__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -49,6 +49,10 @@ Core and Builtins
|
||||||
Exception (KeyboardInterrupt, and SystemExit) propagate instead of
|
Exception (KeyboardInterrupt, and SystemExit) propagate instead of
|
||||||
ignoring them.
|
ignoring them.
|
||||||
|
|
||||||
|
- #3021 Exception reraising sematics have been significantly improved. However,
|
||||||
|
f_exc_type, f_exc_value, and f_exc_traceback cannot be accessed from Python
|
||||||
|
code anymore.
|
||||||
|
|
||||||
Extension Modules
|
Extension Modules
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
|
|
@ -1262,9 +1262,6 @@ Special informative state attributes for some types:
|
||||||
f_lineno (int, R/O): current line number
|
f_lineno (int, R/O): current line number
|
||||||
f_lasti (int, R/O): precise instruction (index into bytecode)
|
f_lasti (int, R/O): precise instruction (index into bytecode)
|
||||||
f_trace (function/None, R/W): debug hook called at start of each source line
|
f_trace (function/None, R/W): debug hook called at start of each source line
|
||||||
f_exc_type (Type/None, R/W): Most recent exception type
|
|
||||||
f_exc_value (any, R/W): Most recent exception value
|
|
||||||
f_exc_traceback (traceback/None, R/W): Most recent exception traceback
|
|
||||||
Tracebacks:
|
Tracebacks:
|
||||||
tb_next (frame/None, R/O): next level in stack trace (toward the frame where
|
tb_next (frame/None, R/O): next level in stack trace (toward the frame where
|
||||||
the exception occurred)
|
the exception occurred)
|
||||||
|
|
|
@ -20,9 +20,6 @@ static PyMemberDef frame_memberlist[] = {
|
||||||
{"f_builtins", T_OBJECT, OFF(f_builtins),READONLY},
|
{"f_builtins", T_OBJECT, OFF(f_builtins),READONLY},
|
||||||
{"f_globals", T_OBJECT, OFF(f_globals), READONLY},
|
{"f_globals", T_OBJECT, OFF(f_globals), READONLY},
|
||||||
{"f_lasti", T_INT, OFF(f_lasti), READONLY},
|
{"f_lasti", T_INT, OFF(f_lasti), READONLY},
|
||||||
{"f_exc_type", T_OBJECT, OFF(f_exc_type)},
|
|
||||||
{"f_exc_value", T_OBJECT, OFF(f_exc_value)},
|
|
||||||
{"f_exc_traceback", T_OBJECT, OFF(f_exc_traceback)},
|
|
||||||
{NULL} /* Sentinel */
|
{NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
369
Python/ceval.c
369
Python/ceval.c
|
@ -116,8 +116,6 @@ static int maybe_call_line_trace(Py_tracefunc, PyObject *,
|
||||||
static PyObject * cmp_outcome(int, PyObject *, PyObject *);
|
static PyObject * cmp_outcome(int, PyObject *, PyObject *);
|
||||||
static PyObject * import_from(PyObject *, PyObject *);
|
static PyObject * import_from(PyObject *, PyObject *);
|
||||||
static int import_all_from(PyObject *, PyObject *);
|
static int import_all_from(PyObject *, PyObject *);
|
||||||
static void set_exc_info(PyThreadState *, PyObject *, PyObject *, PyObject *);
|
|
||||||
static void reset_exc_info(PyThreadState *);
|
|
||||||
static void format_exc_check_arg(PyObject *, const char *, PyObject *);
|
static void format_exc_check_arg(PyObject *, const char *, PyObject *);
|
||||||
static PyObject * unicode_concatenate(PyObject *, PyObject *,
|
static PyObject * unicode_concatenate(PyObject *, PyObject *,
|
||||||
PyFrameObject *, unsigned char *);
|
PyFrameObject *, unsigned char *);
|
||||||
|
@ -483,7 +481,8 @@ enum why_code {
|
||||||
WHY_RETURN = 0x0008, /* 'return' statement */
|
WHY_RETURN = 0x0008, /* 'return' statement */
|
||||||
WHY_BREAK = 0x0010, /* 'break' statement */
|
WHY_BREAK = 0x0010, /* 'break' statement */
|
||||||
WHY_CONTINUE = 0x0020, /* 'continue' statement */
|
WHY_CONTINUE = 0x0020, /* 'continue' statement */
|
||||||
WHY_YIELD = 0x0040 /* 'yield' operator */
|
WHY_YIELD = 0x0040, /* 'yield' operator */
|
||||||
|
WHY_SILENCED = 0x0080 /* Exception silenced by 'with' */
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum why_code do_raise(PyObject *, PyObject *);
|
static enum why_code do_raise(PyObject *, PyObject *);
|
||||||
|
@ -692,6 +691,53 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
GETLOCAL(i) = value; \
|
GETLOCAL(i) = value; \
|
||||||
Py_XDECREF(tmp); } while (0)
|
Py_XDECREF(tmp); } while (0)
|
||||||
|
|
||||||
|
|
||||||
|
#define UNWIND_BLOCK(b) \
|
||||||
|
while (STACK_LEVEL() > (b)->b_level) { \
|
||||||
|
PyObject *v = POP(); \
|
||||||
|
Py_XDECREF(v); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define UNWIND_EXCEPT_HANDLER(b) \
|
||||||
|
assert(STACK_LEVEL() >= (b)->b_level + 3); \
|
||||||
|
while (STACK_LEVEL() > (b)->b_level + 3) { \
|
||||||
|
PyObject *v = POP(); \
|
||||||
|
Py_XDECREF(v); \
|
||||||
|
} \
|
||||||
|
Py_XDECREF(tstate->exc_type); \
|
||||||
|
tstate->exc_type = POP(); \
|
||||||
|
Py_XDECREF(tstate->exc_value); \
|
||||||
|
tstate->exc_value = POP(); \
|
||||||
|
Py_XDECREF(tstate->exc_traceback); \
|
||||||
|
tstate->exc_traceback = POP();
|
||||||
|
|
||||||
|
#define SAVE_EXC_STATE() \
|
||||||
|
{ \
|
||||||
|
Py_XINCREF(tstate->exc_type); \
|
||||||
|
Py_XINCREF(tstate->exc_value); \
|
||||||
|
Py_XINCREF(tstate->exc_traceback); \
|
||||||
|
Py_XDECREF(f->f_exc_type); \
|
||||||
|
Py_XDECREF(f->f_exc_value); \
|
||||||
|
Py_XDECREF(f->f_exc_traceback); \
|
||||||
|
f->f_exc_type = tstate->exc_type; \
|
||||||
|
f->f_exc_value = tstate->exc_value; \
|
||||||
|
f->f_exc_traceback = tstate->exc_traceback; \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define SWAP_EXC_STATE() \
|
||||||
|
{ \
|
||||||
|
PyObject *tmp; \
|
||||||
|
tmp = tstate->exc_type; \
|
||||||
|
tstate->exc_type = f->f_exc_type; \
|
||||||
|
f->f_exc_type = tmp; \
|
||||||
|
tmp = tstate->exc_value; \
|
||||||
|
tstate->exc_value = f->f_exc_value; \
|
||||||
|
f->f_exc_value = tmp; \
|
||||||
|
tmp = tstate->exc_traceback; \
|
||||||
|
tstate->exc_traceback = f->f_exc_traceback; \
|
||||||
|
f->f_exc_traceback = tmp; \
|
||||||
|
}
|
||||||
|
|
||||||
/* Start of code */
|
/* Start of code */
|
||||||
|
|
||||||
if (f == NULL)
|
if (f == NULL)
|
||||||
|
@ -765,6 +811,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
assert(stack_pointer != NULL);
|
assert(stack_pointer != NULL);
|
||||||
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
|
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
|
||||||
|
|
||||||
|
if (f->f_code->co_flags & CO_GENERATOR) {
|
||||||
|
if (f->f_exc_type != NULL && f->f_exc_type != Py_None) {
|
||||||
|
/* We were in an except handler when we left,
|
||||||
|
restore the exception state which was put aside
|
||||||
|
(see YIELD_VALUE). */
|
||||||
|
SWAP_EXC_STATE();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SAVE_EXC_STATE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef LLTRACE
|
#ifdef LLTRACE
|
||||||
lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL;
|
lltrace = PyDict_GetItemString(f->f_globals, "__lltrace__") != NULL;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1443,15 +1501,29 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
retval = POP();
|
retval = POP();
|
||||||
f->f_stacktop = stack_pointer;
|
f->f_stacktop = stack_pointer;
|
||||||
why = WHY_YIELD;
|
why = WHY_YIELD;
|
||||||
|
/* Put aside the current exception state and restore
|
||||||
|
that of the calling frame. This only serves when
|
||||||
|
"yield" is used inside an except handler. */
|
||||||
|
SWAP_EXC_STATE();
|
||||||
goto fast_yield;
|
goto fast_yield;
|
||||||
|
|
||||||
|
case POP_EXCEPT:
|
||||||
|
{
|
||||||
|
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||||
|
if (b->b_type != EXCEPT_HANDLER) {
|
||||||
|
PyErr_SetString(PyExc_SystemError,
|
||||||
|
"popped block is not an except handler");
|
||||||
|
why = WHY_EXCEPTION;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
UNWIND_EXCEPT_HANDLER(b);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
case POP_BLOCK:
|
case POP_BLOCK:
|
||||||
{
|
{
|
||||||
PyTryBlock *b = PyFrame_BlockPop(f);
|
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||||
while (STACK_LEVEL() > b->b_level) {
|
UNWIND_BLOCK(b);
|
||||||
v = POP();
|
|
||||||
Py_DECREF(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -1464,6 +1536,22 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
if (why == WHY_RETURN ||
|
if (why == WHY_RETURN ||
|
||||||
why == WHY_CONTINUE)
|
why == WHY_CONTINUE)
|
||||||
retval = POP();
|
retval = POP();
|
||||||
|
if (why == WHY_SILENCED) {
|
||||||
|
/* An exception was silenced by 'with', we must
|
||||||
|
manually unwind the EXCEPT_HANDLER block which was
|
||||||
|
created when the exception was caught, otherwise
|
||||||
|
the stack will be in an inconsistent state. */
|
||||||
|
PyTryBlock *b = PyFrame_BlockPop(f);
|
||||||
|
if (b->b_type != EXCEPT_HANDLER) {
|
||||||
|
PyErr_SetString(PyExc_SystemError,
|
||||||
|
"popped block is not an except handler");
|
||||||
|
why = WHY_EXCEPTION;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
UNWIND_EXCEPT_HANDLER(b);
|
||||||
|
why = WHY_NOT;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (PyExceptionClass_Check(v)) {
|
else if (PyExceptionClass_Check(v)) {
|
||||||
w = POP();
|
w = POP();
|
||||||
|
@ -1477,19 +1565,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
"'finally' pops bad exception");
|
"'finally' pops bad exception");
|
||||||
why = WHY_EXCEPTION;
|
why = WHY_EXCEPTION;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
Make sure the exception state is cleaned up before
|
|
||||||
the end of an except block. This ensures objects
|
|
||||||
referenced by the exception state are not kept
|
|
||||||
alive too long.
|
|
||||||
See #2507.
|
|
||||||
*/
|
|
||||||
if (tstate->frame->f_exc_type != NULL)
|
|
||||||
reset_exc_info(tstate);
|
|
||||||
else {
|
|
||||||
assert(tstate->frame->f_exc_value == NULL);
|
|
||||||
assert(tstate->frame->f_exc_traceback == NULL);
|
|
||||||
}
|
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -2056,59 +2131,33 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
||||||
should still be resumed.)
|
should still be resumed.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PyObject *exit_func;
|
PyObject *exit_func = POP();
|
||||||
|
u = TOP();
|
||||||
u = POP();
|
|
||||||
if (u == Py_None) {
|
if (u == Py_None) {
|
||||||
exit_func = TOP();
|
|
||||||
SET_TOP(u);
|
|
||||||
v = w = Py_None;
|
v = w = Py_None;
|
||||||
}
|
}
|
||||||
else if (PyLong_Check(u)) {
|
else if (PyLong_Check(u)) {
|
||||||
switch(PyLong_AS_LONG(u)) {
|
|
||||||
case WHY_RETURN:
|
|
||||||
case WHY_CONTINUE:
|
|
||||||
/* Retval in TOP. */
|
|
||||||
exit_func = SECOND();
|
|
||||||
SET_SECOND(TOP());
|
|
||||||
SET_TOP(u);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
exit_func = TOP();
|
|
||||||
SET_TOP(u);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
u = v = w = Py_None;
|
u = v = w = Py_None;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
v = TOP();
|
v = SECOND();
|
||||||
w = SECOND();
|
w = THIRD();
|
||||||
exit_func = THIRD();
|
|
||||||
SET_TOP(u);
|
|
||||||
SET_SECOND(v);
|
|
||||||
SET_THIRD(w);
|
|
||||||
}
|
}
|
||||||
/* XXX Not the fastest way to call it... */
|
/* XXX Not the fastest way to call it... */
|
||||||
x = PyObject_CallFunctionObjArgs(exit_func, u, v, w,
|
x = PyObject_CallFunctionObjArgs(exit_func, u, v, w,
|
||||||
NULL);
|
NULL);
|
||||||
if (x == NULL) {
|
|
||||||
Py_DECREF(exit_func);
|
Py_DECREF(exit_func);
|
||||||
|
if (x == NULL)
|
||||||
break; /* Go to error exit */
|
break; /* Go to error exit */
|
||||||
}
|
|
||||||
if (u != Py_None && PyObject_IsTrue(x)) {
|
if (u != Py_None && PyObject_IsTrue(x)) {
|
||||||
/* There was an exception and a true return */
|
/* There was an exception and a True return */
|
||||||
STACKADJ(-2);
|
STACKADJ(-2);
|
||||||
Py_INCREF(Py_None);
|
SET_TOP(PyLong_FromLong((long) WHY_SILENCED));
|
||||||
SET_TOP(Py_None);
|
|
||||||
Py_DECREF(u);
|
Py_DECREF(u);
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
Py_DECREF(w);
|
Py_DECREF(w);
|
||||||
} else {
|
|
||||||
/* The stack was rearranged to remove EXIT
|
|
||||||
above. Let END_FINALLY do its thing */
|
|
||||||
}
|
}
|
||||||
Py_DECREF(x);
|
Py_DECREF(x);
|
||||||
Py_DECREF(exit_func);
|
|
||||||
PREDICT(END_FINALLY);
|
PREDICT(END_FINALLY);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2370,50 +2419,63 @@ fast_block_end:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (STACK_LEVEL() > b->b_level) {
|
if (b->b_type == EXCEPT_HANDLER) {
|
||||||
v = POP();
|
UNWIND_EXCEPT_HANDLER(b);
|
||||||
Py_XDECREF(v);
|
if (why == WHY_EXCEPTION) {
|
||||||
|
Py_CLEAR(tstate->exc_type);
|
||||||
|
Py_CLEAR(tstate->exc_value);
|
||||||
|
Py_CLEAR(tstate->exc_traceback);
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
UNWIND_BLOCK(b);
|
||||||
if (b->b_type == SETUP_LOOP && why == WHY_BREAK) {
|
if (b->b_type == SETUP_LOOP && why == WHY_BREAK) {
|
||||||
why = WHY_NOT;
|
why = WHY_NOT;
|
||||||
JUMPTO(b->b_handler);
|
JUMPTO(b->b_handler);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (b->b_type == SETUP_FINALLY ||
|
if (why == WHY_EXCEPTION && (b->b_type == SETUP_EXCEPT
|
||||||
(b->b_type == SETUP_EXCEPT &&
|
|| b->b_type == SETUP_FINALLY)) {
|
||||||
why == WHY_EXCEPTION)) {
|
|
||||||
if (why == WHY_EXCEPTION) {
|
|
||||||
PyObject *exc, *val, *tb;
|
PyObject *exc, *val, *tb;
|
||||||
PyErr_Fetch(&exc, &val, &tb);
|
int handler = b->b_handler;
|
||||||
if (val == NULL) {
|
/* Beware, this invalidates all b->b_* fields */
|
||||||
val = Py_None;
|
PyFrame_BlockSetup(f, EXCEPT_HANDLER, -1, STACK_LEVEL());
|
||||||
Py_INCREF(val);
|
PUSH(tstate->exc_traceback);
|
||||||
|
PUSH(tstate->exc_value);
|
||||||
|
if (tstate->exc_type != NULL) {
|
||||||
|
PUSH(tstate->exc_type);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
PUSH(Py_None);
|
||||||
|
}
|
||||||
|
PyErr_Fetch(&exc, &val, &tb);
|
||||||
/* Make the raw exception data
|
/* Make the raw exception data
|
||||||
available to the handler,
|
available to the handler,
|
||||||
so a program can emulate the
|
so a program can emulate the
|
||||||
Python main loop. Don't do
|
Python main loop. */
|
||||||
this for 'finally'. */
|
|
||||||
if (b->b_type == SETUP_EXCEPT) {
|
|
||||||
PyErr_NormalizeException(
|
PyErr_NormalizeException(
|
||||||
&exc, &val, &tb);
|
&exc, &val, &tb);
|
||||||
set_exc_info(tstate,
|
PyException_SetTraceback(val, tb);
|
||||||
exc, val, tb);
|
Py_INCREF(exc);
|
||||||
}
|
tstate->exc_type = exc;
|
||||||
if (tb == NULL) {
|
Py_INCREF(val);
|
||||||
Py_INCREF(Py_None);
|
tstate->exc_value = val;
|
||||||
PUSH(Py_None);
|
tstate->exc_traceback = tb;
|
||||||
} else
|
if (tb == NULL)
|
||||||
|
tb = Py_None;
|
||||||
|
Py_INCREF(tb);
|
||||||
PUSH(tb);
|
PUSH(tb);
|
||||||
PUSH(val);
|
PUSH(val);
|
||||||
PUSH(exc);
|
PUSH(exc);
|
||||||
|
why = WHY_NOT;
|
||||||
|
JUMPTO(handler);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else {
|
if (b->b_type == SETUP_FINALLY) {
|
||||||
if (why & (WHY_RETURN | WHY_CONTINUE))
|
if (why & (WHY_RETURN | WHY_CONTINUE))
|
||||||
PUSH(retval);
|
PUSH(retval);
|
||||||
v = PyLong_FromLong((long)why);
|
PUSH(PyLong_FromLong((long)why));
|
||||||
PUSH(v);
|
|
||||||
}
|
|
||||||
why = WHY_NOT;
|
why = WHY_NOT;
|
||||||
JUMPTO(b->b_handler);
|
JUMPTO(b->b_handler);
|
||||||
break;
|
break;
|
||||||
|
@ -2471,13 +2533,6 @@ fast_yield:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tstate->frame->f_exc_type != NULL)
|
|
||||||
reset_exc_info(tstate);
|
|
||||||
else {
|
|
||||||
assert(tstate->frame->f_exc_value == NULL);
|
|
||||||
assert(tstate->frame->f_exc_traceback == NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pop frame */
|
/* pop frame */
|
||||||
exit_eval_frame:
|
exit_eval_frame:
|
||||||
Py_LeaveRecursiveCall();
|
Py_LeaveRecursiveCall();
|
||||||
|
@ -2757,150 +2812,6 @@ fail: /* Jump here from prelude on failure */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Implementation notes for set_exc_info() and reset_exc_info():
|
|
||||||
|
|
||||||
- Below, 'exc_ZZZ' stands for 'exc_type', 'exc_value' and
|
|
||||||
'exc_traceback'. These always travel together.
|
|
||||||
|
|
||||||
- tstate->curexc_ZZZ is the "hot" exception that is set by
|
|
||||||
PyErr_SetString(), cleared by PyErr_Clear(), and so on.
|
|
||||||
|
|
||||||
- Once an exception is caught by an except clause, it is transferred
|
|
||||||
from tstate->curexc_ZZZ to tstate->exc_ZZZ, from which sys.exc_info()
|
|
||||||
can pick it up. This is the primary task of set_exc_info().
|
|
||||||
XXX That can't be right: set_exc_info() doesn't look at tstate->curexc_ZZZ.
|
|
||||||
|
|
||||||
- Now let me explain the complicated dance with frame->f_exc_ZZZ.
|
|
||||||
|
|
||||||
Long ago, when none of this existed, there were just a few globals:
|
|
||||||
one set corresponding to the "hot" exception, and one set
|
|
||||||
corresponding to sys.exc_ZZZ. (Actually, the latter weren't C
|
|
||||||
globals; they were simply stored as sys.exc_ZZZ. For backwards
|
|
||||||
compatibility, they still are!) The problem was that in code like
|
|
||||||
this:
|
|
||||||
|
|
||||||
try:
|
|
||||||
"something that may fail"
|
|
||||||
except "some exception":
|
|
||||||
"do something else first"
|
|
||||||
"print the exception from sys.exc_ZZZ."
|
|
||||||
|
|
||||||
if "do something else first" invoked something that raised and caught
|
|
||||||
an exception, sys.exc_ZZZ were overwritten. That was a frequent
|
|
||||||
cause of subtle bugs. I fixed this by changing the semantics as
|
|
||||||
follows:
|
|
||||||
|
|
||||||
- Within one frame, sys.exc_ZZZ will hold the last exception caught
|
|
||||||
*in that frame*.
|
|
||||||
|
|
||||||
- But initially, and as long as no exception is caught in a given
|
|
||||||
frame, sys.exc_ZZZ will hold the last exception caught in the
|
|
||||||
previous frame (or the frame before that, etc.).
|
|
||||||
|
|
||||||
The first bullet fixed the bug in the above example. The second
|
|
||||||
bullet was for backwards compatibility: it was (and is) common to
|
|
||||||
have a function that is called when an exception is caught, and to
|
|
||||||
have that function access the caught exception via sys.exc_ZZZ.
|
|
||||||
(Example: traceback.print_exc()).
|
|
||||||
|
|
||||||
At the same time I fixed the problem that sys.exc_ZZZ weren't
|
|
||||||
thread-safe, by introducing sys.exc_info() which gets it from tstate;
|
|
||||||
but that's really a separate improvement.
|
|
||||||
|
|
||||||
The reset_exc_info() function in ceval.c restores the tstate->exc_ZZZ
|
|
||||||
variables to what they were before the current frame was called. The
|
|
||||||
set_exc_info() function saves them on the frame so that
|
|
||||||
reset_exc_info() can restore them. The invariant is that
|
|
||||||
frame->f_exc_ZZZ is NULL iff the current frame never caught an
|
|
||||||
exception (where "catching" an exception applies only to successful
|
|
||||||
except clauses); and if the current frame ever caught an exception,
|
|
||||||
frame->f_exc_ZZZ is the exception that was stored in tstate->exc_ZZZ
|
|
||||||
at the start of the current frame.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
static void
|
|
||||||
set_exc_info(PyThreadState *tstate,
|
|
||||||
PyObject *type, PyObject *value, PyObject *tb)
|
|
||||||
{
|
|
||||||
PyFrameObject *frame = tstate->frame;
|
|
||||||
PyObject *tmp_type, *tmp_value, *tmp_tb;
|
|
||||||
|
|
||||||
assert(type != NULL);
|
|
||||||
assert(frame != NULL);
|
|
||||||
if (frame->f_exc_type == NULL) {
|
|
||||||
assert(frame->f_exc_value == NULL);
|
|
||||||
assert(frame->f_exc_traceback == NULL);
|
|
||||||
/* This frame didn't catch an exception before. */
|
|
||||||
/* Save previous exception of this thread in this frame. */
|
|
||||||
if (tstate->exc_type == NULL) {
|
|
||||||
/* XXX Why is this set to Py_None? */
|
|
||||||
Py_INCREF(Py_None);
|
|
||||||
tstate->exc_type = Py_None;
|
|
||||||
}
|
|
||||||
Py_INCREF(tstate->exc_type);
|
|
||||||
Py_XINCREF(tstate->exc_value);
|
|
||||||
Py_XINCREF(tstate->exc_traceback);
|
|
||||||
frame->f_exc_type = tstate->exc_type;
|
|
||||||
frame->f_exc_value = tstate->exc_value;
|
|
||||||
frame->f_exc_traceback = tstate->exc_traceback;
|
|
||||||
}
|
|
||||||
/* Set new exception for this thread. */
|
|
||||||
tmp_type = tstate->exc_type;
|
|
||||||
tmp_value = tstate->exc_value;
|
|
||||||
tmp_tb = tstate->exc_traceback;
|
|
||||||
Py_INCREF(type);
|
|
||||||
Py_XINCREF(value);
|
|
||||||
Py_XINCREF(tb);
|
|
||||||
tstate->exc_type = type;
|
|
||||||
tstate->exc_value = value;
|
|
||||||
tstate->exc_traceback = tb;
|
|
||||||
PyException_SetTraceback(value, tb);
|
|
||||||
Py_XDECREF(tmp_type);
|
|
||||||
Py_XDECREF(tmp_value);
|
|
||||||
Py_XDECREF(tmp_tb);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
reset_exc_info(PyThreadState *tstate)
|
|
||||||
{
|
|
||||||
PyFrameObject *frame;
|
|
||||||
PyObject *tmp_type, *tmp_value, *tmp_tb;
|
|
||||||
|
|
||||||
/* It's a precondition that the thread state's frame caught an
|
|
||||||
* exception -- verify in a debug build.
|
|
||||||
*/
|
|
||||||
assert(tstate != NULL);
|
|
||||||
frame = tstate->frame;
|
|
||||||
assert(frame != NULL);
|
|
||||||
assert(frame->f_exc_type != NULL);
|
|
||||||
|
|
||||||
/* Copy the frame's exception info back to the thread state. */
|
|
||||||
tmp_type = tstate->exc_type;
|
|
||||||
tmp_value = tstate->exc_value;
|
|
||||||
tmp_tb = tstate->exc_traceback;
|
|
||||||
Py_INCREF(frame->f_exc_type);
|
|
||||||
Py_XINCREF(frame->f_exc_value);
|
|
||||||
Py_XINCREF(frame->f_exc_traceback);
|
|
||||||
tstate->exc_type = frame->f_exc_type;
|
|
||||||
tstate->exc_value = frame->f_exc_value;
|
|
||||||
tstate->exc_traceback = frame->f_exc_traceback;
|
|
||||||
Py_XDECREF(tmp_type);
|
|
||||||
Py_XDECREF(tmp_value);
|
|
||||||
Py_XDECREF(tmp_tb);
|
|
||||||
|
|
||||||
/* Clear the frame's exception info. */
|
|
||||||
tmp_type = frame->f_exc_type;
|
|
||||||
tmp_value = frame->f_exc_value;
|
|
||||||
tmp_tb = frame->f_exc_traceback;
|
|
||||||
frame->f_exc_type = NULL;
|
|
||||||
frame->f_exc_value = NULL;
|
|
||||||
frame->f_exc_traceback = NULL;
|
|
||||||
Py_DECREF(tmp_type);
|
|
||||||
Py_XDECREF(tmp_value);
|
|
||||||
Py_XDECREF(tmp_tb);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Logic for the raise statement (too complicated for inlining).
|
/* Logic for the raise statement (too complicated for inlining).
|
||||||
This *consumes* a reference count to each of its arguments. */
|
This *consumes* a reference count to each of its arguments. */
|
||||||
static enum why_code
|
static enum why_code
|
||||||
|
|
|
@ -760,6 +760,8 @@ opcode_stack_effect(int opcode, int oparg)
|
||||||
|
|
||||||
case POP_BLOCK:
|
case POP_BLOCK:
|
||||||
return 0;
|
return 0;
|
||||||
|
case POP_EXCEPT:
|
||||||
|
return 0; /* -3 except if bad bytecode */
|
||||||
case END_FINALLY:
|
case END_FINALLY:
|
||||||
return -1; /* or -2 or -3 if exception occurred */
|
return -1; /* or -2 or -3 if exception occurred */
|
||||||
|
|
||||||
|
@ -818,7 +820,8 @@ opcode_stack_effect(int opcode, int oparg)
|
||||||
return 0;
|
return 0;
|
||||||
case SETUP_EXCEPT:
|
case SETUP_EXCEPT:
|
||||||
case SETUP_FINALLY:
|
case SETUP_FINALLY:
|
||||||
return 3; /* actually pushed by an exception */
|
return 6; /* can push 3 values for the new exception
|
||||||
|
+ 3 others for the previous exception state */
|
||||||
|
|
||||||
case LOAD_FAST:
|
case LOAD_FAST:
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -2031,6 +2034,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
|
||||||
/* second # body */
|
/* second # body */
|
||||||
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
||||||
ADDOP(c, POP_BLOCK);
|
ADDOP(c, POP_BLOCK);
|
||||||
|
ADDOP(c, POP_EXCEPT);
|
||||||
compiler_pop_fblock(c, FINALLY_TRY, cleanup_body);
|
compiler_pop_fblock(c, FINALLY_TRY, cleanup_body);
|
||||||
|
|
||||||
/* finally: */
|
/* finally: */
|
||||||
|
@ -2050,9 +2054,20 @@ compiler_try_except(struct compiler *c, stmt_ty s)
|
||||||
compiler_pop_fblock(c, FINALLY_END, cleanup_end);
|
compiler_pop_fblock(c, FINALLY_END, cleanup_end);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
basicblock *cleanup_body;
|
||||||
|
|
||||||
|
cleanup_body = compiler_new_block(c);
|
||||||
|
if(!cleanup_body)
|
||||||
|
return 0;
|
||||||
|
|
||||||
ADDOP(c, POP_TOP);
|
ADDOP(c, POP_TOP);
|
||||||
ADDOP(c, POP_TOP);
|
ADDOP(c, POP_TOP);
|
||||||
|
compiler_use_next_block(c, cleanup_body);
|
||||||
|
if (!compiler_push_fblock(c, FINALLY_TRY, cleanup_body))
|
||||||
|
return 0;
|
||||||
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
VISIT_SEQ(c, stmt, handler->v.ExceptHandler.body);
|
||||||
|
ADDOP(c, POP_EXCEPT);
|
||||||
|
compiler_pop_fblock(c, FINALLY_TRY, cleanup_body);
|
||||||
}
|
}
|
||||||
ADDOP_JREL(c, JUMP_FORWARD, end);
|
ADDOP_JREL(c, JUMP_FORWARD, end);
|
||||||
compiler_use_next_block(c, except);
|
compiler_use_next_block(c, except);
|
||||||
|
@ -3109,7 +3124,7 @@ compiler_with(struct compiler *c, stmt_ty s)
|
||||||
{
|
{
|
||||||
static identifier enter_attr, exit_attr;
|
static identifier enter_attr, exit_attr;
|
||||||
basicblock *block, *finally;
|
basicblock *block, *finally;
|
||||||
identifier tmpvalue = NULL;
|
identifier tmpvalue = NULL, tmpexit = NULL;
|
||||||
|
|
||||||
assert(s->kind == With_kind);
|
assert(s->kind == With_kind);
|
||||||
|
|
||||||
|
@ -3144,6 +3159,10 @@ compiler_with(struct compiler *c, stmt_ty s)
|
||||||
return 0;
|
return 0;
|
||||||
PyArena_AddPyObject(c->c_arena, tmpvalue);
|
PyArena_AddPyObject(c->c_arena, tmpvalue);
|
||||||
}
|
}
|
||||||
|
tmpexit = compiler_new_tmpname(c);
|
||||||
|
if (tmpexit == NULL)
|
||||||
|
return 0;
|
||||||
|
PyArena_AddPyObject(c->c_arena, tmpexit);
|
||||||
|
|
||||||
/* Evaluate EXPR */
|
/* Evaluate EXPR */
|
||||||
VISIT(c, expr, s->v.With.context_expr);
|
VISIT(c, expr, s->v.With.context_expr);
|
||||||
|
@ -3151,7 +3170,8 @@ compiler_with(struct compiler *c, stmt_ty s)
|
||||||
/* Squirrel away context.__exit__ by stuffing it under context */
|
/* Squirrel away context.__exit__ by stuffing it under context */
|
||||||
ADDOP(c, DUP_TOP);
|
ADDOP(c, DUP_TOP);
|
||||||
ADDOP_O(c, LOAD_ATTR, exit_attr, names);
|
ADDOP_O(c, LOAD_ATTR, exit_attr, names);
|
||||||
ADDOP(c, ROT_TWO);
|
if (!compiler_nameop(c, tmpexit, Store))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Call context.__enter__() */
|
/* Call context.__enter__() */
|
||||||
ADDOP_O(c, LOAD_ATTR, enter_attr, names);
|
ADDOP_O(c, LOAD_ATTR, enter_attr, names);
|
||||||
|
@ -3198,6 +3218,9 @@ compiler_with(struct compiler *c, stmt_ty s)
|
||||||
/* Finally block starts; context.__exit__ is on the stack under
|
/* Finally block starts; context.__exit__ is on the stack under
|
||||||
the exception or return information. Just issue our magic
|
the exception or return information. Just issue our magic
|
||||||
opcode. */
|
opcode. */
|
||||||
|
if (!compiler_nameop(c, tmpexit, Load) ||
|
||||||
|
!compiler_nameop(c, tmpexit, Del))
|
||||||
|
return 0;
|
||||||
ADDOP(c, WITH_CLEANUP);
|
ADDOP(c, WITH_CLEANUP);
|
||||||
|
|
||||||
/* Finally block ends. */
|
/* Finally block ends. */
|
||||||
|
|
|
@ -86,8 +86,9 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
|
||||||
3100 (merge from 2.6a0, see 62151)
|
3100 (merge from 2.6a0, see 62151)
|
||||||
3102 (__file__ points to source file)
|
3102 (__file__ points to source file)
|
||||||
Python 3.0a4: 3110 (WITH_CLEANUP optimization).
|
Python 3.0a4: 3110 (WITH_CLEANUP optimization).
|
||||||
|
Python 3.0a5: 3130 (lexical exception stacking, including POP_EXCEPT)
|
||||||
*/
|
*/
|
||||||
#define MAGIC (3110 | ((long)'\r'<<16) | ((long)'\n'<<24))
|
#define MAGIC (3130 | ((long)'\r'<<16) | ((long)'\n'<<24))
|
||||||
|
|
||||||
/* Magic word as global; note that _PyImport_Init() can change the
|
/* Magic word as global; note that _PyImport_Init() can change the
|
||||||
value of this global to accommodate for alterations of how the
|
value of this global to accommodate for alterations of how the
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue