Mailing List Archive

gh-117266: Fix crashes on user-created AST subclasses (GH-117276)
https://github.com/python/cpython/commit/4c71d51a4b7989fc8754ba512c40e21666f9db0d
commit: 4c71d51a4b7989fc8754ba512c40e21666f9db0d
branch: main
author: Jelle Zijlstra <jelle.zijlstra@gmail.com>
committer: encukou <encukou@gmail.com>
date: 2024-03-28T11:30:31+01:00
summary:

gh-117266: Fix crashes on user-created AST subclasses (GH-117276)

Fix crashes on user-created AST subclasses

files:
A Misc/NEWS.d/next/Core and Builtins/2024-03-26-17-22-38.gh-issue-117266.Kwh79O.rst
M Lib/test/test_ast.py
M Parser/asdl_c.py
M Python/Python-ast.c

diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py
index 7cecf319e3638f..3929e4e00d59c2 100644
--- a/Lib/test/test_ast.py
+++ b/Lib/test/test_ast.py
@@ -2916,6 +2916,47 @@ def test_FunctionDef(self):
self.assertEqual(node.name, 'foo')
self.assertEqual(node.decorator_list, [])

+ def test_custom_subclass(self):
+ class NoInit(ast.AST):
+ pass
+
+ obj = NoInit()
+ self.assertIsInstance(obj, NoInit)
+ self.assertEqual(obj.__dict__, {})
+
+ class Fields(ast.AST):
+ _fields = ('a',)
+
+ with self.assertWarnsRegex(DeprecationWarning,
+ r"Fields provides _fields but not _field_types."):
+ obj = Fields()
+ with self.assertRaises(AttributeError):
+ obj.a
+ obj = Fields(a=1)
+ self.assertEqual(obj.a, 1)
+
+ class FieldsAndTypes(ast.AST):
+ _fields = ('a',)
+ _field_types = {'a': int | None}
+ a: int | None = None
+
+ obj = FieldsAndTypes()
+ self.assertIs(obj.a, None)
+ obj = FieldsAndTypes(a=1)
+ self.assertEqual(obj.a, 1)
+
+ class FieldsAndTypesNoDefault(ast.AST):
+ _fields = ('a',)
+ _field_types = {'a': int}
+
+ with self.assertWarnsRegex(DeprecationWarning,
+ r"FieldsAndTypesNoDefault\.__init__ missing 1 required positional argument: 'a'\."):
+ obj = FieldsAndTypesNoDefault()
+ with self.assertRaises(AttributeError):
+ obj.a
+ obj = FieldsAndTypesNoDefault(a=1)
+ self.assertEqual(obj.a, 1)
+

@support.cpython_only
class ModuleStateTests(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Core and Builtins/2024-03-26-17-22-38.gh-issue-117266.Kwh79O.rst b/Misc/NEWS.d/next/Core and Builtins/2024-03-26-17-22-38.gh-issue-117266.Kwh79O.rst
new file mode 100644
index 00000000000000..5055954676b9ab
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2024-03-26-17-22-38.gh-issue-117266.Kwh79O.rst
@@ -0,0 +1,2 @@
+Fix crashes for certain user-created subclasses of :class:`ast.AST`. Such
+classes are now expected to set the ``_field_types`` attribute.
diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py
index 59cc391881ab86..c4df2c52c032bc 100755
--- a/Parser/asdl_c.py
+++ b/Parser/asdl_c.py
@@ -973,11 +973,22 @@ def visitModule(self, mod):
Py_ssize_t size = PySet_Size(remaining_fields);
PyObject *field_types = NULL, *remaining_list = NULL;
if (size > 0) {
- if (!PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), &_Py_ID(_field_types),
- &field_types)) {
+ if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), &_Py_ID(_field_types),
+ &field_types) < 0) {
res = -1;
goto cleanup;
}
+ if (field_types == NULL) {
+ if (PyErr_WarnFormat(
+ PyExc_DeprecationWarning, 1,
+ "%.400s provides _fields but not _field_types. "
+ "This will become an error in Python 3.15.",
+ Py_TYPE(self)->tp_name
+ ) < 0) {
+ res = -1;
+ }
+ goto cleanup;
+ }
remaining_list = PySequence_List(remaining_fields);
if (!remaining_list) {
goto set_remaining_cleanup;
diff --git a/Python/Python-ast.c b/Python/Python-ast.c
index 7b591ddaa29869..60b46263a0d329 100644
--- a/Python/Python-ast.c
+++ b/Python/Python-ast.c
@@ -5119,11 +5119,22 @@ ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
Py_ssize_t size = PySet_Size(remaining_fields);
PyObject *field_types = NULL, *remaining_list = NULL;
if (size > 0) {
- if (!PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), &_Py_ID(_field_types),
- &field_types)) {
+ if (PyObject_GetOptionalAttr((PyObject*)Py_TYPE(self), &_Py_ID(_field_types),
+ &field_types) < 0) {
res = -1;
goto cleanup;
}
+ if (field_types == NULL) {
+ if (PyErr_WarnFormat(
+ PyExc_DeprecationWarning, 1,
+ "%.400s provides _fields but not _field_types. "
+ "This will become an error in Python 3.15.",
+ Py_TYPE(self)->tp_name
+ ) < 0) {
+ res = -1;
+ }
+ goto cleanup;
+ }
remaining_list = PySequence_List(remaining_fields);
if (!remaining_list) {
goto set_remaining_cleanup;

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-leave@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: list-python-checkins@lists.gossamer-threads.com