[Python-checkins] bpo-33475: Fix and improve converting annotations to strings. (GH-6774)

Łukasz Langa webhook-mailer at python.org
Wed May 16 23:17:54 EDT 2018


https://github.com/python/cpython/commit/64fddc423fcbe90b8088446c63385ec0aaf3077c
commit: 64fddc423fcbe90b8088446c63385ec0aaf3077c
branch: master
author: Serhiy Storchaka <storchaka at gmail.com>
committer: Łukasz Langa <lukasz at langa.pl>
date: 2018-05-16T23:17:48-04:00
summary:

bpo-33475: Fix and improve converting annotations to strings. (GH-6774)

files:
A Misc/NEWS.d/next/Core and Builtins/2018-05-13-01-26-18.bpo-33475.rI0y1U.rst
M Include/ast.h
M Lib/test/test_future.py
M Python/ast_unparse.c
M Python/compile.c

diff --git a/Include/ast.h b/Include/ast.h
index 639c4f82325a..5bc2b05b3e94 100644
--- a/Include/ast.h
+++ b/Include/ast.h
@@ -19,9 +19,7 @@ PyAPI_FUNC(mod_ty) PyAST_FromNodeObject(
 #ifndef Py_LIMITED_API
 
 /* _PyAST_ExprAsUnicode is defined in ast_unparse.c */
-PyAPI_FUNC(PyObject *) _PyAST_ExprAsUnicode(
-    expr_ty e,
-    int omit_parens);
+PyAPI_FUNC(PyObject *) _PyAST_ExprAsUnicode(expr_ty);
 
 #endif /* !Py_LIMITED_API */
 
diff --git a/Lib/test/test_future.py b/Lib/test/test_future.py
index 29c4632e7e50..61cd63479d85 100644
--- a/Lib/test/test_future.py
+++ b/Lib/test/test_future.py
@@ -157,55 +157,76 @@ def test_annotations(self):
         eq('True or False or None')
         eq('True and False')
         eq('True and False and None')
-        eq('(Name1 and Name2) or Name3')
-        eq('Name1 or (Name2 and Name3)')
-        eq('(Name1 and Name2) or (Name3 and Name4)')
-        eq('Name1 or (Name2 and Name3) or Name4')
+        eq('Name1 and Name2 or Name3')
+        eq('Name1 and (Name2 or Name3)')
+        eq('Name1 or Name2 and Name3')
+        eq('(Name1 or Name2) and Name3')
+        eq('Name1 and Name2 or Name3 and Name4')
+        eq('Name1 or Name2 and Name3 or Name4')
+        eq('a + b + (c + d)')
+        eq('a * b * (c * d)')
+        eq('(a ** b) ** c ** d')
         eq('v1 << 2')
         eq('1 >> v2')
-        eq(r'1 % finished')
-        eq('((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8)')
+        eq('1 % finished')
+        eq('1 + v2 - v3 * 4 ^ 5 ** v6 / 7 // 8')
         eq('not great')
+        eq('not not great')
         eq('~great')
         eq('+value')
+        eq('++value')
         eq('-1')
-        eq('(~int) and (not ((v1 ^ (123 + v2)) | True))')
+        eq('~int and not v1 ^ 123 + v2 | True')
+        eq('a + (not b)')
         eq('lambda arg: None')
         eq('lambda a=True: a')
         eq('lambda a, b, c=True: a')
-        eq("lambda a, b, c=True, *, d=(1 << v2), e='str': a")
-        eq("lambda a, b, c=True, *vararg, d=(v1 << 2), e='str', **kwargs: a + b")
+        eq("lambda a, b, c=True, *, d=1 << v2, e='str': a")
+        eq("lambda a, b, c=True, *vararg, d=v1 << 2, e='str', **kwargs: a + b")
+        eq('lambda x: lambda y: x + y')
         eq('1 if True else 2')
-        eq('(str or None) if True else (str or bytes or None)')
-        eq('(str or None) if (1 if True else 2) else (str or bytes or None)')
-        eq("{'2.7': dead, '3.7': (long_live or die_hard)}")
-        eq("{'2.7': dead, '3.7': (long_live or die_hard), **{'3.6': verygood}}")
+        eq('str or None if int or True else str or bytes or None')
+        eq('str or None if (1 if True else 2) else str or bytes or None')
+        eq("0 if not x else 1 if x > 0 else -1")
+        eq("(1 if x > 0 else -1) if x else 0")
+        eq("{'2.7': dead, '3.7': long_live or die_hard}")
+        eq("{'2.7': dead, '3.7': long_live or die_hard, **{'3.6': verygood}}")
         eq("{**a, **b, **c}")
-        eq("{'2.7', '3.6', '3.7', '3.8', '3.9', ('4.0' if gilectomy else '3.10')}")
-        eq("({'a': 'b'}, (True or False), (+value), 'string', b'bytes') or None")
+        eq("{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}")
+        eq("{*a, *b, *c}")
+        eq("({'a': 'b'}, True or False, +value, 'string', b'bytes') or None")
         eq("()")
-        eq("(1,)")
-        eq("(1, 2)")
-        eq("(1, 2, 3)")
+        eq("(a,)")
+        eq("(a, b)")
+        eq("(a, b, c)")
+        eq("(*a, *b, *c)")
         eq("[]")
-        eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or C)]")
+        eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]")
+        eq("[*a, *b, *c]")
         eq("{i for i in (1, 2, 3)}")
-        eq("{(i ** 2) for i in (1, 2, 3)}")
-        eq("{(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}")
-        eq("{((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)}")
+        eq("{i ** 2 for i in (1, 2, 3)}")
+        eq("{i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))}")
+        eq("{i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)}")
         eq("[i for i in (1, 2, 3)]")
-        eq("[(i ** 2) for i in (1, 2, 3)]")
-        eq("[(i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))]")
-        eq("[((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3)]")
-        eq(r"{i: 0 for i in (1, 2, 3)}")
+        eq("[i ** 2 for i in (1, 2, 3)]")
+        eq("[i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c'))]")
+        eq("[i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3)]")
+        eq("(i for i in (1, 2, 3))")
+        eq("(i ** 2 for i in (1, 2, 3))")
+        eq("(i ** 2 for i, _ in ((1, 'a'), (2, 'b'), (3, 'c')))")
+        eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))")
+        eq("{i: 0 for i in (1, 2, 3)}")
         eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}")
+        eq("[(x, y) for x, y in (a, b)]")
+        eq("[(x,) for x, in (a,)]")
         eq("Python3 > Python2 > COBOL")
         eq("Life is Life")
         eq("call()")
         eq("call(arg)")
         eq("call(kwarg='hey')")
         eq("call(arg, kwarg='hey')")
-        eq("call(arg, another, kwarg='hey', **kwargs)")
+        eq("call(arg, *args, another, kwarg='hey')")
+        eq("call(arg, another, kwarg='hey', **kwargs, kwarg2='ho')")
         eq("lukasz.langa.pl")
         eq("call.me(maybe)")
         eq("1 .real")
@@ -213,6 +234,7 @@ def test_annotations(self):
         eq("....__class__")
         eq("list[str]")
         eq("dict[str, int]")
+        eq("set[str,]")
         eq("tuple[str, ...]")
         eq("tuple[str, int, float, dict[str, int]]")
         eq("slice[0]")
@@ -222,49 +244,28 @@ def test_annotations(self):
         eq("slice[:-1]")
         eq("slice[1:]")
         eq("slice[::-1]")
-        eq('(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None)')
+        eq("slice[()]")
+        eq("slice[a, b:c, d:e:f]")
+        eq("slice[(x for x in a)]")
+        eq('str or None if sys.version_info[0] > (3,) else str or bytes or None')
         eq("f'f-string without formatted values is just a string'")
         eq("f'{{NOT a formatted value}}'")
         eq("f'some f-string with {a} {few():.2f} {formatted.values!r}'")
         eq('''f"{f'{nested} inner'} outer"''')
         eq("f'space between opening braces: { {a for a in (1, 2, 3)}}'")
-
-    def test_annotations_inexact(self):
-        """Source formatting is not always preserved
-
-        This is due to reconstruction from AST.  We *need to* put the parens
-        in nested expressions because we don't know if the source code
-        had them in the first place or not.
-        """
-        eq = partial(self.assertAnnotationEqual, drop_parens=True)
-        eq('Name1 and Name2 or Name3')
-        eq('Name1 or Name2 and Name3')
-        eq('Name1 and Name2 or Name3 and Name4')
-        eq('Name1 or Name2 and Name3 or Name4')
-        eq('1 + v2 - v3 * 4 ^ v5 ** 6 / 7 // 8')
-        eq('~int and not v1 ^ 123 + v2 | True')
-        eq('str or None if True else str or bytes or None')
-        eq("{'2.7': dead, '3.7': long_live or die_hard}")
-        eq("{'2.7', '3.6', '3.7', '3.8', '3.9', '4.0' if gilectomy else '3.10'}")
-        eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or A, 11 or B, 12 or C]")
-        # Consequently, we always drop unnecessary parens if they were given in
-        # the outer scope:
-        some_name = self.getActual("(SomeName)")
-        self.assertEqual(some_name, 'SomeName')
-        # Interestingly, in the case of tuples (and generator expressions) the
-        # parens are *required* by the Python syntax in the annotation context.
-        # But there's no point storing that detail in __annotations__ so we're
-        # fine with the parens-less form.
-        eq = partial(self.assertAnnotationEqual, is_tuple=True)
-        eq("(Good, Bad, Ugly)")
-        eq("(i for i in (1, 2, 3))")
-        eq("((i ** 2) for i in (1, 2, 3))")
-        eq("((i ** 2) for i, _ in ((1, 'a'), (2, 'b'), (3, 'c')))")
-        eq("(((i ** 2) + j) for i in (1, 2, 3) for j in (1, 2, 3))")
-        eq("(*starred)")
+        eq("f'{(lambda x: x)}'")
+        eq("f'{(None if a else lambda x: x)}'")
         eq('(yield from outside_of_generator)')
         eq('(yield)')
-        eq('(await some.complicated[0].call(with_args=(True or (1 is not 1))))')
+        eq('(yield a + b)')
+        eq('await some.complicated[0].call(with_args=True or 1 is not 1)')
+        eq('[x for x in (a if b else c)]')
+        eq('[x for x in a if (b if c else d)]')
+        eq('f(x for x in a)')
+        eq('f(1, (x for x in a))')
+        eq('f((x for x in a), 2)')
+        eq('(((a)))', 'a')
+        eq('(((a, b)))', '(a, b)')
 
 
 if __name__ == "__main__":
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-05-13-01-26-18.bpo-33475.rI0y1U.rst b/Misc/NEWS.d/next/Core and Builtins/2018-05-13-01-26-18.bpo-33475.rI0y1U.rst
new file mode 100644
index 000000000000..cd714b9d1e89
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-05-13-01-26-18.bpo-33475.rI0y1U.rst	
@@ -0,0 +1,2 @@
+Fixed miscellaneous bugs in converting annotations to strings and optimized
+parentheses in the string representation.
diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c
index 1345271e599b..725ce31fe3c0 100644
--- a/Python/ast_unparse.c
+++ b/Python/ast_unparse.c
@@ -9,13 +9,15 @@ static PyObject *_str_dbl_close_br;
 
 /* Forward declarations for recursion via helper functions. */
 static PyObject *
-expr_as_unicode(expr_ty e, bool omit_parens);
+expr_as_unicode(expr_ty e, int level);
 static int
-append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens);
+append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, int level);
 static int
 append_joinedstr(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec);
 static int
 append_formattedvalue(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec);
+static int
+append_ast_slice(_PyUnicodeWriter *writer, slice_ty slice);
 
 static int
 append_charp(_PyUnicodeWriter *writer, const char *charp)
@@ -23,6 +25,39 @@ append_charp(_PyUnicodeWriter *writer, const char *charp)
     return _PyUnicodeWriter_WriteASCIIString(writer, charp, -1);
 }
 
+#define APPEND_STR_FINISH(str)  do { \
+        return append_charp(writer, (str)); \
+    } while (0)
+
+#define APPEND_STR(str)  do { \
+        if (-1 == append_charp(writer, (str))) { \
+            return -1; \
+        } \
+    } while (0)
+
+#define APPEND_STR_IF(cond, str)  do { \
+        if ((cond) && -1 == append_charp(writer, (str))) { \
+            return -1; \
+        } \
+    } while (0)
+
+#define APPEND_STR_IF_NOT_FIRST(str)  do { \
+        APPEND_STR_IF(!first, (str)); \
+        first = false; \
+    } while (0)
+
+#define APPEND_EXPR(expr, pr)  do { \
+        if (-1 == append_ast_expr(writer, (expr), (pr))) { \
+            return -1; \
+        } \
+    } while (0)
+
+#define APPEND(type, value)  do { \
+        if (-1 == append_ast_ ## type(writer, (value))) { \
+            return -1; \
+        } \
+    } while (0)
+
 static int
 append_repr(_PyUnicodeWriter *writer, PyObject *obj)
 {
@@ -37,111 +72,108 @@ append_repr(_PyUnicodeWriter *writer, PyObject *obj)
     return ret;
 }
 
-static int
-append_ast_boolop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+/* Priority levels */
+
+enum {
+    PR_TUPLE,
+    PR_TEST,            /* 'if'-'else', 'lambda' */
+    PR_OR,              /* 'or' */
+    PR_AND,             /* 'and' */
+    PR_NOT,             /* 'not' */
+    PR_CMP,             /* '<', '>', '==', '>=', '<=', '!=',
+                           'in', 'not in', 'is', 'is not' */
+    PR_EXPR,
+    PR_BOR = PR_EXPR,   /* '|' */
+    PR_BXOR,            /* '^' */
+    PR_BAND,            /* '&' */
+    PR_SHIFT,           /* '<<', '>>' */
+    PR_ARITH,           /* '+', '-' */
+    PR_TERM,            /* '*', '@', '/', '%', '//' */
+    PR_FACTOR,          /* unary '+', '-', '~' */
+    PR_POWER,           /* '**' */
+    PR_AWAIT,           /* 'await' */
+    PR_ATOM,
+};
+
+static int
+append_ast_boolop(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
     Py_ssize_t i, value_count;
     asdl_seq *values;
+    const char *op = (e->v.BoolOp.op == And) ? " and " : " or ";
+    int pr = (e->v.BoolOp.op == And) ? PR_AND : PR_OR;
 
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
+    APPEND_STR_IF(level > pr, "(");
 
     values = e->v.BoolOp.values;
-    value_count = asdl_seq_LEN(values) - 1;
-    assert(value_count >= 0);
+    value_count = asdl_seq_LEN(values);
 
-    if (-1 == append_ast_expr(writer,
-                              (expr_ty)asdl_seq_GET(values, 0),
-                              false)) {
-        return -1;
-    }
-
-    const char *op = (e->v.BoolOp.op == And) ? " and " : " or ";
-    for (i = 1; i <= value_count; ++i) {
-        if (-1 == append_charp(writer, op)) {
-            return -1;
-        }
-
-        if (-1 == append_ast_expr(writer,
-                                  (expr_ty)asdl_seq_GET(values, i),
-                                  false)) {
-            return -1;
-        }
+    for (i = 0; i < value_count; ++i) {
+        APPEND_STR_IF(i > 0, op);
+        APPEND_EXPR((expr_ty)asdl_seq_GET(values, i), pr + 1);
     }
 
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR_IF(level > pr, ")");
+    return 0;
 }
 
 static int
-append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
     const char *op;
-
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.BinOp.left, false)) {
-        return -1;
-    }
-
-    switch(e->v.BinOp.op) {
-    case Add: op = " + "; break;
-    case Sub: op = " - "; break;
-    case Mult: op = " * "; break;
-    case MatMult: op = " @ "; break;
-    case Div: op = " / "; break;
-    case Mod: op = " % "; break;
-    case LShift: op = " << "; break;
-    case RShift: op = " >> "; break;
-    case BitOr: op = " | "; break;
-    case BitXor: op = " ^ "; break;
-    case BitAnd: op = " & "; break;
-    case FloorDiv: op = " // "; break;
-    case Pow: op = " ** "; break;
+    int pr;
+    bool rassoc = false;  /* is right-associative? */
+
+    switch (e->v.BinOp.op) {
+    case Add: op = " + "; pr = PR_ARITH; break;
+    case Sub: op = " - "; pr = PR_ARITH; break;
+    case Mult: op = " * "; pr = PR_TERM; break;
+    case MatMult: op = " @ "; pr = PR_TERM; break;
+    case Div: op = " / "; pr = PR_TERM; break;
+    case Mod: op = " % "; pr = PR_TERM; break;
+    case LShift: op = " << "; pr = PR_SHIFT; break;
+    case RShift: op = " >> "; pr = PR_SHIFT; break;
+    case BitOr: op = " | "; pr = PR_BOR; break;
+    case BitXor: op = " ^ "; pr = PR_BXOR; break;
+    case BitAnd: op = " & "; pr = PR_BAND; break;
+    case FloorDiv: op = " // "; pr = PR_TERM; break;
+    case Pow: op = " ** "; pr = PR_POWER; rassoc = true; break;
     default:
-        Py_UNREACHABLE();
-    }
-
-    if (-1 == append_charp(writer, op)) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.BinOp.right, false)) {
+        PyErr_SetString(PyExc_SystemError,
+                        "unknown binary operator");
         return -1;
     }
 
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR_IF(level > pr, "(");
+    APPEND_EXPR(e->v.BinOp.left, pr + rassoc);
+    APPEND_STR(op);
+    APPEND_EXPR(e->v.BinOp.right, pr + !rassoc);
+    APPEND_STR_IF(level > pr, ")");
+    return 0;
 }
 
 static int
-append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
     const char *op;
+    int pr;
 
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
-
-    switch(e->v.UnaryOp.op) {
-    case Invert: op = "~"; break;
-    case Not: op = "not "; break;
-    case UAdd: op = "+"; break;
-    case USub: op = "-"; break;
+    switch (e->v.UnaryOp.op) {
+    case Invert: op = "~"; pr = PR_FACTOR; break;
+    case Not: op = "not "; pr = PR_NOT; break;
+    case UAdd: op = "+"; pr = PR_FACTOR; break;
+    case USub: op = "-"; pr = PR_FACTOR; break;
     default:
-        Py_UNREACHABLE();
-    }
-
-    if (-1 == append_charp(writer, op)) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.UnaryOp.operand, false)) {
+        PyErr_SetString(PyExc_SystemError,
+                        "unknown unary operator");
         return -1;
     }
 
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR_IF(level > pr, "(");
+    APPEND_STR(op);
+    APPEND_EXPR(e->v.UnaryOp.operand, pr);
+    APPEND_STR_IF(level > pr, ")");
+    return 0;
 }
 
 static int
@@ -151,12 +183,8 @@ append_ast_arg(_PyUnicodeWriter *writer, arg_ty arg)
         return -1;
     }
     if (arg->annotation) {
-        if (-1 == append_charp(writer, ": ")) {
-            return -1;
-        }
-        if (-1 == append_ast_expr(writer, arg->annotation, true)) {
-            return -1;
-        }
+        APPEND_STR(": ");
+        APPEND_EXPR(arg->annotation, PR_TEST);
     }
     return 0;
 }
@@ -166,8 +194,6 @@ append_ast_args(_PyUnicodeWriter *writer, arguments_ty args)
 {
     bool first;
     Py_ssize_t i, di, arg_count, default_count;
-    arg_ty arg;
-    expr_ty default_;
 
     first = true;
 
@@ -175,47 +201,22 @@ append_ast_args(_PyUnicodeWriter *writer, arguments_ty args)
     arg_count = asdl_seq_LEN(args->args);
     default_count = asdl_seq_LEN(args->defaults);
     for (i = 0; i < arg_count; i++) {
-        if (first) {
-            first = false;
-        }
-        else if (-1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-
-        arg = (arg_ty)asdl_seq_GET(args->args, i);
-        if (-1 == append_ast_arg(writer, arg)) {
-            return -1;
-        }
+        APPEND_STR_IF_NOT_FIRST(", ");
+        APPEND(arg, (arg_ty)asdl_seq_GET(args->args, i));
 
         di = i - arg_count + default_count;
         if (di >= 0) {
-            if (-1 == append_charp(writer, "=")) {
-                return -1;
-            }
-            default_ = (expr_ty)asdl_seq_GET(args->defaults, di);
-            if (-1 == append_ast_expr(writer, default_, false)) {
-                return -1;
-            }
+            APPEND_STR("=");
+            APPEND_EXPR((expr_ty)asdl_seq_GET(args->defaults, di), PR_TEST);
         }
     }
 
     /* vararg, or bare '*' if no varargs but keyword-only arguments present */
     if (args->vararg || args->kwonlyargs) {
-        if (first) {
-            first = false;
-        }
-        else if (-1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-
-        if (-1 == append_charp(writer, "*")) {
-            return -1;
-        }
-
+        APPEND_STR_IF_NOT_FIRST(", ");
+        APPEND_STR("*");
         if (args->vararg) {
-            if (-1 == append_ast_arg(writer, args->vararg)) {
-                return -1;
-            }
+            APPEND(arg, args->vararg);
         }
     }
 
@@ -223,223 +224,127 @@ append_ast_args(_PyUnicodeWriter *writer, arguments_ty args)
     arg_count = asdl_seq_LEN(args->kwonlyargs);
     default_count = asdl_seq_LEN(args->kw_defaults);
     for (i = 0; i < arg_count; i++) {
-        if (first) {
-            first = false;
-        }
-        else if (-1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-
-        arg = (arg_ty)asdl_seq_GET(args->kwonlyargs, i);
-        if (-1 == append_ast_arg(writer, arg)) {
-            return -1;
-        }
+        APPEND_STR_IF_NOT_FIRST(", ");
+        APPEND(arg, (arg_ty)asdl_seq_GET(args->kwonlyargs, i));
 
         di = i - arg_count + default_count;
         if (di >= 0) {
-            if (-1 == append_charp(writer, "=")) {
-                return -1;
-            }
-            default_ = (expr_ty)asdl_seq_GET(args->kw_defaults, di);
-            if (-1 == append_ast_expr(writer, default_, false)) {
-                return -1;
-            }
+            APPEND_STR("=");
+            APPEND_EXPR((expr_ty)asdl_seq_GET(args->kw_defaults, di), PR_TEST);
         }
     }
 
     /* **kwargs */
     if (args->kwarg) {
-        if (first) {
-            first = false;
-        }
-        else if (-1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-
-        if (-1 == append_charp(writer, "**")) {
-            return -1;
-        }
-
-        if (-1 == append_ast_arg(writer, args->kwarg)) {
-            return -1;
-        }
+        APPEND_STR_IF_NOT_FIRST(", ");
+        APPEND_STR("**");
+        APPEND(arg, args->kwarg);
     }
 
     return 0;
 }
 
 static int
-append_ast_lambda(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_lambda(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, "lambda ")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_args(writer, e->v.Lambda.args)) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, ": ")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.Lambda.body, true)) {
-        return -1;
-    }
-
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR_IF(level > PR_TEST, "(");
+    APPEND_STR("lambda ");
+    APPEND(args, e->v.Lambda.args);
+    APPEND_STR(": ");
+    APPEND_EXPR(e->v.Lambda.body, PR_TEST);
+    APPEND_STR_IF(level > PR_TEST, ")");
+    return 0;
 }
 
 static int
-append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.IfExp.body, false)) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, " if ")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.IfExp.test, false)) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, " else ")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.IfExp.orelse, false)) {
-        return -1;
-    }
-
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR_IF(level > PR_TEST, "(");
+    APPEND_EXPR(e->v.IfExp.body, PR_TEST + 1);
+    APPEND_STR(" if ");
+    APPEND_EXPR(e->v.IfExp.test, PR_TEST + 1);
+    APPEND_STR(" else ");
+    APPEND_EXPR(e->v.IfExp.orelse, PR_TEST);
+    APPEND_STR_IF(level > PR_TEST, ")");
+    return 0;
 }
 
 static int
 append_ast_dict(_PyUnicodeWriter *writer, expr_ty e)
 {
     Py_ssize_t i, value_count;
-    expr_ty key_node, value_node;
-
-    if (-1 == append_charp(writer, "{")) {
-        return -1;
-    }
+    expr_ty key_node;
 
+    APPEND_STR("{");
     value_count = asdl_seq_LEN(e->v.Dict.values);
 
     for (i = 0; i < value_count; i++) {
-        if (i > 0 && -1 == append_charp(writer, ", ")) {
-            return -1;
-        }
+        APPEND_STR_IF(i > 0, ", ");
         key_node = (expr_ty)asdl_seq_GET(e->v.Dict.keys, i);
         if (key_node != NULL) {
-            if (-1 == append_ast_expr(writer, key_node, false)) {
-                return -1;
-            }
-
-            if (-1 == append_charp(writer, ": ")) {
-                return -1;
-            }
-        }
-        else if (-1 == append_charp(writer, "**")) {
-            return -1;
+            APPEND_EXPR(key_node, PR_TEST);
+            APPEND_STR(": ");
+            APPEND_EXPR((expr_ty)asdl_seq_GET(e->v.Dict.values, i), PR_TEST);
         }
-
-        value_node = (expr_ty)asdl_seq_GET(e->v.Dict.values, i);
-        if (-1 == append_ast_expr(writer, value_node, false)) {
-            return -1;
+        else {
+            APPEND_STR("**");
+            APPEND_EXPR((expr_ty)asdl_seq_GET(e->v.Dict.values, i), PR_EXPR);
         }
     }
 
-    return append_charp(writer, "}");
+    APPEND_STR_FINISH("}");
 }
 
 static int
 append_ast_set(_PyUnicodeWriter *writer, expr_ty e)
 {
     Py_ssize_t i, elem_count;
-    expr_ty elem_node;
-
-    if (-1 == append_charp(writer, "{")) {
-        return -1;
-    }
 
+    APPEND_STR("{");
     elem_count = asdl_seq_LEN(e->v.Set.elts);
     for (i = 0; i < elem_count; i++) {
-        if (i > 0 && -1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-
-        elem_node = (expr_ty)asdl_seq_GET(e->v.Set.elts, i);
-        if (-1 == append_ast_expr(writer, elem_node, false)) {
-            return -1;
-        }
+        APPEND_STR_IF(i > 0, ", ");
+        APPEND_EXPR((expr_ty)asdl_seq_GET(e->v.Set.elts, i), PR_TEST);
     }
 
-    return append_charp(writer, "}");
+    APPEND_STR_FINISH("}");
 }
 
 static int
 append_ast_list(_PyUnicodeWriter *writer, expr_ty e)
 {
     Py_ssize_t i, elem_count;
-    expr_ty elem_node;
-
-    if (-1 == append_charp(writer, "[")) {
-        return -1;
-    }
 
+    APPEND_STR("[");
     elem_count = asdl_seq_LEN(e->v.List.elts);
     for (i = 0; i < elem_count; i++) {
-        if (i > 0 && -1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-        elem_node = (expr_ty)asdl_seq_GET(e->v.List.elts, i);
-        if (-1 == append_ast_expr(writer, elem_node, false)) {
-            return -1;
-        }
+        APPEND_STR_IF(i > 0, ", ");
+        APPEND_EXPR((expr_ty)asdl_seq_GET(e->v.List.elts, i), PR_TEST);
     }
 
-    return append_charp(writer, "]");
+    APPEND_STR_FINISH("]");
 }
 
 static int
-append_ast_tuple(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_tuple(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
     Py_ssize_t i, elem_count;
-    expr_ty elem_node;
 
     elem_count = asdl_seq_LEN(e->v.Tuple.elts);
 
-    if (!omit_parens || elem_count < 2) {
-        if (-1 == append_charp(writer, "(")) {
-            return -1;
-        }
+    if (elem_count == 0) {
+        APPEND_STR_FINISH("()");
     }
 
-    for (i = 0; i < elem_count; i++) {
-        if ((i > 0 || elem_count == 1) && -1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-        elem_node = (expr_ty)asdl_seq_GET(e->v.Tuple.elts, i);
-        if (-1 == append_ast_expr(writer, elem_node, false)) {
-            return -1;
-        }
-    }
+    APPEND_STR_IF(level > PR_TUPLE, "(");
 
-    if (!omit_parens || elem_count < 2) {
-        return append_charp(writer, ")");
+    for (i = 0; i < elem_count; i++) {
+        APPEND_STR_IF(i > 0, ", ");
+        APPEND_EXPR((expr_ty)asdl_seq_GET(e->v.Tuple.elts, i), PR_TEST);
     }
 
+    APPEND_STR_IF(elem_count == 1, ",");
+    APPEND_STR_IF(level > PR_TUPLE, ")");
     return 0;
 }
 
@@ -448,33 +353,15 @@ append_ast_comprehension(_PyUnicodeWriter *writer, comprehension_ty gen)
 {
     Py_ssize_t i, if_count;
 
-    if (-1 == append_charp(writer, gen->is_async ? " async for " : " for ")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, gen->target, true)) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, " in ")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, gen->iter, false)) {
-        return -1;
-    }
+    APPEND_STR(gen->is_async ? " async for " : " for ");
+    APPEND_EXPR(gen->target, PR_TUPLE);
+    APPEND_STR(" in ");
+    APPEND_EXPR(gen->iter, PR_TEST + 1);
 
     if_count = asdl_seq_LEN(gen->ifs);
     for (i = 0; i < if_count; i++) {
-        if (-1 == append_charp(writer, " if ")) {
-            return -1;
-        }
-
-        if (-1 == append_ast_expr(writer,
-                                  (expr_ty)asdl_seq_GET(gen->ifs, i),
-                                  false)) {
-            return -1;
-        }
+        APPEND_STR(" if ");
+        APPEND_EXPR((expr_ty)asdl_seq_GET(gen->ifs, i), PR_TEST + 1);
     }
     return 0;
 }
@@ -483,110 +370,62 @@ static int
 append_ast_comprehensions(_PyUnicodeWriter *writer, asdl_seq *comprehensions)
 {
     Py_ssize_t i, gen_count;
-    comprehension_ty comp_node;
     gen_count = asdl_seq_LEN(comprehensions);
 
     for (i = 0; i < gen_count; i++) {
-        comp_node = (comprehension_ty)asdl_seq_GET(comprehensions, i);
-        if (-1 == append_ast_comprehension(writer, comp_node)) {
-            return -1;
-        }
+        APPEND(comprehension, (comprehension_ty)asdl_seq_GET(comprehensions, i));
     }
 
     return 0;
 }
 
 static int
-append_ast_genexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_genexp(_PyUnicodeWriter *writer, expr_ty e)
 {
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.GeneratorExp.elt, false)) {
-        return -1;
-    }
-
-    if (-1 == append_ast_comprehensions(writer, e->v.GeneratorExp.generators)) {
-        return -1;
-    }
-
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR("(");
+    APPEND_EXPR(e->v.GeneratorExp.elt, PR_TEST);
+    APPEND(comprehensions, e->v.GeneratorExp.generators);
+    APPEND_STR_FINISH(")");
 }
 
 static int
 append_ast_listcomp(_PyUnicodeWriter *writer, expr_ty e)
 {
-    if (-1 == append_charp(writer, "[")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.ListComp.elt, false)) {
-        return -1;
-    }
-
-    if (-1 == append_ast_comprehensions(writer, e->v.ListComp.generators)) {
-        return -1;
-    }
-
-    return append_charp(writer, "]");
+    APPEND_STR("[");
+    APPEND_EXPR(e->v.ListComp.elt, PR_TEST);
+    APPEND(comprehensions, e->v.ListComp.generators);
+    APPEND_STR_FINISH("]");
 }
 
 static int
 append_ast_setcomp(_PyUnicodeWriter *writer, expr_ty e)
 {
-    if (-1 == append_charp(writer, "{")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.SetComp.elt, false)) {
-        return -1;
-    }
-
-    if (-1 == append_ast_comprehensions(writer, e->v.SetComp.generators)) {
-        return -1;
-    }
-
-    return append_charp(writer, "}");
+    APPEND_STR("{");
+    APPEND_EXPR(e->v.SetComp.elt, PR_TEST);
+    APPEND(comprehensions, e->v.SetComp.generators);
+    APPEND_STR_FINISH("}");
 }
 
 static int
 append_ast_dictcomp(_PyUnicodeWriter *writer, expr_ty e)
 {
-    if (-1 == append_charp(writer, "{")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.DictComp.key, false)) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, ": ")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_expr(writer, e->v.DictComp.value, false)) {
-        return -1;
-    }
-
-    if (-1 == append_ast_comprehensions(writer, e->v.DictComp.generators)) {
-        return -1;
-    }
-
-    return append_charp(writer, "}");
+    APPEND_STR("{");
+    APPEND_EXPR(e->v.DictComp.key, PR_TEST);
+    APPEND_STR(": ");
+    APPEND_EXPR(e->v.DictComp.value, PR_TEST);
+    APPEND(comprehensions, e->v.DictComp.generators);
+    APPEND_STR_FINISH("}");
 }
 
 static int
-append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
     const char *op;
     Py_ssize_t i, comparator_count;
     asdl_seq *comparators;
     asdl_int_seq *ops;
 
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
+    APPEND_STR_IF(level > PR_CMP, "(");
 
     comparators = e->v.Compare.comparators;
     ops = e->v.Compare.ops;
@@ -594,9 +433,7 @@ append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
     assert(comparator_count > 0);
     assert(comparator_count == asdl_seq_LEN(ops));
 
-    if (-1 == append_ast_expr(writer, e->v.Compare.left, false)) {
-        return -1;
-    }
+    APPEND_EXPR(e->v.Compare.left, PR_CMP + 1);
 
     for (i = 0; i < comparator_count; i++) {
         switch ((cmpop_ty)asdl_seq_GET(ops, i)) {
@@ -636,39 +473,30 @@ append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
             return -1;
         }
 
-        if (-1 == append_charp(writer, op)) {
-            return -1;
-        }
-
-        if (-1 == append_ast_expr(writer,
-                                  (expr_ty)asdl_seq_GET(comparators, i),
-                                  false)) {
-            return -1;
-        }
+        APPEND_STR(op);
+        APPEND_EXPR((expr_ty)asdl_seq_GET(comparators, i), PR_CMP + 1);
     }
 
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR_IF(level > PR_CMP, ")");
+    return 0;
 }
 
 static int
 append_ast_keyword(_PyUnicodeWriter *writer, keyword_ty kw)
 {
     if (kw->arg == NULL) {
-        if (-1 == append_charp(writer, "**")) {
-            return -1;
-        }
+        APPEND_STR("**");
     }
     else {
         if (-1 == _PyUnicodeWriter_WriteStr(writer, kw->arg)) {
             return -1;
         }
 
-        if (-1 == append_charp(writer, "=")) {
-            return -1;
-        }
+        APPEND_STR("=");
     }
 
-    return append_ast_expr(writer, kw->value, false);
+    APPEND_EXPR(kw->value, PR_TEST);
+    return 0;
 }
 
 static int
@@ -677,48 +505,33 @@ append_ast_call(_PyUnicodeWriter *writer, expr_ty e)
     bool first;
     Py_ssize_t i, arg_count, kw_count;
     expr_ty expr;
-    keyword_ty kw;
 
-    if (-1 == append_ast_expr(writer, e->v.Call.func, false)) {
-        return -1;
-    }
+    APPEND_EXPR(e->v.Call.func, PR_ATOM);
 
-    if (-1 == append_charp(writer, "(")) {
-        return -1;
+    arg_count = asdl_seq_LEN(e->v.Call.args);
+    kw_count = asdl_seq_LEN(e->v.Call.keywords);
+    if (arg_count == 1 && kw_count == 0) {
+        expr = (expr_ty)asdl_seq_GET(e->v.Call.args, 0);
+        if (expr->kind == GeneratorExp_kind) {
+            /* Special case: a single generator expression. */
+            return append_ast_genexp(writer, expr);
+        }
     }
 
+    APPEND_STR("(");
+
     first = true;
-    arg_count = asdl_seq_LEN(e->v.Call.args);
     for (i = 0; i < arg_count; i++) {
-        if (first) {
-            first = false;
-        }
-        else if (-1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-
-        expr = (expr_ty)asdl_seq_GET(e->v.Call.args, i);
-        if (-1 == append_ast_expr(writer, expr, false)) {
-            return -1;
-        }
+        APPEND_STR_IF_NOT_FIRST(", ");
+        APPEND_EXPR((expr_ty)asdl_seq_GET(e->v.Call.args, i), PR_TEST);
     }
 
-    kw_count = asdl_seq_LEN(e->v.Call.keywords);
     for (i = 0; i < kw_count; i++) {
-        if (first) {
-            first = false;
-        }
-        else if (-1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-
-        kw = (keyword_ty)asdl_seq_GET(e->v.Call.keywords, i);
-        if (-1 == append_ast_keyword(writer, kw)) {
-            return -1;
-        }
+        APPEND_STR_IF_NOT_FIRST(", ");
+        APPEND(keyword, (keyword_ty)asdl_seq_GET(e->v.Call.keywords, i));
     }
 
-    return append_charp(writer, ")");
+    APPEND_STR_FINISH(")");
 }
 
 static PyObject *
@@ -778,9 +591,8 @@ build_fstring_body(asdl_seq *values, bool is_format_spec)
     body_writer.min_length = 256;
     body_writer.overallocate = 1;
 
-    value_count = asdl_seq_LEN(values) - 1;
-    assert(value_count >= 0);
-    for (i = 0; i <= value_count; ++i) {
+    value_count = asdl_seq_LEN(values);
+    for (i = 0; i < value_count; ++i) {
         if (-1 == append_fstring_element(&body_writer,
                                          (expr_ty)asdl_seq_GET(values, i),
                                          is_format_spec
@@ -819,9 +631,11 @@ append_joinedstr(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
 static int
 append_formattedvalue(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
 {
-    char *conversion;
-    char *outer_brace = "{";
-    PyObject *temp_fv_str = expr_as_unicode(e->v.FormattedValue.value, true);
+    const char *conversion;
+    const char *outer_brace = "{";
+    /* Grammar allows PR_TUPLE, but use >PR_TEST for adding parenthesis
+       around a lambda with ':' */
+    PyObject *temp_fv_str = expr_as_unicode(e->v.FormattedValue.value, PR_TEST + 1);
     if (!temp_fv_str) {
         return -1;
     }
@@ -842,13 +656,13 @@ append_formattedvalue(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
 
     if (e->v.FormattedValue.conversion > 0) {
         switch (e->v.FormattedValue.conversion) {
-        case 97:
+        case 'a':
             conversion = "!a";
             break;
-        case 114:
+        case 'r':
             conversion = "!r";
             break;
-        case 115:
+        case 's':
             conversion = "!s";
             break;
         default:
@@ -856,9 +670,7 @@ append_formattedvalue(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
                             "unknown f-value conversion kind");
             return -1;
         }
-        if (-1 == append_charp(writer, conversion)) {
-            return -1;
-        }
+        APPEND_STR(conversion);
     }
     if (e->v.FormattedValue.format_spec) {
         if (-1 == _PyUnicodeWriter_WriteASCIIString(writer, ":", 1) ||
@@ -870,16 +682,15 @@ append_formattedvalue(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
             return -1;
         }
     }
-    return append_charp(writer, "}");
+
+    APPEND_STR_FINISH("}");
 }
 
 static int
 append_ast_attribute(_PyUnicodeWriter *writer, expr_ty e)
 {
     const char *period;
-    if (-1 == append_ast_expr(writer, e->v.Attribute.value, false)) {
-        return -1;
-    }
+    APPEND_EXPR(e->v.Attribute.value, 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. */
@@ -891,9 +702,7 @@ append_ast_attribute(_PyUnicodeWriter *writer, expr_ty e)
     else {
         period = ".";
     }
-    if (-1 == append_charp(writer, period)) {
-        return -1;
-    }
+    APPEND_STR(period);
 
     return _PyUnicodeWriter_WriteStr(writer, e->v.Attribute.attr);
 }
@@ -902,28 +711,18 @@ static int
 append_ast_simple_slice(_PyUnicodeWriter *writer, slice_ty slice)
 {
     if (slice->v.Slice.lower) {
-        if (-1 == append_ast_expr(writer, slice->v.Slice.lower, false)) {
-            return -1;
-        }
+        APPEND_EXPR(slice->v.Slice.lower, PR_TEST);
     }
 
-    if (-1 == append_charp(writer, ":")) {
-        return -1;
-    }
+    APPEND_STR(":");
 
     if (slice->v.Slice.upper) {
-        if (-1 == append_ast_expr(writer, slice->v.Slice.upper, false)) {
-            return -1;
-        }
+        APPEND_EXPR(slice->v.Slice.upper, PR_TEST);
     }
 
     if (slice->v.Slice.step) {
-        if (-1 == append_charp(writer, ":")) {
-            return -1;
-        }
-        if (-1 == append_ast_expr(writer, slice->v.Slice.step, false)) {
-            return -1;
-        }
+        APPEND_STR(":");
+        APPEND_EXPR(slice->v.Slice.step, PR_TEST);
     }
     return 0;
 }
@@ -934,28 +733,23 @@ append_ast_ext_slice(_PyUnicodeWriter *writer, slice_ty slice)
     Py_ssize_t i, dims_count;
     dims_count = asdl_seq_LEN(slice->v.ExtSlice.dims);
     for (i = 0; i < dims_count; i++) {
-        if (i > 0 && -1 == append_charp(writer, ", ")) {
-            return -1;
-        }
-        if (-1 == append_ast_expr(writer,
-                                  (expr_ty)asdl_seq_GET(slice->v.ExtSlice.dims, i),
-                                  false)) {
-            return -1;
-        }
+        APPEND_STR_IF(i > 0, ", ");
+        APPEND(slice, (slice_ty)asdl_seq_GET(slice->v.ExtSlice.dims, i));
     }
     return 0;
 }
 
 static int
-append_ast_slice(_PyUnicodeWriter *writer, slice_ty slice, bool omit_parens)
+append_ast_slice(_PyUnicodeWriter *writer, slice_ty slice)
 {
-    switch(slice->kind) {
+    switch (slice->kind) {
     case Slice_kind:
         return append_ast_simple_slice(writer, slice);
     case ExtSlice_kind:
         return append_ast_ext_slice(writer, slice);
     case Index_kind:
-        return append_ast_expr(writer, slice->v.Index.value, omit_parens);
+        APPEND_EXPR(slice->v.Index.value, PR_TUPLE);
+        return 0;
     default:
         PyErr_SetString(PyExc_SystemError,
                         "unexpected slice kind");
@@ -966,109 +760,70 @@ append_ast_slice(_PyUnicodeWriter *writer, slice_ty slice, bool omit_parens)
 static int
 append_ast_subscript(_PyUnicodeWriter *writer, expr_ty e)
 {
-    if (-1 == append_ast_expr(writer, e->v.Subscript.value, false)) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, "[")) {
-        return -1;
-    }
-
-    if (-1 == append_ast_slice(writer, e->v.Subscript.slice, true)) {
-        return -1;
-    }
-
-    return append_charp(writer, "]");
+    APPEND_EXPR(e->v.Subscript.value, PR_ATOM);
+    APPEND_STR("[");
+    APPEND(slice, e->v.Subscript.slice);
+    APPEND_STR_FINISH("]");
 }
 
 static int
 append_ast_starred(_PyUnicodeWriter *writer, expr_ty e)
 {
-    if (-1 == append_charp(writer, "*")) {
-        return -1;
-    }
-
-    return append_ast_expr(writer, e->v.Starred.value, false);
+    APPEND_STR("*");
+    APPEND_EXPR(e->v.Starred.value, PR_EXPR);
+    return 0;
 }
 
 static int
-append_ast_yield(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_yield(_PyUnicodeWriter *writer, expr_ty e)
 {
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, e->v.Yield.value ? "yield " : "yield")) {
-        return -1;
+    if (!e->v.Yield.value) {
+        APPEND_STR_FINISH("(yield)");
     }
 
-    if (e->v.Yield.value) {
-        if (-1 == append_ast_expr(writer, e->v.Yield.value, false)) {
-            return -1;
-        }
-    }
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR("(yield ");
+    APPEND_EXPR(e->v.Yield.value, PR_TEST);
+    APPEND_STR_FINISH(")");
 }
 
 static int
-append_ast_yield_from(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_yield_from(_PyUnicodeWriter *writer, expr_ty e)
 {
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer,
-                           e->v.YieldFrom.value ? "yield from " : "yield from")) {
-        return -1;
-    }
-
-    if (e->v.YieldFrom.value) {
-        if (-1 == append_ast_expr(writer, e->v.YieldFrom.value, false)) {
-            return -1;
-        }
-    }
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR("(yield from ");
+    APPEND_EXPR(e->v.YieldFrom.value, PR_TEST);
+    APPEND_STR_FINISH(")");
 }
 
 static int
-append_ast_await(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_await(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
-    if (!omit_parens && -1 == append_charp(writer, "(")) {
-        return -1;
-    }
-
-    if (-1 == append_charp(writer, e->v.Await.value ? "await " : "await")) {
-        return -1;
-    }
-
-    if (e->v.Await.value) {
-        if (-1 == append_ast_expr(writer, e->v.Await.value, false)) {
-            return -1;
-        }
-    }
-    return omit_parens ? 0 : append_charp(writer, ")");
+    APPEND_STR_IF(level > PR_AWAIT, "(");
+    APPEND_STR("await ");
+    APPEND_EXPR(e->v.Await.value, PR_ATOM);
+    APPEND_STR_IF(level > PR_AWAIT, ")");
+    return 0;
 }
 
 static int
-append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, int level)
 {
     switch (e->kind) {
     case BoolOp_kind:
-        return append_ast_boolop(writer, e, omit_parens);
+        return append_ast_boolop(writer, e, level);
     case BinOp_kind:
-        return append_ast_binop(writer, e, omit_parens);
+        return append_ast_binop(writer, e, level);
     case UnaryOp_kind:
-        return append_ast_unaryop(writer, e, omit_parens);
+        return append_ast_unaryop(writer, e, level);
     case Lambda_kind:
-        return append_ast_lambda(writer, e, omit_parens);
+        return append_ast_lambda(writer, e, level);
     case IfExp_kind:
-        return append_ast_ifexp(writer, e, omit_parens);
+        return append_ast_ifexp(writer, e, level);
     case Dict_kind:
         return append_ast_dict(writer, e);
     case Set_kind:
         return append_ast_set(writer, e);
     case GeneratorExp_kind:
-        return append_ast_genexp(writer, e, omit_parens);
+        return append_ast_genexp(writer, e);
     case ListComp_kind:
         return append_ast_listcomp(writer, e);
     case SetComp_kind:
@@ -1076,13 +831,13 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
     case DictComp_kind:
         return append_ast_dictcomp(writer, e);
     case Yield_kind:
-        return append_ast_yield(writer, e, omit_parens);
+        return append_ast_yield(writer, e);
     case YieldFrom_kind:
-        return append_ast_yield_from(writer, e, omit_parens);
+        return append_ast_yield_from(writer, e);
     case Await_kind:
-        return append_ast_await(writer, e, omit_parens);
+        return append_ast_await(writer, e, level);
     case Compare_kind:
-        return append_ast_compare(writer, e, omit_parens);
+        return append_ast_compare(writer, e, level);
     case Call_kind:
         return append_ast_call(writer, e);
     case Constant_kind:
@@ -1098,7 +853,7 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
     case Bytes_kind:
         return append_repr(writer, e->v.Bytes.s);
     case Ellipsis_kind:
-        return append_charp(writer, "...");
+        APPEND_STR_FINISH("...");
     case NameConstant_kind:
         return append_repr(writer, e->v.NameConstant.value);
     /* The following exprs can be assignment targets. */
@@ -1110,11 +865,10 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
         return append_ast_starred(writer, e);
     case Name_kind:
         return _PyUnicodeWriter_WriteStr(writer, e->v.Name.id);
-    /* child nodes of List and Tuple will have expr_context set */
     case List_kind:
         return append_ast_list(writer, e);
     case Tuple_kind:
-        return append_ast_tuple(writer, e, omit_parens);
+        return append_ast_tuple(writer, e, level);
     default:
         PyErr_SetString(PyExc_SystemError,
                         "unknown expression kind");
@@ -1145,14 +899,14 @@ maybe_init_static_strings(void)
 }
 
 static PyObject *
-expr_as_unicode(expr_ty e, bool omit_parens)
+expr_as_unicode(expr_ty e, int level)
 {
     _PyUnicodeWriter writer;
     _PyUnicodeWriter_Init(&writer);
     writer.min_length = 256;
     writer.overallocate = 1;
     if (-1 == maybe_init_static_strings() ||
-        -1 == append_ast_expr(&writer, e, omit_parens))
+        -1 == append_ast_expr(&writer, e, level))
     {
         _PyUnicodeWriter_Dealloc(&writer);
         return NULL;
@@ -1161,7 +915,7 @@ expr_as_unicode(expr_ty e, bool omit_parens)
 }
 
 PyObject *
-_PyAST_ExprAsUnicode(expr_ty e, bool omit_parens)
+_PyAST_ExprAsUnicode(expr_ty e)
 {
-    return expr_as_unicode(e, omit_parens);
+    return expr_as_unicode(e, PR_TEST);
 }
diff --git a/Python/compile.c b/Python/compile.c
index 7960f09319e7..c3397f0c455d 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1822,7 +1822,7 @@ compiler_visit_kwonlydefaults(struct compiler *c, asdl_seq *kwonlyargs,
 static int
 compiler_visit_annexpr(struct compiler *c, expr_ty annotation)
 {
-    ADDOP_LOAD_CONST_NEW(c, _PyAST_ExprAsUnicode(annotation, 1));
+    ADDOP_LOAD_CONST_NEW(c, _PyAST_ExprAsUnicode(annotation));
     return 1;
 }
 



More information about the Python-checkins mailing list