Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
c86ac884
Commit
c86ac884
authored
Jun 11, 2008
by
Benjamin Peterson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
#3021: Antoine Pitrou's Lexical exception handlers
parent
55d03e06
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
410 additions
and
303 deletions
+410
-303
Doc/library/dis.rst
Doc/library/dis.rst
+21
-15
Doc/library/inspect.rst
Doc/library/inspect.rst
+0
-11
Doc/library/sys.rst
Doc/library/sys.rst
+2
-2
Doc/reference/datamodel.rst
Doc/reference/datamodel.rst
+4
-9
Include/frameobject.h
Include/frameobject.h
+7
-7
Include/opcode.h
Include/opcode.h
+8
-0
Lib/doctest.py
Lib/doctest.py
+3
-4
Lib/inspect.py
Lib/inspect.py
+0
-3
Lib/opcode.py
Lib/opcode.py
+1
-0
Lib/test/test_exceptions.py
Lib/test/test_exceptions.py
+108
-0
Lib/test/test_raise.py
Lib/test/test_raise.py
+72
-1
Misc/NEWS
Misc/NEWS
+4
-0
Misc/cheatsheet
Misc/cheatsheet
+0
-3
Objects/frameobject.c
Objects/frameobject.c
+0
-3
Python/ceval.c
Python/ceval.c
+151
-240
Python/compile.c
Python/compile.c
+27
-4
Python/import.c
Python/import.c
+2
-1
No files found.
Doc/library/dis.rst
View file @
c86ac884
...
...
@@ -397,6 +397,14 @@ Miscellaneous opcodes.
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 ()
Terminates a :keyword:`finally` clause. The interpreter recalls whether the
...
...
@@ -412,24 +420,22 @@ Miscellaneous opcodes.
.. opcode:: WITH_CLEANUP ()
Cleans up the stack when a :keyword:`with` statement block exits. On top of
the stack are 1--3 values indicating how/why the finally clause was entered:
* TOP = ``None``
* (TOP, SECOND) = (``WHY_{RETURN,CONTINUE}``), retval
* TOP = ``WHY_*``; no retval below it
* (TOP, SECOND, THIRD) = exc_info()
Cleans up the stack when a :keyword:`with` statement block exits. TOS is
the context manager's :meth:`__exit__` bound method. Below TOS are 1--3
values indicating how/why the finally clause was entered:
Under them is EXIT, the context manager's :meth:`__exit__` bound method.
* SECOND = ``None``
* (SECOND, THIRD) = (``WHY_{RETURN,CONTINUE}``), retval
* SECOND = ``WHY_*``; no retval below it
* (SECOND, THIRD, FOURTH) = exc_info()
In the last case, ``
EXIT(TOP, SECOND, THIRD
)`` is called, otherwise
``
EXIT(None, None, None)``
.
In the last case, ``
TOS(SECOND, THIRD, FOURTH
)`` is called, otherwise
``
TOS(None, None, None)``. In addition, TOS is removed from the stack
.
EXIT is removed from the stack, leaving the values above it in the same
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.)
If the stack represents an exception, *and* the function call returns
a 'true' value, this information is "zapped" and replaced with a single
``WHY_SILENCED`` to prevent ``END_FINALLY`` from re-raising the exception.
(But non-local gotos will still be resumed.)
.. XXX explain the WHY stuff!
...
...
Doc/library/inspect.rst
View file @
c86ac884
...
...
@@ -94,17 +94,6 @@ attributes:
| | 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 |
+-----------+-----------------+---------------------------+
...
...
Doc/library/sys.rst
View file @
c86ac884
...
...
@@ -136,8 +136,8 @@ always available.
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
handling an exception. Here, "handling an exception" is defined as "executing
or having executed an except clause." For any stack frame, only informa
tion
about the most recently handled exception
is accessible.
an except clause." For any stack frame, only information about the excep
tion
being currently handled
is accessible.
.. index:: object: traceback
...
...
Doc/reference/datamodel.rst
View file @
c86ac884
...
...
@@ -875,19 +875,14 @@ Internal types
.. index::
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)
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);
:attr:`f_exc_type`, :attr:`f_exc_value`, :attr:`f_exc_traceback` represent the
last exception raised in the parent frame provided another exception was ever
raised in the current frame (in all other cases they are None); :attr:`f_lineno`
is the current line number of the frame --- writing to this from within a trace
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.
:attr:`f_lineno` is the current line number of the frame --- writing to this
from within a trace 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
.. index::
...
...
Include/frameobject.h
View file @
c86ac884
...
...
@@ -27,13 +27,13 @@ typedef struct _frame {
PyObject
**
f_stacktop
;
PyObject
*
f_trace
;
/* Trace function */
/* If an exception is raised in this frame, the next three are used to
* record the exception info (if any) originally in the thread state. See
* comments before set_exc_info() -- it's not obvious.
* Invariant: if _type is NULL, then so are _value and _traceback
.
* Desired invariant: all three are NULL, or all three are non-NULL. That
* one isn't currently true, but "should be".
*/
/* In a generator, we need to be able to swap between the exception
state inside the generator and the exception state of the calling
frame (which shouldn't be impacted when the generator "yields"
from an except handler)
.
These three fields exist exactly for that, and are unused for
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
;
PyThreadState
*
f_tstate
;
...
...
Include/opcode.h
View file @
c86ac884
...
...
@@ -70,6 +70,7 @@ extern "C" {
#define YIELD_VALUE 86
#define POP_BLOCK 87
#define END_FINALLY 88
#define POP_EXCEPT 89
#define HAVE_ARGUMENT 90
/* Opcodes from here have an argument: */
...
...
@@ -133,6 +134,13 @@ extern "C" {
#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
,
PyCmp_IN
,
PyCmp_NOT_IN
,
PyCmp_IS
,
PyCmp_IS_NOT
,
PyCmp_EXC_MATCH
,
PyCmp_BAD
};
...
...
Lib/doctest.py
View file @
c86ac884
...
...
@@ -1242,10 +1242,9 @@ class DocTestRunner:
# The example raised an exception: check if it was expected.
else
:
exc_info
=
sys
.
exc_info
()
exc_msg
=
traceback
.
format_exception_only
(
*
exc_info
[:
2
])[
-
1
]
exc_msg
=
traceback
.
format_exception_only
(
*
exception
[:
2
])[
-
1
]
if
not
quiet
:
got
+=
_exception_traceback
(
exc
_info
)
got
+=
_exception_traceback
(
exc
eption
)
# If `example.exc_msg` is None, then we weren't expecting
# an exception.
...
...
@@ -1275,7 +1274,7 @@ class DocTestRunner:
elif
outcome
is
BOOM
:
if
not
quiet
:
self
.
report_unexpected_exception
(
out
,
test
,
example
,
exc
_info
)
exc
eption
)
failures
+=
1
else
:
assert
False
,
(
"unknown outcome"
,
outcome
)
...
...
Lib/inspect.py
View file @
c86ac884
...
...
@@ -197,9 +197,6 @@ def isframe(object):
f_back next outer frame object (this frame's caller)
f_builtins built-in namespace seen by 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_lasti index of last attempted instruction in bytecode
f_lineno current line number in Python source code
...
...
Lib/opcode.py
View file @
c86ac884
...
...
@@ -105,6 +105,7 @@ def_op('MAKE_BYTES', 85)
def_op
(
'YIELD_VALUE'
,
86
)
def_op
(
'POP_BLOCK'
,
87
)
def_op
(
'END_FINALLY'
,
88
)
def_op
(
'POP_EXCEPT'
,
89
)
HAVE_ARGUMENT
=
90
# Opcodes from here have an argument:
...
...
Lib/test/test_exceptions.py
View file @
c86ac884
...
...
@@ -427,6 +427,7 @@ class ExceptionTests(unittest.TestCase):
local_ref
=
obj
raise
MyException
(
obj
)
# Qualified "except" with "as"
obj
=
MyObj
()
wr
=
weakref
.
ref
(
obj
)
try
:
...
...
@@ -437,6 +438,113 @@ class ExceptionTests(unittest.TestCase):
obj
=
wr
()
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
():
run_unittest
(
ExceptionTests
)
...
...
Lib/test/test_raise.py
View file @
c86ac884
...
...
@@ -16,6 +16,13 @@ def get_tb():
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
):
def
test_invalid_reraise
(
self
):
try
:
...
...
@@ -37,6 +44,71 @@ class TestRaise(unittest.TestCase):
else
:
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
):
class
MyException
(
Exception
):
def
__init__
(
self
):
...
...
@@ -158,6 +230,5 @@ class TestRemovedFunctionality(unittest.TestCase):
def
test_main
():
support
.
run_unittest
(
__name__
)
if
__name__
==
"__main__"
:
unittest
.
main
()
Misc/NEWS
View file @
c86ac884
...
...
@@ -49,6 +49,10 @@ Core and Builtins
Exception (KeyboardInterrupt, and SystemExit) propagate instead of
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
-----------------
...
...
Misc/cheatsheet
View file @
c86ac884
...
...
@@ -1262,9 +1262,6 @@ Special informative state attributes for some types:
f_lineno (int, R/O): current line number
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_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:
tb_next (frame/None, R/O): next level in stack trace (toward the frame where
the exception occurred)
...
...
Objects/frameobject.c
View file @
c86ac884
...
...
@@ -20,9 +20,6 @@ static PyMemberDef frame_memberlist[] = {
{
"f_builtins"
,
T_OBJECT
,
OFF
(
f_builtins
),
READONLY
},
{
"f_globals"
,
T_OBJECT
,
OFF
(
f_globals
),
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 */
};
...
...
Python/ceval.c
View file @
c86ac884
...
...
@@ -116,8 +116,6 @@ static int maybe_call_line_trace(Py_tracefunc, PyObject *,
static
PyObject
*
cmp_outcome
(
int
,
PyObject
*
,
PyObject
*
);
static
PyObject
*
import_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
PyObject
*
unicode_concatenate
(
PyObject
*
,
PyObject
*
,
PyFrameObject
*
,
unsigned
char
*
);
...
...
@@ -483,7 +481,8 @@ enum why_code {
WHY_RETURN
=
0x0008
,
/* 'return' statement */
WHY_BREAK
=
0x0010
,
/* 'break' 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
*
);
...
...
@@ -692,6 +691,53 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
GETLOCAL(i) = value; \
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 */
if
(
f
==
NULL
)
...
...
@@ -765,6 +811,18 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
assert
(
stack_pointer
!=
NULL
);
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
lltrace
=
PyDict_GetItemString
(
f
->
f_globals
,
"__lltrace__"
)
!=
NULL
;
#endif
...
...
@@ -1443,15 +1501,29 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
retval
=
POP
();
f
->
f_stacktop
=
stack_pointer
;
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
;
case
POP_
BLOCK
:
case
POP_
EXCEPT
:
{
PyTryBlock
*
b
=
PyFrame_BlockPop
(
f
);
while
(
STACK_LEVEL
()
>
b
->
b_level
)
{
v
=
POP
();
Py_DECREF
(
v
);
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
:
{
PyTryBlock
*
b
=
PyFrame_BlockPop
(
f
);
UNWIND_BLOCK
(
b
);
}
continue
;
...
...
@@ -1464,6 +1536,22 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
if
(
why
==
WHY_RETURN
||
why
==
WHY_CONTINUE
)
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
))
{
w
=
POP
();
...
...
@@ -1477,19 +1565,6 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
"'finally' pops bad 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
);
break
;
...
...
@@ -2056,59 +2131,33 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
should still be resumed.)
*/
PyObject
*
exit_func
;
u
=
POP
();
PyObject
*
exit_func
=
POP
();
u
=
TOP
();
if
(
u
==
Py_None
)
{
exit_func
=
TOP
();
SET_TOP
(
u
);
v
=
w
=
Py_None
;
}
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
;
}
else
{
v
=
TOP
();
w
=
SECOND
();
exit_func
=
THIRD
();
SET_TOP
(
u
);
SET_SECOND
(
v
);
SET_THIRD
(
w
);
v
=
SECOND
();
w
=
THIRD
();
}
/* XXX Not the fastest way to call it... */
x
=
PyObject_CallFunctionObjArgs
(
exit_func
,
u
,
v
,
w
,
NULL
);
if
(
x
==
NULL
)
{
Py_DECREF
(
exit_func
);
Py_DECREF
(
exit_func
);
if
(
x
==
NULL
)
break
;
/* Go to error exit */
}
if
(
u
!=
Py_None
&&
PyObject_IsTrue
(
x
))
{
/* There was an exception and a
t
rue return */
/* There was an exception and a
T
rue return */
STACKADJ
(
-
2
);
Py_INCREF
(
Py_None
);
SET_TOP
(
Py_None
);
SET_TOP
(
PyLong_FromLong
((
long
)
WHY_SILENCED
));
Py_DECREF
(
u
);
Py_DECREF
(
v
);
Py_DECREF
(
w
);
}
else
{
/* The stack was rearranged to remove EXIT
above. Let END_FINALLY do its thing */
}
Py_DECREF
(
x
);
Py_DECREF
(
exit_func
);
PREDICT
(
END_FINALLY
);
break
;
}
...
...
@@ -2370,50 +2419,63 @@ fast_block_end:
break
;
}
while
(
STACK_LEVEL
()
>
b
->
b_level
)
{
v
=
POP
();
Py_XDECREF
(
v
);
if
(
b
->
b_type
==
EXCEPT_HANDLER
)
{
UNWIND_EXCEPT_HANDLER
(
b
);
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
)
{
why
=
WHY_NOT
;
JUMPTO
(
b
->
b_handler
);
break
;
}
if
(
b
->
b_type
==
SETUP_FINALLY
||
(
b
->
b_type
==
SETUP_EXCEPT
&&
why
==
WHY_EXCEPTION
))
{
if
(
why
==
WHY_EXCEPTION
)
{
PyObject
*
exc
,
*
val
,
*
tb
;
PyErr_Fetch
(
&
exc
,
&
val
,
&
tb
);
if
(
val
==
NULL
)
{
val
=
Py_None
;
Py_INCREF
(
val
);
}
/* Make the raw exception data
available to the handler,
so a program can emulate the
Python main loop. Don't do
this for 'finally'. */
if
(
b
->
b_type
==
SETUP_EXCEPT
)
{
PyErr_NormalizeException
(
&
exc
,
&
val
,
&
tb
);
set_exc_info
(
tstate
,
exc
,
val
,
tb
);
}
if
(
tb
==
NULL
)
{
Py_INCREF
(
Py_None
);
PUSH
(
Py_None
);
}
else
PUSH
(
tb
);
PUSH
(
val
);
PUSH
(
exc
);
if
(
why
==
WHY_EXCEPTION
&&
(
b
->
b_type
==
SETUP_EXCEPT
||
b
->
b_type
==
SETUP_FINALLY
))
{
PyObject
*
exc
,
*
val
,
*
tb
;
int
handler
=
b
->
b_handler
;
/* Beware, this invalidates all b->b_* fields */
PyFrame_BlockSetup
(
f
,
EXCEPT_HANDLER
,
-
1
,
STACK_LEVEL
());
PUSH
(
tstate
->
exc_traceback
);
PUSH
(
tstate
->
exc_value
);
if
(
tstate
->
exc_type
!=
NULL
)
{
PUSH
(
tstate
->
exc_type
);
}
else
{
if
(
why
&
(
WHY_RETURN
|
WHY_CONTINUE
))
PUSH
(
retval
);
v
=
PyLong_FromLong
((
long
)
why
);
PUSH
(
v
);
Py_INCREF
(
Py_None
);
PUSH
(
Py_None
);
}
PyErr_Fetch
(
&
exc
,
&
val
,
&
tb
);
/* Make the raw exception data
available to the handler,
so a program can emulate the
Python main loop. */
PyErr_NormalizeException
(
&
exc
,
&
val
,
&
tb
);
PyException_SetTraceback
(
val
,
tb
);
Py_INCREF
(
exc
);
tstate
->
exc_type
=
exc
;
Py_INCREF
(
val
);
tstate
->
exc_value
=
val
;
tstate
->
exc_traceback
=
tb
;
if
(
tb
==
NULL
)
tb
=
Py_None
;
Py_INCREF
(
tb
);
PUSH
(
tb
);
PUSH
(
val
);
PUSH
(
exc
);
why
=
WHY_NOT
;
JUMPTO
(
handler
);
break
;
}
if
(
b
->
b_type
==
SETUP_FINALLY
)
{
if
(
why
&
(
WHY_RETURN
|
WHY_CONTINUE
))
PUSH
(
retval
);
PUSH
(
PyLong_FromLong
((
long
)
why
));
why
=
WHY_NOT
;
JUMPTO
(
b
->
b_handler
);
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 */
exit_eval_frame:
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).
This *consumes* a reference count to each of its arguments. */
static
enum
why_code
...
...
Python/compile.c
View file @
c86ac884
...
...
@@ -760,6 +760,8 @@ opcode_stack_effect(int opcode, int oparg)
case
POP_BLOCK
:
return
0
;
case
POP_EXCEPT
:
return
0
;
/* -3 except if bad bytecode */
case
END_FINALLY
:
return
-
1
;
/* or -2 or -3 if exception occurred */
...
...
@@ -818,7 +820,8 @@ opcode_stack_effect(int opcode, int oparg)
return
0
;
case
SETUP_EXCEPT
:
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
:
return
1
;
...
...
@@ -2031,6 +2034,7 @@ compiler_try_except(struct compiler *c, stmt_ty s)
/* second # body */
VISIT_SEQ
(
c
,
stmt
,
handler
->
v
.
ExceptHandler
.
body
);
ADDOP
(
c
,
POP_BLOCK
);
ADDOP
(
c
,
POP_EXCEPT
);
compiler_pop_fblock
(
c
,
FINALLY_TRY
,
cleanup_body
);
/* finally: */
...
...
@@ -2050,9 +2054,20 @@ compiler_try_except(struct compiler *c, stmt_ty s)
compiler_pop_fblock
(
c
,
FINALLY_END
,
cleanup_end
);
}
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
);
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
);
ADDOP
(
c
,
POP_EXCEPT
);
compiler_pop_fblock
(
c
,
FINALLY_TRY
,
cleanup_body
);
}
ADDOP_JREL
(
c
,
JUMP_FORWARD
,
end
);
compiler_use_next_block
(
c
,
except
);
...
...
@@ -3109,7 +3124,7 @@ compiler_with(struct compiler *c, stmt_ty s)
{
static
identifier
enter_attr
,
exit_attr
;
basicblock
*
block
,
*
finally
;
identifier
tmpvalue
=
NULL
;
identifier
tmpvalue
=
NULL
,
tmpexit
=
NULL
;
assert
(
s
->
kind
==
With_kind
);
...
...
@@ -3144,6 +3159,10 @@ compiler_with(struct compiler *c, stmt_ty s)
return
0
;
PyArena_AddPyObject
(
c
->
c_arena
,
tmpvalue
);
}
tmpexit
=
compiler_new_tmpname
(
c
);
if
(
tmpexit
==
NULL
)
return
0
;
PyArena_AddPyObject
(
c
->
c_arena
,
tmpexit
);
/* Evaluate 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 */
ADDOP
(
c
,
DUP_TOP
);
ADDOP_O
(
c
,
LOAD_ATTR
,
exit_attr
,
names
);
ADDOP
(
c
,
ROT_TWO
);
if
(
!
compiler_nameop
(
c
,
tmpexit
,
Store
))
return
0
;
/* Call context.__enter__() */
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
the exception or return information. Just issue our magic
opcode. */
if
(
!
compiler_nameop
(
c
,
tmpexit
,
Load
)
||
!
compiler_nameop
(
c
,
tmpexit
,
Del
))
return
0
;
ADDOP
(
c
,
WITH_CLEANUP
);
/* Finally block ends. */
...
...
Python/import.c
View file @
c86ac884
...
...
@@ -86,8 +86,9 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
3100 (merge from 2.6a0, see 62151)
3102 (__file__ points to source file)
Python 3.0a4: 3110 (WITH_CLEANUP optimization).
Python 3.0a5: 3130 (lexical exception stacking, including POP_EXCEPT)
*/
#define MAGIC (31
1
0 | ((long)'\r'<<16) | ((long)'\n'<<24))
#define MAGIC (31
3
0 | ((long)'\r'<<16) | ((long)'\n'<<24))
/* Magic word as global; note that _PyImport_Init() can change the
value of this global to accommodate for alterations of how the
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment