Commits

Georg Brandl committed 42c5d28

#2505: allow easier creation of AST nodes.

Comments (0)

Files changed (3)

Doc/library/_ast.rst

 If these attributes are marked as optional in the grammar (using a question
 mark), the value might be ``None``. If the attributes can have zero-or-more
 values (marked with an asterisk), the values are represented as Python lists.
+All possible attributes must be present and have valid values when compiling an
+AST with :func:`compile`.
 
-The constructors of all ``_ast`` classes don't take arguments; instead, if you
-create instances, you must assign the required attributes separately.
+The constructor of a class ``_ast.T`` parses their arguments as follows:
+
+* If there are positional arguments, there must be as many as there are items in
+  ``T._fields``; they will be assigned as attributes of these names.
+* If there are keyword arguments, they will set the attributes of the same names
+  to the given values.
+
+For example, to create and populate a ``UnaryOp`` node, you could use ::
+
+   node = _ast.UnaryOp()
+   node.op = _ast.USub()
+   node.operand = _ast.Num()
+   node.operand.n = 5
+   node.operand.lineno = 0
+   node.operand.col_offset = 0
+   node.lineno = 0
+   node.col_offset = 0
+
+or the more compact ::
+
+   node = _ast.UnaryOp(_ast.USub(), _ast.Num(5, lineno=0, col_offset=0),
+                       lineno=0, col_offset=0)
+
 
 
 Abstract Grammar
 
     def visitModule(self, mod):
         self.emit("""
+static int
+ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
+{
+    Py_ssize_t i, numfields = 0;
+    int res = -1;
+    PyObject *key, *value, *fields;
+    fields = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "_fields");
+    if (!fields)
+        PyErr_Clear();
+    if (fields) {
+        numfields = PySequence_Size(fields);
+        if (numfields == -1)
+            goto cleanup;
+    }
+    res = 0; /* if no error occurs, this stays 0 to the end */
+    if (PyTuple_GET_SIZE(args) > 0) {
+        if (numfields != PyTuple_GET_SIZE(args)) {
+            PyErr_Format(PyExc_TypeError, "%.400s constructor takes either 0 or "
+                         "%d positional argument%s", Py_TYPE(self)->tp_name,
+                         numfields, numfields == 1 ? "" : "s");
+            res = -1;
+            goto cleanup;
+        }
+        for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
+            /* cannot be reached when fields is NULL */
+            PyObject *name = PySequence_GetItem(fields, i);
+            if (!name) {
+                res = -1;
+                goto cleanup;
+            }
+            res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
+            Py_DECREF(name);
+            if (res < 0)
+                goto cleanup;
+        }
+    }
+    if (kw) {
+        i = 0;  /* needed by PyDict_Next */
+        while (PyDict_Next(kw, &i, &key, &value)) {
+            res = PyObject_SetAttr(self, key, value);
+            if (res < 0)
+                goto cleanup;
+        }
+    }
+  cleanup:
+    Py_XDECREF(fields);
+    return res;
+}
+
+static PyTypeObject AST_type = {
+    PyVarObject_HEAD_INIT(&PyType_Type, 0)
+    "AST",
+    sizeof(PyObject),
+    0,
+    0,                       /* tp_dealloc */
+    0,                       /* tp_print */
+    0,                       /* tp_getattr */
+    0,                       /* tp_setattr */
+    0,                       /* tp_compare */
+    0,                       /* tp_repr */
+    0,                       /* tp_as_number */
+    0,                       /* tp_as_sequence */
+    0,                       /* tp_as_mapping */
+    0,                       /* tp_hash */
+    0,                       /* tp_call */
+    0,                       /* tp_str */
+    PyObject_GenericGetAttr, /* tp_getattro */
+    PyObject_GenericSetAttr, /* tp_setattro */
+    0,                       /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    0,                       /* tp_doc */
+    0,                       /* tp_traverse */
+    0,                       /* tp_clear */
+    0,                       /* tp_richcompare */
+    0,                       /* tp_weaklistoffset */
+    0,                       /* tp_iter */
+    0,                       /* tp_iternext */
+    0,                       /* tp_methods */
+    0,                       /* tp_members */
+    0,                       /* tp_getset */
+    0,                       /* tp_base */
+    0,                       /* tp_dict */
+    0,                       /* tp_descr_get */
+    0,                       /* tp_descr_set */
+    0,                       /* tp_dictoffset */
+    (initproc)ast_type_init, /* tp_init */
+    PyType_GenericAlloc,     /* tp_alloc */
+    PyType_GenericNew,       /* tp_new */
+    PyObject_Del,            /* tp_free */
+};
+
+
 static PyTypeObject* make_type(char *type, PyTypeObject* base, char**fields, int num_fields)
 {
     PyObject *fnames, *result;
 static int add_attributes(PyTypeObject* type, char**attrs, int num_fields)
 {
     int i, result;
-    PyObject *s, *l = PyList_New(num_fields);
+    PyObject *s, *l = PyTuple_New(num_fields);
     if (!l) return 0;
     for(i = 0; i < num_fields; i++) {
         s = PyString_FromString(attrs[i]);
             Py_DECREF(l);
             return 0;
         }
-        PyList_SET_ITEM(l, i, s);
+        PyTuple_SET_ITEM(l, i, s);
     }
     result = PyObject_SetAttrString((PyObject*)type, "_attributes", l) >= 0;
     Py_DECREF(l);
         self.emit("{", 0)
         self.emit("static int initialized;", 1)
         self.emit("if (initialized) return 1;", 1)
-        self.emit('AST_type = make_type("AST", &PyBaseObject_Type, NULL, 0);', 1)
         for dfn in mod.dfns:
             self.visit(dfn)
         self.emit("initialized = 1;", 1)
             fields = name.value+"_fields"
         else:
             fields = "NULL"
-        self.emit('%s_type = make_type("%s", AST_type, %s, %d);' %
+        self.emit('%s_type = make_type("%s", &AST_type, %s, %d);' %
                         (name, name, fields, len(prod.fields)), 1)
         self.emit("if (!%s_type) return 0;" % name, 1)
 
     def visitSum(self, sum, name):
-        self.emit('%s_type = make_type("%s", AST_type, NULL, 0);' % (name, name), 1)
+        self.emit('%s_type = make_type("%s", &AST_type, NULL, 0);' %
+                  (name, name), 1)
         self.emit("if (!%s_type) return 0;" % name, 1)
         if sum.attributes:
             self.emit("if (!add_attributes(%s_type, %s_attributes, %d)) return 0;" %
         self.emit('m = Py_InitModule3("_ast", NULL, NULL);', 1)
         self.emit("if (!m) return;", 1)
         self.emit("d = PyModule_GetDict(m);", 1)
-        self.emit('if (PyDict_SetItemString(d, "AST", (PyObject*)AST_type) < 0) return;', 1)
+        self.emit('if (PyDict_SetItemString(d, "AST", (PyObject*)&AST_type) < 0) return;', 1)
         self.emit('if (PyModule_AddIntConstant(m, "PyCF_ONLY_AST", PyCF_ONLY_AST) < 0)', 1)
         self.emit("return;", 2)
         # Value of version: "$Revision$"
 int PyAST_Check(PyObject* obj)
 {
     init_types();
-    return PyObject_IsInstance(obj, (PyObject*)AST_type);
+    return PyObject_IsInstance(obj, (PyObject*)&AST_type);
 }
 """
 
         print >> f, '#include "Python.h"'
         print >> f, '#include "%s-ast.h"' % mod.name
         print >> f
-        print >>f, "static PyTypeObject* AST_type;"
+        print >>f, "static PyTypeObject AST_type;"
         v = ChainOfVisitors(
             PyTypesDeclareVisitor(f),
             PyTypesVisitor(f),

Python/Python-ast.c

 
 
 /*
-   __version__ 60978.
+   __version__ 62047.
 
    This module must be committed separately after each AST grammar change;
    The __version__ number is set to the revision number of the commit
 #include "Python.h"
 #include "Python-ast.h"
 
-static PyTypeObject* AST_type;
+static PyTypeObject AST_type;
 static PyTypeObject *mod_type;
 static PyObject* ast2obj_mod(void*);
 static PyTypeObject *Module_type;
 };
 
 
+static int
+ast_type_init(PyObject *self, PyObject *args, PyObject *kw)
+{
+    Py_ssize_t i, numfields = 0;
+    int res = -1;
+    PyObject *key, *value, *fields;
+    fields = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "_fields");
+    if (!fields)
+        PyErr_Clear();
+    if (fields) {
+        numfields = PySequence_Size(fields);
+        if (numfields == -1)
+            goto cleanup;
+    }
+    res = 0; /* if no error occurs, this stays 0 to the end */
+    if (PyTuple_GET_SIZE(args) > 0) {
+        if (numfields != PyTuple_GET_SIZE(args)) {
+            PyErr_Format(PyExc_TypeError, "%.400s constructor takes either 0 or "
+                         "%d positional argument%s", Py_TYPE(self)->tp_name,
+                         numfields, numfields == 1 ? "" : "s");
+            res = -1;
+            goto cleanup;
+        }
+        for (i = 0; i < PyTuple_GET_SIZE(args); i++) {
+            /* cannot be reached when fields is NULL */
+            PyObject *name = PySequence_GetItem(fields, i);
+            if (!name) {
+                res = -1;
+                goto cleanup;
+            }
+            res = PyObject_SetAttr(self, name, PyTuple_GET_ITEM(args, i));
+            Py_DECREF(name);
+            if (res < 0)
+                goto cleanup;
+        }
+    }
+    if (kw) {
+        i = 0;  /* needed by PyDict_Next */
+        while (PyDict_Next(kw, &i, &key, &value)) {
+            res = PyObject_SetAttr(self, key, value);
+            if (res < 0)
+                goto cleanup;
+        }
+    }
+  cleanup:
+    Py_XDECREF(fields);
+    return res;
+}
+
+static PyTypeObject AST_type = {
+    PyVarObject_HEAD_INIT(&PyType_Type, 0)
+    "AST",
+    sizeof(PyObject),
+    0,
+    0,                       /* tp_dealloc */
+    0,                       /* tp_print */
+    0,                       /* tp_getattr */
+    0,                       /* tp_setattr */
+    0,                       /* tp_compare */
+    0,                       /* tp_repr */
+    0,                       /* tp_as_number */
+    0,                       /* tp_as_sequence */
+    0,                       /* tp_as_mapping */
+    0,                       /* tp_hash */
+    0,                       /* tp_call */
+    0,                       /* tp_str */
+    PyObject_GenericGetAttr, /* tp_getattro */
+    PyObject_GenericSetAttr, /* tp_setattro */
+    0,                       /* tp_as_buffer */
+    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
+    0,                       /* tp_doc */
+    0,                       /* tp_traverse */
+    0,                       /* tp_clear */
+    0,                       /* tp_richcompare */
+    0,                       /* tp_weaklistoffset */
+    0,                       /* tp_iter */
+    0,                       /* tp_iternext */
+    0,                       /* tp_methods */
+    0,                       /* tp_members */
+    0,                       /* tp_getset */
+    0,                       /* tp_base */
+    0,                       /* tp_dict */
+    0,                       /* tp_descr_get */
+    0,                       /* tp_descr_set */
+    0,                       /* tp_dictoffset */
+    (initproc)ast_type_init, /* tp_init */
+    PyType_GenericAlloc,     /* tp_alloc */
+    PyType_GenericNew,       /* tp_new */
+    PyObject_Del,            /* tp_free */
+};
+
+
 static PyTypeObject* make_type(char *type, PyTypeObject* base, char**fields, int num_fields)
 {
     PyObject *fnames, *result;
 static int add_attributes(PyTypeObject* type, char**attrs, int num_fields)
 {
     int i, result;
-    PyObject *s, *l = PyList_New(num_fields);
+    PyObject *s, *l = PyTuple_New(num_fields);
     if (!l) return 0;
     for(i = 0; i < num_fields; i++) {
         s = PyString_FromString(attrs[i]);
             Py_DECREF(l);
             return 0;
         }
-        PyList_SET_ITEM(l, i, s);
+        PyTuple_SET_ITEM(l, i, s);
     }
     result = PyObject_SetAttrString((PyObject*)type, "_attributes", l) >= 0;
     Py_DECREF(l);
 {
         static int initialized;
         if (initialized) return 1;
-        AST_type = make_type("AST", &PyBaseObject_Type, NULL, 0);
-        mod_type = make_type("mod", AST_type, NULL, 0);
+        mod_type = make_type("mod", &AST_type, NULL, 0);
         if (!mod_type) return 0;
         if (!add_attributes(mod_type, NULL, 0)) return 0;
         Module_type = make_type("Module", mod_type, Module_fields, 1);
         if (!Expression_type) return 0;
         Suite_type = make_type("Suite", mod_type, Suite_fields, 1);
         if (!Suite_type) return 0;
-        stmt_type = make_type("stmt", AST_type, NULL, 0);
+        stmt_type = make_type("stmt", &AST_type, NULL, 0);
         if (!stmt_type) return 0;
         if (!add_attributes(stmt_type, stmt_attributes, 2)) return 0;
         FunctionDef_type = make_type("FunctionDef", stmt_type,
         if (!Break_type) return 0;
         Continue_type = make_type("Continue", stmt_type, NULL, 0);
         if (!Continue_type) return 0;
-        expr_type = make_type("expr", AST_type, NULL, 0);
+        expr_type = make_type("expr", &AST_type, NULL, 0);
         if (!expr_type) return 0;
         if (!add_attributes(expr_type, expr_attributes, 2)) return 0;
         BoolOp_type = make_type("BoolOp", expr_type, BoolOp_fields, 2);
         if (!List_type) return 0;
         Tuple_type = make_type("Tuple", expr_type, Tuple_fields, 2);
         if (!Tuple_type) return 0;
-        expr_context_type = make_type("expr_context", AST_type, NULL, 0);
+        expr_context_type = make_type("expr_context", &AST_type, NULL, 0);
         if (!expr_context_type) return 0;
         if (!add_attributes(expr_context_type, NULL, 0)) return 0;
         Load_type = make_type("Load", expr_context_type, NULL, 0);
         if (!Param_type) return 0;
         Param_singleton = PyType_GenericNew(Param_type, NULL, NULL);
         if (!Param_singleton) return 0;
-        slice_type = make_type("slice", AST_type, NULL, 0);
+        slice_type = make_type("slice", &AST_type, NULL, 0);
         if (!slice_type) return 0;
         if (!add_attributes(slice_type, NULL, 0)) return 0;
         Ellipsis_type = make_type("Ellipsis", slice_type, NULL, 0);
         if (!ExtSlice_type) return 0;
         Index_type = make_type("Index", slice_type, Index_fields, 1);
         if (!Index_type) return 0;
-        boolop_type = make_type("boolop", AST_type, NULL, 0);
+        boolop_type = make_type("boolop", &AST_type, NULL, 0);
         if (!boolop_type) return 0;
         if (!add_attributes(boolop_type, NULL, 0)) return 0;
         And_type = make_type("And", boolop_type, NULL, 0);
         if (!Or_type) return 0;
         Or_singleton = PyType_GenericNew(Or_type, NULL, NULL);
         if (!Or_singleton) return 0;
-        operator_type = make_type("operator", AST_type, NULL, 0);
+        operator_type = make_type("operator", &AST_type, NULL, 0);
         if (!operator_type) return 0;
         if (!add_attributes(operator_type, NULL, 0)) return 0;
         Add_type = make_type("Add", operator_type, NULL, 0);
         if (!FloorDiv_type) return 0;
         FloorDiv_singleton = PyType_GenericNew(FloorDiv_type, NULL, NULL);
         if (!FloorDiv_singleton) return 0;
-        unaryop_type = make_type("unaryop", AST_type, NULL, 0);
+        unaryop_type = make_type("unaryop", &AST_type, NULL, 0);
         if (!unaryop_type) return 0;
         if (!add_attributes(unaryop_type, NULL, 0)) return 0;
         Invert_type = make_type("Invert", unaryop_type, NULL, 0);
         if (!USub_type) return 0;
         USub_singleton = PyType_GenericNew(USub_type, NULL, NULL);
         if (!USub_singleton) return 0;
-        cmpop_type = make_type("cmpop", AST_type, NULL, 0);
+        cmpop_type = make_type("cmpop", &AST_type, NULL, 0);
         if (!cmpop_type) return 0;
         if (!add_attributes(cmpop_type, NULL, 0)) return 0;
         Eq_type = make_type("Eq", cmpop_type, NULL, 0);
         if (!NotIn_type) return 0;
         NotIn_singleton = PyType_GenericNew(NotIn_type, NULL, NULL);
         if (!NotIn_singleton) return 0;
-        comprehension_type = make_type("comprehension", AST_type,
+        comprehension_type = make_type("comprehension", &AST_type,
                                        comprehension_fields, 3);
         if (!comprehension_type) return 0;
-        excepthandler_type = make_type("excepthandler", AST_type, NULL, 0);
+        excepthandler_type = make_type("excepthandler", &AST_type, NULL, 0);
         if (!excepthandler_type) return 0;
         if (!add_attributes(excepthandler_type, excepthandler_attributes, 2))
             return 0;
         ExceptHandler_type = make_type("ExceptHandler", excepthandler_type,
                                        ExceptHandler_fields, 3);
         if (!ExceptHandler_type) return 0;
-        arguments_type = make_type("arguments", AST_type, arguments_fields, 4);
+        arguments_type = make_type("arguments", &AST_type, arguments_fields, 4);
         if (!arguments_type) return 0;
-        keyword_type = make_type("keyword", AST_type, keyword_fields, 2);
+        keyword_type = make_type("keyword", &AST_type, keyword_fields, 2);
         if (!keyword_type) return 0;
-        alias_type = make_type("alias", AST_type, alias_fields, 2);
+        alias_type = make_type("alias", &AST_type, alias_fields, 2);
         if (!alias_type) return 0;
         initialized = 1;
         return 1;
         m = Py_InitModule3("_ast", NULL, NULL);
         if (!m) return;
         d = PyModule_GetDict(m);
-        if (PyDict_SetItemString(d, "AST", (PyObject*)AST_type) < 0) return;
+        if (PyDict_SetItemString(d, "AST", (PyObject*)&AST_type) < 0) return;
         if (PyModule_AddIntConstant(m, "PyCF_ONLY_AST", PyCF_ONLY_AST) < 0)
                 return;
-        if (PyModule_AddStringConstant(m, "__version__", "60978") < 0)
+        if (PyModule_AddStringConstant(m, "__version__", "62047") < 0)
                 return;
         if (PyDict_SetItemString(d, "mod", (PyObject*)mod_type) < 0) return;
         if (PyDict_SetItemString(d, "Module", (PyObject*)Module_type) < 0)
 int PyAST_Check(PyObject* obj)
 {
     init_types();
-    return PyObject_IsInstance(obj, (PyObject*)AST_type);
+    return PyObject_IsInstance(obj, (PyObject*)&AST_type);
 }
 
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.