From 1a87ea015adf9fb93a836d85e710f9512eff1fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 10 Nov 2017 10:51:32 -0800 Subject: [PATCH 1/7] Document `from __future__ import annotations` (PEP 563) --- Doc/glossary.rst | 17 ++++++----- Doc/library/__future__.rst | 5 ++++ Doc/reference/compound_stmts.rst | 25 ++++++++++++---- Doc/reference/simple_stmts.rst | 15 ++++++---- Doc/whatsnew/3.7.rst | 51 ++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 19 deletions(-) diff --git a/Doc/glossary.rst b/Doc/glossary.rst index f0fcb6273e0b23..dcfe086b38b12f 100644 --- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -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 diff --git a/Doc/library/__future__.rst b/Doc/library/__future__.rst index 73d8b6b7e8aff0..e3d749e6017847 100644 --- a/Doc/library/__future__.rst +++ b/Doc/library/__future__.rst @@ -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:: diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index dca9362400455c..d7792f1eaf825f 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -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: diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst index 8d17383853a3c4..ef9a5f0dc85422 100644 --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -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 diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 1924881219a279..5cc04310daaf56 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -177,6 +177,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 compatibiltiy, 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 ---------------------------------------------------------- From 863d3e986fd0beec767383c302b06dd59436938f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 10 Nov 2017 11:05:49 -0800 Subject: [PATCH 2/7] Plumbing for `from __future__ import annotations` (PEP 563) The string form is recovered by unparsing the AST. --- Include/ast.h | 4 ++++ Include/code.h | 1 + Include/compile.h | 3 ++- Lib/__future__.py | 20 +++++++++++++------- Python/compile.c | 21 +++++++++++++++++++-- Python/future.c | 2 ++ 6 files changed, 41 insertions(+), 10 deletions(-) diff --git a/Include/ast.h b/Include/ast.h index 6a8c8165c0550f..58d5937923995b 100644 --- a/Include/ast.h +++ b/Include/ast.h @@ -15,6 +15,10 @@ PyAPI_FUNC(mod_ty) PyAST_FromNodeObject( PyCompilerFlags *flags, PyObject *filename, PyArena *arena); +PyAPI_FUNC(PyObject *) PyAST_UnicodeFromAstExpr( + expr_ty e, + int omit_parens, + int omit_string_brackets); #ifdef __cplusplus } diff --git a/Include/code.h b/Include/code.h index 8b0f84042372d9..2e661e8b36b7f7 100644 --- a/Include/code.h +++ b/Include/code.h @@ -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. */ diff --git a/Include/compile.h b/Include/compile.h index edfd8bbfac69fe..edb961f4d72042 100644 --- a/Include/compile.h +++ b/Include/compile.h @@ -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) diff --git a/Lib/__future__.py b/Lib/__future__.py index 63b2be3524fc7c..ce8bed7a643cdd 100644 --- a/Lib/__future__.py +++ b/Lib/__future__.py @@ -57,13 +57,14 @@ "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_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 @@ def __repr__(self): 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) diff --git a/Python/compile.c b/Python/compile.c index cd039168ebace6..c4250bfcf1c51c 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1658,13 +1658,27 @@ compiler_visit_kwonlydefaults(struct compiler *c, asdl_seq *kwonlyargs, return 0; } +static int +compiler_visit_annexpr(struct compiler *c, expr_ty annotation) +{ + static PyObject *ann_as_str; + ann_as_str = PyAST_UnicodeFromAstExpr(annotation, 1, 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; @@ -4646,7 +4660,10 @@ 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); } diff --git a/Python/future.c b/Python/future.c index 5cb21ac61c2a23..53faa6b9016c66 100644 --- a/Python/future.c +++ b/Python/future.c @@ -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"); From bc3ad37b1ca035752f567f978bf7933cf708fe2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Fri, 10 Nov 2017 11:08:37 -0800 Subject: [PATCH 3/7] 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. --- Lib/test/test_future.py | 156 ++++++ Makefile.pre.in | 3 +- PCbuild/pythoncore.vcxproj | 1 + Python/ast_unparse.c | 1040 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1199 insertions(+), 1 deletion(-) create mode 100644 Python/ast_unparse.c diff --git a/Lib/test/test_future.py b/Lib/test/test_future.py index 2f1c410b1bfa25..3b029ba3bec4f5 100644 --- a/Lib/test/test_future.py +++ b/Lib/test/test_future.py @@ -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,160 @@ def test_unicode_literals_exec(self): 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}: + ... + var: {ann} + var2: {ann} = None + """ + ) + + def getActual(self, annotation): + scope = {} + exec(self.template.format(ann=annotation), {}, scope) + func_ann = scope['f'].__annotations__['return'] + var_ann1 = scope['__annotations__']['var'] + var_ann2 = scope['__annotations__']['var2'] + self.assertEqual(func_ann, var_ann1) + self.assertEqual(func_ann, var_ann2) + return func_ann + + def assertAnnotationEqual( + self, annotation, drop_parens=False, is_tuple=False + ): + actual = self.getActual(annotation) + if annotation.startswith("'"): + # strings are passed verbatim + expected = eval(annotation) + elif is_tuple: + expected = annotation[1:-1] + else: + expected = annotation + 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('1 << 2') + eq('1 >> 2') + eq('((1 + 2) - (3 * 4)) ^ (((5 ** 6) / 7) // 8)') + eq('not True') + eq('~True') + eq('+1') + eq('-1') + eq('(~int) and (not ((True ^ (123 + 2)) | 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 << 2), e='str': a") + eq("lambda a, b, c=True, *vararg, d=(1 << 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), (+1), 'string', b'bytes') or None") + eq("()") + eq("(1, )") + 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("{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("list[str]") + eq("dict[str, int]") + eq("tuple[str, ...]") + eq("tuple[str, int, float, dict[str, int]]") + eq('(str or None) if (sys.version_info[0] > (3, )) else (str or bytes or None)') + + def test_annotations_no_fstring_support_implemented(self): + # FIXME: Add f-string support in ast_unparse.c. + eq = self.assertAnnotationEqual + with self.assertRaises(SystemError) as err: + eq("""f'some f-string with {a} {few():.2f} {formatted.values!r}'""") + self.assertEqual( + str(err.exception), + "f-string support in annotations not implemented yet", + ) + + 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 + 2 - 3 * 4 ^ 5 ** 6 / 7 // 8') + eq('~int and not True ^ 123 + 2 | 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__": diff --git a/Makefile.pre.in b/Makefile.pre.in index 4fe9affe0d25e9..360db19bea8349 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -320,6 +320,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 \ @@ -833,7 +834,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 diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj index 25651d00049f27..66e927a65cce02 100644 --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -357,6 +357,7 @@ + diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c new file mode 100644 index 00000000000000..d7f0f4514730e1 --- /dev/null +++ b/Python/ast_unparse.c @@ -0,0 +1,1040 @@ +#include +#include "Python.h" +#include "Python-ast.h" + +/* Forward declaration for recursion via helper functions. */ +static int +append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens, + bool omit_string_brackets); + +static int +append_charp(_PyUnicodeWriter *writer, char *charp) +{ + int n_digits; + n_digits = strlen(charp); + return _PyUnicodeWriter_WriteASCIIString(writer, charp, n_digits); +} + +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) +{ + int ret; + Py_ssize_t i, value_count; + asdl_seq *values; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + values = e->v.BoolOp.values; + value_count = asdl_seq_LEN(values) - 1; + assert(value_count >= 0); + + ret = append_ast_expr(writer, (expr_ty)asdl_seq_GET(values, 0), false, false); + if (ret == -1) + return ret; + + for (i = 1; i <= value_count; ++i) { + if (e->v.BoolOp.op == And) { + ret = append_charp(writer, " and "); + if (ret == -1) + return ret; + } + else { + ret = append_charp(writer, " or "); + if (ret == -1) + return ret; + } + + ret = append_ast_expr(writer, (expr_ty)asdl_seq_GET(values, i), false, false); + if (ret == -1) + return ret; + } + + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + char *op; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.BinOp.left, false, false); + if (ret == -1) + return ret; + + 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; + } + + ret = append_charp(writer, op); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.BinOp.right, false, false); + if (ret == -1) + return ret; + + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + char *op; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + switch(e->v.UnaryOp.op) { + case Invert: op = "~"; break; + case Not: op = "not "; break; + case UAdd: op = "+"; break; + case USub: op = "-"; break; + } + + ret = append_charp(writer, op); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.UnaryOp.operand, false, false); + if (ret == -1) + return ret; + + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_arg(_PyUnicodeWriter *writer, arg_ty arg) +{ + int ret; + ret = _PyUnicodeWriter_WriteStr(writer, arg->arg); + if (ret == -1) + return ret; + if (arg->annotation) { + ret = append_charp(writer, ": "); + if (ret == -1) + return ret; + ret = append_ast_expr(writer, arg->annotation, true, false); + if (ret == -1) + return ret; + } + return 0; +} + +static int +append_ast_args(_PyUnicodeWriter *writer, arguments_ty args) +{ + int ret; + 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 = args->defaults ? asdl_seq_LEN(args->defaults) : 0; + for (i = 0; i < arg_count; i++) { + if (first) + first = false; + else { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + + arg = (arg_ty)asdl_seq_GET(args->args, i); + ret = append_ast_arg(writer, arg); + if (ret == -1) + return ret; + + di = i - arg_count + default_count; + if (di >= 0) { + ret = append_charp(writer, "="); + if (ret == -1) + return ret; + default_ = (expr_ty)asdl_seq_GET(args->defaults, di); + ret = append_ast_expr(writer, default_, false, false); + if (ret == -1) + return ret; + } + } + + /* vararg, or bare '*' in no varargs but keyword-only arguments present */ + if (args->vararg || args->kwonlyargs) { + if (first) + first = false; + else { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + + ret = append_charp(writer, "*"); + if (ret == -1) + return ret; + + if (args->vararg) { + ret = append_ast_arg(writer, args->vararg); + if (ret == -1) + return ret; + } + } + + /* keyword-only arguments */ + arg_count = asdl_seq_LEN(args->kwonlyargs); + default_count = args->defaults ? asdl_seq_LEN(args->kw_defaults) : 0; + for (i = 0; i < arg_count; i++) { + if (first) + first = false; + else { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + + arg = (arg_ty)asdl_seq_GET(args->kwonlyargs, i); + ret = append_ast_arg(writer, arg); + if (ret == -1) + return ret; + + di = i - arg_count + default_count; + if (di >= 0) { + ret = append_charp(writer, "="); + if (ret == -1) + return ret; + default_ = (expr_ty)asdl_seq_GET(args->kw_defaults, di); + ret = append_ast_expr(writer, default_, false, false); + if (ret == -1) + return ret; + } + } + + /* **kwargs */ + if (args->kwarg) { + if (first) + first = false; + else { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + + ret = append_charp(writer, "**"); + if (ret == -1) + return ret; + + ret = append_ast_arg(writer, args->kwarg); + if (ret == -1) + return ret; + } + + return 0; +} + +static int +append_ast_lambda(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + ret = append_charp(writer, "lambda "); + if (ret == -1) + return ret; + + ret = append_ast_args(writer, e->v.Lambda.args); + if (ret == -1) + return ret; + + ret = append_charp(writer, ": "); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.Lambda.body, true, false); + if (ret == -1) + return ret; + + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.IfExp.body, false, false); + if (ret == -1) + return ret; + + ret = append_charp(writer, " if "); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.IfExp.test, false, false); + if (ret == -1) + return ret; + + ret = append_charp(writer, " else "); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.IfExp.orelse, false, false); + if (ret == -1) + return ret; + + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_dict(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + Py_ssize_t i, value_count; + expr_ty key_node, value_node; + + ret = append_charp(writer, "{"); + if (ret == -1) + return ret; + + value_count = asdl_seq_LEN(e->v.Dict.values); + + for (i = 0; i < value_count; i++) { + key_node = (expr_ty)asdl_seq_GET(e->v.Dict.keys, i); + if (key_node != NULL) { + ret = append_ast_expr(writer, key_node, false, false); + if (ret == -1) + return ret; + + ret = append_charp(writer, ": "); + if (ret == -1) + return ret; + } + else { + ret = append_charp(writer, "**"); + if (ret == -1) + return ret; + } + + value_node = (expr_ty)asdl_seq_GET(e->v.Dict.values, i); + ret = append_ast_expr(writer, value_node, false, false); + if (ret == -1) + return ret; + + if (i + 1 < value_count) { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + } + + return append_charp(writer, "}"); +} + +static int +append_ast_set(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + Py_ssize_t i, elem_count; + expr_ty elem_node; + + ret = append_charp(writer, "{"); + if (ret == -1) + return ret; + + elem_count = asdl_seq_LEN(e->v.Set.elts); + for (i = 0; i < elem_count; i++) { + elem_node = (expr_ty)asdl_seq_GET(e->v.Set.elts, i); + ret = append_ast_expr(writer, elem_node, false, false); + if (ret == -1) + return ret; + + if (i + 1 < elem_count) { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + } + + return append_charp(writer, "}"); +} + +static int +append_ast_list(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + Py_ssize_t i, elem_count; + expr_ty elem_node; + + ret = append_charp(writer, "["); + if (ret == -1) + return ret; + + elem_count = asdl_seq_LEN(e->v.List.elts); + for (i = 0; i < elem_count; i++) { + elem_node = (expr_ty)asdl_seq_GET(e->v.List.elts, i); + ret = append_ast_expr(writer, elem_node, false, false); + if (ret == -1) + return ret; + + if (i + 1 < elem_count) { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + } + + return append_charp(writer, "]"); +} + +static int +append_ast_tuple(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + 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) { + ret = append_charp(writer, "("); + if (ret == -1) + return ret; + } + + for (i = 0; i < elem_count; i++) { + elem_node = (expr_ty)asdl_seq_GET(e->v.Tuple.elts, i); + ret = append_ast_expr(writer, elem_node, false, false); + if (ret == -1) + return ret; + + if (i + 1 < elem_count || elem_count == 1) { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + } + + if (!omit_parens || elem_count < 2) + return append_charp(writer, ")"); + + return 0; +} + +static int +append_ast_comprehension(_PyUnicodeWriter *writer, comprehension_ty gen) +{ + int ret; + Py_ssize_t i, if_count; + + ret = append_charp(writer, gen->is_async ? " async for " : " for "); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, gen->target, true, false); + if (ret == -1) + return ret; + + ret = append_charp(writer, " in "); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, gen->iter, false, false); + if (ret == -1) + return ret; + + if_count = asdl_seq_LEN(gen->ifs); + for (i = 0; i < if_count; i++) { + ret = append_charp(writer, " if "); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, (expr_ty)asdl_seq_GET(gen->ifs, i), false, false); + if (ret == -1) + return ret; + } + return 0; +} + +static int +append_ast_comprehensions(_PyUnicodeWriter *writer, asdl_seq *comprehensions) +{ + int ret; + 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); + ret = append_ast_comprehension(writer, comp_node); + if (ret == -1) + return ret; + } + + return 0; +} + +static int +append_ast_genexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.GeneratorExp.elt, false, false); + if (ret == -1) + return ret; + + ret = append_ast_comprehensions(writer, e->v.GeneratorExp.generators); + if (ret == -1) + return ret; + + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_listcomp(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + + ret = append_charp(writer, "["); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.ListComp.elt, false, false); + if (ret == -1) + return ret; + + ret = append_ast_comprehensions(writer, e->v.ListComp.generators); + if (ret == -1) + return ret; + + return append_charp(writer, "]"); +} + +static int +append_ast_setcomp(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + + ret = append_charp(writer, "{"); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.SetComp.elt, false, false); + if (ret == -1) + return ret; + + ret = append_ast_comprehensions(writer, e->v.SetComp.generators); + if (ret == -1) + return ret; + + return append_charp(writer, "}"); +} + +static int +append_ast_dictcomp(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + + ret = append_charp(writer, "{"); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.DictComp.key, false, false); + if (ret == -1) + return ret; + + ret = append_charp(writer, ": "); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, e->v.DictComp.value, false, false); + if (ret == -1) + return ret; + + ret = append_ast_comprehensions(writer, e->v.DictComp.generators); + if (ret == -1) + return ret; + + return append_charp(writer, "}"); +} + +static int +append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + char *op; + Py_ssize_t i, comparator_count; + asdl_seq *comparators; + asdl_int_seq *ops; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + 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)); + + ret = append_ast_expr(writer, e->v.Compare.left, false, false); + if (ret == -1) + return ret; + + 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; + } + + ret = append_charp(writer, op); + if (ret == -1) + return ret; + + ret = append_ast_expr(writer, (expr_ty)asdl_seq_GET(comparators, i), false, false); + if (ret == -1) + return ret; + } + + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_keyword(_PyUnicodeWriter *writer, keyword_ty kw) +{ + int ret; + if (kw->arg == NULL) { + ret = append_charp(writer, "**"); + if (ret == -1) + return ret; + } + else { + ret = _PyUnicodeWriter_WriteStr(writer, kw->arg); + if (ret == -1) + return ret; + + ret = append_charp(writer, "="); + } + + return append_ast_expr(writer, kw->value, false, false); +} + +static int +append_ast_call(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + bool first; + Py_ssize_t i, arg_count, kw_count; + expr_ty expr; + keyword_ty kw; + + ret = append_ast_expr(writer, e->v.Call.func, false, false); + if (ret == -1) + return ret; + + ret = append_charp(writer, "("); + if (ret == -1) + return ret; + + first = true; + arg_count = asdl_seq_LEN(e->v.Call.args); + for (i = 0; i < arg_count; i++) { + if (first) + first = false; + else { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + + expr = (expr_ty)asdl_seq_GET(e->v.Call.args, i); + ret = append_ast_expr(writer, expr, false, false); + if (ret == -1) + return ret; + } + + kw_count = asdl_seq_LEN(e->v.Call.keywords); + for (i = 0; i < kw_count; i++) { + if (first) + first = false; + else { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + + kw = (keyword_ty)asdl_seq_GET(e->v.Call.keywords, i); + ret = append_ast_keyword(writer, kw); + if (ret == -1) + return ret; + } + + return append_charp(writer, ")"); +} + +static int +append_ast_attribute(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + + ret = append_ast_expr(writer, e->v.Attribute.value, false, false); + if (ret == -1) + return ret; + + /* Special case: integers require a space for attribute access to be + unambiguous. Floats and complex numbers don't but work with it, too. */ + ret = append_charp(writer, e->kind == Num_kind ? " ." : "."); + if (ret == -1) + return ret; + + return _PyUnicodeWriter_WriteStr(writer, e->v.Attribute.attr); +} + +static int +append_ast_simple_slice(_PyUnicodeWriter *writer, slice_ty slice) +{ + int ret; + if (slice->v.Slice.lower) { + ret = append_ast_expr(writer, slice->v.Slice.lower, false, false); + if (ret == -1) + return ret; + } + ret = append_charp(writer, ":"); + if (ret == -1) + return ret; + + if (slice->v.Slice.upper) { + ret = append_ast_expr(writer, slice->v.Slice.upper, false, false); + if (ret == -1) + return ret; + } + ret = append_charp(writer, ":"); + if (ret == -1) + return ret; + + if (slice->v.Slice.step) { + ret = append_ast_expr(writer, slice->v.Slice.step, false, false); + if (ret == -1) + return ret; + } + return ret; +} + +static int +append_ast_ext_slice(_PyUnicodeWriter *writer, slice_ty slice) +{ + int ret; + Py_ssize_t i, dims_count; + dims_count = asdl_seq_LEN(slice->v.ExtSlice.dims); + for (i = 0; i < dims_count; i++) { + ret = append_ast_expr(writer, + (expr_ty)asdl_seq_GET(slice->v.ExtSlice.dims, i), + false, + false); + if (ret == -1) + return ret; + + if (i + 1 < dims_count) { + ret = append_charp(writer, ", "); + if (ret == -1) + return ret; + } + } + 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, false); + default: + PyErr_SetString(PyExc_SystemError, + "unexpected slice kind"); + return -1; + } +} + +static int +append_ast_subscript(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + + ret = append_ast_expr(writer, e->v.Subscript.value, false, false); + if (ret == -1) + return ret; + + ret = append_charp(writer, "["); + if (ret == -1) + return ret; + + ret = append_ast_slice(writer, e->v.Subscript.slice, true); + if (ret == -1) + return ret; + + return append_charp(writer, "]"); +} + +static int +append_ast_starred(_PyUnicodeWriter *writer, expr_ty e) +{ + int ret; + + ret = append_charp(writer, "*"); + if (ret == -1) + return ret; + + return append_ast_expr(writer, e->v.Starred.value, false, false); +} + +static int +append_ast_yield(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + ret = append_charp(writer, e->v.Yield.value ? "yield " : "yield"); + if (ret == -1) + return ret; + + if (e->v.Yield.value) + { + ret = append_ast_expr(writer, e->v.Yield.value, false, false); + if (ret == -1) + return ret; + } + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_yield_from(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + ret = append_charp(writer, e->v.YieldFrom.value ? "yield from " : "yield from"); + if (ret == -1) + return ret; + + if (e->v.Yield.value) + { + ret = append_ast_expr(writer, e->v.YieldFrom.value, false, false); + if (ret == -1) + return ret; + } + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_await(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) +{ + int ret; + + ret = omit_parens ? 0 : append_charp(writer, "("); + if (ret == -1) + return ret; + + ret = append_charp(writer, e->v.Await.value ? "await " : "await"); + if (ret == -1) + return ret; + + if (e->v.Yield.value) + { + ret = append_ast_expr(writer, e->v.Await.value, false, false); + if (ret == -1) + return ret; + } + return omit_parens ? 0 : append_charp(writer, ")"); +} + +static int +append_ast_str(_PyUnicodeWriter *writer, expr_ty e, bool omit_string_brackets) +{ + PyObject *s = e->v.Str.s; + if (omit_string_brackets) + return _PyUnicodeWriter_WriteStr(writer, s); + + return append_repr(writer, s); +} + +static int +append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens, + bool omit_string_brackets) +{ + 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_ast_str(writer, e, omit_string_brackets); + case JoinedStr_kind: + PyErr_SetString(PyExc_SystemError, + "f-string support in annotations not implemented yet"); + return -1; + case FormattedValue_kind: + PyErr_SetString(PyExc_SystemError, + "f-string support in annotations not implemented yet"); + return -1; + 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; + } +} + +PyObject * +PyAST_UnicodeFromAstExpr(expr_ty e, bool omit_parens, bool omit_string_brackets) +{ + _PyUnicodeWriter writer; + int ret; + + _PyUnicodeWriter_Init(&writer); + writer.min_length = 256; + writer.overallocate = 1; + ret = append_ast_expr(&writer, e, omit_parens, omit_string_brackets); + if (ret == -1) { + _PyUnicodeWriter_Dealloc(&writer); + return NULL; + } + return _PyUnicodeWriter_Finish(&writer); +} From 7f88115354474dafa8f613bca31e988e8e129926 Mon Sep 17 00:00:00 2001 From: Lukasz Langa Date: Tue, 5 Dec 2017 10:12:48 -0800 Subject: [PATCH 4/7] Response to Serhiy's review --- Include/ast.h | 8 +- Lib/test/test_future.py | 63 ++- PCbuild/pythoncore.vcxproj.filters | 3 + Python/ast_unparse.c | 804 ++++++++++++++--------------- Python/compile.c | 19 +- 5 files changed, 440 insertions(+), 457 deletions(-) diff --git a/Include/ast.h b/Include/ast.h index 58d5937923995b..da4b55376a9f75 100644 --- a/Include/ast.h +++ b/Include/ast.h @@ -15,11 +15,17 @@ PyAPI_FUNC(mod_ty) PyAST_FromNodeObject( PyCompilerFlags *flags, PyObject *filename, PyArena *arena); -PyAPI_FUNC(PyObject *) PyAST_UnicodeFromAstExpr( + +#ifndef Py_LIMITED_API + +/* _PyAST_ExprAsUnicode is defined in ast_unparse.c */ +PyAPI_FUNC(PyObject *) _PyAST_ExprAsUnicode( expr_ty e, int omit_parens, int omit_string_brackets); +#endif /* !Py_LIMITED_API */ + #ifdef __cplusplus } #endif diff --git a/Lib/test/test_future.py b/Lib/test/test_future.py index 3b029ba3bec4f5..5891bbd9d4d781 100644 --- a/Lib/test/test_future.py +++ b/Lib/test/test_future.py @@ -110,6 +110,8 @@ class AnnotationsFutureTestCase(unittest.TestCase): from __future__ import annotations def f() -> {ann}: ... + def g(arg: {ann}) -> None: + ... var: {ann} var2: {ann} = None """ @@ -118,12 +120,14 @@ def f() -> {ann}: def getActual(self, annotation): scope = {} exec(self.template.format(ann=annotation), {}, scope) - func_ann = scope['f'].__annotations__['return'] + 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_ann, var_ann1) - self.assertEqual(func_ann, var_ann2) - return func_ann + 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, drop_parens=False, is_tuple=False @@ -162,19 +166,20 @@ def test_annotations(self): eq('Name1 or (Name2 and Name3)') eq('(Name1 and Name2) or (Name3 and Name4)') eq('Name1 or (Name2 and Name3) or Name4') - eq('1 << 2') - eq('1 >> 2') - eq('((1 + 2) - (3 * 4)) ^ (((5 ** 6) / 7) // 8)') - eq('not True') - eq('~True') - eq('+1') + 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 ((True ^ (123 + 2)) | True))') + 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 << 2), e='str': a") - eq("lambda a, b, c=True, *vararg, d=(1 << 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('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)') @@ -182,21 +187,23 @@ def test_annotations(self): 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), (+1), 'string', b'bytes') or None") + eq("({'a': 'b'}, (True or False), (+value), 'string', b'bytes') or None") eq("()") - eq("(1, )") + 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) 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) 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(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()") @@ -206,11 +213,21 @@ def test_annotations(self): 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('(str or None) if (sys.version_info[0] > (3, )) else (str or bytes or None)') + 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)') def test_annotations_no_fstring_support_implemented(self): # FIXME: Add f-string support in ast_unparse.c. @@ -234,8 +251,8 @@ def test_annotations_inexact(self): eq('Name1 or Name2 and Name3') eq('Name1 and Name2 or Name3 and Name4') eq('Name1 or Name2 and Name3 or Name4') - eq('1 + 2 - 3 * 4 ^ 5 ** 6 / 7 // 8') - eq('~int and not True ^ 123 + 2 | True') + 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'}") @@ -252,7 +269,7 @@ def test_annotations_inexact(self): 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) 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)') diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters index 42b2fc3ea7c7f5..2f45761f5b165c 100644 --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -827,6 +827,9 @@ Python + + Python + Python diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c index d7f0f4514730e1..0196fdd7707cb4 100644 --- a/Python/ast_unparse.c +++ b/Python/ast_unparse.c @@ -8,11 +8,9 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens, bool omit_string_brackets); static int -append_charp(_PyUnicodeWriter *writer, char *charp) +append_charp(_PyUnicodeWriter *writer, const char *charp) { - int n_digits; - n_digits = strlen(charp); - return _PyUnicodeWriter_WriteASCIIString(writer, charp, n_digits); + return _PyUnicodeWriter_WriteASCIIString(writer, charp, -1); } static int @@ -21,8 +19,9 @@ append_repr(_PyUnicodeWriter *writer, PyObject *obj) int ret; PyObject *repr; repr = PyObject_Repr(obj); - if (!repr) + if (!repr) { return -1; + } ret = _PyUnicodeWriter_WriteStr(writer, repr); Py_DECREF(repr); return ret; @@ -31,37 +30,36 @@ append_repr(_PyUnicodeWriter *writer, PyObject *obj) static int append_ast_boolop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; Py_ssize_t i, value_count; asdl_seq *values; - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + 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); - ret = append_ast_expr(writer, (expr_ty)asdl_seq_GET(values, 0), false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, + (expr_ty)asdl_seq_GET(values, 0), + false, + false)) { + return -1; + } + const char *op = (e->v.BoolOp.op == And) ? " and " : " or "; for (i = 1; i <= value_count; ++i) { - if (e->v.BoolOp.op == And) { - ret = append_charp(writer, " and "); - if (ret == -1) - return ret; - } - else { - ret = append_charp(writer, " or "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, op)) { + return -1; } - ret = append_ast_expr(writer, (expr_ty)asdl_seq_GET(values, i), false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, + (expr_ty)asdl_seq_GET(values, i), + false, + false)) { + return -1; + } } return omit_parens ? 0 : append_charp(writer, ")"); @@ -70,16 +68,15 @@ append_ast_boolop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - char *op; + const char *op; - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } - ret = append_ast_expr(writer, e->v.BinOp.left, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.BinOp.left, false, false)) { + return -1; + } switch(e->v.BinOp.op) { case Add: op = " + "; break; @@ -87,7 +84,7 @@ append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) case Mult: op = " * "; break; case MatMult: op = " @ "; break; case Div: op = " / "; break; - case Mod: op = " %% "; break; + case Mod: op = " % "; break; case LShift: op = " << "; break; case RShift: op = " >> "; break; case BitOr: op = " | "; break; @@ -97,13 +94,13 @@ append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) case Pow: op = " ** "; break; } - ret = append_charp(writer, op); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, op)) { + return -1; + } - ret = append_ast_expr(writer, e->v.BinOp.right, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.BinOp.right, false, false)) { + return -1; + } return omit_parens ? 0 : append_charp(writer, ")"); } @@ -111,12 +108,11 @@ append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - char *op; + const char *op; - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } switch(e->v.UnaryOp.op) { case Invert: op = "~"; break; @@ -125,13 +121,13 @@ append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) case USub: op = "-"; break; } - ret = append_charp(writer, op); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, op)) { + return -1; + } - ret = append_ast_expr(writer, e->v.UnaryOp.operand, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.UnaryOp.operand, false, false)) { + return -1; + } return omit_parens ? 0 : append_charp(writer, ")"); } @@ -139,17 +135,16 @@ append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_arg(_PyUnicodeWriter *writer, arg_ty arg) { - int ret; - ret = _PyUnicodeWriter_WriteStr(writer, arg->arg); - if (ret == -1) - return ret; + if (-1 == _PyUnicodeWriter_WriteStr(writer, arg->arg)) { + return -1; + } if (arg->annotation) { - ret = append_charp(writer, ": "); - if (ret == -1) - return ret; - ret = append_ast_expr(writer, arg->annotation, true, false); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, ": ")) { + return -1; + } + if (-1 == append_ast_expr(writer, arg->annotation, true, false)) { + return -1; + } } return 0; } @@ -157,7 +152,6 @@ append_ast_arg(_PyUnicodeWriter *writer, arg_ty arg) static int append_ast_args(_PyUnicodeWriter *writer, arguments_ty args) { - int ret; bool first; Py_ssize_t i, di, arg_count, default_count; arg_ty arg; @@ -167,100 +161,96 @@ append_ast_args(_PyUnicodeWriter *writer, arguments_ty args) /* positional arguments with defaults */ arg_count = asdl_seq_LEN(args->args); - default_count = args->defaults ? asdl_seq_LEN(args->defaults) : 0; + default_count = asdl_seq_LEN(args->defaults); for (i = 0; i < arg_count; i++) { - if (first) + if (first) { first = false; - else { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + } + else if (-1 == append_charp(writer, ", ")) { + return -1; } arg = (arg_ty)asdl_seq_GET(args->args, i); - ret = append_ast_arg(writer, arg); - if (ret == -1) - return ret; + if (-1 == append_ast_arg(writer, arg)) { + return -1; + } di = i - arg_count + default_count; if (di >= 0) { - ret = append_charp(writer, "="); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "=")) { + return -1; + } default_ = (expr_ty)asdl_seq_GET(args->defaults, di); - ret = append_ast_expr(writer, default_, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, default_, false, false)) { + return -1; + } } } - /* vararg, or bare '*' in no varargs but keyword-only arguments present */ + /* vararg, or bare '*' if no varargs but keyword-only arguments present */ if (args->vararg || args->kwonlyargs) { - if (first) + if (first) { first = false; - else { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + } + else if (-1 == append_charp(writer, ", ")) { + return -1; } - ret = append_charp(writer, "*"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "*")) { + return -1; + } if (args->vararg) { - ret = append_ast_arg(writer, args->vararg); - if (ret == -1) - return ret; + if (-1 == append_ast_arg(writer, args->vararg)) { + return -1; + } } } /* keyword-only arguments */ arg_count = asdl_seq_LEN(args->kwonlyargs); - default_count = args->defaults ? asdl_seq_LEN(args->kw_defaults) : 0; + default_count = asdl_seq_LEN(args->kw_defaults); for (i = 0; i < arg_count; i++) { - if (first) + if (first) { first = false; - else { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + } + else if (-1 == append_charp(writer, ", ")) { + return -1; } arg = (arg_ty)asdl_seq_GET(args->kwonlyargs, i); - ret = append_ast_arg(writer, arg); - if (ret == -1) - return ret; + if (-1 == append_ast_arg(writer, arg)) { + return -1; + } di = i - arg_count + default_count; if (di >= 0) { - ret = append_charp(writer, "="); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "=")) { + return -1; + } default_ = (expr_ty)asdl_seq_GET(args->kw_defaults, di); - ret = append_ast_expr(writer, default_, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, default_, false, false)) { + return -1; + } } } /* **kwargs */ if (args->kwarg) { - if (first) + if (first) { first = false; - else { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + } + else if (-1 == append_charp(writer, ", ")) { + return -1; } - ret = append_charp(writer, "**"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "**")) { + return -1; + } - ret = append_ast_arg(writer, args->kwarg); - if (ret == -1) - return ret; + if (-1 == append_ast_arg(writer, args->kwarg)) { + return -1; + } } return 0; @@ -269,27 +259,25 @@ append_ast_args(_PyUnicodeWriter *writer, arguments_ty args) static int append_ast_lambda(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } - ret = append_charp(writer, "lambda "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "lambda ")) { + return -1; + } - ret = append_ast_args(writer, e->v.Lambda.args); - if (ret == -1) - return ret; + if (-1 == append_ast_args(writer, e->v.Lambda.args)) { + return -1; + } - ret = append_charp(writer, ": "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, ": ")) { + return -1; + } - ret = append_ast_expr(writer, e->v.Lambda.body, true, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.Lambda.body, true, false)) { + return -1; + } return omit_parens ? 0 : append_charp(writer, ")"); } @@ -297,31 +285,29 @@ append_ast_lambda(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } - ret = append_ast_expr(writer, e->v.IfExp.body, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.IfExp.body, false, false)) { + return -1; + } - ret = append_charp(writer, " if "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, " if ")) { + return -1; + } - ret = append_ast_expr(writer, e->v.IfExp.test, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.IfExp.test, false, false)) { + return -1; + } - ret = append_charp(writer, " else "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, " else ")) { + return -1; + } - ret = append_ast_expr(writer, e->v.IfExp.orelse, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.IfExp.orelse, false, false)) { + return -1; + } return omit_parens ? 0 : append_charp(writer, ")"); } @@ -329,42 +315,36 @@ append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_dict(_PyUnicodeWriter *writer, expr_ty e) { - int ret; Py_ssize_t i, value_count; expr_ty key_node, value_node; - ret = append_charp(writer, "{"); - if (ret == -1) - return ret; + 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) { - ret = append_ast_expr(writer, key_node, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, key_node, false, false)) { + return -1; + } - ret = append_charp(writer, ": "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, ": ")) { + return -1; + } } - else { - ret = append_charp(writer, "**"); - if (ret == -1) - return ret; + else if (-1 == append_charp(writer, "**")) { + return -1; } value_node = (expr_ty)asdl_seq_GET(e->v.Dict.values, i); - ret = append_ast_expr(writer, value_node, false, false); - if (ret == -1) - return ret; - - if (i + 1 < value_count) { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, value_node, false, false)) { + return -1; } } @@ -374,25 +354,22 @@ append_ast_dict(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_set(_PyUnicodeWriter *writer, expr_ty e) { - int ret; Py_ssize_t i, elem_count; expr_ty elem_node; - ret = append_charp(writer, "{"); - if (ret == -1) - return ret; + 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); - ret = append_ast_expr(writer, elem_node, false, false); - if (ret == -1) - return ret; - - if (i + 1 < elem_count) { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, elem_node, false, false)) { + return -1; } } @@ -402,25 +379,21 @@ append_ast_set(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_list(_PyUnicodeWriter *writer, expr_ty e) { - int ret; Py_ssize_t i, elem_count; expr_ty elem_node; - ret = append_charp(writer, "["); - if (ret == -1) - return ret; + 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); - ret = append_ast_expr(writer, elem_node, false, false); - if (ret == -1) - return ret; - - if (i + 1 < elem_count) { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, elem_node, false, false)) { + return -1; } } @@ -430,33 +403,30 @@ append_ast_list(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_tuple(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; 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) { - ret = append_charp(writer, "("); - if (ret == -1) - return ret; + 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); - ret = append_ast_expr(writer, elem_node, false, false); - if (ret == -1) - return ret; - - if (i + 1 < elem_count || elem_count == 1) { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, elem_node, false, false)) { + return -1; } } - if (!omit_parens || elem_count < 2) + if (!omit_parens || elem_count < 2) { return append_charp(writer, ")"); + } return 0; } @@ -464,34 +434,36 @@ append_ast_tuple(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_comprehension(_PyUnicodeWriter *writer, comprehension_ty gen) { - int ret; Py_ssize_t i, if_count; - ret = append_charp(writer, gen->is_async ? " async for " : " for "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, gen->is_async ? " async for " : " for ")) { + return -1; + } - ret = append_ast_expr(writer, gen->target, true, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, gen->target, true, false)) { + return -1; + } - ret = append_charp(writer, " in "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, " in ")) { + return -1; + } - ret = append_ast_expr(writer, gen->iter, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, gen->iter, false, false)) { + return -1; + } if_count = asdl_seq_LEN(gen->ifs); for (i = 0; i < if_count; i++) { - ret = append_charp(writer, " if "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, " if ")) { + return -1; + } - ret = append_ast_expr(writer, (expr_ty)asdl_seq_GET(gen->ifs, i), false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, + (expr_ty)asdl_seq_GET(gen->ifs, i), + false, + false)) { + return -1; + } } return 0; } @@ -499,16 +471,15 @@ append_ast_comprehension(_PyUnicodeWriter *writer, comprehension_ty gen) static int append_ast_comprehensions(_PyUnicodeWriter *writer, asdl_seq *comprehensions) { - int ret; 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); - ret = append_ast_comprehension(writer, comp_node); - if (ret == -1) - return ret; + if (-1 == append_ast_comprehension(writer, comp_node)) { + return -1; + } } return 0; @@ -517,19 +488,17 @@ append_ast_comprehensions(_PyUnicodeWriter *writer, asdl_seq *comprehensions) static int append_ast_genexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } - ret = append_ast_expr(writer, e->v.GeneratorExp.elt, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.GeneratorExp.elt, false, false)) { + return -1; + } - ret = append_ast_comprehensions(writer, e->v.GeneratorExp.generators); - if (ret == -1) - return ret; + if (-1 == append_ast_comprehensions(writer, e->v.GeneratorExp.generators)) { + return -1; + } return omit_parens ? 0 : append_charp(writer, ")"); } @@ -537,19 +506,17 @@ append_ast_genexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_listcomp(_PyUnicodeWriter *writer, expr_ty e) { - int ret; - - ret = append_charp(writer, "["); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "[")) { + return -1; + } - ret = append_ast_expr(writer, e->v.ListComp.elt, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.ListComp.elt, false, false)) { + return -1; + } - ret = append_ast_comprehensions(writer, e->v.ListComp.generators); - if (ret == -1) - return ret; + if (-1 == append_ast_comprehensions(writer, e->v.ListComp.generators)) { + return -1; + } return append_charp(writer, "]"); } @@ -557,19 +524,17 @@ append_ast_listcomp(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_setcomp(_PyUnicodeWriter *writer, expr_ty e) { - int ret; - - ret = append_charp(writer, "{"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "{")) { + return -1; + } - ret = append_ast_expr(writer, e->v.SetComp.elt, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.SetComp.elt, false, false)) { + return -1; + } - ret = append_ast_comprehensions(writer, e->v.SetComp.generators); - if (ret == -1) - return ret; + if (-1 == append_ast_comprehensions(writer, e->v.SetComp.generators)) { + return -1; + } return append_charp(writer, "}"); } @@ -577,27 +542,25 @@ append_ast_setcomp(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_dictcomp(_PyUnicodeWriter *writer, expr_ty e) { - int ret; - - ret = append_charp(writer, "{"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "{")) { + return -1; + } - ret = append_ast_expr(writer, e->v.DictComp.key, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.DictComp.key, false, false)) { + return -1; + } - ret = append_charp(writer, ": "); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, ": ")) { + return -1; + } - ret = append_ast_expr(writer, e->v.DictComp.value, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.DictComp.value, false, false)) { + return -1; + } - ret = append_ast_comprehensions(writer, e->v.DictComp.generators); - if (ret == -1) - return ret; + if (-1 == append_ast_comprehensions(writer, e->v.DictComp.generators)) { + return -1; + } return append_charp(writer, "}"); } @@ -605,15 +568,14 @@ append_ast_dictcomp(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - char *op; + const char *op; Py_ssize_t i, comparator_count; asdl_seq *comparators; asdl_int_seq *ops; - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } comparators = e->v.Compare.comparators; ops = e->v.Compare.ops; @@ -621,9 +583,9 @@ append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) assert(comparator_count > 0); assert(comparator_count == asdl_seq_LEN(ops)); - ret = append_ast_expr(writer, e->v.Compare.left, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.Compare.left, false, false)) { + return -1; + } for (i = 0; i < comparator_count; i++) { switch ((cmpop_ty)asdl_seq_GET(ops, i)) { @@ -663,13 +625,16 @@ append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - ret = append_charp(writer, op); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, op)) { + return -1; + } - ret = append_ast_expr(writer, (expr_ty)asdl_seq_GET(comparators, i), false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, + (expr_ty)asdl_seq_GET(comparators, i), + false, + false)) { + return -1; + } } return omit_parens ? 0 : append_charp(writer, ")"); @@ -678,18 +643,19 @@ append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_keyword(_PyUnicodeWriter *writer, keyword_ty kw) { - int ret; if (kw->arg == NULL) { - ret = append_charp(writer, "**"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "**")) { + return -1; + } } else { - ret = _PyUnicodeWriter_WriteStr(writer, kw->arg); - if (ret == -1) - return ret; + if (-1 == _PyUnicodeWriter_WriteStr(writer, kw->arg)) { + return -1; + } - ret = append_charp(writer, "="); + if (-1 == append_charp(writer, "=")) { + return -1; + } } return append_ast_expr(writer, kw->value, false, false); @@ -698,51 +664,48 @@ append_ast_keyword(_PyUnicodeWriter *writer, keyword_ty kw) static int append_ast_call(_PyUnicodeWriter *writer, expr_ty e) { - int ret; bool first; Py_ssize_t i, arg_count, kw_count; expr_ty expr; keyword_ty kw; - ret = append_ast_expr(writer, e->v.Call.func, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.Call.func, false, false)) { + return -1; + } - ret = append_charp(writer, "("); - if (ret == -1) - return ret; + 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) + if (first) { first = false; - else { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + } + else if (-1 == append_charp(writer, ", ")) { + return -1; } expr = (expr_ty)asdl_seq_GET(e->v.Call.args, i); - ret = append_ast_expr(writer, expr, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, expr, false, false)) { + return -1; + } } kw_count = asdl_seq_LEN(e->v.Call.keywords); for (i = 0; i < kw_count; i++) { - if (first) + if (first) { first = false; - else { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + } + else if (-1 == append_charp(writer, ", ")) { + return -1; } kw = (keyword_ty)asdl_seq_GET(e->v.Call.keywords, i); - ret = append_ast_keyword(writer, kw); - if (ret == -1) - return ret; + if (-1 == append_ast_keyword(writer, kw)) { + return -1; + } } return append_charp(writer, ")"); @@ -751,17 +714,24 @@ append_ast_call(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_attribute(_PyUnicodeWriter *writer, expr_ty e) { - int ret; - - ret = append_ast_expr(writer, e->v.Attribute.value, false, false); - if (ret == -1) - return ret; + const char *period; + if (-1 == append_ast_expr(writer, e->v.Attribute.value, false, 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. */ - ret = append_charp(writer, e->kind == Num_kind ? " ." : "."); - if (ret == -1) - return ret; + 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); } @@ -769,51 +739,47 @@ append_ast_attribute(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_simple_slice(_PyUnicodeWriter *writer, slice_ty slice) { - int ret; if (slice->v.Slice.lower) { - ret = append_ast_expr(writer, slice->v.Slice.lower, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, slice->v.Slice.lower, false, false)) { + return -1; + } + } + + if (-1 == append_charp(writer, ":")) { + return -1; } - ret = append_charp(writer, ":"); - if (ret == -1) - return ret; if (slice->v.Slice.upper) { - ret = append_ast_expr(writer, slice->v.Slice.upper, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, slice->v.Slice.upper, false, false)) { + return -1; + } } - ret = append_charp(writer, ":"); - if (ret == -1) - return ret; if (slice->v.Slice.step) { - ret = append_ast_expr(writer, slice->v.Slice.step, false, false); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, ":")) { + return -1; + } + if (-1 == append_ast_expr(writer, slice->v.Slice.step, false, false)) { + return -1; + } } - return ret; + return 0; } static int append_ast_ext_slice(_PyUnicodeWriter *writer, slice_ty slice) { - int ret; Py_ssize_t i, dims_count; dims_count = asdl_seq_LEN(slice->v.ExtSlice.dims); for (i = 0; i < dims_count; i++) { - ret = append_ast_expr(writer, - (expr_ty)asdl_seq_GET(slice->v.ExtSlice.dims, i), - false, - false); - if (ret == -1) - return ret; - - if (i + 1 < dims_count) { - ret = append_charp(writer, ", "); - if (ret == -1) - return ret; + 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, + false)) { + return -1; } } return 0; @@ -839,19 +805,17 @@ append_ast_slice(_PyUnicodeWriter *writer, slice_ty slice, bool omit_parens) static int append_ast_subscript(_PyUnicodeWriter *writer, expr_ty e) { - int ret; - - ret = append_ast_expr(writer, e->v.Subscript.value, false, false); - if (ret == -1) - return ret; + if (-1 == append_ast_expr(writer, e->v.Subscript.value, false, false)) { + return -1; + } - ret = append_charp(writer, "["); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "[")) { + return -1; + } - ret = append_ast_slice(writer, e->v.Subscript.slice, true); - if (ret == -1) - return ret; + if (-1 == append_ast_slice(writer, e->v.Subscript.slice, true)) { + return -1; + } return append_charp(writer, "]"); } @@ -859,11 +823,9 @@ append_ast_subscript(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_starred(_PyUnicodeWriter *writer, expr_ty e) { - int ret; - - ret = append_charp(writer, "*"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, "*")) { + return -1; + } return append_ast_expr(writer, e->v.Starred.value, false, false); } @@ -871,21 +833,18 @@ append_ast_starred(_PyUnicodeWriter *writer, expr_ty e) static int append_ast_yield(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } - ret = append_charp(writer, e->v.Yield.value ? "yield " : "yield"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, e->v.Yield.value ? "yield " : "yield")) { + return -1; + } - if (e->v.Yield.value) - { - ret = append_ast_expr(writer, e->v.Yield.value, false, false); - if (ret == -1) - return ret; + if (e->v.Yield.value) { + if (-1 == append_ast_expr(writer, e->v.Yield.value, false, false)) { + return -1; + } } return omit_parens ? 0 : append_charp(writer, ")"); } @@ -893,21 +852,19 @@ append_ast_yield(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_yield_from(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } - ret = append_charp(writer, e->v.YieldFrom.value ? "yield from " : "yield from"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, + e->v.YieldFrom.value ? "yield from " : "yield from")) { + return -1; + } - if (e->v.Yield.value) - { - ret = append_ast_expr(writer, e->v.YieldFrom.value, false, false); - if (ret == -1) - return ret; + if (e->v.YieldFrom.value) { + if (-1 == append_ast_expr(writer, e->v.YieldFrom.value, false, false)) { + return -1; + } } return omit_parens ? 0 : append_charp(writer, ")"); } @@ -915,21 +872,18 @@ append_ast_yield_from(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) static int append_ast_await(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { - int ret; - - ret = omit_parens ? 0 : append_charp(writer, "("); - if (ret == -1) - return ret; + if (!omit_parens && -1 == append_charp(writer, "(")) { + return -1; + } - ret = append_charp(writer, e->v.Await.value ? "await " : "await"); - if (ret == -1) - return ret; + if (-1 == append_charp(writer, e->v.Await.value ? "await " : "await")) { + return -1; + } - if (e->v.Yield.value) - { - ret = append_ast_expr(writer, e->v.Await.value, false, false); - if (ret == -1) - return ret; + if (e->v.Await.value) { + if (-1 == append_ast_expr(writer, e->v.Await.value, false, false)) { + return -1; + } } return omit_parens ? 0 : append_charp(writer, ")"); } @@ -938,8 +892,9 @@ static int append_ast_str(_PyUnicodeWriter *writer, expr_ty e, bool omit_string_brackets) { PyObject *s = e->v.Str.s; - if (omit_string_brackets) + if (omit_string_brackets) { return _PyUnicodeWriter_WriteStr(writer, s); + } return append_repr(writer, s); } @@ -1023,16 +978,13 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens, } PyObject * -PyAST_UnicodeFromAstExpr(expr_ty e, bool omit_parens, bool omit_string_brackets) +_PyAST_ExprAsUnicode(expr_ty e, bool omit_parens, bool omit_string_brackets) { _PyUnicodeWriter writer; - int ret; - _PyUnicodeWriter_Init(&writer); writer.min_length = 256; writer.overallocate = 1; - ret = append_ast_expr(&writer, e, omit_parens, omit_string_brackets); - if (ret == -1) { + if (-1 == append_ast_expr(&writer, e, omit_parens, omit_string_brackets)) { _PyUnicodeWriter_Dealloc(&writer); return NULL; } diff --git a/Python/compile.c b/Python/compile.c index c4250bfcf1c51c..7c2eb02b204c99 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1661,10 +1661,11 @@ compiler_visit_kwonlydefaults(struct compiler *c, asdl_seq *kwonlyargs, static int compiler_visit_annexpr(struct compiler *c, expr_ty annotation) { - static PyObject *ann_as_str; - ann_as_str = PyAST_UnicodeFromAstExpr(annotation, 1, 1); - if (!ann_as_str) + PyObject *ann_as_str; + ann_as_str = _PyAST_ExprAsUnicode(annotation, 1, 1); + if (!ann_as_str) { return 0; + } ADDOP_N(c, LOAD_CONST, ann_as_str, consts); return 1; } @@ -1675,10 +1676,12 @@ compiler_visit_argannotation(struct compiler *c, identifier id, { if (annotation) { PyObject *mangled; - if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) + if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) { VISIT(c, annexpr, annotation) - else + } + else { VISIT(c, expr, annotation); + } mangled = _Py_Mangle(c->u->u_private, id); if (!mangled) return 0; @@ -4660,10 +4663,12 @@ compiler_annassign(struct compiler *c, stmt_ty s) if (!mangled) { return 0; } - if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) + if (c->c_future->ff_features & CO_FUTURE_ANNOTATIONS) { VISIT(c, annexpr, s->v.AnnAssign.annotation) - else + } + else { VISIT(c, expr, s->v.AnnAssign.annotation); + } /* ADDOP_N decrefs its argument */ ADDOP_N(c, STORE_ANNOTATION, mangled, names); } From 08f86007bae1f0df2b4cbf5e76ec46f3c73746ac Mon Sep 17 00:00:00 2001 From: Lukasz Langa Date: Sun, 31 Dec 2017 00:54:31 -0800 Subject: [PATCH 5/7] Strings are no longer treated special See: https://www.python.org/dev/peps/pep-0563/#passing-string-literals-in-annotations-verbatim-to-annotations --- Include/ast.h | 3 +- Lib/test/test_future.py | 5 +- Python/ast_unparse.c | 100 ++++++++++++++++------------------------ Python/compile.c | 2 +- 4 files changed, 44 insertions(+), 66 deletions(-) diff --git a/Include/ast.h b/Include/ast.h index da4b55376a9f75..639c4f82325a6b 100644 --- a/Include/ast.h +++ b/Include/ast.h @@ -21,8 +21,7 @@ PyAPI_FUNC(mod_ty) PyAST_FromNodeObject( /* _PyAST_ExprAsUnicode is defined in ast_unparse.c */ PyAPI_FUNC(PyObject *) _PyAST_ExprAsUnicode( expr_ty e, - int omit_parens, - int omit_string_brackets); + int omit_parens); #endif /* !Py_LIMITED_API */ diff --git a/Lib/test/test_future.py b/Lib/test/test_future.py index 5891bbd9d4d781..131ecfb3d1d66b 100644 --- a/Lib/test/test_future.py +++ b/Lib/test/test_future.py @@ -133,10 +133,7 @@ def assertAnnotationEqual( self, annotation, drop_parens=False, is_tuple=False ): actual = self.getActual(annotation) - if annotation.startswith("'"): - # strings are passed verbatim - expected = eval(annotation) - elif is_tuple: + if is_tuple: expected = annotation[1:-1] else: expected = annotation diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c index 0196fdd7707cb4..f3e4b44ff74385 100644 --- a/Python/ast_unparse.c +++ b/Python/ast_unparse.c @@ -4,8 +4,7 @@ /* Forward declaration for recursion via helper functions. */ static int -append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens, - bool omit_string_brackets); +append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens); static int append_charp(_PyUnicodeWriter *writer, const char *charp) @@ -43,7 +42,6 @@ append_ast_boolop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) if (-1 == append_ast_expr(writer, (expr_ty)asdl_seq_GET(values, 0), - false, false)) { return -1; } @@ -56,7 +54,6 @@ append_ast_boolop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) if (-1 == append_ast_expr(writer, (expr_ty)asdl_seq_GET(values, i), - false, false)) { return -1; } @@ -74,7 +71,7 @@ append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - if (-1 == append_ast_expr(writer, e->v.BinOp.left, false, false)) { + if (-1 == append_ast_expr(writer, e->v.BinOp.left, false)) { return -1; } @@ -98,7 +95,7 @@ append_ast_binop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - if (-1 == append_ast_expr(writer, e->v.BinOp.right, false, false)) { + if (-1 == append_ast_expr(writer, e->v.BinOp.right, false)) { return -1; } @@ -125,7 +122,7 @@ append_ast_unaryop(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - if (-1 == append_ast_expr(writer, e->v.UnaryOp.operand, false, false)) { + if (-1 == append_ast_expr(writer, e->v.UnaryOp.operand, false)) { return -1; } @@ -142,7 +139,7 @@ append_ast_arg(_PyUnicodeWriter *writer, arg_ty arg) if (-1 == append_charp(writer, ": ")) { return -1; } - if (-1 == append_ast_expr(writer, arg->annotation, true, false)) { + if (-1 == append_ast_expr(writer, arg->annotation, true)) { return -1; } } @@ -181,7 +178,7 @@ append_ast_args(_PyUnicodeWriter *writer, arguments_ty args) return -1; } default_ = (expr_ty)asdl_seq_GET(args->defaults, di); - if (-1 == append_ast_expr(writer, default_, false, false)) { + if (-1 == append_ast_expr(writer, default_, false)) { return -1; } } @@ -229,7 +226,7 @@ append_ast_args(_PyUnicodeWriter *writer, arguments_ty args) return -1; } default_ = (expr_ty)asdl_seq_GET(args->kw_defaults, di); - if (-1 == append_ast_expr(writer, default_, false, false)) { + if (-1 == append_ast_expr(writer, default_, false)) { return -1; } } @@ -275,7 +272,7 @@ append_ast_lambda(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - if (-1 == append_ast_expr(writer, e->v.Lambda.body, true, false)) { + if (-1 == append_ast_expr(writer, e->v.Lambda.body, true)) { return -1; } @@ -289,7 +286,7 @@ append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - if (-1 == append_ast_expr(writer, e->v.IfExp.body, false, false)) { + if (-1 == append_ast_expr(writer, e->v.IfExp.body, false)) { return -1; } @@ -297,7 +294,7 @@ append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - if (-1 == append_ast_expr(writer, e->v.IfExp.test, false, false)) { + if (-1 == append_ast_expr(writer, e->v.IfExp.test, false)) { return -1; } @@ -305,7 +302,7 @@ append_ast_ifexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - if (-1 == append_ast_expr(writer, e->v.IfExp.orelse, false, false)) { + if (-1 == append_ast_expr(writer, e->v.IfExp.orelse, false)) { return -1; } @@ -330,7 +327,7 @@ append_ast_dict(_PyUnicodeWriter *writer, expr_ty e) } 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, false)) { + if (-1 == append_ast_expr(writer, key_node, false)) { return -1; } @@ -343,7 +340,7 @@ append_ast_dict(_PyUnicodeWriter *writer, expr_ty e) } value_node = (expr_ty)asdl_seq_GET(e->v.Dict.values, i); - if (-1 == append_ast_expr(writer, value_node, false, false)) { + if (-1 == append_ast_expr(writer, value_node, false)) { return -1; } } @@ -368,7 +365,7 @@ append_ast_set(_PyUnicodeWriter *writer, expr_ty e) } elem_node = (expr_ty)asdl_seq_GET(e->v.Set.elts, i); - if (-1 == append_ast_expr(writer, elem_node, false, false)) { + if (-1 == append_ast_expr(writer, elem_node, false)) { return -1; } } @@ -392,7 +389,7 @@ append_ast_list(_PyUnicodeWriter *writer, expr_ty e) return -1; } elem_node = (expr_ty)asdl_seq_GET(e->v.List.elts, i); - if (-1 == append_ast_expr(writer, elem_node, false, false)) { + if (-1 == append_ast_expr(writer, elem_node, false)) { return -1; } } @@ -419,7 +416,7 @@ append_ast_tuple(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } elem_node = (expr_ty)asdl_seq_GET(e->v.Tuple.elts, i); - if (-1 == append_ast_expr(writer, elem_node, false, false)) { + if (-1 == append_ast_expr(writer, elem_node, false)) { return -1; } } @@ -440,7 +437,7 @@ append_ast_comprehension(_PyUnicodeWriter *writer, comprehension_ty gen) return -1; } - if (-1 == append_ast_expr(writer, gen->target, true, false)) { + if (-1 == append_ast_expr(writer, gen->target, true)) { return -1; } @@ -448,7 +445,7 @@ append_ast_comprehension(_PyUnicodeWriter *writer, comprehension_ty gen) return -1; } - if (-1 == append_ast_expr(writer, gen->iter, false, false)) { + if (-1 == append_ast_expr(writer, gen->iter, false)) { return -1; } @@ -460,7 +457,6 @@ append_ast_comprehension(_PyUnicodeWriter *writer, comprehension_ty gen) if (-1 == append_ast_expr(writer, (expr_ty)asdl_seq_GET(gen->ifs, i), - false, false)) { return -1; } @@ -492,7 +488,7 @@ append_ast_genexp(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) return -1; } - if (-1 == append_ast_expr(writer, e->v.GeneratorExp.elt, false, false)) { + if (-1 == append_ast_expr(writer, e->v.GeneratorExp.elt, false)) { return -1; } @@ -510,7 +506,7 @@ append_ast_listcomp(_PyUnicodeWriter *writer, expr_ty e) return -1; } - if (-1 == append_ast_expr(writer, e->v.ListComp.elt, false, false)) { + if (-1 == append_ast_expr(writer, e->v.ListComp.elt, false)) { return -1; } @@ -528,7 +524,7 @@ append_ast_setcomp(_PyUnicodeWriter *writer, expr_ty e) return -1; } - if (-1 == append_ast_expr(writer, e->v.SetComp.elt, false, false)) { + if (-1 == append_ast_expr(writer, e->v.SetComp.elt, false)) { return -1; } @@ -546,7 +542,7 @@ append_ast_dictcomp(_PyUnicodeWriter *writer, expr_ty e) return -1; } - if (-1 == append_ast_expr(writer, e->v.DictComp.key, false, false)) { + if (-1 == append_ast_expr(writer, e->v.DictComp.key, false)) { return -1; } @@ -554,7 +550,7 @@ append_ast_dictcomp(_PyUnicodeWriter *writer, expr_ty e) return -1; } - if (-1 == append_ast_expr(writer, e->v.DictComp.value, false, false)) { + if (-1 == append_ast_expr(writer, e->v.DictComp.value, false)) { return -1; } @@ -583,7 +579,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, false)) { + if (-1 == append_ast_expr(writer, e->v.Compare.left, false)) { return -1; } @@ -631,7 +627,6 @@ append_ast_compare(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) if (-1 == append_ast_expr(writer, (expr_ty)asdl_seq_GET(comparators, i), - false, false)) { return -1; } @@ -658,7 +653,7 @@ append_ast_keyword(_PyUnicodeWriter *writer, keyword_ty kw) } } - return append_ast_expr(writer, kw->value, false, false); + return append_ast_expr(writer, kw->value, false); } static int @@ -669,7 +664,7 @@ append_ast_call(_PyUnicodeWriter *writer, expr_ty e) expr_ty expr; keyword_ty kw; - if (-1 == append_ast_expr(writer, e->v.Call.func, false, false)) { + if (-1 == append_ast_expr(writer, e->v.Call.func, false)) { return -1; } @@ -688,7 +683,7 @@ append_ast_call(_PyUnicodeWriter *writer, expr_ty e) } expr = (expr_ty)asdl_seq_GET(e->v.Call.args, i); - if (-1 == append_ast_expr(writer, expr, false, false)) { + if (-1 == append_ast_expr(writer, expr, false)) { return -1; } } @@ -715,7 +710,7 @@ static int append_ast_attribute(_PyUnicodeWriter *writer, expr_ty e) { const char *period; - if (-1 == append_ast_expr(writer, e->v.Attribute.value, false, false)) { + if (-1 == append_ast_expr(writer, e->v.Attribute.value, false)) { return -1; } @@ -740,7 +735,7 @@ 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, false)) { + if (-1 == append_ast_expr(writer, slice->v.Slice.lower, false)) { return -1; } } @@ -750,7 +745,7 @@ append_ast_simple_slice(_PyUnicodeWriter *writer, slice_ty slice) } if (slice->v.Slice.upper) { - if (-1 == append_ast_expr(writer, slice->v.Slice.upper, false, false)) { + if (-1 == append_ast_expr(writer, slice->v.Slice.upper, false)) { return -1; } } @@ -759,7 +754,7 @@ append_ast_simple_slice(_PyUnicodeWriter *writer, slice_ty slice) if (-1 == append_charp(writer, ":")) { return -1; } - if (-1 == append_ast_expr(writer, slice->v.Slice.step, false, false)) { + if (-1 == append_ast_expr(writer, slice->v.Slice.step, false)) { return -1; } } @@ -777,7 +772,6 @@ append_ast_ext_slice(_PyUnicodeWriter *writer, slice_ty slice) } if (-1 == append_ast_expr(writer, (expr_ty)asdl_seq_GET(slice->v.ExtSlice.dims, i), - false, false)) { return -1; } @@ -794,7 +788,7 @@ append_ast_slice(_PyUnicodeWriter *writer, slice_ty slice, bool omit_parens) case ExtSlice_kind: return append_ast_ext_slice(writer, slice); case Index_kind: - return append_ast_expr(writer, slice->v.Index.value, omit_parens, false); + return append_ast_expr(writer, slice->v.Index.value, omit_parens); default: PyErr_SetString(PyExc_SystemError, "unexpected slice kind"); @@ -805,7 +799,7 @@ 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, false)) { + if (-1 == append_ast_expr(writer, e->v.Subscript.value, false)) { return -1; } @@ -827,7 +821,7 @@ append_ast_starred(_PyUnicodeWriter *writer, expr_ty e) return -1; } - return append_ast_expr(writer, e->v.Starred.value, false, false); + return append_ast_expr(writer, e->v.Starred.value, false); } static int @@ -842,7 +836,7 @@ append_ast_yield(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) } if (e->v.Yield.value) { - if (-1 == append_ast_expr(writer, e->v.Yield.value, false, false)) { + if (-1 == append_ast_expr(writer, e->v.Yield.value, false)) { return -1; } } @@ -862,7 +856,7 @@ append_ast_yield_from(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) } if (e->v.YieldFrom.value) { - if (-1 == append_ast_expr(writer, e->v.YieldFrom.value, false, false)) { + if (-1 == append_ast_expr(writer, e->v.YieldFrom.value, false)) { return -1; } } @@ -881,7 +875,7 @@ append_ast_await(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) } if (e->v.Await.value) { - if (-1 == append_ast_expr(writer, e->v.Await.value, false, false)) { + if (-1 == append_ast_expr(writer, e->v.Await.value, false)) { return -1; } } @@ -889,19 +883,7 @@ append_ast_await(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) } static int -append_ast_str(_PyUnicodeWriter *writer, expr_ty e, bool omit_string_brackets) -{ - PyObject *s = e->v.Str.s; - if (omit_string_brackets) { - return _PyUnicodeWriter_WriteStr(writer, s); - } - - return append_repr(writer, s); -} - -static int -append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens, - bool omit_string_brackets) +append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) { switch (e->kind) { case BoolOp_kind: @@ -941,7 +923,7 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens, case Num_kind: return append_repr(writer, e->v.Num.n); case Str_kind: - return append_ast_str(writer, e, omit_string_brackets); + return append_repr(writer, e->v.Str.s); case JoinedStr_kind: PyErr_SetString(PyExc_SystemError, "f-string support in annotations not implemented yet"); @@ -978,13 +960,13 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens, } PyObject * -_PyAST_ExprAsUnicode(expr_ty e, bool omit_parens, bool omit_string_brackets) +_PyAST_ExprAsUnicode(expr_ty e, bool omit_parens) { _PyUnicodeWriter writer; _PyUnicodeWriter_Init(&writer); writer.min_length = 256; writer.overallocate = 1; - if (-1 == append_ast_expr(&writer, e, omit_parens, omit_string_brackets)) { + if (-1 == append_ast_expr(&writer, e, omit_parens)) { _PyUnicodeWriter_Dealloc(&writer); return NULL; } diff --git a/Python/compile.c b/Python/compile.c index 7c2eb02b204c99..9770aedc4db10b 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -1662,7 +1662,7 @@ static int compiler_visit_annexpr(struct compiler *c, expr_ty annotation) { PyObject *ann_as_str; - ann_as_str = _PyAST_ExprAsUnicode(annotation, 1, 1); + ann_as_str = _PyAST_ExprAsUnicode(annotation, 1); if (!ann_as_str) { return 0; } From de9e5b0f1bde23db138c8d1dcf0dcec31b321b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Mon, 15 Jan 2018 13:59:16 -0800 Subject: [PATCH 6/7] f-string support --- Lib/test/test_future.py | 23 ++--- Python/ast_unparse.c | 209 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 207 insertions(+), 25 deletions(-) diff --git a/Lib/test/test_future.py b/Lib/test/test_future.py index 131ecfb3d1d66b..29c4632e7e5044 100644 --- a/Lib/test/test_future.py +++ b/Lib/test/test_future.py @@ -130,13 +130,11 @@ def getActual(self, annotation): return func_ret_ann def assertAnnotationEqual( - self, annotation, drop_parens=False, is_tuple=False + self, annotation, expected=None, drop_parens=False, is_tuple=False, ): actual = self.getActual(annotation) - if is_tuple: - expected = annotation[1:-1] - else: - expected = 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(")", "") @@ -225,16 +223,11 @@ def test_annotations(self): eq("slice[1:]") eq("slice[::-1]") eq('(str or None) if (sys.version_info[0] > (3,)) else (str or bytes or None)') - - def test_annotations_no_fstring_support_implemented(self): - # FIXME: Add f-string support in ast_unparse.c. - eq = self.assertAnnotationEqual - with self.assertRaises(SystemError) as err: - eq("""f'some f-string with {a} {few():.2f} {formatted.values!r}'""") - self.assertEqual( - str(err.exception), - "f-string support in annotations not implemented yet", - ) + 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 diff --git a/Python/ast_unparse.c b/Python/ast_unparse.c index f3e4b44ff74385..ef9e948dc8254c 100644 --- a/Python/ast_unparse.c +++ b/Python/ast_unparse.c @@ -2,9 +2,20 @@ #include "Python.h" #include "Python-ast.h" -/* Forward declaration for recursion via helper functions. */ +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) @@ -706,6 +717,158 @@ append_ast_call(_PyUnicodeWriter *writer, expr_ty e) 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) { @@ -925,13 +1088,9 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) case Str_kind: return append_repr(writer, e->v.Str.s); case JoinedStr_kind: - PyErr_SetString(PyExc_SystemError, - "f-string support in annotations not implemented yet"); - return -1; + return append_joinedstr(writer, e, false); case FormattedValue_kind: - PyErr_SetString(PyExc_SystemError, - "f-string support in annotations not implemented yet"); - return -1; + return append_formattedvalue(writer, e, false); case Bytes_kind: return append_repr(writer, e->v.Bytes.s); case Ellipsis_kind: @@ -959,16 +1118,46 @@ append_ast_expr(_PyUnicodeWriter *writer, expr_ty e, bool omit_parens) } } -PyObject * -_PyAST_ExprAsUnicode(expr_ty e, bool omit_parens) +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 == append_ast_expr(&writer, e, omit_parens)) { + 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); +} From 26c028c3356eea73b3c7b8c7bf02ee272beb1ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Langa?= Date: Thu, 25 Jan 2018 14:28:28 -0800 Subject: [PATCH 7/7] compatibility typo --- Doc/whatsnew/3.7.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index 5cc04310daaf56..a25ad82539f508 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -214,7 +214,7 @@ following syntax valid:: class B: ... -Since this change breaks compatibiltiy, the new behavior can be enabled +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::