Skip to content

Commit 47e2175

Browse files
authored
gh-85260: Extend the AST Validator to validate all identifiers (GH-21069)
1 parent ee4fe00 commit 47e2175

3 files changed

Lines changed: 56 additions & 2 deletions

File tree

Lib/test/test_ast/test_ast.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -983,6 +983,34 @@ def test_constant_as_name(self):
983983
with self.assertRaisesRegex(ValueError, f"identifier field can't represent '{constant}' constant"):
984984
compile(expr, "<test>", "eval")
985985

986+
def test_constant_in_identifier_fields(self):
987+
# gh-85260: an identifier field holding a constant name used to
988+
# crash the compiler
989+
for statement in [
990+
"def x(): pass",
991+
"async def x(): pass",
992+
"class x: pass",
993+
"from a import x",
994+
"from a import b as x",
995+
"from a import b, c, d as x",
996+
"import x",
997+
"import a, b, x",
998+
"try: pass\nexcept A as x: pass",
999+
"try: pass\nexcept A as b: pass\nexcept B as x: pass\n",
1000+
]:
1001+
for constant in "True", "False", "None":
1002+
with self.subTest(statement=statement, constant=constant):
1003+
tree = ast.parse(statement)
1004+
for node in ast.walk(tree):
1005+
for field, value in ast.iter_fields(node):
1006+
if value == "x":
1007+
setattr(node, field, constant)
1008+
with self.assertRaisesRegex(
1009+
ValueError,
1010+
f"identifier field can't represent "
1011+
f"'{constant}' constant"):
1012+
compile(tree, "<test>", "exec")
1013+
9861014
def test_constant_as_unicode_name(self):
9871015
constants = [
9881016
("True", b"Tru\xe1\xb5\x89"),
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
:func:`compile` now raises :exc:`ValueError` instead of crashing on a debug
2+
build if an identifier field of an AST node (such as the name of a function,
3+
a class, an imported module or a caught exception) is ``"None"``, ``"True"``
4+
or ``"False"``.

Python/ast.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -710,6 +710,23 @@ _validate_nonempty_seq(asdl_seq *seq, const char *what, const char *owner)
710710
}
711711
#define validate_nonempty_seq(seq, what, owner) _validate_nonempty_seq((asdl_seq*)seq, what, owner)
712712

713+
static int
714+
validate_import_names(asdl_alias_seq *seq, const char *what, const char *owner)
715+
{
716+
if (!validate_nonempty_seq(seq, what, owner)) {
717+
return 0;
718+
}
719+
Py_ssize_t n = asdl_seq_LEN(seq);
720+
for (Py_ssize_t i = 0; i < n; i++) {
721+
alias_ty alias = asdl_seq_GET(seq, i);
722+
if (!validate_name(alias->name) ||
723+
(alias->asname && !validate_name(alias->asname))) {
724+
return 0;
725+
}
726+
}
727+
return 1;
728+
}
729+
713730
static int
714731
validate_assignlist(asdl_expr_seq *targets, expr_context_ty ctx)
715732
{
@@ -735,6 +752,7 @@ validate_stmt(stmt_ty stmt)
735752
switch (stmt->kind) {
736753
case FunctionDef_kind:
737754
ret = validate_body(stmt->v.FunctionDef.body, "FunctionDef") &&
755+
validate_name(stmt->v.FunctionDef.name) &&
738756
validate_type_params(stmt->v.FunctionDef.type_params) &&
739757
validate_arguments(stmt->v.FunctionDef.args) &&
740758
validate_exprs(stmt->v.FunctionDef.decorator_list, Load, 0) &&
@@ -743,6 +761,7 @@ validate_stmt(stmt_ty stmt)
743761
break;
744762
case ClassDef_kind:
745763
ret = validate_body(stmt->v.ClassDef.body, "ClassDef") &&
764+
validate_name(stmt->v.ClassDef.name) &&
746765
validate_type_params(stmt->v.ClassDef.type_params) &&
747766
validate_exprs(stmt->v.ClassDef.bases, Load, 0) &&
748767
validate_keywords(stmt->v.ClassDef.keywords) &&
@@ -873,6 +892,8 @@ validate_stmt(stmt_ty stmt)
873892
VALIDATE_POSITIONS(handler);
874893
if ((handler->v.ExceptHandler.type &&
875894
!validate_expr(handler->v.ExceptHandler.type, Load)) ||
895+
(handler->v.ExceptHandler.name &&
896+
!validate_name(handler->v.ExceptHandler.name)) ||
876897
!validate_body(handler->v.ExceptHandler.body, "ExceptHandler"))
877898
return 0;
878899
}
@@ -911,14 +932,14 @@ validate_stmt(stmt_ty stmt)
911932
(!stmt->v.Assert.msg || validate_expr(stmt->v.Assert.msg, Load));
912933
break;
913934
case Import_kind:
914-
ret = validate_nonempty_seq(stmt->v.Import.names, "names", "Import");
935+
ret = validate_import_names(stmt->v.Import.names, "names", "Import");
915936
break;
916937
case ImportFrom_kind:
917938
if (stmt->v.ImportFrom.level < 0) {
918939
PyErr_SetString(PyExc_ValueError, "Negative ImportFrom level");
919940
return 0;
920941
}
921-
ret = validate_nonempty_seq(stmt->v.ImportFrom.names, "names", "ImportFrom");
942+
ret = validate_import_names(stmt->v.ImportFrom.names, "names", "ImportFrom");
922943
break;
923944
case Global_kind:
924945
ret = validate_nonempty_seq(stmt->v.Global.names, "names", "Global");
@@ -931,6 +952,7 @@ validate_stmt(stmt_ty stmt)
931952
break;
932953
case AsyncFunctionDef_kind:
933954
ret = validate_body(stmt->v.AsyncFunctionDef.body, "AsyncFunctionDef") &&
955+
validate_name(stmt->v.AsyncFunctionDef.name) &&
934956
validate_type_params(stmt->v.AsyncFunctionDef.type_params) &&
935957
validate_arguments(stmt->v.AsyncFunctionDef.args) &&
936958
validate_exprs(stmt->v.AsyncFunctionDef.decorator_list, Load, 0) &&

0 commit comments

Comments
 (0)