]> granicus.if.org Git - python/commitdiff
String annotations [PEP 563] (#4390)
authorGuido van Rossum <guido@python.org>
Fri, 26 Jan 2018 16:20:18 +0000 (08:20 -0800)
committerŁukasz Langa <lukasz@langa.pl>
Fri, 26 Jan 2018 16:20:18 +0000 (08:20 -0800)
* Document `from __future__ import annotations`
* Provide plumbing and tests for `from __future__ import annotations`
* Implement unparsing the AST back to string form

This is required for PEP 563 and as such only implements a part of the
unparsing process that covers expressions.

16 files changed:
Doc/glossary.rst
Doc/library/__future__.rst
Doc/reference/compound_stmts.rst
Doc/reference/simple_stmts.rst
Doc/whatsnew/3.7.rst
Include/ast.h
Include/code.h
Include/compile.h
Lib/__future__.py
Lib/test/test_future.py
Makefile.pre.in
PCbuild/pythoncore.vcxproj
PCbuild/pythoncore.vcxproj.filters
Python/ast_unparse.c [new file with mode: 0644]
Python/compile.c
Python/future.c

index f0fcb6273e0b23062a5ef6c7c822d06412509a41..dcfe086b38b12ff0fbd1fb5ff5763086db5811b3 100644 (file)
@@ -372,9 +372,11 @@ Glossary
       may be accessed via the :attr:`__annotations__` special attribute of a
       function object.
 
-      Python itself does not assign any particular meaning to function
-      annotations. They are intended to be interpreted by third-party libraries
-      or tools.  See :pep:`3107`, which describes some of their potential uses.
+      See also the :term:`variable annotation` glossary entry.
+
+      Annotations are meant to provide a standard way for programmers to
+      document types of functions they design.  See :pep:`484`, which
+      describes this functionality.
 
    __future__
       A pseudo-module which programmers can use to enable new language features
@@ -1021,10 +1023,11 @@ Glossary
       attribute of a class or module object and can be accessed using
       :func:`typing.get_type_hints`.
 
-      Python itself does not assign any particular meaning to variable
-      annotations. They are intended to be interpreted by third-party libraries
-      or type checking tools. See :pep:`526`, :pep:`484` which describe
-      some of their potential uses.
+      See also the :term:`function annotation` glossary entry.
+
+      Annotations are meant to provide a standard way for programmers to
+      document types of functions they design.  See :pep:`484` and :pep:`526`
+      which describe this functionality.
 
    virtual environment
       A cooperatively isolated runtime environment that allows Python users
index 73d8b6b7e8aff025e6cc552fa7dcdc5ea39a744f..e3d749e60178478fe8645fa9b6ad5465b449ef8f 100644 (file)
@@ -90,6 +90,11 @@ language using this mechanism:
 | generator_stop   | 3.5.0b1     | 3.7          | :pep:`479`:                                 |
 |                  |             |              | *StopIteration handling inside generators*  |
 +------------------+-------------+--------------+---------------------------------------------+
+| annotations      | 3.7.0b1     | 4.0          | :pep:`563`:                                 |
+|                  |             |              | *Postponed evaluation of annotations*       |
++------------------+-------------+--------------+---------------------------------------------+
+
+.. XXX Adding a new entry?  Remember to update simple_stmts.rst, too.
 
 
 .. seealso::
index dca9362400455c87bc53fa1b9e93896971d417b2..d7792f1eaf825fc067d17a3a13e286411d6f578b 100644 (file)
@@ -559,12 +559,14 @@ Parameters may have annotations of the form "``: expression``" following the
 parameter name.  Any parameter may have an annotation even those of the form
 ``*identifier`` or ``**identifier``.  Functions may have "return" annotation of
 the form "``-> expression``" after the parameter list.  These annotations can be
-any valid Python expression and are evaluated when the function definition is
-executed.  Annotations may be evaluated in a different order than they appear in
-the source code.  The presence of annotations does not change the semantics of a
-function.  The annotation values are available as values of a dictionary keyed
-by the parameters' names in the :attr:`__annotations__` attribute of the
-function object.
+any valid Python expression.  The presence of annotations does not change the
+semantics of a function.  The annotation values are available as values of
+a dictionary keyed by the parameters' names in the :attr:`__annotations__`
+attribute of the function object.  If the ``annotations`` import from
+:mod:`__future__` is used, annotations are preserved as strings at runtime which
+enables postponed evaluation.  Otherwise, they are evaluated when the function
+definition is executed.  In this case annotations may be evaluated in
+a different order than they appear in the source code.
 
 .. index:: pair: lambda; expression
 
@@ -587,6 +589,17 @@ access the local variables of the function containing the def.  See section
    :pep:`3107` - Function Annotations
       The original specification for function annotations.
 
+   :pep:`484` - Type Hints
+      Definition of a standard meaning for annotations: type hints.
+
+   :pep:`526` - Syntax for Variable Annotations
+      Ability to type hint variable declarations, including class
+      variables and instance variables
+
+   :pep:`563` - Postponed Evaluation of Annotations
+      Support for forward references within annotations by preserving
+      annotations in a string form at runtime instead of eager evaluation.
+
 
 .. _class:
 
index 8d17383853a3c4ac6e3961d147eec12137aadb38..ef9a5f0dc85422d226ce59b0c648717c02003d18 100644 (file)
@@ -853,12 +853,15 @@ can appear before a future statement are:
 * blank lines, and
 * other future statements.
 
-.. XXX change this if future is cleaned out
-
-The features recognized by Python 3.0 are ``absolute_import``, ``division``,
-``generators``, ``unicode_literals``, ``print_function``, ``nested_scopes`` and
-``with_statement``.  They are all redundant because they are always enabled, and
-only kept for backwards compatibility.
+The only feature in Python 3.7 that requires using the future statement is
+``annotations``.
+
+All historical features enabled by the future statement are still recognized
+by Python 3.  The list includes ``absolute_import``, ``division``,
+``generators``, ``generator_stop``, ``unicode_literals``,
+``print_function``, ``nested_scopes`` and ``with_statement``.  They are
+all redundant because they are always enabled, and only kept for
+backwards compatibility.
 
 A future statement is recognized and treated specially at compile time: Changes
 to the semantics of core constructs are often implemented by generating
index 0e1714ea17e732e4f79c7d83c0e28ee64ebae9d5..a350919bb34fc62fc042966762f5291c3106cd06 100644 (file)
@@ -179,6 +179,57 @@ a normal ``__getattr__`` method, except that it will be defined on module
        PEP written and implemented by Ivan Levkivskyi
 
 
+PEP 563: Postponed evaluation of annotations
+--------------------------------------------
+
+The advent of type hints in Python uncovered two glaring usability issues
+with the functionality of annotations added in :pep:`3107` and refined
+further in :pep:`526`:
+
+* annotations could only use names which were already available in the
+  current scope, in other words they didn't support forward references
+  of any kind; and
+
+* annotating source code had adverse effects on startup time of Python
+  programs.
+
+Both of these issues are fixed by postponing the evaluation of
+annotations.  Instead of compiling code which executes expressions in
+annotations at their definition time, the compiler stores the annotation
+in a string form equivalent to the AST of the expression in question.
+If needed, annotations can be resolved at runtime using
+``typing.get_type_hints()``.  In the common case where this is not
+required, the annotations are cheaper to store (since short strings
+are interned by the interpreter) and make startup time faster.
+
+Usability-wise, annotations now support forward references, making the
+following syntax valid::
+
+    class C:
+        @classmethod
+        def from_string(cls, source: str) -> C:
+            ...
+
+        def validate_b(self, obj: B) -> bool:
+            ...
+
+    class B:
+        ...
+
+Since this change breaks compatibility, the new behavior can be enabled
+on a per-module basis in Python 3.7 using a ``__future__`` import, like
+this::
+
+    from __future__ import annotations
+
+It will become the default in Python 4.0.
+
+.. seealso::
+
+    :pep:`563` -- Postponed evaluation of annotations
+       PEP written and implemented by Łukasz Langa.
+
+
 PEP 564: Add new time functions with nanosecond resolution
 ----------------------------------------------------------
 
index 6a8c8165c0550f80c4acb606b10a6d0d9c322e34..639c4f82325a6b335f7432176a6915728570ada0 100644 (file)
@@ -16,6 +16,15 @@ PyAPI_FUNC(mod_ty) PyAST_FromNodeObject(
     PyObject *filename,
     PyArena *arena);
 
+#ifndef Py_LIMITED_API
+
+/* _PyAST_ExprAsUnicode is defined in ast_unparse.c */
+PyAPI_FUNC(PyObject *) _PyAST_ExprAsUnicode(
+    expr_ty e,
+    int omit_parens);
+
+#endif /* !Py_LIMITED_API */
+
 #ifdef __cplusplus
 }
 #endif
index 8b0f84042372d9c6b15e31629789bc2207ff094d..2e661e8b36b7f73b0a7fea64b87697ed837334f9 100644 (file)
@@ -82,6 +82,7 @@ typedef struct {
 
 #define CO_FUTURE_BARRY_AS_BDFL  0x40000
 #define CO_FUTURE_GENERATOR_STOP  0x80000
+#define CO_FUTURE_ANNOTATIONS    0x100000
 
 /* This value is found in the co_cell2arg array when the associated cell
    variable does not correspond to an argument. */
index edfd8bbfac69feae54b6c20daf058e0cd0491676..edb961f4d72042fb177d84effb0d94c5bfb33304 100644 (file)
@@ -16,7 +16,7 @@ PyAPI_FUNC(PyCodeObject *) PyNode_Compile(struct _node *, const char *);
 #define PyCF_MASK (CO_FUTURE_DIVISION | CO_FUTURE_ABSOLUTE_IMPORT | \
                    CO_FUTURE_WITH_STATEMENT | CO_FUTURE_PRINT_FUNCTION | \
                    CO_FUTURE_UNICODE_LITERALS | CO_FUTURE_BARRY_AS_BDFL | \
-                   CO_FUTURE_GENERATOR_STOP)
+                   CO_FUTURE_GENERATOR_STOP | CO_FUTURE_ANNOTATIONS)
 #define PyCF_MASK_OBSOLETE (CO_NESTED)
 #define PyCF_SOURCE_IS_UTF8  0x0100
 #define PyCF_DONT_IMPLY_DEDENT 0x0200
@@ -45,6 +45,7 @@ typedef struct {
 #define FUTURE_UNICODE_LITERALS "unicode_literals"
 #define FUTURE_BARRY_AS_BDFL "barry_as_FLUFL"
 #define FUTURE_GENERATOR_STOP "generator_stop"
+#define FUTURE_ANNOTATIONS "annotations"
 
 struct _mod; /* Declare the existence of this type */
 #define PyAST_Compile(mod, s, f, ar) PyAST_CompileEx(mod, s, f, -1, ar)
index 63b2be3524fc7c4aeaed33445591dbcfaa19014a..ce8bed7a643cdd48e09af501bcef88f98786dc06 100644 (file)
@@ -57,13 +57,14 @@ all_feature_names = [
     "unicode_literals",
     "barry_as_FLUFL",
     "generator_stop",
+    "annotations",
 ]
 
 __all__ = ["all_feature_names"] + all_feature_names
 
-# The CO_xxx symbols are defined here under the same names used by
-# compile.h, so that an editor search will find them here.  However,
-# they're not exported in __all__, because they don't really belong to
+# The CO_xxx symbols are defined here under the same names defined in
+# code.h and used by compile.h, so that an editor search will find them here.
+# However, they're not exported in __all__, because they don't really belong to
 # this module.
 CO_NESTED            = 0x0010   # nested_scopes
 CO_GENERATOR_ALLOWED = 0        # generators (obsolete, was 0x1000)
@@ -74,6 +75,7 @@ CO_FUTURE_PRINT_FUNCTION  = 0x10000   # print function
 CO_FUTURE_UNICODE_LITERALS = 0x20000 # unicode string literals
 CO_FUTURE_BARRY_AS_BDFL = 0x40000
 CO_FUTURE_GENERATOR_STOP  = 0x80000 # StopIteration becomes RuntimeError in generators
+CO_FUTURE_ANNOTATIONS     = 0x100000  # annotations become strings at runtime
 
 class _Feature:
     def __init__(self, optionalRelease, mandatoryRelease, compiler_flag):
@@ -132,9 +134,13 @@ unicode_literals = _Feature((2, 6, 0, "alpha", 2),
                             CO_FUTURE_UNICODE_LITERALS)
 
 barry_as_FLUFL = _Feature((3, 1, 0, "alpha", 2),
-                         (3, 9, 0, "alpha", 0),
-                         CO_FUTURE_BARRY_AS_BDFL)
+                          (3, 9, 0, "alpha", 0),
+                          CO_FUTURE_BARRY_AS_BDFL)
 
 generator_stop = _Feature((3, 5, 0, "beta", 1),
-                         (3, 7, 0, "alpha", 0),
-                         CO_FUTURE_GENERATOR_STOP)
+                          (3, 7, 0, "alpha", 0),
+                          CO_FUTURE_GENERATOR_STOP)
+
+annotations = _Feature((3, 7, 0, "beta", 1),
+                       (4, 0, 0, "alpha", 0),
+                       CO_FUTURE_ANNOTATIONS)
index 2f1c410b1bfa255dd6200d30a88a4a6fb3d58b8e..29c4632e7e504406eda68b75a85a12e6fa82c90f 100644 (file)
@@ -1,7 +1,9 @@
 # Test various flavors of legal and illegal future statements
 
+from functools import partial
 import unittest
 from test import support
+from textwrap import dedent
 import os
 import re
 
@@ -102,6 +104,167 @@ class FutureTest(unittest.TestCase):
         exec("from __future__ import unicode_literals; x = ''", {}, scope)
         self.assertIsInstance(scope["x"], str)
 
+class AnnotationsFutureTestCase(unittest.TestCase):
+    template = dedent(
+        """
+        from __future__ import annotations
+        def f() -> {ann}:
+            ...
+        def g(arg: {ann}) -> None:
+            ...
+        var: {ann}
+        var2: {ann} = None
+        """
+    )
+
+    def getActual(self, annotation):
+        scope = {}
+        exec(self.template.format(ann=annotation), {}, scope)
+        func_ret_ann = scope['f'].__annotations__['return']
+        func_arg_ann = scope['g'].__annotations__['arg']
+        var_ann1 = scope['__annotations__']['var']
+        var_ann2 = scope['__annotations__']['var2']
+        self.assertEqual(func_ret_ann, func_arg_ann)
+        self.assertEqual(func_ret_ann, var_ann1)
+        self.assertEqual(func_ret_ann, var_ann2)
+        return func_ret_ann
+
+    def assertAnnotationEqual(
+        self, annotation, expected=None, drop_parens=False, is_tuple=False,
+    ):
+        actual = self.getActual(annotation)
+        if expected is None:
+            expected = annotation if not is_tuple else annotation[1:-1]
+        if drop_parens:
+            self.assertNotEqual(actual, expected)
+            actual = actual.replace("(", "").replace(")", "")
+
+        self.assertEqual(actual, expected)
+
+    def test_annotations(self):
+        eq = self.assertAnnotationEqual
+        eq('...')
+        eq("'some_string'")
+        eq("b'\\xa3'")
+        eq('Name')
+        eq('None')
+        eq('True')
+        eq('False')
+        eq('1')
+        eq('1.0')
+        eq('1j')
+        eq('True or False')
+        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('v1 << 2')
+        eq('1 >> v2')
+        eq(r'1 % finished')
+        eq('((1 + v2) - (v3 * 4)) ^ (((5 ** v6) / 7) // 8)')
+        eq('not great')
+        eq('~great')
+        eq('+value')
+        eq('-1')
+        eq('(~int) and (not ((v1 ^ (123 + v2)) | True))')
+        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('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("{**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("()")
+        eq("(1,)")
+        eq("(1, 2)")
+        eq("(1, 2, 3)")
+        eq("[]")
+        eq("[1, 2, 3, 4, 5, 6, 7, 8, 9, (10 or A), (11 or B), (12 or 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 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: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}")
+        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("lukasz.langa.pl")
+        eq("call.me(maybe)")
+        eq("1 .real")
+        eq("1.0 .real")
+        eq("....__class__")
+        eq("list[str]")
+        eq("dict[str, int]")
+        eq("tuple[str, ...]")
+        eq("tuple[str, int, float, dict[str, int]]")
+        eq("slice[0]")
+        eq("slice[0:1]")
+        eq("slice[0:1:2]")
+        eq("slice[:]")
+        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("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('(yield from outside_of_generator)')
+        eq('(yield)')
+        eq('(await some.complicated[0].call(with_args=(True or (1 is not 1))))')
 
 
 if __name__ == "__main__":
index 162006a2f9c77ce38a9646b002baa8d29b29cf0b..f58b715378876a16057f6bf32b8e5247b022a27f 100644 (file)
@@ -325,6 +325,7 @@ PYTHON_OBJS=        \
                Python/asdl.o \
                Python/ast.o \
                Python/ast_opt.o \
+               Python/ast_unparse.o \
                Python/bltinmodule.o \
                Python/ceval.o \
                Python/compile.o \
@@ -840,7 +841,7 @@ regen-opcode:
                $(srcdir)/Include/opcode.h.new
        $(UPDATE_FILE) $(srcdir)/Include/opcode.h $(srcdir)/Include/opcode.h.new
 
-Python/compile.o Python/symtable.o Python/ast.o: $(srcdir)/Include/graminit.h $(srcdir)/Include/Python-ast.h
+Python/compile.o Python/symtable.o Python/ast_unparse.o Python/ast.o: $(srcdir)/Include/graminit.h $(srcdir)/Include/Python-ast.h
 
 Python/getplatform.o: $(srcdir)/Python/getplatform.c
                $(CC) -c $(PY_CORE_CFLAGS) -DPLATFORM='"$(MACHDEP)"' -o $@ $(srcdir)/Python/getplatform.c
index fbcd0512139b9a50b7d5c7edaac120e22828860f..f30945508698afa36ff628a128269109d2138e61 100644 (file)
     <ClCompile Include="..\Python\asdl.c" />
     <ClCompile Include="..\Python\ast.c" />
     <ClCompile Include="..\Python\ast_opt.c" />
+    <ClCompile Include="..\Python\ast_unparse.c" />
     <ClCompile Include="..\Python\bltinmodule.c" />
     <ClCompile Include="..\Python\bootstrap_hash.c" />
     <ClCompile Include="..\Python\ceval.c" />
index a10686c194b71ad57227a41ee9cd2c3ec7b21ac1..5f980388e5f321724648fb0800393b7ab849454a 100644 (file)
     <ClCompile Include="..\Python\ast_opt.c">
       <Filter>Python</Filter>
     </ClCompile>
+    <ClCompile Include="..\Python\ast_unparse.c">
+      <Filter>Python</Filter>
+    </ClCompile>
     <ClCompile Include="..\Python\bltinmodule.c">
       <Filter>Python</Filter>
     </ClCompile>
diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c
new file mode 100644 (file)
index 0000000..ef9e948
--- /dev/null
@@ -0,0 +1,1163 @@
+#include <stdbool.h>
+#include "Python.h"
+#include "Python-ast.h"
+
+static PyObject *_str_open_br;
+static PyObject *_str_dbl_open_br;
+static PyObject *_str_close_br;
+static PyObject *_str_dbl_close_br;
+
+/* Forward declarations for recursion via helper functions. */
+static PyObject *
+expr_as_unicode(expr_ty e, bool omit_parens);
+static int
+append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens);
+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_charp(_PyUnicodeWriter *writer, const char *charp)
+{
+        return _PyUnicodeWriter_WriteASCIIString(writer, charp, -1);
+}
+
+static int
+append_repr(_PyUnicodeWriter *writer, PyObject *obj)
+{
+    int ret;
+    PyObject *repr;
+    repr = PyObject_Repr(obj);
+    if (!repr) {
+        return -1;
+    }
+    ret = _PyUnicodeWriter_WriteStr(writer, repr);
+    Py_DECREF(repr);
+    return ret;
+}
+
+static int
+append_ast_boolop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    Py_ssize_t i, value_count;
+    asdl_seq *values;
+
+    if (!omit_parens && -1 == append_charp(writer, "(")) {
+        return -1;
+    }
+
+    values = e->v.BoolOp.values;
+    value_count = asdl_seq_LEN(values) - 1;
+    assert(value_count >= 0);
+
+    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;
+        }
+    }
+
+    return omit_parens ? 0 : append_charp(writer, ")");
+}
+
+static int
+append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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;
+    }
+
+    if (-1 == append_charp(writer, op)) {
+        return -1;
+    }
+
+    if (-1 == append_ast_expr(writer, e->v.BinOp.right, false)) {
+        return -1;
+    }
+
+    return omit_parens ? 0 : append_charp(writer, ")");
+}
+
+static int
+append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    const char *op;
+
+    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;
+    }
+
+    if (-1 == append_charp(writer, op)) {
+        return -1;
+    }
+
+    if (-1 == append_ast_expr(writer, e->v.UnaryOp.operand, false)) {
+        return -1;
+    }
+
+    return omit_parens ? 0 : append_charp(writer, ")");
+}
+
+static int
+append_ast_arg(_PyUnicodeWriter *writer, arg_ty arg)
+{
+    if (-1 == _PyUnicodeWriter_WriteStr(writer, arg->arg)) {
+        return -1;
+    }
+    if (arg->annotation) {
+        if (-1 == append_charp(writer, ": ")) {
+            return -1;
+        }
+        if (-1 == append_ast_expr(writer, arg->annotation, true)) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+static int
+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;
+
+    /* positional arguments with defaults */
+    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;
+        }
+
+        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;
+            }
+        }
+    }
+
+    /* 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;
+        }
+
+        if (args->vararg) {
+            if (-1 == append_ast_arg(writer, args->vararg)) {
+                return -1;
+            }
+        }
+    }
+
+    /* keyword-only arguments */
+    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;
+        }
+
+        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;
+            }
+        }
+    }
+
+    /* **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;
+        }
+    }
+
+    return 0;
+}
+
+static int
+append_ast_lambda(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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, ")");
+}
+
+static int
+append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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, ")");
+}
+
+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;
+    }
+
+    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;
+        }
+        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;
+        }
+
+        value_node = (expr_ty)asdl_seq_GET(e->v.Dict.values, i);
+        if (-1 == append_ast_expr(writer, value_node, false)) {
+            return -1;
+        }
+    }
+
+    return append_charp(writer, "}");
+}
+
+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;
+    }
+
+    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;
+        }
+    }
+
+    return append_charp(writer, "}");
+}
+
+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;
+    }
+
+    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;
+        }
+    }
+
+    return append_charp(writer, "]");
+}
+
+static int
+append_ast_tuple(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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;
+        }
+    }
+
+    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;
+        }
+    }
+
+    if (!omit_parens || elem_count < 2) {
+        return append_charp(writer, ")");
+    }
+
+    return 0;
+}
+
+static int
+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;
+    }
+
+    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;
+        }
+    }
+    return 0;
+}
+
+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;
+        }
+    }
+
+    return 0;
+}
+
+static int
+append_ast_genexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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, ")");
+}
+
+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, "]");
+}
+
+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, "}");
+}
+
+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, "}");
+}
+
+static int
+append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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;
+    }
+
+    comparators = e->v.Compare.comparators;
+    ops = e->v.Compare.ops;
+    comparator_count = asdl_seq_LEN(comparators);
+    assert(comparator_count > 0);
+    assert(comparator_count == asdl_seq_LEN(ops));
+
+    if (-1 == append_ast_expr(writer, e->v.Compare.left, false)) {
+        return -1;
+    }
+
+    for (i = 0; i < comparator_count; i++) {
+        switch ((cmpop_ty)asdl_seq_GET(ops, i)) {
+        case Eq:
+            op = " == ";
+            break;
+        case NotEq:
+            op = " != ";
+            break;
+        case Lt:
+            op = " < ";
+            break;
+        case LtE:
+            op = " <= ";
+            break;
+        case Gt:
+            op = " > ";
+            break;
+        case GtE:
+            op = " >= ";
+            break;
+        case Is:
+            op = " is ";
+            break;
+        case IsNot:
+            op = " is not ";
+            break;
+        case In:
+            op = " in ";
+            break;
+        case NotIn:
+            op = " not in ";
+            break;
+        default:
+            PyErr_SetString(PyExc_SystemError,
+                            "unexpected comparison kind");
+            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;
+        }
+    }
+
+    return omit_parens ? 0 : append_charp(writer, ")");
+}
+
+static int
+append_ast_keyword(_PyUnicodeWriter *writer, keyword_ty kw)
+{
+    if (kw->arg == NULL) {
+        if (-1 == append_charp(writer, "**")) {
+            return -1;
+        }
+    }
+    else {
+        if (-1 == _PyUnicodeWriter_WriteStr(writer, kw->arg)) {
+            return -1;
+        }
+
+        if (-1 == append_charp(writer, "=")) {
+            return -1;
+        }
+    }
+
+    return append_ast_expr(writer, kw->value, false);
+}
+
+static int
+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;
+    }
+
+    if (-1 == append_charp(writer, "(")) {
+        return -1;
+    }
+
+    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;
+        }
+    }
+
+    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;
+        }
+    }
+
+    return append_charp(writer, ")");
+}
+
+static PyObject *
+escape_braces(PyObject *orig)
+{
+    PyObject *temp;
+    PyObject *result;
+    temp = PyUnicode_Replace(orig, _str_open_br, _str_dbl_open_br, -1);
+    if (!temp) {
+        return NULL;
+    }
+    result = PyUnicode_Replace(temp, _str_close_br, _str_dbl_close_br, -1);
+    Py_DECREF(temp);
+    return result;
+}
+
+static int
+append_fstring_unicode(_PyUnicodeWriter *writer, PyObject *unicode)
+{
+    PyObject *escaped;
+    int result = -1;
+    escaped = escape_braces(unicode);
+    if (escaped) {
+        result = _PyUnicodeWriter_WriteStr(writer, escaped);
+        Py_DECREF(escaped);
+    }
+    return result;
+}
+
+static int
+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:
+        return append_formattedvalue(writer, e, is_format_spec);
+    default:
+        PyErr_SetString(PyExc_SystemError,
+                        "unknown expression kind inside f-string");
+        return -1;
+    }
+}
+
+/* Build body separately to enable wrapping the entire stream of Strs,
+   Constants and FormattedValues in one opening and one closing quote. */
+static PyObject *
+build_fstring_body(asdl_seq *values, bool is_format_spec)
+{
+    Py_ssize_t i, value_count;
+    _PyUnicodeWriter body_writer;
+    _PyUnicodeWriter_Init(&body_writer);
+    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) {
+        if (-1 == append_fstring_element(&body_writer,
+                                         (expr_ty)asdl_seq_GET(values, i),
+                                         is_format_spec
+                                         )) {
+            _PyUnicodeWriter_Dealloc(&body_writer);
+            return NULL;
+        }
+    }
+
+    return _PyUnicodeWriter_Finish(&body_writer);
+}
+
+static int
+append_joinedstr(_PyUnicodeWriter *writer, expr_ty e, bool is_format_spec)
+{
+    int result = -1;
+    PyObject *body = build_fstring_body(e->v.JoinedStr.values, is_format_spec);
+    if (!body) {
+        return -1;
+    }
+
+    if (!is_format_spec) {
+        if (-1 != append_charp(writer, "f") &&
+            -1 != append_repr(writer, body))
+        {
+            result = 0;
+        }
+    }
+    else {
+        result = _PyUnicodeWriter_WriteStr(writer, body);
+    }
+    Py_DECREF(body);
+    return result;
+}
+
+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);
+    if (!temp_fv_str) {
+        return -1;
+    }
+    if (PyUnicode_Find(temp_fv_str, _str_open_br, 0, 1, 1) == 0) {
+        /* Expression starts with a brace, split it with a space from the outer
+           one. */
+        outer_brace = "{ ";
+    }
+    if (-1 == append_charp(writer, outer_brace)) {
+        Py_DECREF(temp_fv_str);
+        return -1;
+    }
+    if (-1 == _PyUnicodeWriter_WriteStr(writer, temp_fv_str)) {
+        Py_DECREF(temp_fv_str);
+        return -1;
+    }
+    Py_DECREF(temp_fv_str);
+
+    if (e->v.FormattedValue.conversion > 0) {
+        switch (e->v.FormattedValue.conversion) {
+        case 97:
+            conversion = "!a";
+            break;
+        case 114:
+            conversion = "!r";
+            break;
+        case 115:
+            conversion = "!s";
+            break;
+        default:
+            PyErr_SetString(PyExc_SystemError,
+                            "unknown f-value conversion kind");
+            return -1;
+        }
+        if (-1 == append_charp(writer, conversion)) {
+            return -1;
+        }
+    }
+    if (e->v.FormattedValue.format_spec > 0) {
+        if (-1 == _PyUnicodeWriter_WriteASCIIString(writer, ":", 1) ||
+            -1 == append_fstring_element(writer,
+                                         e->v.FormattedValue.format_spec,
+                                         true
+                                        ))
+        {
+            return -1;
+        }
+    }
+    return append_charp(writer, "}");
+}
+
+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;
+    }
+
+    /* 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)
+    {
+        period = " .";
+    }
+    else {
+        period = ".";
+    }
+    if (-1 == append_charp(writer, period)) {
+        return -1;
+    }
+
+    return _PyUnicodeWriter_WriteStr(writer, e->v.Attribute.attr);
+}
+
+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;
+        }
+    }
+
+    if (-1 == append_charp(writer, ":")) {
+        return -1;
+    }
+
+    if (slice->v.Slice.upper) {
+        if (-1 == append_ast_expr(writer, slice->v.Slice.upper, false)) {
+            return -1;
+        }
+    }
+
+    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;
+        }
+    }
+    return 0;
+}
+
+static int
+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;
+        }
+    }
+    return 0;
+}
+
+static int
+append_ast_slice(_PyUnicodeWriter *writer, slice_ty slice, bool omit_parens)
+{
+    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);
+    default:
+        PyErr_SetString(PyExc_SystemError,
+                        "unexpected slice kind");
+        return -1;
+    }
+}
+
+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, "]");
+}
+
+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);
+}
+
+static int
+append_ast_yield(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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) {
+        if (-1 == append_ast_expr(writer, e->v.Yield.value, false)) {
+            return -1;
+        }
+    }
+    return omit_parens ? 0 : append_charp(writer, ")");
+}
+
+static int
+append_ast_yield_from(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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, ")");
+}
+
+static int
+append_ast_await(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    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, ")");
+}
+
+static int
+append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens)
+{
+    switch (e->kind) {
+    case BoolOp_kind:
+        return append_ast_boolop(writer, e, omit_parens);
+    case BinOp_kind:
+        return append_ast_binop(writer, e, omit_parens);
+    case UnaryOp_kind:
+        return append_ast_unaryop(writer, e, omit_parens);
+    case Lambda_kind:
+        return append_ast_lambda(writer, e, omit_parens);
+    case IfExp_kind:
+        return append_ast_ifexp(writer, e, omit_parens);
+    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);
+    case ListComp_kind:
+        return append_ast_listcomp(writer, e);
+    case SetComp_kind:
+        return append_ast_setcomp(writer, e);
+    case DictComp_kind:
+        return append_ast_dictcomp(writer, e);
+    case Yield_kind:
+        return append_ast_yield(writer, e, omit_parens);
+    case YieldFrom_kind:
+        return append_ast_yield_from(writer, e, omit_parens);
+    case Await_kind:
+        return append_ast_await(writer, e, omit_parens);
+    case Compare_kind:
+        return append_ast_compare(writer, e, omit_parens);
+    case Call_kind:
+        return append_ast_call(writer, e);
+    case Constant_kind:
+        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:
+        return append_charp(writer, "...");
+    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);
+    case Subscript_kind:
+        return append_ast_subscript(writer, e);
+    case Starred_kind:
+        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);
+    default:
+        PyErr_SetString(PyExc_SystemError,
+                        "unknown expression kind");
+        return -1;
+    }
+}
+
+static int
+maybe_init_static_strings()
+{
+    if (!_str_open_br &&
+        !(_str_open_br = PyUnicode_InternFromString("{"))) {
+        return -1;
+    }
+    if (!_str_dbl_open_br &&
+        !(_str_dbl_open_br = PyUnicode_InternFromString("{{"))) {
+        return -1;
+    }
+    if (!_str_close_br &&
+        !(_str_close_br = PyUnicode_InternFromString("}"))) {
+        return -1;
+    }
+    if (!_str_dbl_close_br &&
+        !(_str_dbl_close_br = PyUnicode_InternFromString("}}"))) {
+        return -1;
+    }
+    return 0;
+}
+
+static PyObject *
+expr_as_unicode(expr_ty e, bool omit_parens)
+{
+    _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))
+    {
+        _PyUnicodeWriter_Dealloc(&writer);
+        return NULL;
+    }
+    return _PyUnicodeWriter_Finish(&writer);
+}
+
+PyObject *
+_PyAST_ExprAsUnicode(expr_ty e, bool omit_parens)
+{
+    return expr_as_unicode(e, omit_parens);
+}
index 31efc28da43fa0e09b8383318ac9ba769dc8888b..3e8323b933f49d9fce3137dae74d3de6d981d839 100644 (file)
@@ -1699,13 +1699,30 @@ error:
     return 0;
 }
 
+static int
+compiler_visit_annexpr(struct compiler *c, expr_ty annotation)
+{
+    PyObject *ann_as_str;
+    ann_as_str = _PyAST_ExprAsUnicode(annotation, 1);
+    if (!ann_as_str) {
+        return 0;
+    }
+    ADDOP_N(c, LOAD_CONST, ann_as_str, consts);
+    return 1;
+}
+
 static int
 compiler_visit_argannotation(struct compiler *c, identifier id,
     expr_ty annotation, PyObject *names)
 {
     if (annotation) {
         PyObject *mangled;
-        VISIT(c, expr, annotation);
+        if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) {
+            VISIT(c, annexpr, annotation)
+        }
+        else {
+            VISIT(c, expr, annotation);
+        }
         mangled = _Py_Mangle(c->u->u_private, id);
         if (!mangled)
             return 0;
@@ -4688,7 +4705,12 @@ compiler_annassign(struct compiler *c, stmt_ty s)
             if (!mangled) {
                 return 0;
             }
-            VISIT(c, expr, s->v.AnnAssign.annotation);
+            if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) {
+                VISIT(c, annexpr, s->v.AnnAssign.annotation)
+            }
+            else {
+                VISIT(c, expr, s->v.AnnAssign.annotation);
+            }
             /* ADDOP_N decrefs its argument */
             ADDOP_N(c, STORE_ANNOTATION, mangled, names);
         }
index 5cb21ac61c2a2311789fea8f93124d1eccd06653..53faa6b9016c6640a87ef8bec04c6c1353d7cf3a 100644 (file)
@@ -42,6 +42,8 @@ future_check_features(PyFutureFeatures *ff, stmt_ty s, PyObject *filename)
             ff->ff_features |= CO_FUTURE_BARRY_AS_BDFL;
         } else if (strcmp(feature, FUTURE_GENERATOR_STOP) == 0) {
             ff->ff_features |= CO_FUTURE_GENERATOR_STOP;
+        } else if (strcmp(feature, FUTURE_ANNOTATIONS) == 0) {
+            ff->ff_features |= CO_FUTURE_ANNOTATIONS;
         } else if (strcmp(feature, "braces") == 0) {
             PyErr_SetString(PyExc_SyntaxError,
                             "not a chance");