import _ast_unparse
+import _ast
import ast
import builtins
import contextlib
self.assertEqual(ast_node._fields, ast_node.__match_args__)
def test_AST_objects(self):
- x = ast.AST()
+ # Directly instantiating abstract node class AST is allowed (but deprecated)
+ with self.assertWarns(DeprecationWarning):
+ x = ast.AST()
self.assertEqual(x._fields, ())
x.foobar = 42
self.assertEqual(x.foobar, 42)
with self.assertRaises(AttributeError):
x.vararg
- with self.assertRaises(TypeError):
+ with self.assertRaises(TypeError), self.assertWarns(DeprecationWarning):
# "ast.AST constructor takes 0 positional arguments"
ast.AST(2)
msg = "type object 'ast.AST' has no attribute '_fields'"
# Both examples used to crash:
- with self.assertRaisesRegex(AttributeError, msg):
+ with (
+ self.assertRaisesRegex(AttributeError, msg),
+ self.assertWarns(DeprecationWarning),
+ ):
ast.AST(arg1=123)
- with self.assertRaisesRegex(AttributeError, msg):
+ with (
+ self.assertRaisesRegex(AttributeError, msg),
+ self.assertWarns(DeprecationWarning),
+ ):
ast.AST()
- def test_AST_garbage_collection(self):
+ def test_node_garbage_collection(self):
class X:
pass
- a = ast.AST()
+ a = ast.Module()
a.x = X()
a.x.a = a
ref = weakref.ref(a.x)
elif typ is object:
kwargs[name] = b'capybara'
elif isinstance(typ, type) and issubclass(typ, ast.AST):
- kwargs[name] = self._construct_ast_class(typ)
+ if _ast._is_abstract(typ):
+ # Use an arbitrary concrete subclass
+ concrete = next((sub for sub in typ.__subclasses__()
+ if not _ast._is_abstract(sub)), None)
+ msg = f"abstract node class {typ} has no concrete subclasses"
+ self.assertIsNotNone(concrete, msg)
+ else:
+ concrete = typ
+ kwargs[name] = self._construct_ast_class(concrete)
return cls(**kwargs)
def test_arguments(self):
with self.assertRaisesRegex(TypeError, re.escape(msg)):
ast.BinOp(1, 2, 3, foobarbaz=42)
+ # Directly instantiating abstract node types is allowed (but deprecated)
+ self.assertWarns(DeprecationWarning, ast.stmt)
+ self.assertWarns(DeprecationWarning, ast.expr_context)
+
def test_no_fields(self):
# this used to fail because Sub._fields was None
x = ast.Sub()
def test_invalid_sum(self):
pos = dict(lineno=2, col_offset=3)
- m = ast.Module([ast.Expr(ast.expr(**pos), **pos)], [])
+ with self.assertWarns(DeprecationWarning):
+ # Creating instances of ast.expr is deprecated
+ e = ast.expr(**pos)
+ m = ast.Module([ast.Expr(e, **pos)], [])
with self.assertRaises(TypeError) as cm:
compile(m, "<test>", "exec")
self.assertIn("but got expr()", str(cm.exception))
def iter_ast_classes():
"""Iterate over the (native) subclasses of ast.AST recursively.
- This excludes the special class ast.Index since its constructor
- returns an integer.
+ This excludes:
+ * abstract AST nodes
+ * the special class ast.Index, since its constructor returns
+ an integer.
"""
def do(cls):
if cls.__module__ != 'ast':
return
if cls is ast.Index:
return
+ # Don't attempt to create instances of abstract AST nodes
+ if _ast._is_abstract(cls):
+ return
yield cls
for sub in cls.__subclasses__():
return -1;
}
+ int contains = PySet_Contains(state->abstract_types, (PyObject *)Py_TYPE(self));
+ if (contains == -1) {
+ return -1;
+ }
+ else if (contains == 1) {
+ if (PyErr_WarnFormat(
+ PyExc_DeprecationWarning, 1,
+ "Instantiating abstract AST node class %T is deprecated. "
+ "This will become an error in Python 3.20", self) < 0) {
+ return -1;
+ }
+ }
+
Py_ssize_t i, numfields = 0;
int res = -1;
PyObject *key, *value, *fields, *attributes = NULL, *remaining_fields = NULL;
if (!state->AST_type) {
return -1;
}
+ state->abstract_types = PySet_New(NULL);
+ if (!state->abstract_types) {
+ return -1;
+ }
+ if (PySet_Add(state->abstract_types, state->AST_type) < 0) {
+ return -1;
+ }
if (add_ast_fields(state) < 0) {
return -1;
}
(name, name, len(sum.attributes)), 1)
else:
self.emit("if (add_attributes(state, state->%s_type, NULL, 0) < 0) return -1;" % name, 1)
+ self.emit("if (PySet_Add(state->abstract_types, state->%s_type) < 0) return -1;" % name, 1)
self.emit_defaults(name, sum.attributes, 1)
simple = is_simple(sum)
for t in sum.types:
class ASTModuleVisitor(PickleVisitor):
def visitModule(self, mod):
+ self.emit("""
+/* Helper for checking if a node class is abstract in the tests. */
+static PyObject *
+ast_is_abstract(PyObject *Py_UNUSED(module), PyObject *cls) {
+ struct ast_state *state = get_ast_state();
+ if (state == NULL) {
+ return NULL;
+ }
+ int contains = PySet_Contains(state->abstract_types, cls);
+ if (contains == -1) {
+ return NULL;
+ }
+ else if (contains == 1) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+static struct PyMethodDef astmodule_methods[] = {
+ {"_is_abstract", ast_is_abstract, METH_O, NULL},
+ {NULL} /* Sentinel */
+};
+""".strip(), 0, reflow=False)
+ self.emit("", 0)
self.emit("static int", 0)
self.emit("astmodule_exec(PyObject *m)", 0)
self.emit("{", 0)
.m_name = "_ast",
// The _ast module uses a per-interpreter state (PyInterpreterState.ast)
.m_size = 0,
- .m_slots = astmodule_slots,
+ .m_methods = astmodule_methods,
+ .m_slots = astmodule_slots
};
PyMODINIT_FUNC
"%s_type" % type
for type in metadata.types
)
+ module_state.add("abstract_types")
state_strings = sorted(state_strings)
module_state = sorted(module_state)
Py_CLEAR(state->__module__);
Py_CLEAR(state->_attributes);
Py_CLEAR(state->_fields);
+ Py_CLEAR(state->abstract_types);
Py_CLEAR(state->alias_type);
Py_CLEAR(state->annotation);
Py_CLEAR(state->arg);
return -1;
}
+ int contains = PySet_Contains(state->abstract_types, (PyObject *)Py_TYPE(self));
+ if (contains == -1) {
+ return -1;
+ }
+ else if (contains == 1) {
+ if (PyErr_WarnFormat(
+ PyExc_DeprecationWarning, 1,
+ "Instantiating abstract AST node class %T is deprecated. "
+ "This will become an error in Python 3.20", self) < 0) {
+ return -1;
+ }
+ }
+
Py_ssize_t i, numfields = 0;
int res = -1;
PyObject *key, *value, *fields, *attributes = NULL, *remaining_fields = NULL;
if (!state->AST_type) {
return -1;
}
+ state->abstract_types = PySet_New(NULL);
+ if (!state->abstract_types) {
+ return -1;
+ }
+ if (PySet_Add(state->abstract_types, state->AST_type) < 0) {
+ return -1;
+ }
if (add_ast_fields(state) < 0) {
return -1;
}
" | FunctionType(expr* argtypes, expr returns)");
if (!state->mod_type) return -1;
if (add_attributes(state, state->mod_type, NULL, 0) < 0) return -1;
+ if (PySet_Add(state->abstract_types, state->mod_type) < 0) return -1;
state->Module_type = make_type(state, "Module", state->mod_type,
Module_fields, 2,
"Module(stmt* body, type_ignore* type_ignores)");
if (!state->stmt_type) return -1;
if (add_attributes(state, state->stmt_type, stmt_attributes, 4) < 0) return
-1;
+ if (PySet_Add(state->abstract_types, state->stmt_type) < 0) return -1;
if (PyObject_SetAttr(state->stmt_type, state->end_lineno, Py_None) == -1)
return -1;
if (PyObject_SetAttr(state->stmt_type, state->end_col_offset, Py_None) ==
if (!state->expr_type) return -1;
if (add_attributes(state, state->expr_type, expr_attributes, 4) < 0) return
-1;
+ if (PySet_Add(state->abstract_types, state->expr_type) < 0) return -1;
if (PyObject_SetAttr(state->expr_type, state->end_lineno, Py_None) == -1)
return -1;
if (PyObject_SetAttr(state->expr_type, state->end_col_offset, Py_None) ==
"expr_context = Load | Store | Del");
if (!state->expr_context_type) return -1;
if (add_attributes(state, state->expr_context_type, NULL, 0) < 0) return -1;
+ if (PySet_Add(state->abstract_types, state->expr_context_type) < 0) return
+ -1;
state->Load_type = make_type(state, "Load", state->expr_context_type, NULL,
0,
"Load");
"boolop = And | Or");
if (!state->boolop_type) return -1;
if (add_attributes(state, state->boolop_type, NULL, 0) < 0) return -1;
+ if (PySet_Add(state->abstract_types, state->boolop_type) < 0) return -1;
state->And_type = make_type(state, "And", state->boolop_type, NULL, 0,
"And");
if (!state->And_type) return -1;
"operator = Add | Sub | Mult | MatMult | Div | Mod | Pow | LShift | RShift | BitOr | BitXor | BitAnd | FloorDiv");
if (!state->operator_type) return -1;
if (add_attributes(state, state->operator_type, NULL, 0) < 0) return -1;
+ if (PySet_Add(state->abstract_types, state->operator_type) < 0) return -1;
state->Add_type = make_type(state, "Add", state->operator_type, NULL, 0,
"Add");
if (!state->Add_type) return -1;
"unaryop = Invert | Not | UAdd | USub");
if (!state->unaryop_type) return -1;
if (add_attributes(state, state->unaryop_type, NULL, 0) < 0) return -1;
+ if (PySet_Add(state->abstract_types, state->unaryop_type) < 0) return -1;
state->Invert_type = make_type(state, "Invert", state->unaryop_type, NULL,
0,
"Invert");
"cmpop = Eq | NotEq | Lt | LtE | Gt | GtE | Is | IsNot | In | NotIn");
if (!state->cmpop_type) return -1;
if (add_attributes(state, state->cmpop_type, NULL, 0) < 0) return -1;
+ if (PySet_Add(state->abstract_types, state->cmpop_type) < 0) return -1;
state->Eq_type = make_type(state, "Eq", state->cmpop_type, NULL, 0,
"Eq");
if (!state->Eq_type) return -1;
if (!state->excepthandler_type) return -1;
if (add_attributes(state, state->excepthandler_type,
excepthandler_attributes, 4) < 0) return -1;
+ if (PySet_Add(state->abstract_types, state->excepthandler_type) < 0) return
+ -1;
if (PyObject_SetAttr(state->excepthandler_type, state->end_lineno, Py_None)
== -1)
return -1;
if (!state->pattern_type) return -1;
if (add_attributes(state, state->pattern_type, pattern_attributes, 4) < 0)
return -1;
+ if (PySet_Add(state->abstract_types, state->pattern_type) < 0) return -1;
state->MatchValue_type = make_type(state, "MatchValue",
state->pattern_type, MatchValue_fields,
1,
"type_ignore = TypeIgnore(int lineno, string tag)");
if (!state->type_ignore_type) return -1;
if (add_attributes(state, state->type_ignore_type, NULL, 0) < 0) return -1;
+ if (PySet_Add(state->abstract_types, state->type_ignore_type) < 0) return
+ -1;
state->TypeIgnore_type = make_type(state, "TypeIgnore",
state->type_ignore_type,
TypeIgnore_fields, 2,
if (!state->type_param_type) return -1;
if (add_attributes(state, state->type_param_type, type_param_attributes, 4)
< 0) return -1;
+ if (PySet_Add(state->abstract_types, state->type_param_type) < 0) return -1;
state->TypeVar_type = make_type(state, "TypeVar", state->type_param_type,
TypeVar_fields, 3,
"TypeVar(identifier name, expr? bound, expr? default_value)");
}
+/* Helper for checking if a node class is abstract in the tests. */
+static PyObject *
+ast_is_abstract(PyObject *Py_UNUSED(module), PyObject *cls) {
+ struct ast_state *state = get_ast_state();
+ if (state == NULL) {
+ return NULL;
+ }
+ int contains = PySet_Contains(state->abstract_types, cls);
+ if (contains == -1) {
+ return NULL;
+ }
+ else if (contains == 1) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
+static struct PyMethodDef astmodule_methods[] = {
+ {"_is_abstract", ast_is_abstract, METH_O, NULL},
+ {NULL} /* Sentinel */
+};
+
static int
astmodule_exec(PyObject *m)
{
.m_name = "_ast",
// The _ast module uses a per-interpreter state (PyInterpreterState.ast)
.m_size = 0,
- .m_slots = astmodule_slots,
+ .m_methods = astmodule_methods,
+ .m_slots = astmodule_slots
};
PyMODINIT_FUNC