8000 gh-67224: Show source lines in tracebacks when using the -c option wh… · python/cpython@90a1b28 · GitHub
[go: up one dir, main page]

Skip to content

Commit 90a1b28

Browse files
authored
gh-67224: Show source lines in tracebacks when using the -c option when running Python (#111200)
1 parent 3f84a19 commit 90a1b28

File tree

13 files changed

+104
-36
lines changed

13 files changed

+104
-36
lines changed

Include/internal/pycore_pylifecycle.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ extern int _Py_LegacyLocaleDetected(int warn);
114114
// Export for 'readline' shared extension
115115
PyAPI_FUNC(char*) _Py_SetLocaleFromEnv(int category);
116116

117+
// Export for special main.c string compiling with source tracebacks
118+
int _PyRun_SimpleStringFlagsWithName(const char *command, const char* name, PyCompilerFlags *flags);
119+
117120
#ifdef __cplusplus
118121
}
119122
#endif

Lib/linecache.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,8 @@
55
that name.
66
"""
77

8-
import functools
98
import sys
109
import os
11< 67E6 code>-
import tokenize
1210

1311
__all__ = ["getline", "clearcache", "checkcache", "lazycache"]
1412

@@ -82,6 +80,8 @@ def updatecache(filename, module_globals=None):
8280
If something's wrong, print a message, discard the cache entry,
8381
and return an empty list."""
8482

83+
import tokenize
84+
8585
if filename in cache:
8686
if len(cache[filename]) != 1:
8787
cache.pop(filename, None)
@@ -176,11 +176,13 @@ def lazycache(filename, module_globals):
176176
get_source = getattr(loader, 'get_source', None)
177177

178178
if name and get_source:
179-
get_lines = functools.partial(get_source, name)
179+
def get_lines(name=name, *args, **kwargs):
180+
return get_source(name, *args, **kwargs)
180181
cache[filename] = (get_lines,)
181182
return True
182183
return False
183184

185+
184186
def _register_code(code, string, name):
185187
cache[code] = (
186188
len(string),

Lib/test/test_cmd_line_script.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,16 @@ def test_syntaxerror_null_bytes_in_multiline_string(self):
684684
]
685685
)
686686

687+
def test_source_lines_are_shown_when_running_source(self):
688+
_, _, stderr = assert_python_failure("-c", "1/0")
689+
expected_lines = [
690+
b'Traceback (most recent call last):',
691+
b' File "<string>", line 1, in <module>',
692+
b' 1/0',
693+
b' ~^~',
694+
b'ZeroDivisionError: division by zero']
695+
self.assertEqual(stderr.splitlines(), expected_lines)
696+
687697
def test_syntaxerror_does_not_crash(self):
688698
script = "nonlocal x\n"
689699
with os_helper.temp_dir() as script_dir:

Lib/test/test_io.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4396,11 +4396,11 @@ def test_check_encoding_warning(self):
43964396
''')
43974397
proc = assert_python_ok('-X', 'warn_default_encoding', '-c', code)
43984398
warnings = proc.err.splitlines()
4399-
self.assertEqual(len(warnings), 2)
4399+
self.assertEqual(len(warnings), 4)
44004400
self.assertTrue(
44014401
warnings[0].startswith(b"<string>:5: EncodingWarning: "))
44024402
self.assertTrue(
4403-
warnings[1].startswith(b"<string>:8: EncodingWarning: "))
4403+
warnings[2].startswith(b"<string>:8: EncodingWarning: "))
44044404

44054405
def test_text_encoding(self):
44064406
# PEP 597, bpo-47000. io.text_encoding() returns "locale" or "utf-8"

Lib/test/test_repl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def bar(x):
184184
p.stdin.write(user_input)
185185
user_input2 = dedent("""
186186
import linecache
187-
print(linecache.cache['<python-input-1>'])
187+
print(linecache.cache['<stdin>-1'])
188188
""")
189189
p.stdin.write(user_input2)
190190
output = kill_python(p)

Lib/test/test_subprocess.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,9 +1769,9 @@ def test_encoding_warning(self):
17691769
cp = subprocess.run([sys.executable, "-Xwarn_default_encoding", "-c", code],
17701770
capture_output=True)
17711771
lines = cp.stderr.splitlines()
1772-
self.assertEqual(len(lines), 2, lines)
1772+
self.assertEqual(len(lines), 4, lines)
17731773
self.assertTrue(lines[0].startswith(b"<string>:2: EncodingWarning: "))
1774-
self.assertTrue(lines[1].startswith(b"<string>:3: EncodingWarning: "))
1774+
self.assertTrue(lines[2].startswith(b"<string>:3: EncodingWarning: "))
17751775

17761776

17771777
def _get_test_grp_name():

Lib/test/test_sys.py

Lines changed: 6 additions & 2 deletions
check(10, traceback)
Original file line numberDiff line numberDiff line change
@@ -1114,14 +1114,18 @@ def check(tracebacklimit, expected):
11141114
traceback = [
11151115
b'Traceback (most recent call last):',
11161116
b' File "<string>", line 8, in <module>',
1117+
b' f2()',
11171118
b' File "<string>", line 6, in f2',
1119+
b' f1()',
11181120
b' File "<string>", line 4, in f1',
1121+
b' 1 / 0',
1122+
b' ~~^~~',
11191123
b'ZeroDivisionError: division by zero'
11201124
]
11211125
11221126
check(3, traceback)
1123-
check(2, traceback[:1] + traceback[2:])
1124-
check(1, traceback[:1] + traceback[3:])
1127+
check(2, traceback[:1] + traceback[3:])
1128+
check(1, traceback[:1] + traceback[5:])
11251129
check(0, [traceback[-1]])
11261130
check(-1, [traceback[-1]])
11271131
check(1<<1000, traceback)

Lib/test/test_traceback.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,8 @@ def __del__(self):
313313
rc, stdout, stderr = assert_python_ok('-c', code)
314314
expected = [b'Traceback (most recent call last):',
315315
b' File "<string>", line 8, in __init__',
316+
b' x = 1 / 0',
317+
b' ^^^^^',
316318
b'ZeroDivisionError: division by zero']
317319
self.assertEqual(stderr.splitlines(), expected)
318320

Lib/test/test_warnings/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,10 @@ def test_conflicting_envvar_and_command_line(self):
12331233
self.assertEqual(stderr.splitlines(),
12341234
[b"Traceback (most recent call last):",
12351235
b" File \"<string>\", line 1, in <module>",
1236+
b' import sys, warnings; sys.stdout.write(str(sys.warnoptions)); warnings.w'
1237+
b"arn('Message', DeprecationWarning)",
1238+
b' ^^^^^^^^^^'
1239+
b'^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^',
12361240
b"DeprecationWarning: Message"])
12371241

12381242
def test_default_filter_configuration(self):

Lib/traceback.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -476,12 +476,11 @@ def format_frame_summary(self, frame_summary):
476476
gets called for every frame to be printed in the stack summary.
477477
"""
478478
row = []
479-
if frame_summary.filename.startswith("<python-input"):
480-
row.append(' File "<stdin>", line {}, in {}\n'.format(
481-
frame_summary.lineno, frame_summary.name))
482-
else:
483-
row.append(' File "{}", line {}, in {}\n'.format(
484-
frame_summary.filename, frame_summary.lineno, frame_summary.name))
479+
filename = frame_summary.filename
480+
if frame_summary.filename.startswith("<stdin>-"):
481+
filename = "<stdin>"
482+
row.append(' File "{}", line {}, in {}\n'.format(
483+
filename, frame_summary.lineno, frame_summary.name))
485484
if frame_summary.line:
486485
stripped_line = frame_summary.line.strip()
487486
row.append(' {}\n'.format(stripped_line))
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Show source lines in tracebacks when using the ``-c`` option when running
2+
Python. Patch by Pablo Galindo

Modules/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ pymain_run_command(wchar_t *command)
249249

250250
PyCompilerFlags cf = _PyCompilerFlags_INIT;
251251
cf.cf_flags |= PyCF_IGNORE_COOKIE;
252-
ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), &cf);
252+
ret = _PyRun_SimpleStringFlagsWithName(PyBytes_AsString(bytes), "<string>", &cf);
253253
Py_DECREF(bytes);
254254
return (ret != 0);
255255

Python/pythonrun.c

Lines changed: 61 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,17 @@
4040
/* Forward */
4141
static void flush_io(void);
4242
static PyObject *run_mod(mod_ty, PyObject *, PyObject *, PyObject *,
43-
PyCompilerFlags *, PyArena *, PyObject*);
43+
PyCompilerFlags *, PyArena *, PyObject*, int);
4444
static PyObject *run_pyc_file(FILE *, PyObject *, PyObject *,
4545
PyCompilerFlags *);
4646
static int PyRun_InteractiveOneObjectEx(FILE *, PyObject *, PyCompilerFlags *);
4747
static PyObject* pyrun_file(FILE *fp, PyObject *filename, int start,
4848
PyObject *globals, PyObject *locals, int closeit,
4949
PyCompilerFlags *flags);
50-
50+
static PyObject *
51+
_PyRun_StringFlagsWithName(const char *str, PyObject* name, int start,
52+
PyObject *globals, PyObject *locals, PyCompilerFlags *flags,
53+
int generate_new_source);
5154

5255
int
5356
_PyRun_AnyFileObject(FILE *fp, PyObject *filename, int closeit,
@@ -281,7 +284,7 @@ PyRun_InteractiveOneObjectEx(FILE *fp, PyObject *filename,
281284
}
282285
PyObject *main_dict = PyModule_GetDict(main_module); // borrowed ref
283286

284-
PyObject *res = run_mod(mod, filename, main_dict, main_dict, flags, arena, interactive_src);
287+
PyObject *res = run_mod(mod, filename, main_dict, main_dict, flags, arena, interactive_src, 1);
285288
_PyArena_Free(arena);
286289
Py_DECREF(main_module);
287290
if (res == NULL) {
@@ -499,16 +502,25 @@ PyRun_SimpleFileExFlags(FILE *fp, const char *filename, int closeit,
499502

500503

501504
int
502-
PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
503-
{
505+
_PyRun_SimpleStringFlagsWithName(const char *command, const char* name, PyCompilerFlags *flags) {
504506
PyObject *main_module = PyImport_AddModuleRef("__main__");
505507
if (main_module == NULL) {
506508
return -1;
507509
}
508510
PyObject *dict = PyModule_GetDict(main_module); // borrowed ref
509511

510-
PyObject *res = PyRun_StringFlags(command, Py_file_input,
511-
dict, dict, flags);
512+
PyObject *res = NULL;
513+
if (name == NULL) {
514+
res = PyRun_StringFlags(command, Py_file_input, dict, dict, flags);
515+
} else {
516+
PyObject* the_name = PyUnicode_FromString(name);
517+
if (!the_name) {
518+
PyErr_Print();
519+
return -1;
520+
}
521+
res = _PyRun_StringFlagsWithName(command, the_name, Py_file_input, dict, dict, flags, 0);
522+
Py_DECREF(the_name);
523+
}
512524
Py_DECREF(main_module);
513525
if (res == NULL) {
514526
PyErr_Print();
@@ -519,6 +531,12 @@ PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
519531
return 0;
520532
}
521533

534+
int
535+
PyRun_SimpleStringFlags(const char *command, PyCompilerFlags *flags)
536+
{
537+
return _PyRun_SimpleStringFlagsWithName(command, NULL, flags);
538+
}
539+
522540
int
523541
_Py_HandleSystemExit(int *exitcode_p)
524542
{
@@ -1131,9 +1149,10 @@ void PyErr_DisplayException(PyObject *exc)
11311149
PyErr_Display(NULL, exc, NULL);
11321150
}
11331151

1134-
PyObject *
1135-
PyRun_StringFlags(const char *str, int start, PyObject *globals,
1136-
PyObject *locals, PyCompilerFlags *flags)
1152+
static PyObject *
1153+
_PyRun_StringFlagsWithName(const char *str, PyObject* name, int start,
1154+
PyObject *globals, PyObject *locals, PyCompilerFlags *flags,
1155+
int generate_new_source)
11371156
{
11381157
PyObject *ret = NULL;
11391158
mod_ty mod;
@@ -1143,17 +1162,36 @@ PyRun_StringFlags(const char *str, int start, PyObject *globals,
11431162
if (arena == NULL)
11441163
return NULL;
11451164

1165+
PyObject* source = NULL;
11461166
_Py_DECLARE_STR(anon_string, "<string>");
1147-
mod = _PyParser_ASTFromString(
1148-
str, &_Py_STR(anon_string), start, flags, arena);
11491167

1150-
if (mod != NULL)
1151-
ret = run_mod(mod, &_Py_STR(anon_string), globals, locals, flags, arena, NULL);
1168+
if (name) {
1169+
source = PyUnicode_FromString(str);
1170+
if (!source) {
1171+
PyErr_Clear();
1172+
}
1173+
} else {
1174+
name = &_Py_STR(anon_string);
1175+
}
1176+
1177+
mod = _PyParser_ASTFromString(str, name, start, flags, arena);
1178+
1179+
if (mod != NULL) {
1180+
ret = run_mod(mod, name, globals, locals, flags, arena, source, generate_new_source);
1181+
}
1182+
Py_XDECREF(source);
11521183
_PyArena_Free(arena);
11531184
return ret;
11541185
}
11551186

11561187

1188+
PyObject *
1189+
PyRun_StringFlags(const char *str, int start, PyObject *globals,
1190+
PyObject *locals, PyCompilerFlags *flags) {
1191+
1192+
return _PyRun_StringFlagsWithName(str, NULL, start, globals, locals, flags, 0);
1193+
}
1194+
11571195
static PyObject *
11581196
pyrun_file(FILE *fp, PyObject *filename, int start, PyObject *globals,
11591197
PyObject *locals, int closeit, PyCompilerFlags *flags)
@@ -1173,7 +1211,7 @@ pyrun_file(FILE *fp, PyObject *filename, int start, PyObject *globals,
11731211

11741212
PyObject *ret;
11751213
if (mod != NULL) {
1176-
ret = run_mod(mod, filename, globals, locals, flags, arena, NULL);
1214+
ret = run_mod(mod, filename, globals, locals, flags, arena, NULL, 0);
11771215
}
11781216
else {
11791217
ret = NULL;
@@ -1261,15 +1299,19 @@ run_eval_code_obj(PyThreadState *tstate, PyCodeObject *co, PyObject *globals, Py
12611299

12621300
static PyObject *
12631301
run_mod(mod_ty mod, PyObject *filename, PyObject *globals, PyObject *locals,
1264-
PyCompilerFlags *flags, PyArena *arena, PyObject* interactive_src)
1302+
PyCompilerFlags *flags, PyArena *arena, PyObject* interactive_src,
1303+
int generate_new_source)
12651304
{
12661305
PyThreadState *tstate = _PyThreadState_GET();
12671306
PyObject* interactive_filename = filename;
12681307
if (interactive_src) {
12691308
PyInterpreterState *interp = tstate->interp;
1270-
interactive_filename = PyUnicode_FromFormat(
1271-
"<python-input-%d>", interp->_interactive_src_count++
1272-
);
1309+
if (generate_new_source) {
1310+
interactive_filename = PyUnicode_FromFormat(
1311+
"%U-%d", filename, interp->_interactive_src_count++);
1312+
} else {
1313+
Py_INCREF(interactive_filename);
1314+
}
12731315
if (interactive_filename == NULL) {
12741316
return NULL;
12751317
}

0 commit comments

Comments
 (0)
0