Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
19 changes: 19 additions & 0 deletions Lib/test/test_syntax.py
Original file line number Diff line number Diff line change
Expand Up @@ -2244,6 +2244,25 @@ def test_continuation_bad_indentation(self):

self.assertRaises(IndentationError, exec, code)

@support.cpython_only
def test_disallowed_type_param_names(self):
# See gh-128632

self._check_error(f"class A[__classdict__]: pass",
f"reserved name '__classdict__' cannot be used for type parameter")
self._check_error(f"def f[__classdict__](): pass",
f"reserved name '__classdict__' cannot be used for type parameter")
self._check_error(f"type T[__classdict__] = tuple[__classdict__]",
f"reserved name '__classdict__' cannot be used for type parameter")

# These compilations are here to make sure __class__, __classcell__ and __classdictcell__
# don't break in the future like __classdict__ did in this case.
for name in ('__class__', '__classcell__', '__classdictcell__'):
compile(f"""
class A:
class B[{name}]: pass
""", "<testcase>", mode="exec")

@support.cpython_only
def test_nested_named_except_blocks(self):
code = ""
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Disallow ``__classdict__`` as the name of a type parameter. Using this
name would previously crash the interpreter in some circumstances.
16 changes: 10 additions & 6 deletions Python/assemble.c
Original file line number Diff line number Diff line change
Expand Up @@ -467,19 +467,19 @@
int nlocals = (int)PyDict_GET_SIZE(umd->u_varnames);

// This counter mirrors the fix done in fix_cell_offsets().
int numdropped = 0;
int numdropped = 0, cellvar_offset = -1;

Check warning on line 470 in Python/assemble.c

View workflow job for this annotation

GitHub Actions / Address sanitizer (ubuntu-24.04)

unused variable ‘cellvar_offset’ [-Wunused-variable]
pos = 0;
while (PyDict_Next(umd->u_cellvars, &pos, &k, &v)) {
if (PyDict_GetItem(umd->u_varnames, k) != NULL) {
// Skip cells that are already covered by locals.
numdropped += 1;
continue;
}
int offset = (int)PyLong_AS_LONG(v);
assert(offset >= 0);
offset += nlocals - numdropped;
assert(offset < nlocalsplus);
_Py_set_localsplus_info(offset, k, CO_FAST_CELL, names, kinds);
int cellvar_offset = (int)PyLong_AS_LONG(v);
assert(cellvar_offset >= 0);
cellvar_offset += nlocals - numdropped;
assert(cellvar_offset < nlocalsplus);
_Py_set_localsplus_info(cellvar_offset, k, CO_FAST_CELL, names, kinds);
}

pos = 0;
Expand All @@ -488,6 +488,10 @@
assert(offset >= 0);
offset += nlocals - numdropped;
assert(offset < nlocalsplus);
/* XXX If the assertion below fails it is most likely because a freevar
was added to u_freevars with the wrong index due to not taking into
account cellvars already present, see gh-128632. */
assert(offset > cellvar_offset);
_Py_set_localsplus_info(offset, k, CO_FAST_FREE, names, kinds);
}
}
Expand Down
24 changes: 24 additions & 0 deletions Python/symtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -2191,6 +2191,24 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
VISIT_QUIT(st, 1);
}

static int
symtable_visit_type_param_check_reserved_name(struct symtable *st, type_param_ty tp, identifier name)
{
if (_PyUnicode_Equal(name, &_Py_ID(__classdict__))) {
PyObject *error_msg = PyUnicode_FromFormat("reserved name '%U' cannot be "
"used for type parameter", name);
PyErr_SetObject(PyExc_SyntaxError, error_msg);
Py_DECREF(error_msg);
PyErr_RangedSyntaxLocationObject(st->st_filename,
tp->lineno,
tp->col_offset + 1,
tp->end_lineno,
tp->end_col_offset + 1);
return 0;
}
return 1;
}

static int
symtable_visit_type_param(struct symtable *st, type_param_ty tp)
{
Expand All @@ -2201,6 +2219,8 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
}
switch(tp->kind) {
case TypeVar_kind:
if (!symtable_visit_type_param_check_reserved_name(st, tp, tp->v.TypeVar.name))
VISIT_QUIT(st, 0);
if (!symtable_add_def(st, tp->v.TypeVar.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
VISIT_QUIT(st, 0);
if (tp->v.TypeVar.bound) {
Expand All @@ -2219,10 +2239,14 @@ symtable_visit_type_param(struct symtable *st, type_param_ty tp)
}
break;
case TypeVarTuple_kind:
if (!symtable_visit_type_param_check_reserved_name(st, tp, tp->v.TypeVarTuple.name))
VISIT_QUIT(st, 0);
if (!symtable_add_def(st, tp->v.TypeVarTuple.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
VISIT_QUIT(st, 0);
break;
case ParamSpec_kind:
if (!symtable_visit_type_param_check_reserved_name(st, tp, tp->v.ParamSpec.name))
VISIT_QUIT(st, 0);
if (!symtable_add_def(st, tp->v.ParamSpec.name, DEF_TYPE_PARAM | DEF_LOCAL, LOCATION(tp)))
VISIT_QUIT(st, 0);
break;
Expand Down
Loading