From fa2e4e9d046a9554bc201a0d5a1f7ef739c82bd3 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Mon, 28 Jun 2010 21:14:17 +0000 Subject: Update Demo/parser/unparse.py to current Python 3.x syntax. Additions: - relative imports - keyword-only arguments - function annotations - class decorators - raise ... from ... - except ... as ... - nonlocal - bytes literals - set literals - set comprehensions - dict comprehensions Removals: - print statement. Some of this should be backported to 2.x. --- Demo/parser/test_unparse.py | 83 ++++++++++++++++++++-- Demo/parser/unparse.py | 165 ++++++++++++++++++++++++++++++-------------- 2 files changed, 194 insertions(+), 54 deletions(-) diff --git a/Demo/parser/test_unparse.py b/Demo/parser/test_unparse.py index edc9ee1..5832b8f 100644 --- a/Demo/parser/test_unparse.py +++ b/Demo/parser/test_unparse.py @@ -6,7 +6,7 @@ import ast import _ast import unparse -forelse = """\ +for_else = """\ def f(): for x in range(10): break @@ -15,7 +15,7 @@ def f(): z = 3 """ -whileelse = """\ +while_else = """\ def g(): while True: break @@ -24,6 +24,37 @@ def g(): z = 3 """ +relative_import = """\ +from . import fred +from .. import barney +from .australia import shrimp as prawns +""" + +nonlocal_ex = """\ +def f(): + x = 1 + def g(): + nonlocal x + x = 2 + y = 7 + def h(): + nonlocal x, y +""" + +# also acts as test for 'except ... as ...' +raise_from = """\ +try: + 1 / 0 +except ZeroDivisionError as e: + raise ArithmeticError from e +""" + +class_decorator = """\ +@f1(arg) +@f2 +class Foo: pass +""" + class UnparseTestCase(unittest.TestCase): # Tests for specific bugs found in earlier versions of unparse @@ -43,10 +74,10 @@ class UnparseTestCase(unittest.TestCase): self.check_roundtrip("13 >> 7") def test_for_else(self): - self.check_roundtrip(forelse) + self.check_roundtrip(for_else) def test_while_else(self): - self.check_roundtrip(whileelse) + self.check_roundtrip(while_else) def test_unary_parens(self): self.check_roundtrip("(-1)**7") @@ -57,6 +88,50 @@ class UnparseTestCase(unittest.TestCase): self.check_roundtrip("1 < 4 <= 5") self.check_roundtrip("a is b is c is not d") + def test_function_arguments(self): + self.check_roundtrip("def f(): pass") + self.check_roundtrip("def f(a): pass") + self.check_roundtrip("def f(b = 2): pass") + self.check_roundtrip("def f(a, b): pass") + self.check_roundtrip("def f(a, b = 2): pass") + self.check_roundtrip("def f(a = 5, b = 2): pass") + self.check_roundtrip("def f(*, a = 1, b = 2): pass") + self.check_roundtrip("def f(*, a = 1, b): pass") + self.check_roundtrip("def f(*, a, b = 2): pass") + self.check_roundtrip("def f(a, b = None, *, c, **kwds): pass") + self.check_roundtrip("def f(a=2, *args, c=5, d, **kwds): pass") + self.check_roundtrip("def f(*args, **kwargs): pass") + + def test_relative_import(self): + self.check_roundtrip(relative_import) + + def test_nonlocal(self): + self.check_roundtrip(nonlocal_ex) + + def test_raise_from(self): + self.check_roundtrip(raise_from) + + def test_bytes(self): + self.check_roundtrip("b'123'") + + def test_annotations(self): + self.check_roundtrip("def f(a : int): pass") + self.check_roundtrip("def f(a: int = 5): pass") + self.check_roundtrip("def f(*args: [int]): pass") + self.check_roundtrip("def f(**kwargs: dict): pass") + self.check_roundtrip("def f() -> None: pass") + + def test_set_literal(self): + self.check_roundtrip("{'a', 'b', 'c'}") + + def test_set_comprehension(self): + self.check_roundtrip("{x for x in range(5)}") + + def test_dict_comprehension(self): + self.check_roundtrip("{x: x*x for x in range(10)}") + + def test_class_decorators(self): + self.check_roundtrip(class_decorator) def test_main(): test.support.run_unittest(UnparseTestCase) diff --git a/Demo/parser/unparse.py b/Demo/parser/unparse.py index 0d62e54..715f98b 100644 --- a/Demo/parser/unparse.py +++ b/Demo/parser/unparse.py @@ -1,6 +1,6 @@ "Usage: unparse.py " import sys -import _ast +import ast import io import os @@ -20,7 +20,7 @@ def interleave(inter, f, seq): class Unparser: """Methods in this class recursively traverse an AST and output source code for the abstract syntax; original formatting - is disregarged. """ + is disregarded. """ def __init__(self, tree, file = sys.stdout): """Unparser(tree, file=sys.stdout) -> None. @@ -80,10 +80,11 @@ class Unparser: def _ImportFrom(self, t): self.fill("from ") - self.write(t.module) + self.write("." * t.level) + if t.module: + self.write(t.module) self.write(" import ") interleave(lambda: self.write(", "), self.dispatch, t.names) - # XXX(jpe) what is level for? def _Assign(self, t): self.fill() @@ -124,24 +125,14 @@ class Unparser: self.write(", ") self.dispatch(t.msg) - def _Print(self, t): - self.fill("print ") - do_comma = False - if t.dest: - self.write(">>") - self.dispatch(t.dest) - do_comma = True - for e in t.values: - if do_comma:self.write(", ") - else:do_comma=True - self.dispatch(e) - if not t.nl: - self.write(",") - def _Global(self, t): self.fill("global ") interleave(lambda: self.write(", "), self.write, t.names) + def _Nonlocal(self, t): + self.fill("nonlocal ") + interleave(lambda: self.write(", "), self.write, t.names) + def _Yield(self, t): self.write("(") self.write("yield") @@ -151,15 +142,15 @@ class Unparser: self.write(")") def _Raise(self, t): - self.fill('raise ') - if t.type: - self.dispatch(t.type) - if t.inst: - self.write(", ") - self.dispatch(t.inst) - if t.tback: - self.write(", ") - self.dispatch(t.tback) + self.fill("raise") + if not t.exc: + assert not t.cause + return + self.write(" ") + self.dispatch(t.exc) + if t.cause: + self.write(" from ") + self.dispatch(t.cause) def _TryExcept(self, t): self.fill("try") @@ -192,21 +183,40 @@ class Unparser: self.write(" ") self.dispatch(t.type) if t.name: - self.write(", ") - self.dispatch(t.name) + self.write(" as ") + self.write(t.name) self.enter() self.dispatch(t.body) self.leave() def _ClassDef(self, t): self.write("\n") + for deco in t.decorator_list: + self.fill("@") + self.dispatch(deco) self.fill("class "+t.name) - if t.bases: - self.write("(") - for a in t.bases: - self.dispatch(a) - self.write(", ") - self.write(")") + self.write("(") + comma = False + for e in t.bases: + if comma: self.write(", ") + else: comma = True + self.dispatch(e) + for e in t.keywords: + if comma: self.write(", ") + else: comma = True + self.dispatch(e) + if t.starargs: + if comma: self.write(", ") + else: comma = True + self.write("*") + self.dispatch(t.starargs) + if t.kwargs: + if comma: self.write(", ") + else: comma = True + self.write("*") + self.dispatch(t.kwargs) + self.write(")") + self.enter() self.dispatch(t.body) self.leave() @@ -219,6 +229,9 @@ class Unparser: self.fill("def "+t.name + "(") self.dispatch(t.args) self.write(")") + if t.returns: + self.write(" -> ") + self.dispatch(t.returns) self.enter() self.dispatch(t.body) self.leave() @@ -273,6 +286,9 @@ class Unparser: self.leave() # expr + def _Bytes(self, t): + self.write(repr(t.s)) + def _Str(self, tree): self.write(repr(tree.s)) @@ -294,7 +310,7 @@ class Unparser: self.write(strnum) self.write(")") else: - self.write(repr(t.n)) + self.write(strnum) def _List(self, t): self.write("[") @@ -315,6 +331,22 @@ class Unparser: self.dispatch(gen) self.write(")") + def _SetComp(self, t): + self.write("{") + self.dispatch(t.elt) + for gen in t.generators: + self.dispatch(gen) + self.write("}") + + def _DictComp(self, t): + self.write("{") + self.dispatch(t.key) + self.write(": ") + self.dispatch(t.value) + for gen in t.generators: + self.dispatch(gen) + self.write("}") + def _comprehension(self, t): self.write(" for ") self.dispatch(t.target) @@ -333,14 +365,20 @@ class Unparser: self.dispatch(t.orelse) self.write(")") + def _Set(self, t): + assert(t.elts) # should be at least one element + self.write("{") + interleave(lambda: self.write(", "), self.dispatch, t.elts) + self.write("}") + def _Dict(self, t): self.write("{") - def writem(xxx_todo_changeme): - (k, v) = xxx_todo_changeme + def write_pair(pair): + (k, v) = pair self.dispatch(k) self.write(": ") self.dispatch(v) - interleave(lambda: self.write(", "), writem, zip(t.keys, t.values)) + interleave(lambda: self.write(", "), write_pair, zip(t.keys, t.values)) self.write("}") def _Tuple(self, t): @@ -381,7 +419,7 @@ class Unparser: self.dispatch(e) self.write(")") - boolops = {_ast.And: 'and', _ast.Or: 'or'} + boolops = {ast.And: 'and', ast.Or: 'or'} def _BoolOp(self, t): self.write("(") s = " %s " % self.boolops[t.op.__class__] @@ -443,28 +481,55 @@ class Unparser: def _ExtSlice(self, t): interleave(lambda: self.write(', '), self.dispatch, t.dims) + # argument + def _arg(self, t): + self.write(t.arg) + if t.annotation: + self.write(": ") + self.dispatch(t.annotation) + # others def _arguments(self, t): first = True - nonDef = len(t.args)-len(t.defaults) - for a in t.args[0:nonDef]: + # normal arguments + defaults = [None] * (len(t.args) - len(t.defaults)) + t.defaults + for a, d in zip(t.args, defaults): if first:first = False else: self.write(", ") self.dispatch(a) - for a,d in zip(t.args[nonDef:], t.defaults): - if first:first = False - else: self.write(", ") - self.dispatch(a), - self.write("=") - self.dispatch(d) - if t.vararg: + if d: + self.write("=") + self.dispatch(d) + + # varargs, or bare '*' if no varargs but keyword-only arguments present + if t.vararg or t.kwonlyargs: if first:first = False else: self.write(", ") - self.write("*"+t.vararg) + self.write("*") + if t.vararg: + self.write(t.vararg) + if t.varargannotation: + self.write(": ") + self.dispatch(t.varargannotation) + + # keyword-only arguments + if t.kwonlyargs: + for a, d in zip(t.kwonlyargs, t.kw_defaults): + if first:first = False + else: self.write(", ") + self.dispatch(a), + if d: + self.write("=") + self.dispatch(d) + + # kwargs if t.kwarg: if first:first = False else: self.write(", ") self.write("**"+t.kwarg) + if t.kwargannotation: + self.write(": ") + self.dispatch(t.kwargannotation) def _keyword(self, t): self.write(t.arg) -- cgit v0.12