diff --git a/lib/astunparse/unparser.py b/lib/astunparse/unparser.py index edf8c68..0ef6fd8 100644 --- a/lib/astunparse/unparser.py +++ b/lib/astunparse/unparser.py @@ -29,7 +29,7 @@ class Unparser: output source code for the abstract syntax; original formatting is disregarded. """ - def __init__(self, tree, file=sys.stdout): + def __init__(self, tree, file = sys.stdout): """Unparser(tree, file=sys.stdout) -> None. Print the source for tree to file.""" self.f = file @@ -89,6 +89,13 @@ class Unparser: self.fill() self.dispatch(tree.value) + def _NamedExpr(self, tree): + self.write("(") + self.dispatch(tree.target) + self.write(" := ") + self.dispatch(tree.value) + self.write(")") + def _Import(self, t): self.fill("import ") interleave(lambda: self.write(", "), self.dispatch, t.names) @@ -120,11 +127,11 @@ class Unparser: def _AnnAssign(self, t): self.fill() - if not t.simple: - self.write("(") + if not t.simple and isinstance(t.target, ast.Name): + self.write('(') self.dispatch(t.target) - if not t.simple: - self.write(")") + if not t.simple and isinstance(t.target, ast.Name): + self.write(')') self.write(": ") self.dispatch(t.annotation) if t.value: @@ -189,6 +196,14 @@ class Unparser: self.fill("nonlocal ") interleave(lambda: self.write(", "), self.write, t.names) + def _Await(self, t): + self.write("(") + self.write("await") + if t.value: + self.write(" ") + self.dispatch(t.value) + self.write(")") + def _Yield(self, t): self.write("(") self.write("yield") @@ -328,12 +343,19 @@ class Unparser: self.dispatch(t.body) self.leave() - def _generic_FunctionDef(self, t, async_=False): + def _FunctionDef(self, t): + self.__FunctionDef_helper(t, "def") + + def _AsyncFunctionDef(self, t): + self.__FunctionDef_helper(t, "async def") + + def __FunctionDef_helper(self, t, fill_suffix): self.write("\n") for deco in t.decorator_list: self.fill("@") self.dispatch(deco) - self.fill(("async " if async_ else "") + "def " + t.name + "(") + def_str = fill_suffix+" "+t.name + "(" + self.fill(def_str) self.dispatch(t.args) self.write(")") if getattr(t, "returns", False): @@ -343,14 +365,14 @@ class Unparser: self.dispatch(t.body) self.leave() - def _FunctionDef(self, t): - self._generic_FunctionDef(t) + def _For(self, t): + self.__For_helper("for ", t) - def _AsyncFunctionDef(self, t): - self._generic_FunctionDef(t, async_=True) + def _AsyncFor(self, t): + self.__For_helper("async for ", t) - def _generic_For(self, t, async_=False): - self.fill("async for " if async_ else "for ") + def __For_helper(self, fill, t): + self.fill(fill) self.dispatch(t.target) self.write(" in ") self.dispatch(t.iter) @@ -363,12 +385,6 @@ class Unparser: self.dispatch(t.orelse) self.leave() - def _For(self, t): - self._generic_For(t) - - def _AsyncFor(self, t): - self._generic_For(t, async_=True) - def _If(self, t): self.fill("if ") self.dispatch(t.test) @@ -586,8 +604,9 @@ class Unparser: def _comprehension(self, t): if getattr(t, 'is_async', False): - self.write(" async") - self.write(" for ") + self.write(" async for ") + else: + self.write(" for ") self.dispatch(t.target) self.write(" in ") self.dispatch(t.iter) @@ -612,26 +631,27 @@ class Unparser: def _Dict(self, t): self.write("{") - def write_pair(pair): - (k, v) = pair + def write_key_value_pair(k, v): + self.dispatch(k) + self.write(": ") + self.dispatch(v) + + def write_item(item): + k, v = item if k is None: - self.write('**') + # for dictionary unpacking operator in dicts {**{'y': 2}} + # see PEP 448 for details + self.write("**") self.dispatch(v) else: - self.dispatch(k) - self.write(": ") - self.dispatch(v) - self.write(",") - self._indent +=1 - self.fill("") - interleave(lambda: self.fill(""), write_pair, zip(t.keys, t.values)) - self._indent -=1 - self.fill("}") + write_key_value_pair(k, v) + interleave(lambda: self.write(", "), write_item, zip(t.keys, t.values)) + self.write("}") def _Tuple(self, t): self.write("(") if len(t.elts) == 1: - (elt,) = t.elts + elt = t.elts[0] self.dispatch(elt) self.write(",") else: @@ -656,10 +676,9 @@ class Unparser: self.dispatch(t.operand) self.write(")") - binop = { "Add":"+", "Sub":"-", "Mult":"*", "Div":"/", "Mod":"%", + binop = { "Add":"+", "Sub":"-", "Mult":"*", "MatMult":"@", "Div":"/", "Mod":"%", "LShift":"<<", "RShift":">>", "BitOr":"|", "BitXor":"^", "BitAnd":"&", - "FloorDiv":"//", "Pow": "**", - "MatMult":"@"} + "FloorDiv":"//", "Pow": "**"} def _BinOp(self, t): self.write("(") self.dispatch(t.left) @@ -689,7 +708,7 @@ class Unparser: # Special case: 3.__abs__() is a syntax error, so if t.value # is an integer literal then we need to either parenthesize # it or add an extra space to get 3 .__abs__(). - if isinstance(t.value, ast.Num) and isinstance(t.value.n, int): + if isinstance(t.value, getattr(ast, 'Constant', getattr(ast, 'Num', None))) and isinstance(t.value.n, int): self.write(" ") self.write(".") self.write(t.attr) @@ -760,18 +779,22 @@ class Unparser: def _arguments(self, t): first = True # normal arguments - defaults = [None] * (len(t.args) - len(t.defaults)) + t.defaults - for a,d in zip(t.args, defaults): + all_args = getattr(t, 'posonlyargs', []) + t.args + defaults = [None] * (len(all_args) - len(t.defaults)) + t.defaults + for index, elements in enumerate(zip(all_args, defaults), 1): + a, d = elements if first:first = False else: self.write(", ") self.dispatch(a) if d: self.write("=") self.dispatch(d) + if index == len(getattr(t, 'posonlyargs', ())): + self.write(", /") # varargs, or bare '*' if no varargs but keyword-only arguments present if t.vararg or getattr(t, "kwonlyargs", False): - if first: first = False + if first:first = False else: self.write(", ") self.write("*") if t.vararg: @@ -839,14 +862,6 @@ class Unparser: self.write(" as ") self.dispatch(t.optional_vars) - def _Await(self, t): - self.write("(") - self.write("await") - if t.value: - self.write(" ") - self.dispatch(t.value) - self.write(")") - def roundtrip(filename, output=sys.stdout): if six.PY3: with open(filename, "rb") as pyfile: diff --git a/setup.py b/setup.py index 6f62fd9..e5a277a 100755 --- a/setup.py +++ b/setup.py @@ -48,11 +48,10 @@ setup( "Programming Language :: Python :: 2", 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Topic :: Software Development :: Code Generators', ], test_suite='tests', diff --git a/tests/common.py b/tests/common.py index c8db903..95b9755 100644 --- a/tests/common.py +++ b/tests/common.py @@ -215,6 +215,7 @@ class AstunparseCommonTestCase: self.check_roundtrip("not True or False") self.check_roundtrip("True or not False") + @unittest.skipUnless(sys.version_info < (3, 6), "Only works for Python < 3.6") def test_integer_parens(self): self.check_roundtrip("3 .__abs__()")