mirror of https://github.com/python/cpython
Add ast.Constant
Issue #26146: Add a new kind of AST node: ast.Constant. It can be used by external AST optimizers, but the compiler does not emit directly such node. An optimizer can replace the following AST nodes with ast.Constant: * ast.NameConstant: None, False, True * ast.Num: int, float, complex * ast.Str: str * ast.Bytes: bytes * ast.Tuple if items are constants too: tuple * frozenset Update code to accept ast.Constant instead of ast.Num and/or ast.Str: * compiler * docstrings * ast.literal_eval() * Tools/parser/unparse.py
This commit is contained in:
parent
0dceb91866
commit
f2c1aa1661
|
@ -202,9 +202,9 @@ enum _expr_kind {BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4,
|
||||||
Await_kind=12, Yield_kind=13, YieldFrom_kind=14,
|
Await_kind=12, Yield_kind=13, YieldFrom_kind=14,
|
||||||
Compare_kind=15, Call_kind=16, Num_kind=17, Str_kind=18,
|
Compare_kind=15, Call_kind=16, Num_kind=17, Str_kind=18,
|
||||||
FormattedValue_kind=19, JoinedStr_kind=20, Bytes_kind=21,
|
FormattedValue_kind=19, JoinedStr_kind=20, Bytes_kind=21,
|
||||||
NameConstant_kind=22, Ellipsis_kind=23, Attribute_kind=24,
|
NameConstant_kind=22, Ellipsis_kind=23, Constant_kind=24,
|
||||||
Subscript_kind=25, Starred_kind=26, Name_kind=27,
|
Attribute_kind=25, Subscript_kind=26, Starred_kind=27,
|
||||||
List_kind=28, Tuple_kind=29};
|
Name_kind=28, List_kind=29, Tuple_kind=30};
|
||||||
struct _expr {
|
struct _expr {
|
||||||
enum _expr_kind kind;
|
enum _expr_kind kind;
|
||||||
union {
|
union {
|
||||||
|
@ -315,6 +315,10 @@ struct _expr {
|
||||||
singleton value;
|
singleton value;
|
||||||
} NameConstant;
|
} NameConstant;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
constant value;
|
||||||
|
} Constant;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
expr_ty value;
|
expr_ty value;
|
||||||
identifier attr;
|
identifier attr;
|
||||||
|
@ -567,6 +571,9 @@ expr_ty _Py_NameConstant(singleton value, int lineno, int col_offset, PyArena
|
||||||
*arena);
|
*arena);
|
||||||
#define Ellipsis(a0, a1, a2) _Py_Ellipsis(a0, a1, a2)
|
#define Ellipsis(a0, a1, a2) _Py_Ellipsis(a0, a1, a2)
|
||||||
expr_ty _Py_Ellipsis(int lineno, int col_offset, PyArena *arena);
|
expr_ty _Py_Ellipsis(int lineno, int col_offset, PyArena *arena);
|
||||||
|
#define Constant(a0, a1, a2, a3) _Py_Constant(a0, a1, a2, a3)
|
||||||
|
expr_ty _Py_Constant(constant value, int lineno, int col_offset, PyArena
|
||||||
|
*arena);
|
||||||
#define Attribute(a0, a1, a2, a3, a4, a5) _Py_Attribute(a0, a1, a2, a3, a4, a5)
|
#define Attribute(a0, a1, a2, a3, a4, a5) _Py_Attribute(a0, a1, a2, a3, a4, a5)
|
||||||
expr_ty _Py_Attribute(expr_ty value, identifier attr, expr_context_ty ctx, int
|
expr_ty _Py_Attribute(expr_ty value, identifier attr, expr_context_ty ctx, int
|
||||||
lineno, int col_offset, PyArena *arena);
|
lineno, int col_offset, PyArena *arena);
|
||||||
|
|
|
@ -6,6 +6,7 @@ typedef PyObject * string;
|
||||||
typedef PyObject * bytes;
|
typedef PyObject * bytes;
|
||||||
typedef PyObject * object;
|
typedef PyObject * object;
|
||||||
typedef PyObject * singleton;
|
typedef PyObject * singleton;
|
||||||
|
typedef PyObject * constant;
|
||||||
|
|
||||||
/* It would be nice if the code generated by asdl_c.py was completely
|
/* It would be nice if the code generated by asdl_c.py was completely
|
||||||
independent of Python, but it is a goal the requires too much work
|
independent of Python, but it is a goal the requires too much work
|
||||||
|
|
52
Lib/ast.py
52
Lib/ast.py
|
@ -35,6 +35,8 @@ def parse(source, filename='<unknown>', mode='exec'):
|
||||||
return compile(source, filename, mode, PyCF_ONLY_AST)
|
return compile(source, filename, mode, PyCF_ONLY_AST)
|
||||||
|
|
||||||
|
|
||||||
|
_NUM_TYPES = (int, float, complex)
|
||||||
|
|
||||||
def literal_eval(node_or_string):
|
def literal_eval(node_or_string):
|
||||||
"""
|
"""
|
||||||
Safely evaluate an expression node or a string containing a Python
|
Safely evaluate an expression node or a string containing a Python
|
||||||
|
@ -47,7 +49,9 @@ def literal_eval(node_or_string):
|
||||||
if isinstance(node_or_string, Expression):
|
if isinstance(node_or_string, Expression):
|
||||||
node_or_string = node_or_string.body
|
node_or_string = node_or_string.body
|
||||||
def _convert(node):
|
def _convert(node):
|
||||||
if isinstance(node, (Str, Bytes)):
|
if isinstance(node, Constant):
|
||||||
|
return node.value
|
||||||
|
elif isinstance(node, (Str, Bytes)):
|
||||||
return node.s
|
return node.s
|
||||||
elif isinstance(node, Num):
|
elif isinstance(node, Num):
|
||||||
return node.n
|
return node.n
|
||||||
|
@ -62,24 +66,21 @@ def literal_eval(node_or_string):
|
||||||
in zip(node.keys, node.values))
|
in zip(node.keys, node.values))
|
||||||
elif isinstance(node, NameConstant):
|
elif isinstance(node, NameConstant):
|
||||||
return node.value
|
return node.value
|
||||||
elif isinstance(node, UnaryOp) and \
|
elif isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
|
||||||
isinstance(node.op, (UAdd, USub)) and \
|
|
||||||
isinstance(node.operand, (Num, UnaryOp, BinOp)):
|
|
||||||
operand = _convert(node.operand)
|
operand = _convert(node.operand)
|
||||||
if isinstance(node.op, UAdd):
|
if isinstance(operand, _NUM_TYPES):
|
||||||
return + operand
|
if isinstance(node.op, UAdd):
|
||||||
else:
|
return + operand
|
||||||
return - operand
|
else:
|
||||||
elif isinstance(node, BinOp) and \
|
return - operand
|
||||||
isinstance(node.op, (Add, Sub)) and \
|
elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
|
||||||
isinstance(node.right, (Num, UnaryOp, BinOp)) and \
|
|
||||||
isinstance(node.left, (Num, UnaryOp, BinOp)):
|
|
||||||
left = _convert(node.left)
|
left = _convert(node.left)
|
||||||
right = _convert(node.right)
|
right = _convert(node.right)
|
||||||
if isinstance(node.op, Add):
|
if isinstance(left, _NUM_TYPES) and isinstance(right, _NUM_TYPES):
|
||||||
return left + right
|
if isinstance(node.op, Add):
|
||||||
else:
|
return left + right
|
||||||
return left - right
|
else:
|
||||||
|
return left - right
|
||||||
raise ValueError('malformed node or string: ' + repr(node))
|
raise ValueError('malformed node or string: ' + repr(node))
|
||||||
return _convert(node_or_string)
|
return _convert(node_or_string)
|
||||||
|
|
||||||
|
@ -196,12 +197,19 @@ def get_docstring(node, clean=True):
|
||||||
"""
|
"""
|
||||||
if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
|
if not isinstance(node, (AsyncFunctionDef, FunctionDef, ClassDef, Module)):
|
||||||
raise TypeError("%r can't have docstrings" % node.__class__.__name__)
|
raise TypeError("%r can't have docstrings" % node.__class__.__name__)
|
||||||
if node.body and isinstance(node.body[0], Expr) and \
|
if not(node.body and isinstance(node.body[0], Expr)):
|
||||||
isinstance(node.body[0].value, Str):
|
return
|
||||||
if clean:
|
node = node.body[0].value
|
||||||
import inspect
|
if isinstance(node, Str):
|
||||||
return inspect.cleandoc(node.body[0].value.s)
|
text = node.s
|
||||||
return node.body[0].value.s
|
elif isinstance(node, Constant) and isinstance(node.value, str):
|
||||||
|
text = node.value
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
if clean:
|
||||||
|
import inspect
|
||||||
|
text = inspect.cleandoc(text)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
def walk(node):
|
def walk(node):
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import ast
|
||||||
|
import dis
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
import ast
|
|
||||||
import weakref
|
import weakref
|
||||||
|
|
||||||
from test import support
|
from test import support
|
||||||
|
@ -933,6 +934,123 @@ class ASTValidatorTests(unittest.TestCase):
|
||||||
compile(mod, fn, "exec")
|
compile(mod, fn, "exec")
|
||||||
|
|
||||||
|
|
||||||
|
class ConstantTests(unittest.TestCase):
|
||||||
|
"""Tests on the ast.Constant node type."""
|
||||||
|
|
||||||
|
def compile_constant(self, value):
|
||||||
|
tree = ast.parse("x = 123")
|
||||||
|
|
||||||
|
node = tree.body[0].value
|
||||||
|
new_node = ast.Constant(value=value)
|
||||||
|
ast.copy_location(new_node, node)
|
||||||
|
tree.body[0].value = new_node
|
||||||
|
|
||||||
|
code = compile(tree, "<string>", "exec")
|
||||||
|
|
||||||
|
ns = {}
|
||||||
|
exec(code, ns)
|
||||||
|
return ns['x']
|
||||||
|
|
||||||
|
def test_singletons(self):
|
||||||
|
for const in (None, False, True, Ellipsis, b'', frozenset()):
|
||||||
|
with self.subTest(const=const):
|
||||||
|
value = self.compile_constant(const)
|
||||||
|
self.assertIs(value, const)
|
||||||
|
|
||||||
|
def test_values(self):
|
||||||
|
nested_tuple = (1,)
|
||||||
|
nested_frozenset = frozenset({1})
|
||||||
|
for level in range(3):
|
||||||
|
nested_tuple = (nested_tuple, 2)
|
||||||
|
nested_frozenset = frozenset({nested_frozenset, 2})
|
||||||
|
values = (123, 123.0, 123j,
|
||||||
|
"unicode", b'bytes',
|
||||||
|
tuple("tuple"), frozenset("frozenset"),
|
||||||
|
nested_tuple, nested_frozenset)
|
||||||
|
for value in values:
|
||||||
|
with self.subTest(value=value):
|
||||||
|
result = self.compile_constant(value)
|
||||||
|
self.assertEqual(result, value)
|
||||||
|
|
||||||
|
def test_assign_to_constant(self):
|
||||||
|
tree = ast.parse("x = 1")
|
||||||
|
|
||||||
|
target = tree.body[0].targets[0]
|
||||||
|
new_target = ast.Constant(value=1)
|
||||||
|
ast.copy_location(new_target, target)
|
||||||
|
tree.body[0].targets[0] = new_target
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as cm:
|
||||||
|
compile(tree, "string", "exec")
|
||||||
|
self.assertEqual(str(cm.exception),
|
||||||
|
"expression which can't be assigned "
|
||||||
|
"to in Store context")
|
||||||
|
|
||||||
|
def test_get_docstring(self):
|
||||||
|
tree = ast.parse("'docstring'\nx = 1")
|
||||||
|
self.assertEqual(ast.get_docstring(tree), 'docstring')
|
||||||
|
|
||||||
|
tree.body[0].value = ast.Constant(value='constant docstring')
|
||||||
|
self.assertEqual(ast.get_docstring(tree), 'constant docstring')
|
||||||
|
|
||||||
|
def get_load_const(self, tree):
|
||||||
|
# Compile to bytecode, disassemble and get parameter of LOAD_CONST
|
||||||
|
# instructions
|
||||||
|
co = compile(tree, '<string>', 'exec')
|
||||||
|
consts = []
|
||||||
|
for instr in dis.get_instructions(co):
|
||||||
|
if instr.opname == 'LOAD_CONST':
|
||||||
|
consts.append(instr.argval)
|
||||||
|
return consts
|
||||||
|
|
||||||
|
@support.cpython_only
|
||||||
|
def test_load_const(self):
|
||||||
|
consts = [None,
|
||||||
|
True, False,
|
||||||
|
124,
|
||||||
|
2.0,
|
||||||
|
3j,
|
||||||
|
"unicode",
|
||||||
|
b'bytes',
|
||||||
|
(1, 2, 3)]
|
||||||
|
|
||||||
|
code = '\n'.join(map(repr, consts))
|
||||||
|
code += '\n...'
|
||||||
|
|
||||||
|
code_consts = [const for const in consts
|
||||||
|
if (not isinstance(const, (str, int, float, complex))
|
||||||
|
or isinstance(const, bool))]
|
||||||
|
code_consts.append(Ellipsis)
|
||||||
|
# the compiler adds a final "LOAD_CONST None"
|
||||||
|
code_consts.append(None)
|
||||||
|
|
||||||
|
tree = ast.parse(code)
|
||||||
|
self.assertEqual(self.get_load_const(tree), code_consts)
|
||||||
|
|
||||||
|
# Replace expression nodes with constants
|
||||||
|
for expr_node, const in zip(tree.body, consts):
|
||||||
|
assert isinstance(expr_node, ast.Expr)
|
||||||
|
new_node = ast.Constant(value=const)
|
||||||
|
ast.copy_location(new_node, expr_node.value)
|
||||||
|
expr_node.value = new_node
|
||||||
|
|
||||||
|
self.assertEqual(self.get_load_const(tree), code_consts)
|
||||||
|
|
||||||
|
def test_literal_eval(self):
|
||||||
|
tree = ast.parse("1 + 2")
|
||||||
|
binop = tree.body[0].value
|
||||||
|
|
||||||
|
new_left = ast.Constant(value=10)
|
||||||
|
ast.copy_location(new_left, binop.left)
|
||||||
|
binop.left = new_left
|
||||||
|
|
||||||
|
new_right = ast.Constant(value=20)
|
||||||
|
ast.copy_location(new_right, binop.right)
|
||||||
|
binop.right = new_right
|
||||||
|
|
||||||
|
self.assertEqual(ast.literal_eval(binop), 30)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
if __name__ != '__main__':
|
if __name__ != '__main__':
|
||||||
return
|
return
|
||||||
|
|
|
@ -10,6 +10,10 @@ Release date: tba
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #26146: Add a new kind of AST node: ``ast.Constant``. It can be used
|
||||||
|
by external AST optimizers, but the compiler does not emit directly such
|
||||||
|
node.
|
||||||
|
|
||||||
- Issue #18018: Import raises ImportError instead of SystemError if a relative
|
- Issue #18018: Import raises ImportError instead of SystemError if a relative
|
||||||
import is attempted without a known parent package.
|
import is attempted without a known parent package.
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
-- ASDL's six builtin types are identifier, int, string, bytes, object, singleton
|
-- ASDL's 7 builtin types are:
|
||||||
|
-- identifier, int, string, bytes, object, singleton, constant
|
||||||
|
--
|
||||||
|
-- singleton: None, True or False
|
||||||
|
-- constant can be None, whereas None means "no value" for object.
|
||||||
|
|
||||||
module Python
|
module Python
|
||||||
{
|
{
|
||||||
|
@ -76,6 +80,7 @@ module Python
|
||||||
| Bytes(bytes s)
|
| Bytes(bytes s)
|
||||||
| NameConstant(singleton value)
|
| NameConstant(singleton value)
|
||||||
| Ellipsis
|
| Ellipsis
|
||||||
|
| Constant(constant value)
|
||||||
|
|
||||||
-- the following expression can appear in assignment context
|
-- the following expression can appear in assignment context
|
||||||
| Attribute(expr value, identifier attr, expr_context ctx)
|
| Attribute(expr value, identifier attr, expr_context ctx)
|
||||||
|
|
|
@ -33,7 +33,8 @@ __all__ = [
|
||||||
# See the EBNF at the top of the file to understand the logical connection
|
# See the EBNF at the top of the file to understand the logical connection
|
||||||
# between the various node types.
|
# between the various node types.
|
||||||
|
|
||||||
builtin_types = {'identifier', 'string', 'bytes', 'int', 'object', 'singleton'}
|
builtin_types = {'identifier', 'string', 'bytes', 'int', 'object', 'singleton',
|
||||||
|
'constant'}
|
||||||
|
|
||||||
class AST:
|
class AST:
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|
|
@ -834,6 +834,7 @@ static PyObject* ast2obj_object(void *o)
|
||||||
return (PyObject*)o;
|
return (PyObject*)o;
|
||||||
}
|
}
|
||||||
#define ast2obj_singleton ast2obj_object
|
#define ast2obj_singleton ast2obj_object
|
||||||
|
#define ast2obj_constant ast2obj_object
|
||||||
#define ast2obj_identifier ast2obj_object
|
#define ast2obj_identifier ast2obj_object
|
||||||
#define ast2obj_string ast2obj_object
|
#define ast2obj_string ast2obj_object
|
||||||
#define ast2obj_bytes ast2obj_object
|
#define ast2obj_bytes ast2obj_object
|
||||||
|
@ -871,6 +872,26 @@ static int obj2ast_object(PyObject* obj, PyObject** out, PyArena* arena)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int obj2ast_constant(PyObject* obj, PyObject** out, PyArena* arena)
|
||||||
|
{
|
||||||
|
if (obj == Py_None || obj == Py_True || obj == Py_False) {
|
||||||
|
/* don't increment the reference counter, Constant uses a borrowed
|
||||||
|
* reference, not a strong reference */
|
||||||
|
*out = obj;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
if (PyArena_AddPyObject(arena, obj) < 0) {
|
||||||
|
*out = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Py_INCREF(obj);
|
||||||
|
}
|
||||||
|
*out = obj;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int obj2ast_identifier(PyObject* obj, PyObject** out, PyArena* arena)
|
static int obj2ast_identifier(PyObject* obj, PyObject** out, PyArena* arena)
|
||||||
{
|
{
|
||||||
if (!PyUnicode_CheckExact(obj) && obj != Py_None) {
|
if (!PyUnicode_CheckExact(obj) && obj != Py_None) {
|
||||||
|
|
|
@ -306,6 +306,10 @@ static char *NameConstant_fields[]={
|
||||||
"value",
|
"value",
|
||||||
};
|
};
|
||||||
static PyTypeObject *Ellipsis_type;
|
static PyTypeObject *Ellipsis_type;
|
||||||
|
static PyTypeObject *Constant_type;
|
||||||
|
static char *Constant_fields[]={
|
||||||
|
"value",
|
||||||
|
};
|
||||||
static PyTypeObject *Attribute_type;
|
static PyTypeObject *Attribute_type;
|
||||||
_Py_IDENTIFIER(attr);
|
_Py_IDENTIFIER(attr);
|
||||||
_Py_IDENTIFIER(ctx);
|
_Py_IDENTIFIER(ctx);
|
||||||
|
@ -709,6 +713,7 @@ static PyObject* ast2obj_object(void *o)
|
||||||
return (PyObject*)o;
|
return (PyObject*)o;
|
||||||
}
|
}
|
||||||
#define ast2obj_singleton ast2obj_object
|
#define ast2obj_singleton ast2obj_object
|
||||||
|
#define ast2obj_constant ast2obj_object
|
||||||
#define ast2obj_identifier ast2obj_object
|
#define ast2obj_identifier ast2obj_object
|
||||||
#define ast2obj_string ast2obj_object
|
#define ast2obj_string ast2obj_object
|
||||||
#define ast2obj_bytes ast2obj_object
|
#define ast2obj_bytes ast2obj_object
|
||||||
|
@ -746,6 +751,26 @@ static int obj2ast_object(PyObject* obj, PyObject** out, PyArena* arena)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int obj2ast_constant(PyObject* obj, PyObject** out, PyArena* arena)
|
||||||
|
{
|
||||||
|
if (obj == Py_None || obj == Py_True || obj == Py_False) {
|
||||||
|
/* don't increment the reference counter, Constant uses a borrowed
|
||||||
|
* reference, not a strong reference */
|
||||||
|
*out = obj;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
if (PyArena_AddPyObject(arena, obj) < 0) {
|
||||||
|
*out = NULL;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Py_INCREF(obj);
|
||||||
|
}
|
||||||
|
*out = obj;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int obj2ast_identifier(PyObject* obj, PyObject** out, PyArena* arena)
|
static int obj2ast_identifier(PyObject* obj, PyObject** out, PyArena* arena)
|
||||||
{
|
{
|
||||||
if (!PyUnicode_CheckExact(obj) && obj != Py_None) {
|
if (!PyUnicode_CheckExact(obj) && obj != Py_None) {
|
||||||
|
@ -941,6 +966,8 @@ static int init_types(void)
|
||||||
if (!NameConstant_type) return 0;
|
if (!NameConstant_type) return 0;
|
||||||
Ellipsis_type = make_type("Ellipsis", expr_type, NULL, 0);
|
Ellipsis_type = make_type("Ellipsis", expr_type, NULL, 0);
|
||||||
if (!Ellipsis_type) return 0;
|
if (!Ellipsis_type) return 0;
|
||||||
|
Constant_type = make_type("Constant", expr_type, Constant_fields, 1);
|
||||||
|
if (!Constant_type) return 0;
|
||||||
Attribute_type = make_type("Attribute", expr_type, Attribute_fields, 3);
|
Attribute_type = make_type("Attribute", expr_type, Attribute_fields, 3);
|
||||||
if (!Attribute_type) return 0;
|
if (!Attribute_type) return 0;
|
||||||
Subscript_type = make_type("Subscript", expr_type, Subscript_fields, 3);
|
Subscript_type = make_type("Subscript", expr_type, Subscript_fields, 3);
|
||||||
|
@ -2166,6 +2193,25 @@ Ellipsis(int lineno, int col_offset, PyArena *arena)
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_ty
|
||||||
|
Constant(constant value, int lineno, int col_offset, PyArena *arena)
|
||||||
|
{
|
||||||
|
expr_ty p;
|
||||||
|
if (!value) {
|
||||||
|
PyErr_SetString(PyExc_ValueError,
|
||||||
|
"field value is required for Constant");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
p = (expr_ty)PyArena_Malloc(arena, sizeof(*p));
|
||||||
|
if (!p)
|
||||||
|
return NULL;
|
||||||
|
p->kind = Constant_kind;
|
||||||
|
p->v.Constant.value = value;
|
||||||
|
p->lineno = lineno;
|
||||||
|
p->col_offset = col_offset;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
expr_ty
|
expr_ty
|
||||||
Attribute(expr_ty value, identifier attr, expr_context_ty ctx, int lineno, int
|
Attribute(expr_ty value, identifier attr, expr_context_ty ctx, int lineno, int
|
||||||
col_offset, PyArena *arena)
|
col_offset, PyArena *arena)
|
||||||
|
@ -3267,6 +3313,15 @@ ast2obj_expr(void* _o)
|
||||||
result = PyType_GenericNew(Ellipsis_type, NULL, NULL);
|
result = PyType_GenericNew(Ellipsis_type, NULL, NULL);
|
||||||
if (!result) goto failed;
|
if (!result) goto failed;
|
||||||
break;
|
break;
|
||||||
|
case Constant_kind:
|
||||||
|
result = PyType_GenericNew(Constant_type, NULL, NULL);
|
||||||
|
if (!result) goto failed;
|
||||||
|
value = ast2obj_constant(o->v.Constant.value);
|
||||||
|
if (!value) goto failed;
|
||||||
|
if (_PyObject_SetAttrId(result, &PyId_value, value) == -1)
|
||||||
|
goto failed;
|
||||||
|
Py_DECREF(value);
|
||||||
|
break;
|
||||||
case Attribute_kind:
|
case Attribute_kind:
|
||||||
result = PyType_GenericNew(Attribute_type, NULL, NULL);
|
result = PyType_GenericNew(Attribute_type, NULL, NULL);
|
||||||
if (!result) goto failed;
|
if (!result) goto failed;
|
||||||
|
@ -6240,6 +6295,28 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena)
|
||||||
if (*out == NULL) goto failed;
|
if (*out == NULL) goto failed;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
isinstance = PyObject_IsInstance(obj, (PyObject*)Constant_type);
|
||||||
|
if (isinstance == -1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (isinstance) {
|
||||||
|
constant value;
|
||||||
|
|
||||||
|
if (_PyObject_HasAttrId(obj, &PyId_value)) {
|
||||||
|
int res;
|
||||||
|
tmp = _PyObject_GetAttrId(obj, &PyId_value);
|
||||||
|
if (tmp == NULL) goto failed;
|
||||||
|
res = obj2ast_constant(tmp, &value, arena);
|
||||||
|
if (res != 0) goto failed;
|
||||||
|
Py_CLEAR(tmp);
|
||||||
|
} else {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from Constant");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
*out = Constant(value, lineno, col_offset, arena);
|
||||||
|
if (*out == NULL) goto failed;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
isinstance = PyObject_IsInstance(obj, (PyObject*)Attribute_type);
|
isinstance = PyObject_IsInstance(obj, (PyObject*)Attribute_type);
|
||||||
if (isinstance == -1) {
|
if (isinstance == -1) {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -7517,6 +7594,8 @@ PyInit__ast(void)
|
||||||
0) return NULL;
|
0) return NULL;
|
||||||
if (PyDict_SetItemString(d, "Ellipsis", (PyObject*)Ellipsis_type) < 0)
|
if (PyDict_SetItemString(d, "Ellipsis", (PyObject*)Ellipsis_type) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
if (PyDict_SetItemString(d, "Constant", (PyObject*)Constant_type) < 0)
|
||||||
|
return NULL;
|
||||||
if (PyDict_SetItemString(d, "Attribute", (PyObject*)Attribute_type) < 0)
|
if (PyDict_SetItemString(d, "Attribute", (PyObject*)Attribute_type) < 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (PyDict_SetItemString(d, "Subscript", (PyObject*)Subscript_type) < 0)
|
if (PyDict_SetItemString(d, "Subscript", (PyObject*)Subscript_type) < 0)
|
||||||
|
|
50
Python/ast.c
50
Python/ast.c
|
@ -131,6 +131,50 @@ validate_arguments(arguments_ty args)
|
||||||
return validate_exprs(args->defaults, Load, 0) && validate_exprs(args->kw_defaults, Load, 1);
|
return validate_exprs(args->defaults, Load, 0) && validate_exprs(args->kw_defaults, Load, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
validate_constant(PyObject *value)
|
||||||
|
{
|
||||||
|
if (value == Py_None || value == Py_Ellipsis)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (PyLong_CheckExact(value)
|
||||||
|
|| PyFloat_CheckExact(value)
|
||||||
|
|| PyComplex_CheckExact(value)
|
||||||
|
|| PyBool_Check(value)
|
||||||
|
|| PyUnicode_CheckExact(value)
|
||||||
|
|| PyBytes_CheckExact(value))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (PyTuple_CheckExact(value) || PyFrozenSet_CheckExact(value)) {
|
||||||
|
PyObject *it;
|
||||||
|
|
||||||
|
it = PyObject_GetIter(value);
|
||||||
|
if (it == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
PyObject *item = PyIter_Next(it);
|
||||||
|
if (item == NULL) {
|
||||||
|
if (PyErr_Occurred()) {
|
||||||
|
Py_DECREF(it);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!validate_constant(item)) {
|
||||||
|
Py_DECREF(it);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_DECREF(it);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
validate_expr(expr_ty exp, expr_context_ty ctx)
|
validate_expr(expr_ty exp, expr_context_ty ctx)
|
||||||
{
|
{
|
||||||
|
@ -240,6 +284,12 @@ validate_expr(expr_ty exp, expr_context_ty ctx)
|
||||||
return validate_expr(exp->v.Call.func, Load) &&
|
return validate_expr(exp->v.Call.func, Load) &&
|
||||||
validate_exprs(exp->v.Call.args, Load, 0) &&
|
validate_exprs(exp->v.Call.args, Load, 0) &&
|
||||||
validate_keywords(exp->v.Call.keywords);
|
validate_keywords(exp->v.Call.keywords);
|
||||||
|
case Constant_kind:
|
||||||
|
if (!validate_constant(exp->v.Constant.value)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "invalid type in Constant");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
case Num_kind: {
|
case Num_kind: {
|
||||||
PyObject *n = exp->v.Num.n;
|
PyObject *n = exp->v.Num.n;
|
||||||
if (!PyLong_CheckExact(n) && !PyFloat_CheckExact(n) &&
|
if (!PyLong_CheckExact(n) && !PyFloat_CheckExact(n) &&
|
||||||
|
|
|
@ -1314,7 +1314,11 @@ compiler_isdocstring(stmt_ty s)
|
||||||
{
|
{
|
||||||
if (s->kind != Expr_kind)
|
if (s->kind != Expr_kind)
|
||||||
return 0;
|
return 0;
|
||||||
return s->v.Expr.value->kind == Str_kind;
|
if (s->v.Expr.value->kind == Str_kind)
|
||||||
|
return 1;
|
||||||
|
if (s->v.Expr.value->kind == Constant_kind)
|
||||||
|
return PyUnicode_CheckExact(s->v.Expr.value->v.Constant.value);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile a sequence of statements, checking for a docstring. */
|
/* Compile a sequence of statements, checking for a docstring. */
|
||||||
|
@ -1688,8 +1692,12 @@ compiler_function(struct compiler *c, stmt_ty s, int is_async)
|
||||||
|
|
||||||
st = (stmt_ty)asdl_seq_GET(body, 0);
|
st = (stmt_ty)asdl_seq_GET(body, 0);
|
||||||
docstring = compiler_isdocstring(st);
|
docstring = compiler_isdocstring(st);
|
||||||
if (docstring && c->c_optimize < 2)
|
if (docstring && c->c_optimize < 2) {
|
||||||
first_const = st->v.Expr.value->v.Str.s;
|
if (st->v.Expr.value->kind == Constant_kind)
|
||||||
|
first_const = st->v.Expr.value->v.Constant.value;
|
||||||
|
else
|
||||||
|
first_const = st->v.Expr.value->v.Str.s;
|
||||||
|
}
|
||||||
if (compiler_add_o(c, c->u->u_consts, first_const) < 0) {
|
if (compiler_add_o(c, c->u->u_consts, first_const) < 0) {
|
||||||
compiler_exit_scope(c);
|
compiler_exit_scope(c);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -2599,6 +2607,36 @@ compiler_assert(struct compiler *c, stmt_ty s)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
compiler_visit_stmt_expr(struct compiler *c, expr_ty value)
|
||||||
|
{
|
||||||
|
if (c->c_interactive && c->c_nestlevel <= 1) {
|
||||||
|
VISIT(c, expr, value);
|
||||||
|
ADDOP(c, PRINT_EXPR);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value->kind == Str_kind || value->kind == Num_kind) {
|
||||||
|
/* ignore strings and numbers */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value->kind == Constant_kind) {
|
||||||
|
PyObject *cst = value->v.Constant.value;
|
||||||
|
if (PyUnicode_CheckExact(cst)
|
||||||
|
|| PyLong_CheckExact(cst)
|
||||||
|
|| PyFloat_CheckExact(cst)
|
||||||
|
|| PyComplex_CheckExact(cst)) {
|
||||||
|
/* ignore strings and numbers */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VISIT(c, expr, value);
|
||||||
|
ADDOP(c, POP_TOP);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
||||||
{
|
{
|
||||||
|
@ -2669,16 +2707,7 @@ compiler_visit_stmt(struct compiler *c, stmt_ty s)
|
||||||
case Nonlocal_kind:
|
case Nonlocal_kind:
|
||||||
break;
|
break;
|
||||||
case Expr_kind:
|
case Expr_kind:
|
||||||
if (c->c_interactive && c->c_nestlevel <= 1) {
|
return compiler_visit_stmt_expr(c, s->v.Expr.value);
|
||||||
VISIT(c, expr, s->v.Expr.value);
|
|
||||||
ADDOP(c, PRINT_EXPR);
|
|
||||||
}
|
|
||||||
else if (s->v.Expr.value->kind != Str_kind &&
|
|
||||||
s->v.Expr.value->kind != Num_kind) {
|
|
||||||
VISIT(c, expr, s->v.Expr.value);
|
|
||||||
ADDOP(c, POP_TOP);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Pass_kind:
|
case Pass_kind:
|
||||||
break;
|
break;
|
||||||
case Break_kind:
|
case Break_kind:
|
||||||
|
@ -3625,6 +3654,8 @@ expr_constant(struct compiler *c, expr_ty e)
|
||||||
switch (e->kind) {
|
switch (e->kind) {
|
||||||
case Ellipsis_kind:
|
case Ellipsis_kind:
|
||||||
return 1;
|
return 1;
|
||||||
|
case Constant_kind:
|
||||||
|
return PyObject_IsTrue(e->v.Constant.value);
|
||||||
case Num_kind:
|
case Num_kind:
|
||||||
return PyObject_IsTrue(e->v.Num.n);
|
return PyObject_IsTrue(e->v.Num.n);
|
||||||
case Str_kind:
|
case Str_kind:
|
||||||
|
@ -3912,6 +3943,9 @@ compiler_visit_expr(struct compiler *c, expr_ty e)
|
||||||
return compiler_compare(c, e);
|
return compiler_compare(c, e);
|
||||||
case Call_kind:
|
case Call_kind:
|
||||||
return compiler_call(c, e);
|
return compiler_call(c, e);
|
||||||
|
case Constant_kind:
|
||||||
|
ADDOP_O(c, LOAD_CONST, e->v.Constant.value, consts);
|
||||||
|
break;
|
||||||
case Num_kind:
|
case Num_kind:
|
||||||
ADDOP_O(c, LOAD_CONST, e->v.Num.n, consts);
|
ADDOP_O(c, LOAD_CONST, e->v.Num.n, consts);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -79,7 +79,10 @@ future_parse(PyFutureFeatures *ff, mod_ty mod, PyObject *filename)
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
first = (stmt_ty)asdl_seq_GET(mod->v.Module.body, i);
|
first = (stmt_ty)asdl_seq_GET(mod->v.Module.body, i);
|
||||||
if (first->kind == Expr_kind && first->v.Expr.value->kind == Str_kind)
|
if (first->kind == Expr_kind
|
||||||
|
&& (first->v.Expr.value->kind == Str_kind
|
||||||
|
|| (first->v.Expr.value->kind == Constant_kind
|
||||||
|
&& PyUnicode_CheckExact(first->v.Expr.value->v.Constant.value))))
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1455,6 +1455,7 @@ symtable_visit_expr(struct symtable *st, expr_ty e)
|
||||||
case JoinedStr_kind:
|
case JoinedStr_kind:
|
||||||
VISIT_SEQ(st, expr, e->v.JoinedStr.values);
|
VISIT_SEQ(st, expr, e->v.JoinedStr.values);
|
||||||
break;
|
break;
|
||||||
|
case Constant_kind:
|
||||||
case Num_kind:
|
case Num_kind:
|
||||||
case Str_kind:
|
case Str_kind:
|
||||||
case Bytes_kind:
|
case Bytes_kind:
|
||||||
|
|
|
@ -343,6 +343,11 @@ class Unparser:
|
||||||
value = t.s.replace("{", "{{").replace("}", "}}")
|
value = t.s.replace("{", "{{").replace("}", "}}")
|
||||||
write(value)
|
write(value)
|
||||||
|
|
||||||
|
def _fstring_Constant(self, t, write):
|
||||||
|
assert isinstance(t.value, str)
|
||||||
|
value = t.value.replace("{", "{{").replace("}", "}}")
|
||||||
|
write(value)
|
||||||
|
|
||||||
def _fstring_FormattedValue(self, t, write):
|
def _fstring_FormattedValue(self, t, write):
|
||||||
write("{")
|
write("{")
|
||||||
expr = io.StringIO()
|
expr = io.StringIO()
|
||||||
|
@ -364,6 +369,25 @@ class Unparser:
|
||||||
def _Name(self, t):
|
def _Name(self, t):
|
||||||
self.write(t.id)
|
self.write(t.id)
|
||||||
|
|
||||||
|
def _write_constant(self, value):
|
||||||
|
if isinstance(value, (float, complex)):
|
||||||
|
self.write(repr(value).replace("inf", INFSTR))
|
||||||
|
else:
|
||||||
|
self.write(repr(value))
|
||||||
|
|
||||||
|
def _Constant(self, t):
|
||||||
|
value = t.value
|
||||||
|
if isinstance(value, tuple):
|
||||||
|
self.write("(")
|
||||||
|
if len(value) == 1:
|
||||||
|
self._write_constant(value[0])
|
||||||
|
self.write(",")
|
||||||
|
else:
|
||||||
|
interleave(lambda: self.write(", "), self._write_constant, value)
|
||||||
|
self.write(")")
|
||||||
|
else:
|
||||||
|
self._write_constant(t.value)
|
||||||
|
|
||||||
def _NameConstant(self, t):
|
def _NameConstant(self, t):
|
||||||
self.write(repr(t.value))
|
self.write(repr(t.value))
|
||||||
|
|
||||||
|
@ -443,7 +467,7 @@ class Unparser:
|
||||||
def _Tuple(self, t):
|
def _Tuple(self, t):
|
||||||
self.write("(")
|
self.write("(")
|
||||||
if len(t.elts) == 1:
|
if len(t.elts) == 1:
|
||||||
(elt,) = t.elts
|
elt = t.elts[0]
|
||||||
self.dispatch(elt)
|
self.dispatch(elt)
|
||||||
self.write(",")
|
self.write(",")
|
||||||
else:
|
else:
|
||||||
|
@ -490,7 +514,8 @@ class Unparser:
|
||||||
# Special case: 3.__abs__() is a syntax error, so if t.value
|
# Special case: 3.__abs__() is a syntax error, so if t.value
|
||||||
# is an integer literal then we need to either parenthesize
|
# is an integer literal then we need to either parenthesize
|
||||||
# it or add an extra space to get 3 .__abs__().
|
# it or add an extra space to get 3 .__abs__().
|
||||||
if isinstance(t.value, ast.Num) and isinstance(t.value.n, int):
|
if ((isinstance(t.value, ast.Num) and isinstance(t.value.n, int))
|
||||||
|
or (isinstance(t.value, ast.Constant) and isinstance(t.value.value, int))):
|
||||||
self.write(" ")
|
self.write(" ")
|
||||||
self.write(".")
|
self.write(".")
|
||||||
self.write(t.attr)
|
self.write(t.attr)
|
||||||
|
|
Loading…
Reference in New Issue