Kaydet (Commit) 618dc5e0 authored tarafından Martin v. Löwis's avatar Martin v. Löwis

Merged revisions 62004 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r62004 | georg.brandl | 2008-03-28 13:11:56 +0100 (Fr, 28 Mär 2008) | 4 lines

  Patch #1810 by Thomas Lee, reviewed by myself:
  allow compiling Python AST objects into code objects
  in compile().
........
üst d3372793
...@@ -10,16 +10,16 @@ Abstract Syntax Trees ...@@ -10,16 +10,16 @@ Abstract Syntax Trees
The ``_ast`` module helps Python applications to process trees of the Python The ``_ast`` module helps Python applications to process trees of the Python
abstract syntax grammar. The Python compiler currently provides read-only access abstract syntax grammar. The abstract syntax itself might change with each
to such trees, meaning that applications can only create a tree for a given Python release; this module helps to find out programmatically what the current
piece of Python source code; generating :term:`bytecode` from a (potentially modified) grammar looks like.
tree is not supported. The abstract syntax itself might change with each Python
release; this module helps to find out programmatically what the current grammar
looks like.
An abstract syntax tree can be generated by passing ``_ast.PyCF_ONLY_AST`` as a An abstract syntax tree can be generated by passing :data:`_ast.PyCF_ONLY_AST`
flag to the :func:`compile` builtin function. The result will be a tree of as a flag to the :func:`compile` builtin function. The result will be a tree of
objects whose classes all inherit from ``_ast.AST``. objects whose classes all inherit from :class:`_ast.AST`.
A modified abstract syntax tree can be compiled into a Python code object using
the built-in :func:`compile` function.
The actual classes are derived from the ``Parser/Python.asdl`` file, which is The actual classes are derived from the ``Parser/Python.asdl`` file, which is
reproduced below. There is one class defined for each left-hand side symbol in reproduced below. There is one class defined for each left-hand side symbol in
...@@ -39,12 +39,15 @@ attribute ``left`` of type ``_ast.expr``. Instances of ``_ast.expr`` and ...@@ -39,12 +39,15 @@ attribute ``left`` of type ``_ast.expr``. Instances of ``_ast.expr`` and
``_ast.stmt`` subclasses also have lineno and col_offset attributes. The lineno ``_ast.stmt`` subclasses also have lineno and col_offset attributes. The lineno
is the line number of source text (1 indexed so the first line is line 1) and is the line number of source text (1 indexed so the first line is line 1) and
the col_offset is the utf8 byte offset of the first token that generated the the col_offset is the utf8 byte offset of the first token that generated the
node. The utf8 offset is recorded because the parser uses utf8 internally. node. The utf8 offset is recorded because the parser uses utf8 internally.
If these attributes are marked as optional in the grammar (using a question 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 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. values (marked with an asterisk), the values are represented as Python lists.
The constructors of all ``_ast`` classes don't take arguments; instead, if you
create instances, you must assign the required attributes separately.
Abstract Grammar Abstract Grammar
---------------- ----------------
......
...@@ -193,21 +193,21 @@ available. They are listed here in alphabetical order. ...@@ -193,21 +193,21 @@ available. They are listed here in alphabetical order.
.. function:: compile(source, filename, mode[, flags[, dont_inherit]]) .. function:: compile(source, filename, mode[, flags[, dont_inherit]])
Compile the *source* into a code object. Code objects can be executed by a call Compile the *source* into a code object. Code objects can be
to :func:`exec` or evaluated by a call to :func:`eval`. The *filename* argument executed by a call to :func:`exec` or evaluated by a call to
should give the file from which the code was read; pass some recognizable value :func:`eval`. *source* can either be a string or an AST object.
if it wasn't read from a file (``'<string>'`` is commonly used). The *mode* Refer to the :mod:`_ast` module documentation for information on
argument specifies what kind of code must be compiled; it can be ``'exec'`` if how to compile into and from AST objects.
*source* consists of a sequence of statements, ``'eval'`` if it consists of a
single expression, or ``'single'`` if it consists of a single interactive The *filename* argument should give the file from
statement (in the latter case, expression statements that evaluate to something which the code was read; pass some recognizable value if it wasn't
else than ``None`` will be printed). read from a file (``'<string>'`` is commonly used). The *mode*
argument specifies what kind of code must be compiled; it can be
When compiling multi-line statements, two caveats apply: line endings must be ``'exec'`` if *source* consists of a sequence of statements,
represented by a single newline character (``'\n'``), and the input must be ``'eval'`` if it consists of a single expression, or ``'single'``
terminated by at least one newline character. If line endings are represented if it consists of a single interactive statement (in the latter
by ``'\r\n'``, use the string :meth:`replace` method to change them into case, expression statements that evaluate to something else than
``'\n'``. ``None`` will be printed).
The optional arguments *flags* and *dont_inherit* (which are new in Python 2.2) The optional arguments *flags* and *dont_inherit* (which are new in Python 2.2)
control which future statements (see :pep:`236`) affect the compilation of control which future statements (see :pep:`236`) affect the compilation of
...@@ -227,6 +227,9 @@ available. They are listed here in alphabetical order. ...@@ -227,6 +227,9 @@ available. They are listed here in alphabetical order.
This function raises :exc:`SyntaxError` if the compiled source is invalid, This function raises :exc:`SyntaxError` if the compiled source is invalid,
and :exc:`TypeError` if the source contains null bytes. and :exc:`TypeError` if the source contains null bytes.
.. versionadded:: 2.6
Support for compiling AST objects.
.. function:: complex([real[, imag]]) .. function:: complex([real[, imag]])
......
...@@ -542,3 +542,5 @@ keyword_ty _Py_keyword(identifier arg, expr_ty value, PyArena *arena); ...@@ -542,3 +542,5 @@ keyword_ty _Py_keyword(identifier arg, expr_ty value, PyArena *arena);
alias_ty _Py_alias(identifier name, identifier asname, PyArena *arena); alias_ty _Py_alias(identifier name, identifier asname, PyArena *arena);
PyObject* PyAST_mod2obj(mod_ty t); PyObject* PyAST_mod2obj(mod_ty t);
mod_ty PyAST_obj2mod(PyObject* ast, PyArena* arena);
int PyAST_Check(PyObject* obj);
import unittest import unittest
import sys import sys
import _ast
from test import test_support from test import test_support
class TestSpecifics(unittest.TestCase): class TestSpecifics(unittest.TestCase):
...@@ -406,6 +407,28 @@ if 1: ...@@ -406,6 +407,28 @@ if 1:
self.assert_("_A__mangled_mod" in A.f.__code__.co_varnames) self.assert_("_A__mangled_mod" in A.f.__code__.co_varnames)
self.assert_("__package__" in A.f.__code__.co_varnames) self.assert_("__package__" in A.f.__code__.co_varnames)
def test_compile_ast(self):
fname = __file__
if fname.lower().endswith(('pyc', 'pyo')):
fname = fname[:-1]
with open(fname, 'r') as f:
fcontents = f.read()
sample_code = [
['<assign>', 'x = 5'],
['<ifblock>', """if True:\n pass\n"""],
['<forblock>', """for n in [1, 2, 3]:\n print(n)\n"""],
['<deffunc>', """def foo():\n pass\nfoo()\n"""],
[fname, fcontents],
]
for fname, code in sample_code:
co1 = compile(code, '%s1' % fname, 'exec')
ast = compile(code, '%s2' % fname, 'exec', _ast.PyCF_ONLY_AST)
self.assert_(type(ast) == _ast.Module)
co2 = compile(ast, '%s3' % fname, 'exec')
self.assertEqual(co1, co2)
def test_main(): def test_main():
test_support.run_unittest(TestSpecifics) test_support.run_unittest(TestSpecifics)
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
/* Built-in functions */ /* Built-in functions */
#include "Python.h" #include "Python.h"
#include "Python-ast.h"
#include "node.h" #include "node.h"
#include "code.h" #include "code.h"
...@@ -527,10 +528,43 @@ builtin_compile(PyObject *self, PyObject *args, PyObject *kwds) ...@@ -527,10 +528,43 @@ builtin_compile(PyObject *self, PyObject *args, PyObject *kwds)
cf.cf_flags = supplied_flags | PyCF_SOURCE_IS_UTF8; cf.cf_flags = supplied_flags | PyCF_SOURCE_IS_UTF8;
str = source_as_string(cmd); if (supplied_flags &
if (str == NULL) ~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_DONT_IMPLY_DEDENT | PyCF_ONLY_AST))
{
PyErr_SetString(PyExc_ValueError,
"compile(): unrecognised flags");
return NULL; return NULL;
}
/* XXX Warn if (supplied_flags & PyCF_MASK_OBSOLETE) != 0? */
if (!dont_inherit) {
PyEval_MergeCompilerFlags(&cf);
}
if (PyAST_Check(cmd)) {
PyObject *result;
if (supplied_flags & PyCF_ONLY_AST) {
Py_INCREF(cmd);
result = cmd;
}
else {
PyArena *arena;
mod_ty mod;
arena = PyArena_New();
mod = PyAST_obj2mod(cmd, arena);
if (mod == NULL) {
PyArena_Free(arena);
return NULL;
}
result = (PyObject*)PyAST_Compile(mod, filename,
&cf, arena);
PyArena_Free(arena);
}
return result;
}
/* XXX: is it possible to pass start to the PyAST_ branch? */
if (strcmp(startstr, "exec") == 0) if (strcmp(startstr, "exec") == 0)
start = Py_file_input; start = Py_file_input;
else if (strcmp(startstr, "eval") == 0) else if (strcmp(startstr, "eval") == 0)
...@@ -539,22 +573,15 @@ builtin_compile(PyObject *self, PyObject *args, PyObject *kwds) ...@@ -539,22 +573,15 @@ builtin_compile(PyObject *self, PyObject *args, PyObject *kwds)
start = Py_single_input; start = Py_single_input;
else { else {
PyErr_SetString(PyExc_ValueError, PyErr_SetString(PyExc_ValueError,
"compile() arg 3 must be 'exec' or 'eval' or 'single'"); "compile() arg 3 must be 'exec'"
"or 'eval' or 'single'");
return NULL; return NULL;
} }
if (supplied_flags & str = source_as_string(cmd);
~(PyCF_MASK | PyCF_MASK_OBSOLETE | PyCF_DONT_IMPLY_DEDENT | PyCF_ONLY_AST)) if (str == NULL)
{
PyErr_SetString(PyExc_ValueError,
"compile(): unrecognised flags");
return NULL; return NULL;
}
/* XXX Warn if (supplied_flags & PyCF_MASK_OBSOLETE) != 0? */
if (!dont_inherit) {
PyEval_MergeCompilerFlags(&cf);
}
return Py_CompileStringFlags(str, filename, start, &cf); return Py_CompileStringFlags(str, filename, start, &cf);
} }
......
...@@ -2356,8 +2356,11 @@ unaryop(unaryop_ty op) ...@@ -2356,8 +2356,11 @@ unaryop(unaryop_ty op)
return UNARY_POSITIVE; return UNARY_POSITIVE;
case USub: case USub:
return UNARY_NEGATIVE; return UNARY_NEGATIVE;
default:
PyErr_Format(PyExc_SystemError,
"unary op %d should not be possible", op);
return 0;
} }
return 0;
} }
static int static int
...@@ -2388,8 +2391,11 @@ binop(struct compiler *c, operator_ty op) ...@@ -2388,8 +2391,11 @@ binop(struct compiler *c, operator_ty op)
return BINARY_AND; return BINARY_AND;
case FloorDiv: case FloorDiv:
return BINARY_FLOOR_DIVIDE; return BINARY_FLOOR_DIVIDE;
default:
PyErr_Format(PyExc_SystemError,
"binary op %d should not be possible", op);
return 0;
} }
return 0;
} }
static int static int
...@@ -2416,8 +2422,9 @@ cmpop(cmpop_ty op) ...@@ -2416,8 +2422,9 @@ cmpop(cmpop_ty op)
return PyCmp_IN; return PyCmp_IN;
case NotIn: case NotIn:
return PyCmp_NOT_IN; return PyCmp_NOT_IN;
default:
return PyCmp_BAD;
} }
return PyCmp_BAD;
} }
static int static int
...@@ -2448,10 +2455,11 @@ inplace_binop(struct compiler *c, operator_ty op) ...@@ -2448,10 +2455,11 @@ inplace_binop(struct compiler *c, operator_ty op)
return INPLACE_AND; return INPLACE_AND;
case FloorDiv: case FloorDiv:
return INPLACE_FLOOR_DIVIDE; return INPLACE_FLOOR_DIVIDE;
default:
PyErr_Format(PyExc_SystemError,
"inplace binary op %d should not be possible", op);
return 0;
} }
PyErr_Format(PyExc_SystemError,
"inplace binary op %d should not be possible", op);
return 0;
} }
static int static int
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment