Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the repl to show source code and complete tracebacks #110775

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Include/internal/pycore_interp.h
Expand Up @@ -233,6 +233,7 @@ struct _is {

/* the initial PyInterpreterState.threads.head */
PyThreadState _initial_thread;
Py_ssize_t _interactive_src_count;
};


Expand Down
12 changes: 11 additions & 1 deletion Include/internal/pycore_parser.h
Expand Up @@ -58,7 +58,17 @@ extern struct _mod* _PyParser_ASTFromFile(
PyCompilerFlags *flags,
int *errcode,
PyArena *arena);

extern struct _mod* _PyParser_InteractiveASTFromFile(
FILE *fp,
PyObject *filename_ob,
const char *enc,
int mode,
const char *ps1,
const char *ps2,
PyCompilerFlags *flags,
int *errcode,
PyObject **interactive_src,
PyArena *arena);

#ifdef __cplusplus
}
Expand Down
10 changes: 10 additions & 0 deletions Lib/linecache.py
Expand Up @@ -9,6 +9,7 @@
import sys
import os
import tokenize
import types

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

Expand Down Expand Up @@ -42,6 +43,8 @@ def getlines(filename, module_globals=None):
if len(entry) != 1:
return cache[filename][2]

if isinstance(filename, types.CodeType):
return []
try:
return updatecache(filename, module_globals)
except MemoryError:
Expand Down Expand Up @@ -180,3 +183,10 @@ def lazycache(filename, module_globals):
cache[filename] = (get_lines,)
return True
return False

def _register_code(code, string, name):
cache[code] = (
len(string),
None,
[line + '\n' for line in string.splitlines()],
name)
13 changes: 9 additions & 4 deletions Lib/traceback.py
Expand Up @@ -331,7 +331,8 @@ def line(self):
if self._line is None:
if self.lineno is None:
return None
self._line = linecache.getline(self.filename, self.lineno)
if self._line is None:
self._line = linecache.getline(self.filename, self.lineno)
return self._line.strip()


Expand Down Expand Up @@ -434,7 +435,6 @@ def _extract_from_extended_frame_gen(klass, frame_gen, *, limit=None,
co = f.f_code
filename = co.co_filename
name = co.co_name

fnames.add(filename)
linecache.lazycache(filename, f.f_globals)
# Must defer line lookups until we have called checkcache.
Expand All @@ -447,6 +447,7 @@ def _extract_from_extended_frame_gen(klass, frame_gen, *, limit=None,
end_lineno=end_lineno, colno=colno, end_colno=end_colno))
for filename in fnames:
linecache.checkcache(filename)

# If immediate lookup was desired, trigger lookups now.
if lookup_lines:
for f in result:
Expand Down Expand Up @@ -479,8 +480,12 @@ def format_frame_summary(self, frame_summary):
gets called for every frame to be printed in the stack summary.
"""
row = []
row.append(' File "{}", line {}, in {}\n'.format(
frame_summary.filename, frame_summary.lineno, frame_summary.name))
if frame_summary.filename.startswith("<python-input"):
row.append(' REPL, line {}, in {}\n'.format(
frame_summary.lineno, frame_summary.name))
else:
row.append(' File "{}", line {}, in {}\n'.format(
frame_summary.filename, frame_summary.lineno, frame_summary.name))
if frame_summary.line:
stripped_line = frame_summary.line.strip()
row.append(' {}\n'.format(stripped_line))
Expand Down
15 changes: 14 additions & 1 deletion Parser/peg_api.c
Expand Up @@ -23,5 +23,18 @@ _PyParser_ASTFromFile(FILE *fp, PyObject *filename_ob, const char *enc,
return NULL;
}
return _PyPegen_run_parser_from_file_pointer(fp, mode, filename_ob, enc, ps1, ps2,
flags, errcode, arena);
flags, errcode, NULL, arena);
}

mod_ty
_PyParser_InteractiveASTFromFile(FILE *fp, PyObject *filename_ob, const char *enc,
int mode, const char *ps1, const char* ps2,
PyCompilerFlags *flags, int *errcode,
PyObject **interactive_src, PyArena *arena)
{
if (PySys_Audit("compile", "OO", Py_None, filename_ob) < 0) {
return NULL;
}
return _PyPegen_run_parser_from_file_pointer(fp, mode, filename_ob, enc, ps1, ps2,
flags, errcode, interactive_src, arena);
}
12 changes: 11 additions & 1 deletion Parser/pegen.c
Expand Up @@ -878,7 +878,8 @@ _PyPegen_run_parser(Parser *p)
mod_ty
_PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filename_ob,
const char *enc, const char *ps1, const char *ps2,
PyCompilerFlags *flags, int *errcode, PyArena *arena)
PyCompilerFlags *flags, int *errcode,
PyObject **interactive_src, PyArena *arena)
{
struct tok_state *tok = _PyTokenizer_FromFile(fp, enc, ps1, ps2);
if (tok == NULL) {
Expand Down Expand Up @@ -908,6 +909,15 @@ _PyPegen_run_parser_from_file_pointer(FILE *fp, int start_rule, PyObject *filena
result = _PyPegen_run_parser(p);
_PyPegen_Parser_Free(p);

if (tok->fp_interactive && tok->interactive_src_start && result && interactive_src != NULL) {
*interactive_src = PyUnicode_FromString(tok->interactive_src_start);
if (!interactive_src || _PyArena_AddPyObject(arena, *interactive_src) < 0) {
Py_XDECREF(interactive_src);
result = NULL;
goto error;
}
}

error:
_PyTokenizer_Free(tok);
return result;
Expand Down
3 changes: 2 additions & 1 deletion Parser/pegen.h
Expand Up @@ -350,7 +350,8 @@ void *_PyPegen_nonparen_genexp_in_call(Parser *p, expr_ty args, asdl_comprehensi
Parser *_PyPegen_Parser_New(struct tok_state *, int, int, int, int *, PyArena *);
void _PyPegen_Parser_Free(Parser *);
mod_ty _PyPegen_run_parser_from_file_pointer(FILE *, int, PyObject *, const char *,
const char *, const char *, PyCompilerFlags *, int *, PyArena *);
const char *, const char *, PyCompilerFlags *, int *, PyObject **,
PyArena *);
void *_PyPegen_run_parser(Parser *);
mod_ty _PyPegen_run_parser_from_string(const char *, int, PyObject *, PyCompilerFlags *, PyArena *);
asdl_stmt_seq *_PyPegen_interactive_exit(Parser *);
Expand Down