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
e6528211
Commit
e6528211
authored
Jul 15, 2008
by
Benjamin Peterson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
implement chained exception tracebacks
patch from Antoine Pitrou #3112
parent
9bab65c2
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
445 additions
and
129 deletions
+445
-129
Include/traceback.h
Include/traceback.h
+1
-1
Lib/test/test_raise.py
Lib/test/test_raise.py
+24
-0
Lib/test/test_traceback.py
Lib/test/test_traceback.py
+130
-6
Lib/traceback.py
Lib/traceback.py
+63
-21
Misc/NEWS
Misc/NEWS
+4
-0
Modules/_testcapimodule.c
Modules/_testcapimodule.c
+21
-0
Python/_warnings.c
Python/_warnings.c
+1
-2
Python/errors.c
Python/errors.c
+20
-2
Python/pythonrun.c
Python/pythonrun.c
+161
-90
Python/traceback.c
Python/traceback.c
+20
-7
No files found.
Include/traceback.h
View file @
e6528211
...
...
@@ -19,7 +19,7 @@ typedef struct _traceback {
PyAPI_FUNC
(
int
)
PyTraceBack_Here
(
struct
_frame
*
);
PyAPI_FUNC
(
int
)
PyTraceBack_Print
(
PyObject
*
,
PyObject
*
);
PyAPI_FUNC
(
int
)
Py_DisplaySourceLine
(
PyObject
*
,
const
char
*
,
int
);
PyAPI_FUNC
(
int
)
Py_DisplaySourceLine
(
PyObject
*
,
const
char
*
,
int
,
int
);
/* Reveal traceback type so we can typecheck traceback objects */
PyAPI_DATA
(
PyTypeObject
)
PyTraceBack_Type
;
...
...
Lib/test/test_raise.py
View file @
e6528211
...
...
@@ -278,6 +278,30 @@ class TestContext(unittest.TestCase):
else
:
self
.
fail
(
"No exception raised"
)
def
test_cycle_broken
(
self
):
# Self-cycles (when re-raising a caught exception) are broken
try
:
try
:
1
/
0
except
ZeroDivisionError
as
e
:
raise
e
except
ZeroDivisionError
as
e
:
self
.
failUnless
(
e
.
__context__
is
None
,
e
.
__context__
)
def
test_reraise_cycle_broken
(
self
):
# Non-trivial context cycles (through re-raising a previous exception)
# are broken too.
try
:
try
:
xyzzy
except
NameError
as
a
:
try
:
1
/
0
except
ZeroDivisionError
:
raise
a
except
NameError
as
e
:
self
.
failUnless
(
e
.
__context__
.
__context__
is
None
)
class
TestRemovedFunctionality
(
unittest
.
TestCase
):
def
test_tuples
(
self
):
...
...
Lib/test/test_traceback.py
View file @
e6528211
"""Test cases for traceback module"""
from
_testcapi
import
traceback_print
from
_testcapi
import
traceback_print
,
exception_print
from
io
import
StringIO
import
sys
import
unittest
from
test.support
import
run_unittest
,
is_jython
,
Error
import
re
from
test.support
import
run_unittest
,
is_jython
,
Error
,
captured_output
import
traceback
...
...
@@ -19,7 +20,7 @@ else:
raise
Error
(
"unable to create test traceback string"
)
class
TracebackCases
(
unittest
.
TestCase
):
class
Syntax
TracebackCases
(
unittest
.
TestCase
):
# For now, a very minimal set of tests. I want to be sure that
# formatting of SyntaxErrors works based on changes for 2.1.
...
...
@@ -99,12 +100,135 @@ class TracebackFormatTests(unittest.TestCase):
banner
,
location
,
source_line
=
tb_lines
self
.
assert_
(
banner
.
startswith
(
'Traceback'
))
self
.
assert_
(
location
.
startswith
(
' File'
))
self
.
assert_
(
source_line
.
startswith
(
'raise'
))
self
.
assert_
(
source_line
.
startswith
(
'
raise'
))
def
test_main
():
run_unittest
(
TracebackCases
,
TracebackFormatTests
)
cause_message
=
(
"
\
n
The above exception was the direct cause "
"of the following exception:
\
n
\
n
"
)
context_message
=
(
"
\
n
During handling of the above exception, "
"another exception occurred:
\
n
\
n
"
)
boundaries
=
re
.
compile
(
'(%s|%s)'
%
(
re
.
escape
(
cause_message
),
re
.
escape
(
context_message
)))
class
BaseExceptionReportingTests
:
def
get_exception
(
self
,
exception_or_callable
):
if
isinstance
(
exception_or_callable
,
Exception
):
return
exception_or_callable
try
:
exception_or_callable
()
except
Exception
as
e
:
return
e
def
zero_div
(
self
):
1
/
0
# In zero_div
def
check_zero_div
(
self
,
msg
):
lines
=
msg
.
splitlines
()
self
.
assert_
(
lines
[
-
3
].
startswith
(
' File'
))
self
.
assert_
(
'1/0 # In zero_div'
in
lines
[
-
2
],
lines
[
-
2
])
self
.
assert_
(
lines
[
-
1
].
startswith
(
'ZeroDivisionError'
),
lines
[
-
1
])
def
test_simple
(
self
):
try
:
1
/
0
# Marker
except
ZeroDivisionError
as
_
:
e
=
_
lines
=
self
.
get_report
(
e
).
splitlines
()
self
.
assertEquals
(
len
(
lines
),
4
)
self
.
assert_
(
lines
[
0
].
startswith
(
'Traceback'
))
self
.
assert_
(
lines
[
1
].
startswith
(
' File'
))
self
.
assert_
(
'1/0 # Marker'
in
lines
[
2
])
self
.
assert_
(
lines
[
3
].
startswith
(
'ZeroDivisionError'
))
def
test_cause
(
self
):
def
inner_raise
():
try
:
self
.
zero_div
()
except
ZeroDivisionError
as
e
:
raise
KeyError
from
e
def
outer_raise
():
inner_raise
()
# Marker
blocks
=
boundaries
.
split
(
self
.
get_report
(
outer_raise
))
self
.
assertEquals
(
len
(
blocks
),
3
)
self
.
assertEquals
(
blocks
[
1
],
cause_message
)
self
.
check_zero_div
(
blocks
[
0
])
self
.
assert_
(
'inner_raise() # Marker'
in
blocks
[
2
])
def
test_context
(
self
):
def
inner_raise
():
try
:
self
.
zero_div
()
except
ZeroDivisionError
:
raise
KeyError
def
outer_raise
():
inner_raise
()
# Marker
blocks
=
boundaries
.
split
(
self
.
get_report
(
outer_raise
))
self
.
assertEquals
(
len
(
blocks
),
3
)
self
.
assertEquals
(
blocks
[
1
],
context_message
)
self
.
check_zero_div
(
blocks
[
0
])
self
.
assert_
(
'inner_raise() # Marker'
in
blocks
[
2
])
def
test_cause_recursive
(
self
):
def
inner_raise
():
try
:
try
:
self
.
zero_div
()
except
ZeroDivisionError
as
e
:
z
=
e
raise
KeyError
from
e
except
KeyError
as
e
:
raise
z
from
e
def
outer_raise
():
inner_raise
()
# Marker
blocks
=
boundaries
.
split
(
self
.
get_report
(
outer_raise
))
self
.
assertEquals
(
len
(
blocks
),
3
)
self
.
assertEquals
(
blocks
[
1
],
cause_message
)
# The first block is the KeyError raised from the ZeroDivisionError
self
.
assert_
(
'raise KeyError from e'
in
blocks
[
0
])
self
.
assert_
(
'1/0'
not
in
blocks
[
0
])
# The second block (apart from the boundary) is the ZeroDivisionError
# re-raised from the KeyError
self
.
assert_
(
'inner_raise() # Marker'
in
blocks
[
2
])
self
.
check_zero_div
(
blocks
[
2
])
class
PyExcReportingTests
(
BaseExceptionReportingTests
,
unittest
.
TestCase
):
#
# This checks reporting through the 'traceback' module, with both
# format_exception() and print_exception().
#
def
get_report
(
self
,
e
):
e
=
self
.
get_exception
(
e
)
s
=
''
.
join
(
traceback
.
format_exception
(
type
(
e
),
e
,
e
.
__traceback__
))
with
captured_output
(
"stderr"
)
as
sio
:
traceback
.
print_exception
(
type
(
e
),
e
,
e
.
__traceback__
)
self
.
assertEquals
(
sio
.
getvalue
(),
s
)
return
s
class
CExcReportingTests
(
BaseExceptionReportingTests
,
unittest
.
TestCase
):
#
# This checks built-in reporting by the interpreter.
#
def
get_report
(
self
,
e
):
e
=
self
.
get_exception
(
e
)
with
captured_output
(
"stderr"
)
as
s
:
exception_print
(
e
)
return
s
.
getvalue
()
def
test_main
():
run_unittest
(
__name__
)
if
__name__
==
"__main__"
:
test_main
()
Lib/traceback.py
View file @
e6528211
...
...
@@ -3,6 +3,7 @@
import
linecache
import
sys
import
types
import
itertools
__all__
=
[
'extract_stack'
,
'extract_tb'
,
'format_exception'
,
'format_exception_only'
,
'format_list'
,
'format_stack'
,
...
...
@@ -107,7 +108,32 @@ def extract_tb(tb, limit = None):
return
list
def
print_exception
(
etype
,
value
,
tb
,
limit
=
None
,
file
=
None
):
_cause_message
=
(
"
\
n
The above exception was the direct cause "
"of the following exception:
\
n
"
)
_context_message
=
(
"
\
n
During handling of the above exception, "
"another exception occurred:
\
n
"
)
def
_iter_chain
(
exc
,
custom_tb
=
None
,
seen
=
None
):
if
seen
is
None
:
seen
=
set
()
seen
.
add
(
exc
)
its
=
[]
cause
=
exc
.
__cause__
context
=
exc
.
__context__
if
cause
is
not
None
and
cause
not
in
seen
:
its
.
append
(
_iter_chain
(
cause
,
None
,
seen
))
its
.
append
([(
_cause_message
,
None
)])
if
context
is
not
None
and
context
is
not
cause
and
context
not
in
seen
:
its
.
append
(
_iter_chain
(
context
,
None
,
seen
))
its
.
append
([(
_context_message
,
None
)])
its
.
append
([(
exc
,
custom_tb
or
exc
.
__traceback__
)])
return
itertools
.
chain
(
*
its
)
def
print_exception
(
etype
,
value
,
tb
,
limit
=
None
,
file
=
None
,
chain
=
True
):
"""Print exception up to 'limit' stack trace entries from 'tb' to 'file'.
This differs from print_tb() in the following ways: (1) if
...
...
@@ -120,15 +146,23 @@ def print_exception(etype, value, tb, limit=None, file=None):
"""
if
file
is
None
:
file
=
sys
.
stderr
if
tb
:
_print
(
file
,
'Traceback (most recent call last):'
)
print_tb
(
tb
,
limit
,
file
)
lines
=
format_exception_only
(
etype
,
value
)
for
line
in
lines
[:
-
1
]:
_print
(
file
,
line
,
' '
)
_print
(
file
,
lines
[
-
1
],
''
)
def
format_exception
(
etype
,
value
,
tb
,
limit
=
None
):
if
chain
:
values
=
_iter_chain
(
value
,
tb
)
else
:
values
=
[(
value
,
tb
)]
for
value
,
tb
in
values
:
if
isinstance
(
value
,
str
):
_print
(
file
,
value
)
continue
if
tb
:
_print
(
file
,
'Traceback (most recent call last):'
)
print_tb
(
tb
,
limit
,
file
)
lines
=
format_exception_only
(
type
(
value
),
value
)
for
line
in
lines
[:
-
1
]:
_print
(
file
,
line
,
' '
)
_print
(
file
,
lines
[
-
1
],
''
)
def
format_exception
(
etype
,
value
,
tb
,
limit
=
None
,
chain
=
True
):
"""Format a stack trace and the exception information.
The arguments have the same meaning as the corresponding arguments
...
...
@@ -137,12 +171,19 @@ def format_exception(etype, value, tb, limit = None):
these lines are concatenated and printed, exactly the same text is
printed as does print_exception().
"""
if
tb
:
list
=
[
'Traceback (most recent call last):
\
n
'
]
list
=
list
+
format_tb
(
tb
,
limit
)
list
=
[]
if
chain
:
values
=
_iter_chain
(
value
,
tb
)
else
:
list
=
[]
list
=
list
+
format_exception_only
(
etype
,
value
)
values
=
[(
value
,
tb
)]
for
value
,
tb
in
values
:
if
isinstance
(
value
,
str
):
list
.
append
(
value
+
'
\
n
'
)
continue
if
tb
:
list
.
append
(
'Traceback (most recent call last):
\
n
'
)
list
.
extend
(
format_tb
(
tb
,
limit
))
list
.
extend
(
format_exception_only
(
type
(
value
),
value
))
return
list
def
format_exception_only
(
etype
,
value
):
...
...
@@ -208,33 +249,34 @@ def _some_str(value):
return
'<unprintable %s object>'
%
type
(
value
).
__name__
def
print_exc
(
limit
=
None
,
file
=
None
):
def
print_exc
(
limit
=
None
,
file
=
None
,
chain
=
True
):
"""Shorthand for 'print_exception(*sys.exc_info(), limit, file)'."""
if
file
is
None
:
file
=
sys
.
stderr
try
:
etype
,
value
,
tb
=
sys
.
exc_info
()
print_exception
(
etype
,
value
,
tb
,
limit
,
file
)
print_exception
(
etype
,
value
,
tb
,
limit
,
file
,
chain
)
finally
:
etype
=
value
=
tb
=
None
def
format_exc
(
limit
=
None
):
def
format_exc
(
limit
=
None
,
chain
=
True
):
"""Like print_exc() but return a string."""
try
:
etype
,
value
,
tb
=
sys
.
exc_info
()
return
''
.
join
(
format_exception
(
etype
,
value
,
tb
,
limit
))
return
''
.
join
(
format_exception
(
etype
,
value
,
tb
,
limit
,
chain
))
finally
:
etype
=
value
=
tb
=
None
def
print_last
(
limit
=
None
,
file
=
None
):
def
print_last
(
limit
=
None
,
file
=
None
,
chain
=
True
):
"""This is a shorthand for 'print_exception(sys.last_type,
sys.last_value, sys.last_traceback, limit, file)'."""
if
file
is
None
:
file
=
sys
.
stderr
print_exception
(
sys
.
last_type
,
sys
.
last_value
,
sys
.
last_traceback
,
limit
,
file
)
limit
,
file
,
chain
)
def
print_stack
(
f
=
None
,
limit
=
None
,
file
=
None
):
...
...
Misc/NEWS
View file @
e6528211
...
...
@@ -22,6 +22,8 @@ Core and Builtins
- Issue #3236: Return small longs from PyLong_FromString.
- Exception tracebacks now support exception chaining.
Library
-------
...
...
@@ -35,6 +37,8 @@ Library
- All the u* variant functions and methods in gettext have been renamed to their
none u* siblings.
- The traceback module has been expanded to handle chained exceptions.
C API
-----
...
...
Modules/_testcapimodule.c
View file @
e6528211
...
...
@@ -951,6 +951,26 @@ traceback_print(PyObject *self, PyObject *args)
Py_RETURN_NONE
;
}
/* To test the format of exceptions as printed out. */
static
PyObject
*
exception_print
(
PyObject
*
self
,
PyObject
*
args
)
{
PyObject
*
value
;
PyObject
*
tb
;
if
(
!
PyArg_ParseTuple
(
args
,
"O:exception_print"
,
&
value
))
return
NULL
;
tb
=
PyException_GetTraceback
(
value
);
PyErr_Display
((
PyObject
*
)
Py_TYPE
(
value
),
value
,
tb
);
Py_XDECREF
(
tb
);
Py_RETURN_NONE
;
}
static
PyMethodDef
TestMethods
[]
=
{
{
"raise_exception"
,
raise_exception
,
METH_VARARGS
},
{
"test_config"
,
(
PyCFunction
)
test_config
,
METH_NOARGS
},
...
...
@@ -995,6 +1015,7 @@ static PyMethodDef TestMethods[] = {
{
"profile_int"
,
profile_int
,
METH_NOARGS
},
#endif
{
"traceback_print"
,
traceback_print
,
METH_VARARGS
},
{
"exception_print"
,
exception_print
,
METH_VARARGS
},
{
NULL
,
NULL
}
/* sentinel */
};
...
...
Python/_warnings.c
View file @
e6528211
...
...
@@ -256,7 +256,6 @@ show_warning(PyObject *filename, int lineno, PyObject *text, PyObject
Py_XDECREF
(
name
);
/* Print " source_line\n" */
PyFile_WriteString
(
" "
,
f_stderr
);
if
(
sourceline
)
{
char
*
source_line_str
=
PyUnicode_AsString
(
sourceline
);
while
(
*
source_line_str
==
' '
||
*
source_line_str
==
'\t'
||
...
...
@@ -267,7 +266,7 @@ show_warning(PyObject *filename, int lineno, PyObject *text, PyObject
PyFile_WriteString
(
"
\n
"
,
f_stderr
);
}
else
Py_DisplaySourceLine
(
f_stderr
,
PyUnicode_AsString
(
filename
),
lineno
);
Py_DisplaySourceLine
(
f_stderr
,
PyUnicode_AsString
(
filename
),
lineno
,
2
);
PyErr_Clear
();
}
...
...
Python/errors.c
View file @
e6528211
...
...
@@ -84,8 +84,23 @@ PyErr_SetObject(PyObject *exception, PyObject *value)
return
;
value
=
fixed_value
;
}
Py_INCREF
(
tstate
->
exc_value
);
PyException_SetContext
(
value
,
tstate
->
exc_value
);
/* Avoid reference cycles through the context chain.
This is O(chain length) but context chains are
usually very short. Sensitive readers may try
to inline the call to PyException_GetContext. */
if
(
tstate
->
exc_value
!=
value
)
{
PyObject
*
o
=
tstate
->
exc_value
,
*
context
;
while
((
context
=
PyException_GetContext
(
o
)))
{
Py_DECREF
(
context
);
if
(
context
==
value
)
{
PyException_SetContext
(
o
,
NULL
);
break
;
}
o
=
context
;
}
Py_INCREF
(
tstate
->
exc_value
);
PyException_SetContext
(
value
,
tstate
->
exc_value
);
}
}
if
(
value
!=
NULL
&&
PyExceptionInstance_Check
(
value
))
tb
=
PyException_GetTraceback
(
value
);
...
...
@@ -160,6 +175,9 @@ PyErr_ExceptionMatches(PyObject *exc)
/* Used in many places to normalize a raised exception, including in
eval_code2(), do_raise(), and PyErr_Print()
XXX: should PyErr_NormalizeException() also call
PyException_SetTraceback() with the resulting value and tb?
*/
void
PyErr_NormalizeException
(
PyObject
**
exc
,
PyObject
**
val
,
PyObject
**
tb
)
...
...
Python/pythonrun.c
View file @
e6528211
...
...
@@ -1242,18 +1242,19 @@ PyErr_PrintEx(int set_sys_last_vars)
if
(
exception
==
NULL
)
return
;
PyErr_NormalizeException
(
&
exception
,
&
v
,
&
tb
);
tb
=
tb
?
tb
:
Py_None
;
PyException_SetTraceback
(
v
,
tb
);
if
(
exception
==
NULL
)
return
;
/* Now we know v != NULL too */
if
(
set_sys_last_vars
)
{
PySys_SetObject
(
"last_type"
,
exception
);
PySys_SetObject
(
"last_value"
,
v
);
PySys_SetObject
(
"last_traceback"
,
tb
?
tb
:
Py_None
);
PySys_SetObject
(
"last_traceback"
,
tb
);
}
hook
=
PySys_GetObject
(
"excepthook"
);
if
(
hook
)
{
PyObject
*
args
=
PyTuple_Pack
(
3
,
exception
,
v
,
tb
?
tb
:
Py_None
);
PyObject
*
args
=
PyTuple_Pack
(
3
,
exception
,
v
,
tb
);
PyObject
*
result
=
PyEval_CallObject
(
hook
,
args
);
if
(
result
==
NULL
)
{
PyObject
*
exception2
,
*
v2
,
*
tb2
;
...
...
@@ -1293,106 +1294,100 @@ PyErr_PrintEx(int set_sys_last_vars)
Py_XDECREF
(
tb
);
}
void
PyErr_Display
(
PyObject
*
exception
,
PyObject
*
value
,
PyObject
*
tb
)
static
void
print_exception
(
PyObject
*
f
,
PyObject
*
value
)
{
int
err
=
0
;
PyObject
*
f
=
PySys_GetObject
(
"stderr"
);
PyObject
*
type
,
*
tb
;
Py_INCREF
(
value
);
if
(
f
==
Py_None
)
{
/* pass */
fflush
(
stdout
);
type
=
(
PyObject
*
)
Py_TYPE
(
value
);
tb
=
PyException_GetTraceback
(
value
);
if
(
tb
&&
tb
!=
Py_None
)
err
=
PyTraceBack_Print
(
tb
,
f
);
if
(
err
==
0
&&
PyObject_HasAttrString
(
value
,
"print_file_and_line"
))
{
PyObject
*
message
;
const
char
*
filename
,
*
text
;
int
lineno
,
offset
;
if
(
!
parse_syntax_error
(
value
,
&
message
,
&
filename
,
&
lineno
,
&
offset
,
&
text
))
PyErr_Clear
();
else
{
char
buf
[
10
];
PyFile_WriteString
(
" File
\"
"
,
f
);
if
(
filename
==
NULL
)
PyFile_WriteString
(
"<string>"
,
f
);
else
PyFile_WriteString
(
filename
,
f
);
PyFile_WriteString
(
"
\"
, line "
,
f
);
PyOS_snprintf
(
buf
,
sizeof
(
buf
),
"%d"
,
lineno
);
PyFile_WriteString
(
buf
,
f
);
PyFile_WriteString
(
"
\n
"
,
f
);
if
(
text
!=
NULL
)
print_error_text
(
f
,
offset
,
text
);
Py_DECREF
(
value
);
value
=
message
;
/* Can't be bothered to check all those
PyFile_WriteString() calls */
if
(
PyErr_Occurred
())
err
=
-
1
;
}
}
else
if
(
f
==
NULL
)
{
_PyObject_Dump
(
value
);
fprintf
(
stderr
,
"lost sys.stderr
\n
"
);
if
(
err
)
{
/* Don't do anything else */
}
else
{
fflush
(
stdout
);
if
(
tb
&&
tb
!=
Py_None
)
err
=
PyTraceBack_Print
(
tb
,
f
);
if
(
err
==
0
&&
PyObject_HasAttrString
(
value
,
"print_file_and_line"
))
{
PyObject
*
message
;
const
char
*
filename
,
*
text
;
int
lineno
,
offset
;
if
(
!
parse_syntax_error
(
value
,
&
message
,
&
filename
,
&
lineno
,
&
offset
,
&
text
))
PyErr_Clear
();
else
{
char
buf
[
10
];
PyFile_WriteString
(
" File
\"
"
,
f
);
if
(
filename
==
NULL
)
PyFile_WriteString
(
"<string>"
,
f
);
else
PyFile_WriteString
(
filename
,
f
);
PyFile_WriteString
(
"
\"
, line "
,
f
);
PyOS_snprintf
(
buf
,
sizeof
(
buf
),
"%d"
,
lineno
);
PyFile_WriteString
(
buf
,
f
);
PyFile_WriteString
(
"
\n
"
,
f
);
if
(
text
!=
NULL
)
print_error_text
(
f
,
offset
,
text
);
Py_DECREF
(
value
);
value
=
message
;
/* Can't be bothered to check all those
PyFile_WriteString() calls */
if
(
PyErr_Occurred
())
err
=
-
1
;
}
assert
(
PyExceptionClass_Check
(
type
));
PyObject
*
moduleName
;
char
*
className
=
PyExceptionClass_Name
(
type
);
if
(
className
!=
NULL
)
{
char
*
dot
=
strrchr
(
className
,
'.'
);
if
(
dot
!=
NULL
)
className
=
dot
+
1
;
}
if
(
err
)
{
/* Don't do anything else */
}
else
if
(
PyExceptionClass_Check
(
exception
))
{
PyObject
*
moduleName
;
char
*
className
=
PyExceptionClass_Name
(
exception
);
if
(
className
!=
NULL
)
{
char
*
dot
=
strrchr
(
className
,
'.'
);
if
(
dot
!=
NULL
)
className
=
dot
+
1
;
}
moduleName
=
PyObject_GetAttrString
(
exception
,
"__module__"
);
if
(
moduleName
==
NULL
||
!
PyUnicode_Check
(
moduleName
))
moduleName
=
PyObject_GetAttrString
(
type
,
"__module__"
);
if
(
moduleName
==
NULL
||
!
PyUnicode_Check
(
moduleName
))
{
Py_DECREF
(
moduleName
);
err
=
PyFile_WriteString
(
"<unknown>"
,
f
);
}
else
{
char
*
modstr
=
PyUnicode_AsString
(
moduleName
);
if
(
modstr
&&
strcmp
(
modstr
,
"builtins"
))
{
Py_DECREF
(
moduleName
);
err
=
PyFile_WriteString
(
"<unknown>"
,
f
);
}
else
{
char
*
modstr
=
PyUnicode_AsString
(
moduleName
);
if
(
modstr
&&
strcmp
(
modstr
,
"builtins"
))
{
err
=
PyFile_WriteString
(
modstr
,
f
);
err
+=
PyFile_WriteString
(
"."
,
f
);
}
Py_DECREF
(
moduleName
);
}
if
(
err
==
0
)
{
if
(
className
==
NULL
)
err
=
PyFile_WriteString
(
"<unknown>"
,
f
);
else
err
=
PyFile_WriteString
(
className
,
f
);
err
=
PyFile_WriteString
(
modstr
,
f
);
err
+=
PyFile_WriteString
(
"."
,
f
);
}
Py_DECREF
(
moduleName
);
}
else
err
=
PyFile_WriteObject
(
exception
,
f
,
Py_PRINT_RAW
);
if
(
err
==
0
&&
(
value
!=
Py_None
))
{
PyObject
*
s
=
PyObject_Str
(
value
);
/* only print colon if the str() of the
object is not the empty string
*/
if
(
s
==
NULL
)
err
=
-
1
;
else
if
(
!
PyUnicode_Check
(
s
)
||
PyUnicode_GetSize
(
s
)
!=
0
)
err
=
PyFile_WriteString
(
": "
,
f
);
if
(
err
==
0
)
err
=
PyFile_WriteObject
(
s
,
f
,
Py_PRINT_RAW
);
Py_XDECREF
(
s
);
if
(
err
==
0
)
{
if
(
className
==
NULL
)
err
=
PyFile_WriteString
(
"<unknown>"
,
f
);
else
err
=
PyFile_WriteString
(
className
,
f
);
}
/* try to write a newline in any case */
err
+=
PyFile_WriteString
(
"
\n
"
,
f
);
}
if
(
err
==
0
&&
(
value
!=
Py_None
))
{
PyObject
*
s
=
PyObject_Str
(
value
);
/* only print colon if the str() of the
object is not the empty string
*/
if
(
s
==
NULL
)
err
=
-
1
;
else
if
(
!
PyUnicode_Check
(
s
)
||
PyUnicode_GetSize
(
s
)
!=
0
)
err
=
PyFile_WriteString
(
": "
,
f
);
if
(
err
==
0
)
err
=
PyFile_WriteObject
(
s
,
f
,
Py_PRINT_RAW
);
Py_XDECREF
(
s
);
}
/* try to write a newline in any case */
err
+=
PyFile_WriteString
(
"
\n
"
,
f
);
Py_XDECREF
(
tb
);
Py_DECREF
(
value
);
/* If an error happened here, don't show it.
XXX This is wrong, but too many callers rely on this behavior. */
...
...
@@ -1400,6 +1395,82 @@ PyErr_Display(PyObject *exception, PyObject *value, PyObject *tb)
PyErr_Clear
();
}
static
const
char
*
cause_message
=
"
\n
The above exception was the direct cause "
"of the following exception:
\n\n
"
;
static
const
char
*
context_message
=
"
\n
During handling of the above exception, "
"another exception occurred:
\n\n
"
;
static
void
print_exception_recursive
(
PyObject
*
f
,
PyObject
*
value
,
PyObject
*
seen
)
{
int
err
=
0
,
res
;
PyObject
*
cause
,
*
context
;
if
(
seen
!=
NULL
)
{
/* Exception chaining */
if
(
PySet_Add
(
seen
,
value
)
==
-
1
)
PyErr_Clear
();
else
if
(
PyExceptionInstance_Check
(
value
))
{
cause
=
PyException_GetCause
(
value
);
context
=
PyException_GetContext
(
value
);
if
(
cause
)
{
res
=
PySet_Contains
(
seen
,
cause
);
if
(
res
==
-
1
)
PyErr_Clear
();
if
(
res
==
0
)
{
print_exception_recursive
(
f
,
cause
,
seen
);
err
|=
PyFile_WriteString
(
cause_message
,
f
);
}
}
if
(
context
)
{
res
=
PySet_Contains
(
seen
,
context
);
if
(
res
==
-
1
)
PyErr_Clear
();
if
(
res
==
0
)
{
print_exception_recursive
(
f
,
context
,
seen
);
err
|=
PyFile_WriteString
(
context_message
,
f
);
}
}
Py_XDECREF
(
context
);
Py_XDECREF
(
cause
);
}
}
print_exception
(
f
,
value
);
if
(
err
!=
0
)
PyErr_Clear
();
}
void
PyErr_Display
(
PyObject
*
exception
,
PyObject
*
value
,
PyObject
*
tb
)
{
PyObject
*
seen
;
PyObject
*
f
=
PySys_GetObject
(
"stderr"
);
if
(
f
==
Py_None
)
{
/* pass */
}
else
if
(
f
==
NULL
)
{
_PyObject_Dump
(
value
);
fprintf
(
stderr
,
"lost sys.stderr
\n
"
);
}
else
{
/* We choose to ignore seen being possibly NULL, and report
at least the main exception (it could be a MemoryError).
*/
seen
=
PySet_New
(
NULL
);
if
(
seen
==
NULL
)
PyErr_Clear
();
print_exception_recursive
(
f
,
value
,
seen
);
Py_XDECREF
(
seen
);
}
}
PyObject
*
PyRun_StringFlags
(
const
char
*
str
,
int
start
,
PyObject
*
globals
,
PyObject
*
locals
,
PyCompilerFlags
*
flags
)
...
...
Python/traceback.c
View file @
e6528211
...
...
@@ -129,7 +129,7 @@ PyTraceBack_Here(PyFrameObject *frame)
}
int
Py_DisplaySourceLine
(
PyObject
*
f
,
const
char
*
filename
,
int
lineno
)
Py_DisplaySourceLine
(
PyObject
*
f
,
const
char
*
filename
,
int
lineno
,
int
indent
)
{
int
err
=
0
;
FILE
*
xfp
=
NULL
;
...
...
@@ -139,8 +139,6 @@ Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno)
if
(
filename
==
NULL
)
return
-
1
;
/* This is needed by Emacs' compile command */
#define FMT " File \"%.500s\", line %d, in %.500s\n"
xfp
=
fopen
(
filename
,
"r"
PY_STDIOTEXTMODE
);
if
(
xfp
==
NULL
)
{
/* Search tail of filename in sys.path before giving up */
...
...
@@ -203,12 +201,27 @@ Py_DisplaySourceLine(PyObject *f, const char *filename, int lineno)
}
while
(
*
pLastChar
!=
'\0'
&&
*
pLastChar
!=
'\n'
);
}
if
(
i
==
lineno
)
{
char
buf
[
11
];
char
*
p
=
linebuf
;
while
(
*
p
==
' '
||
*
p
==
'\t'
||
*
p
==
'\014'
)
p
++
;
err
=
PyFile_WriteString
(
p
,
f
);
if
(
err
==
0
&&
strchr
(
p
,
'\n'
)
==
NULL
)
err
=
PyFile_WriteString
(
"
\n
"
,
f
);
/* Write some spaces before the line */
strcpy
(
buf
,
" "
);
assert
(
strlen
(
buf
)
==
10
);
while
(
indent
>
0
)
{
if
(
indent
<
10
)
buf
[
indent
]
=
'\0'
;
err
=
PyFile_WriteString
(
buf
,
f
);
if
(
err
!=
0
)
break
;
indent
-=
10
;
}
if
(
err
==
0
)
err
=
PyFile_WriteString
(
p
,
f
);
if
(
err
==
0
&&
strchr
(
p
,
'\n'
)
==
NULL
)
err
=
PyFile_WriteString
(
"
\n
"
,
f
);
}
fclose
(
xfp
);
return
err
;
...
...
@@ -228,7 +241,7 @@ tb_displayline(PyObject *f, const char *filename, int lineno, const char *name)
err
=
PyFile_WriteString
(
linebuf
,
f
);
if
(
err
!=
0
)
return
err
;
return
Py_DisplaySourceLine
(
f
,
filename
,
lineno
);
return
Py_DisplaySourceLine
(
f
,
filename
,
lineno
,
4
);
}
static
int
...
...
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