mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-117266: Fix crashes on user-created AST subclasses (GH-117276)
Fix crashes on user-created AST subclasses
This commit is contained in:
		
							parent
							
								
									8cb7d7ff86
								
							
						
					
					
						commit
						4c71d51a4b
					
				
					 4 changed files with 69 additions and 4 deletions
				
			
		|  | @ -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): | ||||
|  |  | |||
|  | @ -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. | ||||
|  | @ -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; | ||||
|  |  | |||
							
								
								
									
										15
									
								
								Python/Python-ast.c
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								Python/Python-ast.c
									
										
									
										generated
									
									
									
								
							|  | @ -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; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jelle Zijlstra
						Jelle Zijlstra