From 45e1b49415003790b1b9b20cf02bbee8488357b9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Wed, 21 Feb 2018 10:58:54 +0200 Subject: [PATCH 1/6] bpo-32892: Use ast.Constant instead of specific constant AST types. --- Doc/library/ast.rst | 8 +- Doc/whatsnew/3.8.rst | 5 + Include/Python-ast.h | 36 +- Lib/ast.py | 84 ++++- Lib/inspect.py | 10 +- Lib/test/test_ast.py | 118 ++++--- Lib/test/test_fstring.py | 24 +- .../2018-09-20-17-35-05.bpo-32892.TOUBdg.rst | 4 + Parser/Python.asdl | 12 +- Parser/asdl_c.py | 39 +-- Python/Python-ast.c | 312 +----------------- Python/ast.c | 82 ++--- Python/ast_opt.c | 61 +--- Python/ast_unparse.c | 18 +- Python/compile.c | 64 +--- Python/symtable.c | 5 - Tools/clinic/clinic.py | 14 +- Tools/parser/unparse.py | 23 +- 18 files changed, 245 insertions(+), 674 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index 9ff422cdf503cb..a727dd6a6fbca9 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -78,8 +78,8 @@ Node classes node = ast.UnaryOp() node.op = ast.USub() - node.operand = ast.Num() - node.operand.n = 5 + node.operand = ast.Constant() + node.operand.value = 5 node.operand.lineno = 0 node.operand.col_offset = 0 node.lineno = 0 @@ -87,7 +87,7 @@ Node classes or the more compact :: - node = ast.UnaryOp(ast.USub(), ast.Num(5, lineno=0, col_offset=0), + node = ast.UnaryOp(ast.USub(), ast.Constant(5, lineno=0, col_offset=0), lineno=0, col_offset=0) @@ -239,7 +239,7 @@ and classes for traversing abstract syntax trees: def visit_Name(self, node): return copy_location(Subscript( value=Name(id='data', ctx=Load()), - slice=Index(value=Str(s=node.id)), + slice=Index(value=Constant(value=node.id)), ctx=node.ctx ), node) diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 1c129a704429f2..357de3cfb99e31 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -216,6 +216,11 @@ Deprecated (Contributed by Berker Peksag in :issue:`9372`.) +* :mod:`ast` classes ``Num``, ``Str``, ``Bytes``, ``NameConstant`` and + ``Ellipsis`` are considered deprecated and will be removed in future Python + versions. :class:`~ast.Constant` should be used instead. + (Contributed by Serhiy Storchaka in :issue:`32892`.) + Removed ======= diff --git a/Include/Python-ast.h b/Include/Python-ast.h index 8e0f750a8250b0..2913d1d2d0a96b 100644 --- a/Include/Python-ast.h +++ b/Include/Python-ast.h @@ -208,11 +208,10 @@ enum _expr_kind {BoolOp_kind=1, BinOp_kind=2, UnaryOp_kind=3, Lambda_kind=4, IfExp_kind=5, Dict_kind=6, Set_kind=7, ListComp_kind=8, SetComp_kind=9, DictComp_kind=10, GeneratorExp_kind=11, Await_kind=12, Yield_kind=13, YieldFrom_kind=14, - Compare_kind=15, Call_kind=16, Num_kind=17, Str_kind=18, - FormattedValue_kind=19, JoinedStr_kind=20, Bytes_kind=21, - NameConstant_kind=22, Ellipsis_kind=23, Constant_kind=24, - Attribute_kind=25, Subscript_kind=26, Starred_kind=27, - Name_kind=28, List_kind=29, Tuple_kind=30}; + Compare_kind=15, Call_kind=16, FormattedValue_kind=17, + JoinedStr_kind=18, Constant_kind=19, Attribute_kind=20, + Subscript_kind=21, Starred_kind=22, Name_kind=23, + List_kind=24, Tuple_kind=25}; struct _expr { enum _expr_kind kind; union { @@ -297,14 +296,6 @@ struct _expr { asdl_seq *keywords; } Call; - struct { - object n; - } Num; - - struct { - string s; - } Str; - struct { expr_ty value; int conversion; @@ -315,14 +306,6 @@ struct _expr { asdl_seq *values; } JoinedStr; - struct { - bytes s; - } Bytes; - - struct { - singleton value; - } NameConstant; - struct { constant value; } Constant; @@ -566,23 +549,12 @@ expr_ty _Py_Compare(expr_ty left, asdl_int_seq * ops, asdl_seq * comparators, #define Call(a0, a1, a2, a3, a4, a5) _Py_Call(a0, a1, a2, a3, a4, a5) expr_ty _Py_Call(expr_ty func, asdl_seq * args, asdl_seq * keywords, int lineno, int col_offset, PyArena *arena); -#define Num(a0, a1, a2, a3) _Py_Num(a0, a1, a2, a3) -expr_ty _Py_Num(object n, int lineno, int col_offset, PyArena *arena); -#define Str(a0, a1, a2, a3) _Py_Str(a0, a1, a2, a3) -expr_ty _Py_Str(string s, int lineno, int col_offset, PyArena *arena); #define FormattedValue(a0, a1, a2, a3, a4, a5) _Py_FormattedValue(a0, a1, a2, a3, a4, a5) expr_ty _Py_FormattedValue(expr_ty value, int conversion, expr_ty format_spec, int lineno, int col_offset, PyArena *arena); #define JoinedStr(a0, a1, a2, a3) _Py_JoinedStr(a0, a1, a2, a3) expr_ty _Py_JoinedStr(asdl_seq * values, int lineno, int col_offset, PyArena *arena); -#define Bytes(a0, a1, a2, a3) _Py_Bytes(a0, a1, a2, a3) -expr_ty _Py_Bytes(bytes s, int lineno, int col_offset, PyArena *arena); -#define NameConstant(a0, a1, a2, a3) _Py_NameConstant(a0, a1, a2, a3) -expr_ty _Py_NameConstant(singleton value, int lineno, int col_offset, PyArena - *arena); -#define Ellipsis(a0, a1, a2) _Py_Ellipsis(a0, a1, a2) -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); diff --git a/Lib/ast.py b/Lib/ast.py index bfe346bba8e3d7..fab19fbdf3c578 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -48,10 +48,8 @@ def literal_eval(node_or_string): node_or_string = node_or_string.body def _convert_num(node): if isinstance(node, Constant): - if isinstance(node.value, (int, float, complex)): + if type(node.value) in (int, float, complex): return node.value - elif isinstance(node, Num): - return node.n raise ValueError('malformed node or string: ' + repr(node)) def _convert_signed_num(node): if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)): @@ -64,10 +62,6 @@ def _convert_signed_num(node): def _convert(node): if isinstance(node, Constant): return node.value - elif isinstance(node, (Str, Bytes)): - return node.s - elif isinstance(node, Num): - return node.n elif isinstance(node, Tuple): return tuple(map(_convert, node.elts)) elif isinstance(node, List): @@ -77,8 +71,6 @@ def _convert(node): elif isinstance(node, Dict): return dict(zip(map(_convert, node.keys), map(_convert, node.values))) - elif isinstance(node, NameConstant): - return node.value elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)): left = _convert_signed_num(node.left) right = _convert_num(node.right) @@ -329,3 +321,77 @@ def generic_visit(self, node): else: setattr(node, field, new_node) return node + + +def _getter(self): + return self.value + +def _setter(self, value): + self.value = value + +Constant.n = property(_getter, _setter) +Constant.s = property(_getter, _setter) + +_unspecified = object() + + +class Num(Constant): + _fields = ('n',) + + def __new__(cls, s=_unspecified, **kwargs): + if s is _unspecified: + return Constant(**kwargs) + return Constant(value=s, **kwargs) + + @staticmethod + def __instancecheck__(instance): + return (isinstance(instance, Constant) and + type(instance.value) in (int, float, complex)) + + +class Str(Constant): + _fields = ('s',) + + def __new__(cls, s=_unspecified, **kwargs): + if s is _unspecified: + return Constant(**kwargs) + return Constant(value=s, **kwargs) + + @staticmethod + def __instancecheck__(instance): + return isinstance(instance, Constant) and type(instance.value) is str + + +class Bytes(Constant): + _fields = ('s',) + + def __new__(cls, s=_unspecified, **kwargs): + if s is _unspecified: + return Constant(**kwargs) + return Constant(value=s, **kwargs) + + @staticmethod + def __instancecheck__(instance): + return isinstance(instance, Constant) and type(instance.value) is bytes + + +class NameConstant(Constant): + def __new__(cls, *args, **kwargs): + return Constant(*args, **kwargs) + + @staticmethod + def __instancecheck__(instance): + return (isinstance(instance, Constant) and + (instance.value is False or instance.value is True or + instance.value is None)) + + +class Ellipsis(Constant): + _fields = () + + def __new__(cls, **kwargs): + return Constant(..., **kwargs) + + @staticmethod + def __instancecheck__(instance): + return isinstance(instance, Constant) and instance.value is ... diff --git a/Lib/inspect.py b/Lib/inspect.py index e799a83a74046f..5b7f526939b60f 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2005,14 +2005,8 @@ def wrap_value(s): except NameError: raise RuntimeError() - if isinstance(value, str): - return ast.Str(value) - if isinstance(value, (int, float)): - return ast.Num(value) - if isinstance(value, bytes): - return ast.Bytes(value) - if value in (True, False, None): - return ast.NameConstant(value) + if isinstance(value, (str, int, float, bytes, bool, type(None))): + return ast.Constant(value) raise RuntimeError() class RewriteSymbolics(ast.NodeTransformer): diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 72f8467847e8dc..7bfb31a56263cb 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -300,12 +300,16 @@ def test_field_attr_writable(self): def test_classattrs(self): x = ast.Num() - self.assertEqual(x._fields, ('n',)) + self.assertEqual(x._fields, ('value',)) + + with self.assertRaises(AttributeError): + x.value with self.assertRaises(AttributeError): x.n x = ast.Num(42) + self.assertEqual(x.value, 42) self.assertEqual(x.n, 42) with self.assertRaises(AttributeError): @@ -319,7 +323,8 @@ def test_classattrs(self): x = ast.Num(42, lineno=0) self.assertEqual(x.lineno, 0) - self.assertEqual(x._fields, ('n',)) + self.assertEqual(x._fields, ('value',)) + self.assertEqual(x.value, 42) self.assertEqual(x.n, 42) self.assertRaises(TypeError, ast.Num, 1, 2) @@ -446,17 +451,17 @@ def test_dump(self): node = ast.parse('spam(eggs, "and cheese")') self.assertEqual(ast.dump(node), "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load()), " - "args=[Name(id='eggs', ctx=Load()), Str(s='and cheese')], " + "args=[Name(id='eggs', ctx=Load()), Constant(value='and cheese')], " "keywords=[]))])" ) self.assertEqual(ast.dump(node, annotate_fields=False), "Module([Expr(Call(Name('spam', Load()), [Name('eggs', Load()), " - "Str('and cheese')], []))])" + "Constant('and cheese')], []))])" ) self.assertEqual(ast.dump(node, include_attributes=True), "Module(body=[Expr(value=Call(func=Name(id='spam', ctx=Load(), " "lineno=1, col_offset=0), args=[Name(id='eggs', ctx=Load(), " - "lineno=1, col_offset=5), Str(s='and cheese', lineno=1, " + "lineno=1, col_offset=5), Constant(value='and cheese', lineno=1, " "col_offset=11)], keywords=[], " "lineno=1, col_offset=0), lineno=1, col_offset=0)])" ) @@ -465,8 +470,8 @@ def test_copy_location(self): src = ast.parse('1 + 1', mode='eval') src.body.right = ast.copy_location(ast.Num(2), src.body.right) self.assertEqual(ast.dump(src, include_attributes=True), - 'Expression(body=BinOp(left=Num(n=1, lineno=1, col_offset=0), ' - 'op=Add(), right=Num(n=2, lineno=1, col_offset=4), lineno=1, ' + 'Expression(body=BinOp(left=Constant(value=1, lineno=1, col_offset=0), ' + 'op=Add(), right=Constant(value=2, lineno=1, col_offset=4), lineno=1, ' 'col_offset=0))' ) @@ -477,11 +482,11 @@ def test_fix_missing_locations(self): self.assertEqual(src, ast.fix_missing_locations(src)) self.assertEqual(ast.dump(src, include_attributes=True), "Module(body=[Expr(value=Call(func=Name(id='write', ctx=Load(), " - "lineno=1, col_offset=0), args=[Str(s='spam', lineno=1, " + "lineno=1, col_offset=0), args=[Constant(value='spam', lineno=1, " "col_offset=6)], keywords=[], " "lineno=1, col_offset=0), lineno=1, col_offset=0), " "Expr(value=Call(func=Name(id='spam', ctx=Load(), lineno=1, " - "col_offset=0), args=[Str(s='eggs', lineno=1, col_offset=0)], " + "col_offset=0), args=[Constant(value='eggs', lineno=1, col_offset=0)], " "keywords=[], lineno=1, " "col_offset=0), lineno=1, col_offset=0)])" ) @@ -490,16 +495,16 @@ def test_increment_lineno(self): src = ast.parse('1 + 1', mode='eval') self.assertEqual(ast.increment_lineno(src, n=3), src) self.assertEqual(ast.dump(src, include_attributes=True), - 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), ' - 'op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, ' + 'Expression(body=BinOp(left=Constant(value=1, lineno=4, col_offset=0), ' + 'op=Add(), right=Constant(value=1, lineno=4, col_offset=4), lineno=4, ' 'col_offset=0))' ) # issue10869: do not increment lineno of root twice src = ast.parse('1 + 1', mode='eval') self.assertEqual(ast.increment_lineno(src.body, n=3), src.body) self.assertEqual(ast.dump(src, include_attributes=True), - 'Expression(body=BinOp(left=Num(n=1, lineno=4, col_offset=0), ' - 'op=Add(), right=Num(n=1, lineno=4, col_offset=4), lineno=4, ' + 'Expression(body=BinOp(left=Constant(value=1, lineno=4, col_offset=0), ' + 'op=Add(), right=Constant(value=1, lineno=4, col_offset=4), lineno=4, ' 'col_offset=0))' ) @@ -514,10 +519,10 @@ def test_iter_child_nodes(self): self.assertEqual(len(list(ast.iter_child_nodes(node.body))), 4) iterator = ast.iter_child_nodes(node.body) self.assertEqual(next(iterator).id, 'spam') - self.assertEqual(next(iterator).n, 23) - self.assertEqual(next(iterator).n, 42) + self.assertEqual(next(iterator).value, 23) + self.assertEqual(next(iterator).value, 42) self.assertEqual(ast.dump(next(iterator)), - "keyword(arg='eggs', value=Str(s='leek'))" + "keyword(arg='eggs', value=Constant(value='leek'))" ) def test_get_docstring(self): @@ -627,10 +632,13 @@ class ASTValidatorTests(unittest.TestCase): def mod(self, mod, msg=None, mode="exec", *, exc=ValueError): mod.lineno = mod.col_offset = 0 ast.fix_missing_locations(mod) - with self.assertRaises(exc) as cm: + if msg is None: compile(mod, "", mode) - if msg is not None: - self.assertIn(msg, str(cm.exception)) + else: + with self.assertRaises(exc) as cm: + compile(mod, "", mode) + if msg is not None: + self.assertIn(msg, str(cm.exception)) def expr(self, node, msg=None, *, exc=ValueError): mod = ast.Module([ast.Expr(node)]) @@ -927,9 +935,9 @@ def test_compare(self): comp = ast.Compare(left, [ast.In()], [ast.Num(4), ast.Num(5)]) self.expr(comp, "different number of comparators and operands") comp = ast.Compare(ast.Num("blah"), [ast.In()], [left]) - self.expr(comp, "non-numeric", exc=TypeError) + self.expr(comp) comp = ast.Compare(left, [ast.In()], [ast.Num("blah")]) - self.expr(comp, "non-numeric", exc=TypeError) + self.expr(comp) def test_call(self): func = ast.Name("x", ast.Load()) @@ -950,8 +958,10 @@ class subfloat(float): pass class subcomplex(complex): pass - for obj in "0", "hello", subint(), subfloat(), subcomplex(): - self.expr(ast.Num(obj), "non-numeric", exc=TypeError) + for obj in "0", "hello": + self.expr(ast.Num(obj)) + for obj in subint(), subfloat(), subcomplex(): + self.expr(ast.Num(obj), "invalid type", exc=TypeError) def test_attribute(self): attr = ast.Attribute(ast.Name("x", ast.Store()), "y", ast.Load()) @@ -993,7 +1003,7 @@ def test_tuple(self): self._sequence(ast.Tuple) def test_nameconstant(self): - self.expr(ast.NameConstant(4), "singleton must be True, False, or None") + self.expr(ast.NameConstant(4)) def test_stdlib_validates(self): stdlib = os.path.dirname(ast.__file__) @@ -1140,35 +1150,35 @@ def main(): #### EVERYTHING BELOW IS GENERATED ##### exec_results = [ -('Module', [('Expr', (1, 0), ('NameConstant', (1, 0), None))]), -('Module', [('Expr', (1, 0), ('Str', (1, 0), 'module docstring'))]), +('Module', [('Expr', (1, 0), ('Constant', (1, 0), None))]), +('Module', [('Expr', (1, 0), ('Constant', (1, 0), 'module docstring'))]), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Pass', (1, 9))], [], None)]), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (1, 9), ('Str', (1, 9), 'function docstring'))], [], None)]), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (1, 9), ('Constant', (1, 9), 'function docstring'))], [], None)]), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None)], None, [], [], None, []), [('Pass', (1, 10))], [], None)]), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None)], None, [], [], None, [('Num', (1, 8), 0)]), [('Pass', (1, 12))], [], None)]), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None)], None, [], [], None, [('Constant', (1, 8), 0)]), [('Pass', (1, 12))], [], None)]), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], ('arg', (1, 7), 'args', None), [], [], None, []), [('Pass', (1, 14))], [], None)]), ('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], ('arg', (1, 8), 'kwargs', None), []), [('Pass', (1, 17))], [], None)]), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None), ('arg', (1, 9), 'b', None), ('arg', (1, 14), 'c', None), ('arg', (1, 22), 'd', None), ('arg', (1, 28), 'e', None)], ('arg', (1, 35), 'args', None), [('arg', (1, 41), 'f', None)], [('Num', (1, 43), 42)], ('arg', (1, 49), 'kwargs', None), [('Num', (1, 11), 1), ('NameConstant', (1, 16), None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Expr', (1, 58), ('Str', (1, 58), 'doc for f()'))], [], None)]), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [('arg', (1, 6), 'a', None), ('arg', (1, 9), 'b', None), ('arg', (1, 14), 'c', None), ('arg', (1, 22), 'd', None), ('arg', (1, 28), 'e', None)], ('arg', (1, 35), 'args', None), [('arg', (1, 41), 'f', None)], [('Constant', (1, 43), 42)], ('arg', (1, 49), 'kwargs', None), [('Constant', (1, 11), 1), ('Constant', (1, 16), None), ('List', (1, 24), [], ('Load',)), ('Dict', (1, 30), [], [])]), [('Expr', (1, 58), ('Constant', (1, 58), 'doc for f()'))], [], None)]), ('Module', [('ClassDef', (1, 0), 'C', [], [], [('Pass', (1, 8))], [])]), -('Module', [('ClassDef', (1, 0), 'C', [], [], [('Expr', (1, 9), ('Str', (1, 9), 'docstring for class C'))], [])]), +('Module', [('ClassDef', (1, 0), 'C', [], [], [('Expr', (1, 9), ('Constant', (1, 9), 'docstring for class C'))], [])]), ('Module', [('ClassDef', (1, 0), 'C', [('Name', (1, 8), 'object', ('Load',))], [], [('Pass', (1, 17))], [])]), -('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Return', (1, 8), ('Num', (1, 15), 1))], [], None)]), +('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Return', (1, 8), ('Constant', (1, 15), 1))], [], None)]), ('Module', [('Delete', (1, 0), [('Name', (1, 4), 'v', ('Del',))])]), -('Module', [('Assign', (1, 0), [('Name', (1, 0), 'v', ('Store',))], ('Num', (1, 4), 1))]), -('Module', [('AugAssign', (1, 0), ('Name', (1, 0), 'v', ('Store',)), ('Add',), ('Num', (1, 5), 1))]), +('Module', [('Assign', (1, 0), [('Name', (1, 0), 'v', ('Store',))], ('Constant', (1, 4), 1))]), +('Module', [('AugAssign', (1, 0), ('Name', (1, 0), 'v', ('Store',)), ('Add',), ('Constant', (1, 5), 1))]), ('Module', [('For', (1, 0), ('Name', (1, 4), 'v', ('Store',)), ('Name', (1, 9), 'v', ('Load',)), [('Pass', (1, 11))], [])]), ('Module', [('While', (1, 0), ('Name', (1, 6), 'v', ('Load',)), [('Pass', (1, 8))], [])]), ('Module', [('If', (1, 0), ('Name', (1, 3), 'v', ('Load',)), [('Pass', (1, 5))], [])]), ('Module', [('With', (1, 0), [('withitem', ('Name', (1, 5), 'x', ('Load',)), ('Name', (1, 10), 'y', ('Store',)))], [('Pass', (1, 13))])]), ('Module', [('With', (1, 0), [('withitem', ('Name', (1, 5), 'x', ('Load',)), ('Name', (1, 10), 'y', ('Store',))), ('withitem', ('Name', (1, 13), 'z', ('Load',)), ('Name', (1, 18), 'q', ('Store',)))], [('Pass', (1, 21))])]), -('Module', [('Raise', (1, 0), ('Call', (1, 6), ('Name', (1, 6), 'Exception', ('Load',)), [('Str', (1, 16), 'string')], []), None)]), +('Module', [('Raise', (1, 0), ('Call', (1, 6), ('Name', (1, 6), 'Exception', ('Load',)), [('Constant', (1, 16), 'string')], []), None)]), ('Module', [('Try', (1, 0), [('Pass', (2, 2))], [('ExceptHandler', (3, 0), ('Name', (3, 7), 'Exception', ('Load',)), None, [('Pass', (4, 2))])], [], [])]), ('Module', [('Try', (1, 0), [('Pass', (2, 2))], [], [], [('Pass', (4, 2))])]), ('Module', [('Assert', (1, 0), ('Name', (1, 7), 'v', ('Load',)), None)]), ('Module', [('Import', (1, 0), [('alias', 'sys', None)])]), ('Module', [('ImportFrom', (1, 0), 'sys', [('alias', 'v', None)], 0)]), ('Module', [('Global', (1, 0), ['v'])]), -('Module', [('Expr', (1, 0), ('Num', (1, 0), 1))]), +('Module', [('Expr', (1, 0), ('Constant', (1, 0), 1))]), ('Module', [('Pass', (1, 0))]), ('Module', [('For', (1, 0), ('Name', (1, 4), 'v', ('Store',)), ('Name', (1, 9), 'v', ('Load',)), [('Break', (1, 11))], [])]), ('Module', [('For', (1, 0), ('Name', (1, 4), 'v', ('Store',)), ('Name', (1, 9), 'v', ('Load',)), [('Continue', (1, 11))], [])]), @@ -1181,40 +1191,40 @@ def main(): ('Module', [('Expr', (1, 0), ('DictComp', (1, 0), ('Name', (1, 1), 'a', ('Load',)), ('Name', (1, 5), 'b', ('Load',)), [('comprehension', ('Tuple', (1, 11), [('Name', (1, 11), 'v', ('Store',)), ('Name', (1, 13), 'w', ('Store',))], ('Store',)), ('Name', (1, 18), 'x', ('Load',)), [], 0)]))]), ('Module', [('Expr', (1, 0), ('SetComp', (1, 0), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 12), 'x', ('Load',)), [('Name', (1, 17), 'g', ('Load',))], 0)]))]), ('Module', [('Expr', (1, 0), ('SetComp', (1, 0), ('Name', (1, 1), 'r', ('Load',)), [('comprehension', ('Tuple', (1, 7), [('Name', (1, 7), 'l', ('Store',)), ('Name', (1, 9), 'm', ('Store',))], ('Store',)), ('Name', (1, 14), 'x', ('Load',)), [], 0)]))]), -('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('Str', (2, 1), 'async function')), ('Expr', (3, 1), ('Await', (3, 1), ('Call', (3, 7), ('Name', (3, 7), 'something', ('Load',)), [], [])))], [], None)]), -('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('AsyncFor', (2, 1), ('Name', (2, 11), 'e', ('Store',)), ('Name', (2, 16), 'i', ('Load',)), [('Expr', (2, 19), ('Num', (2, 19), 1))], [('Expr', (3, 7), ('Num', (3, 7), 2))])], [], None)]), -('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('AsyncWith', (2, 1), [('withitem', ('Name', (2, 12), 'a', ('Load',)), ('Name', (2, 17), 'b', ('Store',)))], [('Expr', (2, 20), ('Num', (2, 20), 1))])], [], None)]), -('Module', [('Expr', (1, 0), ('Dict', (1, 0), [None, ('Num', (1, 10), 2)], [('Dict', (1, 3), [('Num', (1, 4), 1)], [('Num', (1, 6), 2)]), ('Num', (1, 12), 3)]))]), -('Module', [('Expr', (1, 0), ('Set', (1, 0), [('Starred', (1, 1), ('Set', (1, 2), [('Num', (1, 3), 1), ('Num', (1, 6), 2)]), ('Load',)), ('Num', (1, 10), 3)]))]), +('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('Constant', (2, 1), 'async function')), ('Expr', (3, 1), ('Await', (3, 1), ('Call', (3, 7), ('Name', (3, 7), 'something', ('Load',)), [], [])))], [], None)]), +('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('AsyncFor', (2, 1), ('Name', (2, 11), 'e', ('Store',)), ('Name', (2, 16), 'i', ('Load',)), [('Expr', (2, 19), ('Constant', (2, 19), 1))], [('Expr', (3, 7), ('Constant', (3, 7), 2))])], [], None)]), +('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('AsyncWith', (2, 1), [('withitem', ('Name', (2, 12), 'a', ('Load',)), ('Name', (2, 17), 'b', ('Store',)))], [('Expr', (2, 20), ('Constant', (2, 20), 1))])], [], None)]), +('Module', [('Expr', (1, 0), ('Dict', (1, 0), [None, ('Constant', (1, 10), 2)], [('Dict', (1, 3), [('Constant', (1, 4), 1)], [('Constant', (1, 6), 2)]), ('Constant', (1, 12), 3)]))]), +('Module', [('Expr', (1, 0), ('Set', (1, 0), [('Starred', (1, 1), ('Set', (1, 2), [('Constant', (1, 3), 1), ('Constant', (1, 6), 2)]), ('Load',)), ('Constant', (1, 10), 3)]))]), ('Module', [('AsyncFunctionDef', (1, 0), 'f', ('arguments', [], None, [], [], None, []), [('Expr', (2, 1), ('ListComp', (2, 2), ('Name', (2, 2), 'i', ('Load',)), [('comprehension', ('Name', (2, 14), 'b', ('Store',)), ('Name', (2, 19), 'c', ('Load',)), [], 1)]))], [], None)]), ] single_results = [ -('Interactive', [('Expr', (1, 0), ('BinOp', (1, 0), ('Num', (1, 0), 1), ('Add',), ('Num', (1, 2), 2)))]), +('Interactive', [('Expr', (1, 0), ('BinOp', (1, 0), ('Constant', (1, 0), 1), ('Add',), ('Constant', (1, 2), 2)))]), ] eval_results = [ -('Expression', ('NameConstant', (1, 0), None)), +('Expression', ('Constant', (1, 0), None)), ('Expression', ('BoolOp', (1, 0), ('And',), [('Name', (1, 0), 'a', ('Load',)), ('Name', (1, 6), 'b', ('Load',))])), ('Expression', ('BinOp', (1, 0), ('Name', (1, 0), 'a', ('Load',)), ('Add',), ('Name', (1, 4), 'b', ('Load',)))), ('Expression', ('UnaryOp', (1, 0), ('Not',), ('Name', (1, 4), 'v', ('Load',)))), -('Expression', ('Lambda', (1, 0), ('arguments', [], None, [], [], None, []), ('NameConstant', (1, 7), None))), -('Expression', ('Dict', (1, 0), [('Num', (1, 2), 1)], [('Num', (1, 4), 2)])), +('Expression', ('Lambda', (1, 0), ('arguments', [], None, [], [], None, []), ('Constant', (1, 7), None))), +('Expression', ('Dict', (1, 0), [('Constant', (1, 2), 1)], [('Constant', (1, 4), 2)])), ('Expression', ('Dict', (1, 0), [], [])), -('Expression', ('Set', (1, 0), [('NameConstant', (1, 1), None)])), -('Expression', ('Dict', (1, 0), [('Num', (2, 6), 1)], [('Num', (4, 10), 2)])), +('Expression', ('Set', (1, 0), [('Constant', (1, 1), None)])), +('Expression', ('Dict', (1, 0), [('Constant', (2, 6), 1)], [('Constant', (4, 10), 2)])), ('Expression', ('ListComp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))], 0)])), ('Expression', ('GeneratorExp', (1, 1), ('Name', (1, 1), 'a', ('Load',)), [('comprehension', ('Name', (1, 7), 'b', ('Store',)), ('Name', (1, 12), 'c', ('Load',)), [('Name', (1, 17), 'd', ('Load',))], 0)])), -('Expression', ('Compare', (1, 0), ('Num', (1, 0), 1), [('Lt',), ('Lt',)], [('Num', (1, 4), 2), ('Num', (1, 8), 3)])), -('Expression', ('Call', (1, 0), ('Name', (1, 0), 'f', ('Load',)), [('Num', (1, 2), 1), ('Num', (1, 4), 2), ('Starred', (1, 10), ('Name', (1, 11), 'd', ('Load',)), ('Load',))], [('keyword', 'c', ('Num', (1, 8), 3)), ('keyword', None, ('Name', (1, 15), 'e', ('Load',)))])), -('Expression', ('Num', (1, 0), 10)), -('Expression', ('Str', (1, 0), 'string')), +('Expression', ('Compare', (1, 0), ('Constant', (1, 0), 1), [('Lt',), ('Lt',)], [('Constant', (1, 4), 2), ('Constant', (1, 8), 3)])), +('Expression', ('Call', (1, 0), ('Name', (1, 0), 'f', ('Load',)), [('Constant', (1, 2), 1), ('Constant', (1, 4), 2), ('Starred', (1, 10), ('Name', (1, 11), 'd', ('Load',)), ('Load',))], [('keyword', 'c', ('Constant', (1, 8), 3)), ('keyword', None, ('Name', (1, 15), 'e', ('Load',)))])), +('Expression', ('Constant', (1, 0), 10)), +('Expression', ('Constant', (1, 0), 'string')), ('Expression', ('Attribute', (1, 0), ('Name', (1, 0), 'a', ('Load',)), 'b', ('Load',))), ('Expression', ('Subscript', (1, 0), ('Name', (1, 0), 'a', ('Load',)), ('Slice', ('Name', (1, 2), 'b', ('Load',)), ('Name', (1, 4), 'c', ('Load',)), None), ('Load',))), ('Expression', ('Name', (1, 0), 'v', ('Load',))), -('Expression', ('List', (1, 0), [('Num', (1, 1), 1), ('Num', (1, 3), 2), ('Num', (1, 5), 3)], ('Load',))), +('Expression', ('List', (1, 0), [('Constant', (1, 1), 1), ('Constant', (1, 3), 2), ('Constant', (1, 5), 3)], ('Load',))), ('Expression', ('List', (1, 0), [], ('Load',))), -('Expression', ('Tuple', (1, 0), [('Num', (1, 0), 1), ('Num', (1, 2), 2), ('Num', (1, 4), 3)], ('Load',))), -('Expression', ('Tuple', (1, 1), [('Num', (1, 1), 1), ('Num', (1, 3), 2), ('Num', (1, 5), 3)], ('Load',))), +('Expression', ('Tuple', (1, 0), [('Constant', (1, 0), 1), ('Constant', (1, 2), 2), ('Constant', (1, 4), 3)], ('Load',))), +('Expression', ('Tuple', (1, 1), [('Constant', (1, 1), 1), ('Constant', (1, 3), 2), ('Constant', (1, 5), 3)], ('Load',))), ('Expression', ('Tuple', (1, 0), [], ('Load',))), -('Expression', ('Call', (1, 0), ('Attribute', (1, 0), ('Attribute', (1, 0), ('Attribute', (1, 0), ('Name', (1, 0), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',)), 'd', ('Load',)), [('Subscript', (1, 8), ('Attribute', (1, 8), ('Name', (1, 8), 'a', ('Load',)), 'b', ('Load',)), ('Slice', ('Num', (1, 12), 1), ('Num', (1, 14), 2), None), ('Load',))], [])), +('Expression', ('Call', (1, 0), ('Attribute', (1, 0), ('Attribute', (1, 0), ('Attribute', (1, 0), ('Name', (1, 0), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',)), 'd', ('Load',)), [('Subscript', (1, 8), ('Attribute', (1, 8), ('Name', (1, 8), 'a', ('Load',)), 'b', ('Load',)), ('Slice', ('Constant', (1, 12), 1), ('Constant', (1, 14), 2), None), ('Load',))], [])), ] main() diff --git a/Lib/test/test_fstring.py b/Lib/test/test_fstring.py index 5e7efe25e39ccc..fec72e008e5bc7 100644 --- a/Lib/test/test_fstring.py +++ b/Lib/test/test_fstring.py @@ -116,9 +116,11 @@ def test_ast_line_numbers_multiple_formattedvalues(self): self.assertEqual(type(t.body[1]), ast.Expr) self.assertEqual(type(t.body[1].value), ast.JoinedStr) self.assertEqual(len(t.body[1].value.values), 4) - self.assertEqual(type(t.body[1].value.values[0]), ast.Str) + self.assertEqual(type(t.body[1].value.values[0]), ast.Constant) + self.assertEqual(type(t.body[1].value.values[0].value), str) self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue) - self.assertEqual(type(t.body[1].value.values[2]), ast.Str) + self.assertEqual(type(t.body[1].value.values[2]), ast.Constant) + self.assertEqual(type(t.body[1].value.values[2].value), str) self.assertEqual(type(t.body[1].value.values[3]), ast.FormattedValue) self.assertEqual(t.body[1].lineno, 3) self.assertEqual(t.body[1].value.lineno, 3) @@ -183,9 +185,11 @@ def test_ast_line_numbers_nested(self): self.assertEqual(binop.right.col_offset, 7) # check the nested call location self.assertEqual(len(binop.right.values), 3) - self.assertEqual(type(binop.right.values[0]), ast.Str) + self.assertEqual(type(binop.right.values[0]), ast.Constant) + self.assertEqual(type(binop.right.values[0].value), str) self.assertEqual(type(binop.right.values[1]), ast.FormattedValue) - self.assertEqual(type(binop.right.values[2]), ast.Str) + self.assertEqual(type(binop.right.values[2]), ast.Constant) + self.assertEqual(type(binop.right.values[2].value), str) self.assertEqual(binop.right.values[0].lineno, 3) self.assertEqual(binop.right.values[1].lineno, 3) self.assertEqual(binop.right.values[2].lineno, 3) @@ -215,9 +219,11 @@ def test_ast_line_numbers_duplicate_expression(self): self.assertEqual(type(t.body[1].value), ast.JoinedStr) self.assertEqual(len(t.body[1].value.values), 5) self.assertEqual(type(t.body[1].value.values[0]), ast.FormattedValue) - self.assertEqual(type(t.body[1].value.values[1]), ast.Str) + self.assertEqual(type(t.body[1].value.values[1]), ast.Constant) + self.assertEqual(type(t.body[1].value.values[1].value), str) self.assertEqual(type(t.body[1].value.values[2]), ast.FormattedValue) - self.assertEqual(type(t.body[1].value.values[3]), ast.Str) + self.assertEqual(type(t.body[1].value.values[3]), ast.Constant) + self.assertEqual(type(t.body[1].value.values[3].value), str) self.assertEqual(type(t.body[1].value.values[4]), ast.FormattedValue) self.assertEqual(t.body[1].lineno, 3) self.assertEqual(t.body[1].value.lineno, 3) @@ -287,9 +293,11 @@ def test_ast_line_numbers_multiline_fstring(self): self.assertEqual(type(t.body[1]), ast.Expr) self.assertEqual(type(t.body[1].value), ast.JoinedStr) self.assertEqual(len(t.body[1].value.values), 3) - self.assertEqual(type(t.body[1].value.values[0]), ast.Str) + self.assertEqual(type(t.body[1].value.values[0]), ast.Constant) + self.assertEqual(type(t.body[1].value.values[0].value), str) self.assertEqual(type(t.body[1].value.values[1]), ast.FormattedValue) - self.assertEqual(type(t.body[1].value.values[2]), ast.Str) + self.assertEqual(type(t.body[1].value.values[2]), ast.Constant) + self.assertEqual(type(t.body[1].value.values[2].value), str) # NOTE: the following invalid behavior is described in bpo-16806. # - line number should be the *first* line (3), not the *last* (8) # - column offset should not be -1 diff --git a/Misc/NEWS.d/next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst b/Misc/NEWS.d/next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst new file mode 100644 index 00000000000000..74fafb44b6da84 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst @@ -0,0 +1,4 @@ +The parser now represents all constants as :class:`ast.Constant` instead of +using specific constant AST types (``Num``, ``Str``,``Bytes``, +``NameConstant`` and ``Ellipsis``). These classes are considered deprecated +and will be removed in future Python versions. diff --git a/Parser/Python.asdl b/Parser/Python.asdl index f470ad13b6551e..eee982be1c9545 100644 --- a/Parser/Python.asdl +++ b/Parser/Python.asdl @@ -1,8 +1,5 @@ --- 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. +-- ASDL's 5 builtin types are: +-- identifier, int, string, object, constant module Python { @@ -75,13 +72,8 @@ module Python -- x < 4 < 3 and (x < 4) < 3 | Compare(expr left, cmpop* ops, expr* comparators) | Call(expr func, expr* args, keyword* keywords) - | Num(object n) -- a number as a PyObject. - | Str(string s) -- need to specify raw, unicode, etc? | FormattedValue(expr value, int? conversion, expr? format_spec) | JoinedStr(expr* values) - | Bytes(bytes s) - | NameConstant(singleton value) - | Ellipsis | Constant(constant value) -- the following expression can appear in assignment context diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index 44e3d40c615539..4c280a96c30e7e 100644 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -855,17 +855,6 @@ def visitModule(self, mod): /* Conversion Python -> AST */ -static int obj2ast_singleton(PyObject *obj, PyObject** out, PyArena* arena) -{ - if (obj != Py_None && obj != Py_True && obj != Py_False) { - PyErr_SetString(PyExc_ValueError, - "AST singleton must be True, False, or None"); - return 1; - } - *out = obj; - return 0; -} - static int obj2ast_object(PyObject* obj, PyObject** out, PyArena* arena) { if (obj == Py_None) @@ -883,13 +872,11 @@ def visitModule(self, mod): static int obj2ast_constant(PyObject* obj, PyObject** out, PyArena* arena) { - if (obj) { - if (PyArena_AddPyObject(arena, obj) < 0) { - *out = NULL; - return -1; - } - Py_INCREF(obj); + if (PyArena_AddPyObject(arena, obj) < 0) { + *out = NULL; + return -1; } + Py_INCREF(obj); *out = obj; return 0; } @@ -903,24 +890,6 @@ def visitModule(self, mod): return obj2ast_object(obj, out, arena); } -static int obj2ast_string(PyObject* obj, PyObject** out, PyArena* arena) -{ - if (!PyUnicode_CheckExact(obj) && !PyBytes_CheckExact(obj)) { - PyErr_SetString(PyExc_TypeError, "AST string must be of type str"); - return 1; - } - return obj2ast_object(obj, out, arena); -} - -static int obj2ast_bytes(PyObject* obj, PyObject** out, PyArena* arena) -{ - if (!PyBytes_CheckExact(obj)) { - PyErr_SetString(PyExc_TypeError, "AST bytes must be of type bytes"); - return 1; - } - return obj2ast_object(obj, out, arena); -} - static int obj2ast_int(PyObject* obj, int* out, PyArena* arena) { int i; diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 6a2f28e0e712d5..bbe8e69fdd504c 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -284,16 +284,6 @@ static char *Call_fields[]={ "args", "keywords", }; -static PyTypeObject *Num_type; -_Py_IDENTIFIER(n); -static char *Num_fields[]={ - "n", -}; -static PyTypeObject *Str_type; -_Py_IDENTIFIER(s); -static char *Str_fields[]={ - "s", -}; static PyTypeObject *FormattedValue_type; _Py_IDENTIFIER(conversion); _Py_IDENTIFIER(format_spec); @@ -306,15 +296,6 @@ static PyTypeObject *JoinedStr_type; static char *JoinedStr_fields[]={ "values", }; -static PyTypeObject *Bytes_type; -static char *Bytes_fields[]={ - "s", -}; -static PyTypeObject *NameConstant_type; -static char *NameConstant_fields[]={ - "value", -}; -static PyTypeObject *Ellipsis_type; static PyTypeObject *Constant_type; static char *Constant_fields[]={ "value", @@ -736,17 +717,6 @@ static PyObject* ast2obj_int(long b) /* Conversion Python -> AST */ -static int obj2ast_singleton(PyObject *obj, PyObject** out, PyArena* arena) -{ - if (obj != Py_None && obj != Py_True && obj != Py_False) { - PyErr_SetString(PyExc_ValueError, - "AST singleton must be True, False, or None"); - return 1; - } - *out = obj; - return 0; -} - static int obj2ast_object(PyObject* obj, PyObject** out, PyArena* arena) { if (obj == Py_None) @@ -764,13 +734,11 @@ static int obj2ast_object(PyObject* obj, PyObject** out, PyArena* arena) static int obj2ast_constant(PyObject* obj, PyObject** out, PyArena* arena) { - if (obj) { - if (PyArena_AddPyObject(arena, obj) < 0) { - *out = NULL; - return -1; - } - Py_INCREF(obj); + if (PyArena_AddPyObject(arena, obj) < 0) { + *out = NULL; + return -1; } + Py_INCREF(obj); *out = obj; return 0; } @@ -784,24 +752,6 @@ static int obj2ast_identifier(PyObject* obj, PyObject** out, PyArena* arena) return obj2ast_object(obj, out, arena); } -static int obj2ast_string(PyObject* obj, PyObject** out, PyArena* arena) -{ - if (!PyUnicode_CheckExact(obj) && !PyBytes_CheckExact(obj)) { - PyErr_SetString(PyExc_TypeError, "AST string must be of type str"); - return 1; - } - return obj2ast_object(obj, out, arena); -} - -static int obj2ast_bytes(PyObject* obj, PyObject** out, PyArena* arena) -{ - if (!PyBytes_CheckExact(obj)) { - PyErr_SetString(PyExc_TypeError, "AST bytes must be of type bytes"); - return 1; - } - return obj2ast_object(obj, out, arena); -} - static int obj2ast_int(PyObject* obj, int* out, PyArena* arena) { int i; @@ -943,22 +893,11 @@ static int init_types(void) if (!Compare_type) return 0; Call_type = make_type("Call", expr_type, Call_fields, 3); if (!Call_type) return 0; - Num_type = make_type("Num", expr_type, Num_fields, 1); - if (!Num_type) return 0; - Str_type = make_type("Str", expr_type, Str_fields, 1); - if (!Str_type) return 0; FormattedValue_type = make_type("FormattedValue", expr_type, FormattedValue_fields, 3); if (!FormattedValue_type) return 0; JoinedStr_type = make_type("JoinedStr", expr_type, JoinedStr_fields, 1); if (!JoinedStr_type) return 0; - Bytes_type = make_type("Bytes", expr_type, Bytes_fields, 1); - if (!Bytes_type) return 0; - NameConstant_type = make_type("NameConstant", expr_type, - NameConstant_fields, 1); - if (!NameConstant_type) return 0; - Ellipsis_type = make_type("Ellipsis", expr_type, NULL, 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); @@ -2089,44 +2028,6 @@ Call(expr_ty func, asdl_seq * args, asdl_seq * keywords, int lineno, int return p; } -expr_ty -Num(object n, int lineno, int col_offset, PyArena *arena) -{ - expr_ty p; - if (!n) { - PyErr_SetString(PyExc_ValueError, - "field n is required for Num"); - return NULL; - } - p = (expr_ty)PyArena_Malloc(arena, sizeof(*p)); - if (!p) - return NULL; - p->kind = Num_kind; - p->v.Num.n = n; - p->lineno = lineno; - p->col_offset = col_offset; - return p; -} - -expr_ty -Str(string s, int lineno, int col_offset, PyArena *arena) -{ - expr_ty p; - if (!s) { - PyErr_SetString(PyExc_ValueError, - "field s is required for Str"); - return NULL; - } - p = (expr_ty)PyArena_Malloc(arena, sizeof(*p)); - if (!p) - return NULL; - p->kind = Str_kind; - p->v.Str.s = s; - p->lineno = lineno; - p->col_offset = col_offset; - return p; -} - expr_ty FormattedValue(expr_ty value, int conversion, expr_ty format_spec, int lineno, int col_offset, PyArena *arena) @@ -2163,57 +2064,6 @@ JoinedStr(asdl_seq * values, int lineno, int col_offset, PyArena *arena) return p; } -expr_ty -Bytes(bytes s, int lineno, int col_offset, PyArena *arena) -{ - expr_ty p; - if (!s) { - PyErr_SetString(PyExc_ValueError, - "field s is required for Bytes"); - return NULL; - } - p = (expr_ty)PyArena_Malloc(arena, sizeof(*p)); - if (!p) - return NULL; - p->kind = Bytes_kind; - p->v.Bytes.s = s; - p->lineno = lineno; - p->col_offset = col_offset; - return p; -} - -expr_ty -NameConstant(singleton value, int lineno, int col_offset, PyArena *arena) -{ - expr_ty p; - if (!value) { - PyErr_SetString(PyExc_ValueError, - "field value is required for NameConstant"); - return NULL; - } - p = (expr_ty)PyArena_Malloc(arena, sizeof(*p)); - if (!p) - return NULL; - p->kind = NameConstant_kind; - p->v.NameConstant.value = value; - p->lineno = lineno; - p->col_offset = col_offset; - return p; -} - -expr_ty -Ellipsis(int lineno, int col_offset, PyArena *arena) -{ - expr_ty p; - p = (expr_ty)PyArena_Malloc(arena, sizeof(*p)); - if (!p) - return NULL; - p->kind = Ellipsis_kind; - p->lineno = lineno; - p->col_offset = col_offset; - return p; -} - expr_ty Constant(constant value, int lineno, int col_offset, PyArena *arena) { @@ -3289,24 +3139,6 @@ ast2obj_expr(void* _o) goto failed; Py_DECREF(value); break; - case Num_kind: - result = PyType_GenericNew(Num_type, NULL, NULL); - if (!result) goto failed; - value = ast2obj_object(o->v.Num.n); - if (!value) goto failed; - if (_PyObject_SetAttrId(result, &PyId_n, value) == -1) - goto failed; - Py_DECREF(value); - break; - case Str_kind: - result = PyType_GenericNew(Str_type, NULL, NULL); - if (!result) goto failed; - value = ast2obj_string(o->v.Str.s); - if (!value) goto failed; - if (_PyObject_SetAttrId(result, &PyId_s, value) == -1) - goto failed; - Py_DECREF(value); - break; case FormattedValue_kind: result = PyType_GenericNew(FormattedValue_type, NULL, NULL); if (!result) goto failed; @@ -3335,28 +3167,6 @@ ast2obj_expr(void* _o) goto failed; Py_DECREF(value); break; - case Bytes_kind: - result = PyType_GenericNew(Bytes_type, NULL, NULL); - if (!result) goto failed; - value = ast2obj_bytes(o->v.Bytes.s); - if (!value) goto failed; - if (_PyObject_SetAttrId(result, &PyId_s, value) == -1) - goto failed; - Py_DECREF(value); - break; - case NameConstant_kind: - result = PyType_GenericNew(NameConstant_type, NULL, NULL); - if (!result) goto failed; - value = ast2obj_singleton(o->v.NameConstant.value); - if (!value) goto failed; - if (_PyObject_SetAttrId(result, &PyId_value, value) == -1) - goto failed; - Py_DECREF(value); - break; - case Ellipsis_kind: - result = PyType_GenericNew(Ellipsis_type, NULL, NULL); - if (!result) goto failed; - break; case Constant_kind: result = PyType_GenericNew(Constant_type, NULL, NULL); if (!result) goto failed; @@ -6606,54 +6416,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena) if (*out == NULL) goto failed; return 0; } - isinstance = PyObject_IsInstance(obj, (PyObject*)Num_type); - if (isinstance == -1) { - return 1; - } - if (isinstance) { - object n; - - if (_PyObject_LookupAttrId(obj, &PyId_n, &tmp) < 0) { - return 1; - } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"n\" missing from Num"); - return 1; - } - else { - int res; - res = obj2ast_object(tmp, &n, arena); - if (res != 0) goto failed; - Py_CLEAR(tmp); - } - *out = Num(n, lineno, col_offset, arena); - if (*out == NULL) goto failed; - return 0; - } - isinstance = PyObject_IsInstance(obj, (PyObject*)Str_type); - if (isinstance == -1) { - return 1; - } - if (isinstance) { - string s; - - if (_PyObject_LookupAttrId(obj, &PyId_s, &tmp) < 0) { - return 1; - } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"s\" missing from Str"); - return 1; - } - else { - int res; - res = obj2ast_string(tmp, &s, arena); - if (res != 0) goto failed; - Py_CLEAR(tmp); - } - *out = Str(s, lineno, col_offset, arena); - if (*out == NULL) goto failed; - return 0; - } isinstance = PyObject_IsInstance(obj, (PyObject*)FormattedValue_type); if (isinstance == -1) { return 1; @@ -6748,64 +6510,6 @@ obj2ast_expr(PyObject* obj, expr_ty* out, PyArena* arena) if (*out == NULL) goto failed; return 0; } - isinstance = PyObject_IsInstance(obj, (PyObject*)Bytes_type); - if (isinstance == -1) { - return 1; - } - if (isinstance) { - bytes s; - - if (_PyObject_LookupAttrId(obj, &PyId_s, &tmp) < 0) { - return 1; - } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"s\" missing from Bytes"); - return 1; - } - else { - int res; - res = obj2ast_bytes(tmp, &s, arena); - if (res != 0) goto failed; - Py_CLEAR(tmp); - } - *out = Bytes(s, lineno, col_offset, arena); - if (*out == NULL) goto failed; - return 0; - } - isinstance = PyObject_IsInstance(obj, (PyObject*)NameConstant_type); - if (isinstance == -1) { - return 1; - } - if (isinstance) { - singleton value; - - if (_PyObject_LookupAttrId(obj, &PyId_value, &tmp) < 0) { - return 1; - } - if (tmp == NULL) { - PyErr_SetString(PyExc_TypeError, "required field \"value\" missing from NameConstant"); - return 1; - } - else { - int res; - res = obj2ast_singleton(tmp, &value, arena); - if (res != 0) goto failed; - Py_CLEAR(tmp); - } - *out = NameConstant(value, lineno, col_offset, arena); - if (*out == NULL) goto failed; - return 0; - } - isinstance = PyObject_IsInstance(obj, (PyObject*)Ellipsis_type); - if (isinstance == -1) { - return 1; - } - if (isinstance) { - - *out = Ellipsis(lineno, col_offset, arena); - if (*out == NULL) goto failed; - return 0; - } isinstance = PyObject_IsInstance(obj, (PyObject*)Constant_type); if (isinstance == -1) { return 1; @@ -8244,18 +7948,10 @@ PyInit__ast(void) if (PyDict_SetItemString(d, "Compare", (PyObject*)Compare_type) < 0) return NULL; if (PyDict_SetItemString(d, "Call", (PyObject*)Call_type) < 0) return NULL; - if (PyDict_SetItemString(d, "Num", (PyObject*)Num_type) < 0) return NULL; - if (PyDict_SetItemString(d, "Str", (PyObject*)Str_type) < 0) return NULL; if (PyDict_SetItemString(d, "FormattedValue", (PyObject*)FormattedValue_type) < 0) return NULL; if (PyDict_SetItemString(d, "JoinedStr", (PyObject*)JoinedStr_type) < 0) return NULL; - if (PyDict_SetItemString(d, "Bytes", (PyObject*)Bytes_type) < 0) return - NULL; - if (PyDict_SetItemString(d, "NameConstant", (PyObject*)NameConstant_type) < - 0) return NULL; - if (PyDict_SetItemString(d, "Ellipsis", (PyObject*)Ellipsis_type) < 0) - return NULL; if (PyDict_SetItemString(d, "Constant", (PyObject*)Constant_type) < 0) return NULL; if (PyDict_SetItemString(d, "Attribute", (PyObject*)Attribute_type) < 0) diff --git a/Python/ast.c b/Python/ast.c index b93eb88dae7fce..1df6360aea771e 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -295,23 +295,6 @@ validate_expr(expr_ty exp, expr_context_ty ctx) return 0; } return 1; - case Num_kind: { - PyObject *n = exp->v.Num.n; - if (!PyLong_CheckExact(n) && !PyFloat_CheckExact(n) && - !PyComplex_CheckExact(n)) { - PyErr_SetString(PyExc_TypeError, "non-numeric type in Num"); - return 0; - } - return 1; - } - case Str_kind: { - PyObject *s = exp->v.Str.s; - if (!PyUnicode_CheckExact(s)) { - PyErr_SetString(PyExc_TypeError, "non-string type in Str"); - return 0; - } - return 1; - } case JoinedStr_kind: return validate_exprs(exp->v.JoinedStr.values, Load, 0); case FormattedValue_kind: @@ -320,14 +303,6 @@ validate_expr(expr_ty exp, expr_context_ty ctx) if (exp->v.FormattedValue.format_spec) return validate_expr(exp->v.FormattedValue.format_spec, Load); return 1; - case Bytes_kind: { - PyObject *b = exp->v.Bytes.s; - if (!PyBytes_CheckExact(b)) { - PyErr_SetString(PyExc_TypeError, "non-bytes type in Bytes"); - return 0; - } - return 1; - } case Attribute_kind: return validate_expr(exp->v.Attribute.value, Load); case Subscript_kind: @@ -339,10 +314,8 @@ validate_expr(expr_ty exp, expr_context_ty ctx) return validate_exprs(exp->v.List.elts, ctx, 0); case Tuple_kind: return validate_exprs(exp->v.Tuple.elts, ctx, 0); - /* These last cases don't have any checking. */ + /* This last case doesn't have any checking. */ case Name_kind: - case NameConstant_kind: - case Ellipsis_kind: return 1; default: PyErr_SetString(PyExc_SystemError, "unexpected expression"); @@ -1040,19 +1013,23 @@ set_context(struct compiling *c, expr_ty e, expr_context_ty ctx, const node *n) break; case Dict_kind: case Set_kind: - case Num_kind: - case Str_kind: - case Bytes_kind: case JoinedStr_kind: case FormattedValue_kind: expr_name = "literal"; break; - case NameConstant_kind: - expr_name = "keyword"; - break; - case Ellipsis_kind: - expr_name = "Ellipsis"; + case Constant_kind: { + PyObject *value = e->v.Constant.value; + if (value == Py_None || value == Py_False || value == Py_True) { + expr_name = "keyword"; + } + else if (value == Py_Ellipsis) { + expr_name = "Ellipsis"; + } + else { + expr_name = "literal"; + } break; + } case Compare_kind: expr_name = "comparison"; break; @@ -2091,11 +2068,11 @@ ast_for_atom(struct compiling *c, const node *n) size_t len = strlen(s); if (len >= 4 && len <= 5) { if (!strcmp(s, "None")) - return NameConstant(Py_None, LINENO(n), n->n_col_offset, c->c_arena); + return Constant(Py_None, LINENO(n), n->n_col_offset, c->c_arena); if (!strcmp(s, "True")) - return NameConstant(Py_True, LINENO(n), n->n_col_offset, c->c_arena); + return Constant(Py_True, LINENO(n), n->n_col_offset, c->c_arena); if (!strcmp(s, "False")) - return NameConstant(Py_False, LINENO(n), n->n_col_offset, c->c_arena); + return Constant(Py_False, LINENO(n), n->n_col_offset, c->c_arena); } name = new_identifier(s, c); if (!name) @@ -2144,10 +2121,10 @@ ast_for_atom(struct compiling *c, const node *n) Py_DECREF(pynum); return NULL; } - return Num(pynum, LINENO(n), n->n_col_offset, c->c_arena); + return Constant(pynum, LINENO(n), n->n_col_offset, c->c_arena); } case ELLIPSIS: /* Ellipsis */ - return Ellipsis(LINENO(n), n->n_col_offset, c->c_arena); + return Constant(Py_Ellipsis, LINENO(n), n->n_col_offset, c->c_arena); case LPAR: /* some parenthesized expressions */ ch = CHILD(n, 1); @@ -4751,7 +4728,7 @@ typedef struct { expr_ty's, and then after that start dynamically allocating, doubling the number allocated each time. Note that the f-string f'{0}a{1}' contains 3 expr_ty's: 2 FormattedValue's, and one - Str for the literal 'a'. So you add expr_ty's about twice as + Constant for the literal 'a'. So you add expr_ty's about twice as fast as you add exressions in an f-string. */ Py_ssize_t allocated; /* Number we've allocated. */ @@ -4903,7 +4880,7 @@ FstringParser_Dealloc(FstringParser *state) ExprList_Dealloc(&state->expr_list); } -/* Make a Str node, but decref the PyUnicode object being added. */ +/* Make a Constant node, but decref the PyUnicode object being added. */ static expr_ty make_str_node_and_del(PyObject **str, struct compiling *c, const node* n) { @@ -4914,7 +4891,7 @@ make_str_node_and_del(PyObject **str, struct compiling *c, const node* n) Py_DECREF(s); return NULL; } - return Str(s, LINENO(n), n->n_col_offset, c->c_arena); + return Constant(s, LINENO(n), n->n_col_offset, c->c_arena); } /* Add a non-f-string (that is, a regular literal string). str is @@ -5002,11 +4979,11 @@ FstringParser_ConcatFstring(FstringParser *state, const char **str, break; /* We know we have an expression. Convert any existing string - to a Str node. */ + to a Constant node. */ if (!state->last_str) { /* Do nothing. No previous literal. */ } else { - /* Convert the existing last_str literal to a Str node. */ + /* Convert the existing last_str literal to a Constant node. */ expr_ty str = make_str_node_and_del(&state->last_str, c, n); if (!str || ExprList_Append(&state->expr_list, str) < 0) return -1; @@ -5033,7 +5010,7 @@ FstringParser_ConcatFstring(FstringParser *state, const char **str, } /* Convert the partial state reflected in last_str and expr_list to an - expr_ty. The expr_ty can be a Str, or a JoinedStr. */ + expr_ty. The expr_ty can be a Constant, or a JoinedStr. */ static expr_ty FstringParser_Finish(FstringParser *state, struct compiling *c, const node *n) @@ -5055,7 +5032,7 @@ FstringParser_Finish(FstringParser *state, struct compiling *c, return make_str_node_and_del(&state->last_str, c, n); } - /* Create a Str node out of last_str, if needed. It will be the + /* Create a Constant node out of last_str, if needed. It will be the last node in our expression list. */ if (state->last_str) { expr_ty str = make_str_node_and_del(&state->last_str, c, n); @@ -5206,9 +5183,9 @@ parsestr(struct compiling *c, const node *n, int *bytesmode, int *rawmode, /* Accepts a STRING+ atom, and produces an expr_ty node. Run through each STRING atom, and process it as needed. For bytes, just - concatenate them together, and the result will be a Bytes node. For + concatenate them together, and the result will be a Constant node. For normal strings and f-strings, concatenate them together. The result - will be a Str node if there were no f-strings; a FormattedValue + will be a Constant node if there were no f-strings; a FormattedValue node if there's just an f-string (with no leading or trailing literals), or a JoinedStr node if there are multiple f-strings or any literals involved. */ @@ -5279,7 +5256,7 @@ parsestrplus(struct compiling *c, const node *n) /* Just return the bytes object and we're done. */ if (PyArena_AddPyObject(c->c_arena, bytes_str) < 0) goto error; - return Bytes(bytes_str, LINENO(n), n->n_col_offset, c->c_arena); + return Constant(bytes_str, LINENO(n), n->n_col_offset, c->c_arena); } /* We're not a bytes string, bytes_str should never have been set. */ @@ -5304,9 +5281,6 @@ _PyAST_GetDocString(asdl_seq *body) return NULL; } expr_ty e = st->v.Expr.value; - if (e->kind == Str_kind) { - return e->v.Str.s; - } if (e->kind == Constant_kind && PyUnicode_CheckExact(e->v.Constant.value)) { return e->v.Constant.value; } diff --git a/Python/ast_opt.c b/Python/ast_opt.c index 5e57638e3d0172..1f9cb773ea7b95 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -5,47 +5,6 @@ #include "ast.h" -/* TODO: is_const and get_const_value are copied from Python/compile.c. - It should be deduped in the future. Maybe, we can include this file - from compile.c? -*/ -static int -is_const(expr_ty e) -{ - switch (e->kind) { - case Constant_kind: - case Num_kind: - case Str_kind: - case Bytes_kind: - case Ellipsis_kind: - case NameConstant_kind: - return 1; - default: - return 0; - } -} - -static PyObject * -get_const_value(expr_ty e) -{ - switch (e->kind) { - case Constant_kind: - return e->v.Constant.value; - case Num_kind: - return e->v.Num.n; - case Str_kind: - return e->v.Str.s; - case Bytes_kind: - return e->v.Bytes.s; - case Ellipsis_kind: - return Py_Ellipsis; - case NameConstant_kind: - return e->v.NameConstant.value; - default: - Py_UNREACHABLE(); - } -} - static int make_const(expr_ty node, PyObject *val, PyArena *arena) { @@ -81,7 +40,7 @@ fold_unaryop(expr_ty node, PyArena *arena, int optimize) { expr_ty arg = node->v.UnaryOp.operand; - if (!is_const(arg)) { + if (arg->kind != Constant_kind) { /* Fold not into comparison */ if (node->v.UnaryOp.op == Not && arg->kind == Compare_kind && asdl_seq_LEN(arg->v.Compare.ops) == 1) { @@ -123,7 +82,7 @@ fold_unaryop(expr_ty node, PyArena *arena, int optimize) [UAdd] = PyNumber_Positive, [USub] = PyNumber_Negative, }; - PyObject *newval = ops[node->v.UnaryOp.op](get_const_value(arg)); + PyObject *newval = ops[node->v.UnaryOp.op](arg->v.Constant.value); return make_const(node, newval, arena); } @@ -259,12 +218,12 @@ fold_binop(expr_ty node, PyArena *arena, int optimize) expr_ty lhs, rhs; lhs = node->v.BinOp.left; rhs = node->v.BinOp.right; - if (!is_const(lhs) || !is_const(rhs)) { + if (lhs->kind != Constant_kind || rhs->kind != Constant_kind) { return 1; } - PyObject *lv = get_const_value(lhs); - PyObject *rv = get_const_value(rhs); + PyObject *lv = lhs->v.Constant.value; + PyObject *rv = rhs->v.Constant.value; PyObject *newval; switch (node->v.BinOp.op) { @@ -316,7 +275,7 @@ make_const_tuple(asdl_seq *elts) { for (int i = 0; i < asdl_seq_LEN(elts); i++) { expr_ty e = (expr_ty)asdl_seq_GET(elts, i); - if (!is_const(e)) { + if (e->kind != Constant_kind) { return NULL; } } @@ -328,7 +287,7 @@ make_const_tuple(asdl_seq *elts) for (int i = 0; i < asdl_seq_LEN(elts); i++) { expr_ty e = (expr_ty)asdl_seq_GET(elts, i); - PyObject *v = get_const_value(e); + PyObject *v = e->v.Constant.value; Py_INCREF(v); PyTuple_SET_ITEM(newval, i, v); } @@ -357,16 +316,16 @@ fold_subscr(expr_ty node, PyArena *arena, int optimize) arg = node->v.Subscript.value; slice = node->v.Subscript.slice; if (node->v.Subscript.ctx != Load || - !is_const(arg) || + arg->kind != Constant_kind || /* TODO: handle other types of slices */ slice->kind != Index_kind || - !is_const(slice->v.Index.value)) + slice->v.Index.value->kind != Constant_kind) { return 1; } idx = slice->v.Index.value; - newval = PyObject_GetItem(get_const_value(arg), get_const_value(idx)); + newval = PyObject_GetItem(arg->v.Constant.value, idx->v.Constant.value); return make_const(node, newval, arena); } diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c index 725ce31fe3c037..6303ebbcac9298 100644 --- a/Python/ast_unparse.c +++ b/Python/ast_unparse.c @@ -567,8 +567,6 @@ append_fstring_element(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec) switch (e->kind) { case Constant_kind: return append_fstring_unicode(writer, e->v.Constant.value); - case Str_kind: - return append_fstring_unicode(writer, e->v.Str.s); case JoinedStr_kind: return append_joinedstr(writer, e, is_format_spec); case FormattedValue_kind: @@ -694,8 +692,7 @@ append_ast_attribute(_PyUnicodeWriter *writer, expr_ty e) /* Special case: integers require a space for attribute access to be unambiguous. Floats and complex numbers don't but work with it, too. */ - if (e->v.Attribute.value->kind == Num_kind || - e->v.Attribute.value->kind == Constant_kind) + if (e->v.Attribute.value->kind == Constant_kind) { period = " ."; } @@ -841,21 +838,14 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, int level) case Call_kind: return append_ast_call(writer, e); case Constant_kind: + if (e->v.Constant.value == Py_Ellipsis) { + APPEND_STR_FINISH("..."); + } return append_repr(writer, e->v.Constant.value); - case Num_kind: - return append_repr(writer, e->v.Num.n); - case Str_kind: - return append_repr(writer, e->v.Str.s); case JoinedStr_kind: return append_joinedstr(writer, e, false); case FormattedValue_kind: return append_formattedvalue(writer, e, false); - case Bytes_kind: - return append_repr(writer, e->v.Bytes.s); - case Ellipsis_kind: - APPEND_STR_FINISH("..."); - case NameConstant_kind: - return append_repr(writer, e->v.NameConstant.value); /* The following exprs can be assignment targets. */ case Attribute_kind: return append_ast_attribute(writer, e); diff --git a/Python/compile.c b/Python/compile.c index 707da79ab66278..ab52f71dd2f6a2 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1398,43 +1398,6 @@ compiler_addop_j(struct compiler *c, int opcode, basicblock *b, int absolute) } \ } -static int -is_const(expr_ty e) -{ - switch (e->kind) { - case Constant_kind: - case Num_kind: - case Str_kind: - case Bytes_kind: - case Ellipsis_kind: - case NameConstant_kind: - return 1; - default: - return 0; - } -} - -static PyObject * -get_const_value(expr_ty e) -{ - switch (e->kind) { - case Constant_kind: - return e->v.Constant.value; - case Num_kind: - return e->v.Num.n; - case Str_kind: - return e->v.Str.s; - case Bytes_kind: - return e->v.Bytes.s; - case Ellipsis_kind: - return Py_Ellipsis; - case NameConstant_kind: - return e->v.NameConstant.value; - default: - Py_UNREACHABLE(); - } -} - /* Search if variable annotations are present statically in a block. */ static int @@ -2568,7 +2531,7 @@ static int compiler_return(struct compiler *c, stmt_ty s) { int preserve_tos = ((s->v.Return.value != NULL) && - !is_const(s->v.Return.value)); + (s->v.Return.value->kind != Constant_kind)); if (c->u->u_ste->ste_type != FunctionBlock) return compiler_error(c, "'return' outside function"); if (s->v.Return.value != NULL && @@ -3054,7 +3017,7 @@ compiler_visit_stmt_expr(struct compiler *c, expr_ty value) return 1; } - if (is_const(value)) { + if (value->kind == Constant_kind) { /* ignore constant statement */ return 1; } @@ -3502,7 +3465,7 @@ are_all_items_const(asdl_seq *seq, Py_ssize_t begin, Py_ssize_t end) Py_ssize_t i; for (i = begin; i < end; i++) { expr_ty key = (expr_ty)asdl_seq_GET(seq, i); - if (key == NULL || !is_const(key)) + if (key == NULL || key->kind != Constant_kind) return 0; } return 1; @@ -3522,7 +3485,7 @@ compiler_subdict(struct compiler *c, expr_ty e, Py_ssize_t begin, Py_ssize_t end return 0; } for (i = begin; i < end; i++) { - key = get_const_value((expr_ty)asdl_seq_GET(e->v.Dict.keys, i)); + key = ((expr_ty)asdl_seq_GET(e->v.Dict.keys, i))->v.Constant.value; Py_INCREF(key); PyTuple_SET_ITEM(keys, i - begin, key); } @@ -4244,8 +4207,8 @@ compiler_visit_keyword(struct compiler *c, keyword_ty k) static int expr_constant(expr_ty e) { - if (is_const(e)) { - return PyObject_IsTrue(get_const_value(e)); + if (e->kind == Constant_kind) { + return PyObject_IsTrue(e->v.Constant.value); } return -1; } @@ -4505,25 +4468,10 @@ compiler_visit_expr1(struct compiler *c, expr_ty e) case Constant_kind: ADDOP_LOAD_CONST(c, e->v.Constant.value); break; - case Num_kind: - ADDOP_LOAD_CONST(c, e->v.Num.n); - break; - case Str_kind: - ADDOP_LOAD_CONST(c, e->v.Str.s); - break; case JoinedStr_kind: return compiler_joined_str(c, e); case FormattedValue_kind: return compiler_formatted_value(c, e); - case Bytes_kind: - ADDOP_LOAD_CONST(c, e->v.Bytes.s); - break; - case Ellipsis_kind: - ADDOP_LOAD_CONST(c, Py_Ellipsis); - break; - case NameConstant_kind: - ADDOP_LOAD_CONST(c, e->v.NameConstant.value); - break; /* The following exprs can be assignment targets. */ case Attribute_kind: if (e->v.Attribute.ctx != AugStore) diff --git a/Python/symtable.c b/Python/symtable.c index 3e8c6f5dae30c9..63541bfafd0403 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -1461,11 +1461,6 @@ symtable_visit_expr(struct symtable *st, expr_ty e) VISIT_SEQ(st, expr, e->v.JoinedStr.values); break; case Constant_kind: - case Num_kind: - case Str_kind: - case Bytes_kind: - case Ellipsis_kind: - case NameConstant_kind: /* Nothing to do here. */ break; /* The following exprs can be assignment targets. */ diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index a6a43d1361d0e1..3203c0d6c5b67f 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -3840,9 +3840,6 @@ def bad_node(self, node): # "starred": "a = [1, 2, 3]; *a" visit_Starred = bad_node - # allow ellipsis, for now - # visit_Ellipsis = bad_node - blacklist = DetectBadNodes() blacklist.visit(module) bad = blacklist.bad @@ -3868,10 +3865,14 @@ def bad_node(self, node): py_default = 'None' c_default = "NULL" elif (isinstance(expr, ast.BinOp) or - (isinstance(expr, ast.UnaryOp) and not isinstance(expr.operand, ast.Num))): + (isinstance(expr, ast.UnaryOp) and + not (isinstance(expr.operand, ast.Num) or + isinstance(expr.operand, ast.Constant) and + type(expr.operand.value) in (int, float, complex)) + )): c_default = kwargs.get("c_default") if not (isinstance(c_default, str) and c_default): - fail("When you specify an expression (" + repr(default) + ") as your default value,\nyou MUST specify a valid c_default.") + fail("When you specify an expression (" + repr(default) + ") as your default value,\nyou MUST specify a valid c_default." + ast.dump(expr)) py_default = default value = unknown elif isinstance(expr, ast.Attribute): @@ -3946,6 +3947,9 @@ def bad_node(self, node): self.function.parameters[parameter_name] = p def parse_converter(self, annotation): + if isinstance(annotation, ast.Constant) and type(annotation.value) is str: + return annotation.value, True, {} + if isinstance(annotation, ast.Str): return annotation.s, True, {} diff --git a/Tools/parser/unparse.py b/Tools/parser/unparse.py index 7e1cc4ea5db950..82c3c776807283 100644 --- a/Tools/parser/unparse.py +++ b/Tools/parser/unparse.py @@ -329,12 +329,6 @@ def _AsyncWith(self, t): self.leave() # expr - def _Bytes(self, t): - self.write(repr(t.s)) - - def _Str(self, tree): - self.write(repr(tree.s)) - def _JoinedStr(self, t): self.write("f") string = io.StringIO() @@ -352,10 +346,6 @@ def _fstring_JoinedStr(self, t, write): meth = getattr(self, "_fstring_" + type(value).__name__) meth(value, write) - def _fstring_Str(self, t, write): - value = t.s.replace("{", "{{").replace("}", "}}") - write(value) - def _fstring_Constant(self, t, write): assert isinstance(t.value, str) value = t.value.replace("{", "{{").replace("}", "}}") @@ -384,6 +374,7 @@ def _Name(self, t): def _write_constant(self, value): if isinstance(value, (float, complex)): + # Substitute overflowing decimal literal for AST infinities. self.write(repr(value).replace("inf", INFSTR)) else: self.write(repr(value)) @@ -398,16 +389,11 @@ def _Constant(self, t): else: interleave(lambda: self.write(", "), self._write_constant, value) self.write(")") + elif value is ...: + self.write("...") else: self._write_constant(t.value) - def _NameConstant(self, t): - self.write(repr(t.value)) - - def _Num(self, t): - # Substitute overflowing decimal literal for AST infinities. - self.write(repr(t.n).replace("inf", INFSTR)) - def _List(self, t): self.write("[") interleave(lambda: self.write(", "), self.dispatch, t.elts) @@ -539,8 +525,7 @@ def _Attribute(self,t): # Special case: 3.__abs__() is a syntax error, so if t.value # is an integer literal then we need to either parenthesize # it or add an extra space to get 3 .__abs__(). - if ((isinstance(t.value, ast.Num) and isinstance(t.value.n, int)) - or (isinstance(t.value, ast.Constant) and isinstance(t.value.value, int))): + if isinstance(t.value, ast.Constant) and isinstance(t.value.value, int): self.write(" ") self.write(".") self.write(t.attr) From a0aefdcc5327a9264296feb18c3213f7aeced6c1 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 20 Sep 2018 18:13:15 +0300 Subject: [PATCH 2/6] Fix Argument Clinic on Python 3.5. --- Tools/clinic/clinic.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 3203c0d6c5b67f..ca8096f43a6ffd 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -3867,8 +3867,9 @@ def bad_node(self, node): elif (isinstance(expr, ast.BinOp) or (isinstance(expr, ast.UnaryOp) and not (isinstance(expr.operand, ast.Num) or - isinstance(expr.operand, ast.Constant) and - type(expr.operand.value) in (int, float, complex)) + (hasattr(ast, 'Constant') and + isinstance(expr.operand, ast.Constant) and + type(expr.operand.value) in (int, float, complex))) )): c_default = kwargs.get("c_default") if not (isinstance(c_default, str) and c_default): @@ -3947,7 +3948,9 @@ def bad_node(self, node): self.function.parameters[parameter_name] = p def parse_converter(self, annotation): - if isinstance(annotation, ast.Constant) and type(annotation.value) is str: + if (hasattr(ast, 'Constant') and + isinstance(annotation, ast.Constant) and + type(annotation.value) is str): return annotation.value, True, {} if isinstance(annotation, ast.Str): From e18498694af6855429b011ddbd60a8637fff603e Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Fri, 21 Sep 2018 23:34:10 +0300 Subject: [PATCH 3/6] Fix isinstance and add support of subclasses. --- Lib/ast.py | 93 +++++++++++++++++++------------------------- Lib/test/test_ast.py | 89 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 52 deletions(-) diff --git a/Lib/ast.py b/Lib/ast.py index fab19fbdf3c578..de3df1473e5ea2 100644 --- a/Lib/ast.py +++ b/Lib/ast.py @@ -323,6 +323,9 @@ def generic_visit(self, node): return node +# The following code is for backward compatibility. +# It will be removed in future. + def _getter(self): return self.value @@ -332,66 +335,52 @@ def _setter(self, value): Constant.n = property(_getter, _setter) Constant.s = property(_getter, _setter) -_unspecified = object() - +class _ABC(type): -class Num(Constant): - _fields = ('n',) - - def __new__(cls, s=_unspecified, **kwargs): - if s is _unspecified: - return Constant(**kwargs) - return Constant(value=s, **kwargs) + def __instancecheck__(cls, inst): + if not isinstance(inst, Constant): + return False + if cls in _const_types: + try: + value = inst.value + except AttributeError: + return False + else: + return type(value) in _const_types[cls] + return type.__instancecheck__(cls, inst) - @staticmethod - def __instancecheck__(instance): - return (isinstance(instance, Constant) and - type(instance.value) in (int, float, complex)) +def _new(cls, *args, **kwargs): + if cls in _const_types: + return Constant(*args, **kwargs) + return Constant.__new__(cls, *args, **kwargs) +class Num(Constant, metaclass=_ABC): + _fields = ('n',) + __new__ = _new -class Str(Constant): +class Str(Constant, metaclass=_ABC): _fields = ('s',) + __new__ = _new - def __new__(cls, s=_unspecified, **kwargs): - if s is _unspecified: - return Constant(**kwargs) - return Constant(value=s, **kwargs) - - @staticmethod - def __instancecheck__(instance): - return isinstance(instance, Constant) and type(instance.value) is str - - -class Bytes(Constant): +class Bytes(Constant, metaclass=_ABC): _fields = ('s',) + __new__ = _new - def __new__(cls, s=_unspecified, **kwargs): - if s is _unspecified: - return Constant(**kwargs) - return Constant(value=s, **kwargs) +class NameConstant(Constant, metaclass=_ABC): + __new__ = _new - @staticmethod - def __instancecheck__(instance): - return isinstance(instance, Constant) and type(instance.value) is bytes - - -class NameConstant(Constant): - def __new__(cls, *args, **kwargs): - return Constant(*args, **kwargs) - - @staticmethod - def __instancecheck__(instance): - return (isinstance(instance, Constant) and - (instance.value is False or instance.value is True or - instance.value is None)) - - -class Ellipsis(Constant): +class Ellipsis(Constant, metaclass=_ABC): _fields = () - def __new__(cls, **kwargs): - return Constant(..., **kwargs) - - @staticmethod - def __instancecheck__(instance): - return isinstance(instance, Constant) and instance.value is ... + def __new__(cls, *args, **kwargs): + if cls is Ellipsis: + return Constant(..., *args, **kwargs) + return Constant.__new__(cls, *args, **kwargs) + +_const_types = { + Num: (int, float, complex), + Str: (str,), + Bytes: (bytes,), + NameConstant: (type(None), bool), + Ellipsis: (type(...),), +} diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 7bfb31a56263cb..7cda5ea7bc9d02 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -330,6 +330,95 @@ def test_classattrs(self): self.assertRaises(TypeError, ast.Num, 1, 2) self.assertRaises(TypeError, ast.Num, 1, 2, lineno=0) + self.assertEqual(ast.Num(42).n, 42) + self.assertEqual(ast.Num(4.25).n, 4.25) + self.assertEqual(ast.Num(4.25j).n, 4.25j) + self.assertEqual(ast.Str('42').s, '42') + self.assertEqual(ast.Bytes(b'42').s, b'42') + self.assertIs(ast.NameConstant(True).value, True) + self.assertIs(ast.NameConstant(False).value, False) + self.assertIs(ast.NameConstant(None).value, None) + + self.assertEqual(ast.Constant(42).value, 42) + self.assertEqual(ast.Constant(4.25).value, 4.25) + self.assertEqual(ast.Constant(4.25j).value, 4.25j) + self.assertEqual(ast.Constant('42').value, '42') + self.assertEqual(ast.Constant(b'42').value, b'42') + self.assertIs(ast.Constant(True).value, True) + self.assertIs(ast.Constant(False).value, False) + self.assertIs(ast.Constant(None).value, None) + self.assertIs(ast.Constant(...).value, ...) + + def test_realtype(self): + self.assertEqual(type(ast.Num(42)), ast.Constant) + self.assertEqual(type(ast.Num(4.25)), ast.Constant) + self.assertEqual(type(ast.Num(4.25j)), ast.Constant) + self.assertEqual(type(ast.Str('42')), ast.Constant) + self.assertEqual(type(ast.Bytes(b'42')), ast.Constant) + self.assertEqual(type(ast.NameConstant(True)), ast.Constant) + self.assertEqual(type(ast.NameConstant(False)), ast.Constant) + self.assertEqual(type(ast.NameConstant(None)), ast.Constant) + self.assertEqual(type(ast.Ellipsis()), ast.Constant) + + def test_isinstance(self): + self.assertTrue(isinstance(ast.Num(42), ast.Num)) + self.assertTrue(isinstance(ast.Num(4.2), ast.Num)) + self.assertTrue(isinstance(ast.Num(4.2j), ast.Num)) + self.assertTrue(isinstance(ast.Str('42'), ast.Str)) + self.assertTrue(isinstance(ast.Bytes(b'42'), ast.Bytes)) + self.assertTrue(isinstance(ast.NameConstant(True), ast.NameConstant)) + self.assertTrue(isinstance(ast.NameConstant(False), ast.NameConstant)) + self.assertTrue(isinstance(ast.NameConstant(None), ast.NameConstant)) + self.assertTrue(isinstance(ast.Ellipsis(), ast.Ellipsis)) + + self.assertTrue(isinstance(ast.Constant(42), ast.Num)) + self.assertTrue(isinstance(ast.Constant(4.2), ast.Num)) + self.assertTrue(isinstance(ast.Constant(4.2j), ast.Num)) + self.assertTrue(isinstance(ast.Constant('42'), ast.Str)) + self.assertTrue(isinstance(ast.Constant(b'42'), ast.Bytes)) + self.assertTrue(isinstance(ast.Constant(True), ast.NameConstant)) + self.assertTrue(isinstance(ast.Constant(False), ast.NameConstant)) + self.assertTrue(isinstance(ast.Constant(None), ast.NameConstant)) + self.assertTrue(isinstance(ast.Constant(...), ast.Ellipsis)) + + self.assertFalse(isinstance(ast.Str('42'), ast.Num)) + self.assertFalse(isinstance(ast.Num(42), ast.Str)) + self.assertFalse(isinstance(ast.Str('42'), ast.Bytes)) + self.assertFalse(isinstance(ast.Num(42), ast.NameConstant)) + self.assertFalse(isinstance(ast.Num(42), ast.Ellipsis)) + + self.assertFalse(isinstance(ast.Constant('42'), ast.Num)) + self.assertFalse(isinstance(ast.Constant(42), ast.Str)) + self.assertFalse(isinstance(ast.Constant('42'), ast.Bytes)) + self.assertFalse(isinstance(ast.Constant(42), ast.NameConstant)) + self.assertFalse(isinstance(ast.Constant(42), ast.Ellipsis)) + + self.assertFalse(isinstance(ast.Constant(), ast.Num)) + self.assertFalse(isinstance(ast.Constant(), ast.Str)) + self.assertFalse(isinstance(ast.Constant(), ast.Bytes)) + self.assertFalse(isinstance(ast.Constant(), ast.NameConstant)) + self.assertFalse(isinstance(ast.Constant(), ast.Ellipsis)) + + def test_subclasses(self): + class N(ast.Num): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.z = 'spam' + class N2(ast.Num): + pass + + n = N(42) + self.assertEqual(n.n, 42) + self.assertEqual(n.z, 'spam') + self.assertEqual(type(n), N) + self.assertTrue(isinstance(n, N)) + self.assertTrue(isinstance(n, ast.Num)) + self.assertFalse(isinstance(n, N2)) + self.assertFalse(isinstance(ast.Num(42), N)) + n = N(n=42) + self.assertEqual(n.n, 42) + self.assertEqual(type(n), N) + def test_module(self): body = [ast.Num(42)] x = ast.Module(body) From 8e6472020aa2e90d5e458f656c0a6a46def54803 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 22 Sep 2018 00:28:32 +0300 Subject: [PATCH 4/6] Add the deprecated directive in the module documentation. --- Doc/library/ast.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Doc/library/ast.rst b/Doc/library/ast.rst index a727dd6a6fbca9..97ce2f5281b5a5 100644 --- a/Doc/library/ast.rst +++ b/Doc/library/ast.rst @@ -90,6 +90,13 @@ Node classes node = ast.UnaryOp(ast.USub(), ast.Constant(5, lineno=0, col_offset=0), lineno=0, col_offset=0) +.. deprecated:: 3.8 + + Class :class:`ast.Constant` is now used for all constants. Old classes + :class:`ast.Num`, :class:`ast.Str`, :class:`ast.Bytes`, + :class:`ast.NameConstant` and :class:`ast.Ellipsis` are still available, + but they will be removed in future Python releases. + .. _abstract-grammar: From 14d788eb897283fa9348edfcab989c02116c25fe Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 22 Sep 2018 12:33:37 +0300 Subject: [PATCH 5/6] Fix tests. --- Lib/test/test_ast.py | 3 +-- Lib/test/test_future.py | 2 +- Lib/test/test_pyclbr.py | 3 ++- Python/ast_unparse.c | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Lib/test/test_ast.py b/Lib/test/test_ast.py index 7cda5ea7bc9d02..10be02eee0c03c 100644 --- a/Lib/test/test_ast.py +++ b/Lib/test/test_ast.py @@ -726,8 +726,7 @@ def mod(self, mod, msg=None, mode="exec", *, exc=ValueError): else: with self.assertRaises(exc) as cm: compile(mod, "", mode) - if msg is not None: - self.assertIn(msg, str(cm.exception)) + self.assertIn(msg, str(cm.exception)) def expr(self, node, msg=None, *, exc=ValueError): mod = ast.Module([ast.Expr(node)]) diff --git a/Lib/test/test_future.py b/Lib/test/test_future.py index 61cd63479d85e9..e8f2f761559030 100644 --- a/Lib/test/test_future.py +++ b/Lib/test/test_future.py @@ -230,7 +230,7 @@ def test_annotations(self): eq("lukasz.langa.pl") eq("call.me(maybe)") eq("1 .real") - eq("1.0 .real") + eq("1.0.real") eq("....__class__") eq("list[str]") eq("dict[str, int]") diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index eaab591f74efe4..9e970d9df041b8 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -144,7 +144,8 @@ def defined_in(item, module): def test_easy(self): self.checkModule('pyclbr') - self.checkModule('ast') + # XXX: Metaclasses are not supported + # self.checkModule('ast') self.checkModule('doctest', ignore=("TestResults", "_SpoofOut", "DocTestCase", '_DocTestSuite')) self.checkModule('difflib', ignore=("Match",)) diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c index 6303ebbcac9298..56ea3c424c5bbe 100644 --- a/Python/ast_unparse.c +++ b/Python/ast_unparse.c @@ -688,12 +688,12 @@ static int append_ast_attribute(_PyUnicodeWriter *writer, expr_ty e) { const char *period; - APPEND_EXPR(e->v.Attribute.value, PR_ATOM); + expr_ty v = e->v.Attribute.value; + APPEND_EXPR(v, PR_ATOM); /* Special case: integers require a space for attribute access to be - unambiguous. Floats and complex numbers don't but work with it, too. */ - if (e->v.Attribute.value->kind == Constant_kind) - { + unambiguous. */ + if (v->kind == Constant_kind && PyLong_CheckExact(v->v.Constant.value)) { period = " ."; } else { From 0031ce8e35e88f4f4df24259435c416e120fcac9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 23 Sep 2018 08:37:25 +0300 Subject: [PATCH 6/6] Fix mark up in NEWS. --- .../next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst b/Misc/NEWS.d/next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst index 74fafb44b6da84..9be4bf89008d9c 100644 --- a/Misc/NEWS.d/next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst +++ b/Misc/NEWS.d/next/Library/2018-09-20-17-35-05.bpo-32892.TOUBdg.rst @@ -1,4 +1,4 @@ The parser now represents all constants as :class:`ast.Constant` instead of -using specific constant AST types (``Num``, ``Str``,``Bytes``, +using specific constant AST types (``Num``, ``Str``, ``Bytes``, ``NameConstant`` and ``Ellipsis``). These classes are considered deprecated and will be removed in future Python versions.