Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(fd)
STRUCT_FOR_ID(fd2)
STRUCT_FOR_ID(fdel)
STRUCT_FOR_ID(feature_version)
STRUCT_FOR_ID(fget)
STRUCT_FOR_ID(fields)
STRUCT_FOR_ID(file)
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions Lib/_pyrepl/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@
from __future__ import annotations

import _colorize
import _symtable

from abc import ABC, abstractmethod
import ast
import code
import codeop
import linecache
from dataclasses import dataclass, field
import os.path
Expand Down Expand Up @@ -211,6 +213,17 @@ def runsource(self, source, filename="<input>", symbol="single"):
except (OverflowError, ValueError):
self.showsyntaxerror(filename, source=source)
return False

try:
# validate stuff that cannot be validated with AST parsing only
flags = self.compile.compiler.flags
flags &= ~codeop.PyCF_DONT_IMPLY_DEDENT
flags &= ~codeop.PyCF_ALLOW_INCOMPLETE_INPUT
_symtable.symtable(source, filename, "exec", flags=flags)
except (SyntaxError, OverflowError, ValueError):
self.showsyntaxerror(filename, source=source)
return False

if tree.body:
*_, last_stmt = tree.body
for stmt in tree.body:
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_pyrepl/test_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,15 @@ def test_runsource_shows_syntax_error_for_failed_compilation(self):
console.runsource(source)
mock_showsyntaxerror.assert_called_once()

def test_runsource_shows_syntax_error_for_failed_symtable_checks(self):
# Some checks cannot be performed by AST parsing only.
# See https://github.com/python/cpython/issues/137376.
console = InteractiveColoredConsole()
source = "x = 1; global x; x = 2"
with patch.object(console, "showsyntaxerror") as mock_showsyntaxerror:
console.runsource(source)
mock_showsyntaxerror.assert_called_once()

def test_runsource_survives_null_bytes(self):
console = InteractiveColoredConsole()
source = "\x00\n"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Ensure that :exc:`SyntaxError` is raised when given incorrect REPL inputs
that are only detected when processing symbol tables. Patch by Bénédikt
Tran.
72 changes: 64 additions & 8 deletions Modules/clinic/symtablemodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 14 additions & 3 deletions Modules/symtablemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,33 @@ _symtable.symtable
filename: object(converter='PyUnicode_FSDecoder')
startstr: str
/
*
flags: int = 0
feature_version: int = 0

Return symbol and scope dictionaries used internally by compiler.
[clinic start generated code]*/

static PyObject *
_symtable_symtable_impl(PyObject *module, PyObject *source,
PyObject *filename, const char *startstr)
/*[clinic end generated code: output=59eb0d5fc7285ac4 input=9dd8a50c0c36a4d7]*/
PyObject *filename, const char *startstr, int flags,
int feature_version)
/*[clinic end generated code: output=1e766ac3387e156a input=5ccfb94e8a19a975]*/
{
struct symtable *st;
PyObject *t;
int start;
PyCompilerFlags cf = _PyCompilerFlags_INIT;
PyObject *source_copy = NULL;

cf.cf_flags = PyCF_SOURCE_IS_UTF8;
cf.cf_flags = flags | PyCF_SOURCE_IS_UTF8;
if (feature_version >= 0 && (flags & PyCF_ONLY_AST)) {
cf.cf_feature_version = feature_version;
}
if (flags & ~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_COMPILE_MASK)) {
PyErr_SetString(PyExc_ValueError, "_symtable.symtable(): unrecognised flags");
return NULL;
}

const char *str = _Py_SourceAsString(source, "symtable", "string or bytes", &cf, &source_copy);
if (str == NULL) {
Expand Down
Loading