• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"Usage: unparse.py <path to source file>"
2import sys
3import ast
4import tokenize
5import io
6import os
7
8# Large float and imaginary literals get turned into infinities in the AST.
9# We unparse those infinities to INFSTR.
10INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
11
12def interleave(inter, f, seq):
13    """Call f on each item in seq, calling inter() in between.
14    """
15    seq = iter(seq)
16    try:
17        f(next(seq))
18    except StopIteration:
19        pass
20    else:
21        for x in seq:
22            inter()
23            f(x)
24
25class Unparser:
26    """Methods in this class recursively traverse an AST and
27    output source code for the abstract syntax; original formatting
28    is disregarded. """
29
30    def __init__(self, tree, file = sys.stdout):
31        """Unparser(tree, file=sys.stdout) -> None.
32         Print the source for tree to file."""
33        self.f = file
34        self._indent = 0
35        self.dispatch(tree)
36        print("", file=self.f)
37        self.f.flush()
38
39    def fill(self, text = ""):
40        "Indent a piece of text, according to the current indentation level"
41        self.f.write("\n"+"    "*self._indent + text)
42
43    def write(self, text):
44        "Append a piece of text to the current line."
45        self.f.write(text)
46
47    def enter(self):
48        "Print ':', and increase the indentation."
49        self.write(":")
50        self._indent += 1
51
52    def leave(self):
53        "Decrease the indentation level."
54        self._indent -= 1
55
56    def dispatch(self, tree):
57        "Dispatcher function, dispatching tree type T to method _T."
58        if isinstance(tree, list):
59            for t in tree:
60                self.dispatch(t)
61            return
62        meth = getattr(self, "_"+tree.__class__.__name__)
63        meth(tree)
64
65
66    ############### Unparsing methods ######################
67    # There should be one method per concrete grammar type #
68    # Constructors should be grouped by sum type. Ideally, #
69    # this would follow the order in the grammar, but      #
70    # currently doesn't.                                   #
71    ########################################################
72
73    def _Module(self, tree):
74        for stmt in tree.body:
75            self.dispatch(stmt)
76
77    # stmt
78    def _Expr(self, tree):
79        self.fill()
80        self.dispatch(tree.value)
81
82    def _Import(self, t):
83        self.fill("import ")
84        interleave(lambda: self.write(", "), self.dispatch, t.names)
85
86    def _ImportFrom(self, t):
87        self.fill("from ")
88        self.write("." * t.level)
89        if t.module:
90            self.write(t.module)
91        self.write(" import ")
92        interleave(lambda: self.write(", "), self.dispatch, t.names)
93
94    def _Assign(self, t):
95        self.fill()
96        for target in t.targets:
97            self.dispatch(target)
98            self.write(" = ")
99        self.dispatch(t.value)
100
101    def _AugAssign(self, t):
102        self.fill()
103        self.dispatch(t.target)
104        self.write(" "+self.binop[t.op.__class__.__name__]+"= ")
105        self.dispatch(t.value)
106
107    def _AnnAssign(self, t):
108        self.fill()
109        if not t.simple and isinstance(t.target, ast.Name):
110            self.write('(')
111        self.dispatch(t.target)
112        if not t.simple and isinstance(t.target, ast.Name):
113            self.write(')')
114        self.write(": ")
115        self.dispatch(t.annotation)
116        if t.value:
117            self.write(" = ")
118            self.dispatch(t.value)
119
120    def _Return(self, t):
121        self.fill("return")
122        if t.value:
123            self.write(" ")
124            self.dispatch(t.value)
125
126    def _Pass(self, t):
127        self.fill("pass")
128
129    def _Break(self, t):
130        self.fill("break")
131
132    def _Continue(self, t):
133        self.fill("continue")
134
135    def _Delete(self, t):
136        self.fill("del ")
137        interleave(lambda: self.write(", "), self.dispatch, t.targets)
138
139    def _Assert(self, t):
140        self.fill("assert ")
141        self.dispatch(t.test)
142        if t.msg:
143            self.write(", ")
144            self.dispatch(t.msg)
145
146    def _Global(self, t):
147        self.fill("global ")
148        interleave(lambda: self.write(", "), self.write, t.names)
149
150    def _Nonlocal(self, t):
151        self.fill("nonlocal ")
152        interleave(lambda: self.write(", "), self.write, t.names)
153
154    def _Await(self, t):
155        self.write("(")
156        self.write("await")
157        if t.value:
158            self.write(" ")
159            self.dispatch(t.value)
160        self.write(")")
161
162    def _Yield(self, t):
163        self.write("(")
164        self.write("yield")
165        if t.value:
166            self.write(" ")
167            self.dispatch(t.value)
168        self.write(")")
169
170    def _YieldFrom(self, t):
171        self.write("(")
172        self.write("yield from")
173        if t.value:
174            self.write(" ")
175            self.dispatch(t.value)
176        self.write(")")
177
178    def _Raise(self, t):
179        self.fill("raise")
180        if not t.exc:
181            assert not t.cause
182            return
183        self.write(" ")
184        self.dispatch(t.exc)
185        if t.cause:
186            self.write(" from ")
187            self.dispatch(t.cause)
188
189    def _Try(self, t):
190        self.fill("try")
191        self.enter()
192        self.dispatch(t.body)
193        self.leave()
194        for ex in t.handlers:
195            self.dispatch(ex)
196        if t.orelse:
197            self.fill("else")
198            self.enter()
199            self.dispatch(t.orelse)
200            self.leave()
201        if t.finalbody:
202            self.fill("finally")
203            self.enter()
204            self.dispatch(t.finalbody)
205            self.leave()
206
207    def _ExceptHandler(self, t):
208        self.fill("except")
209        if t.type:
210            self.write(" ")
211            self.dispatch(t.type)
212        if t.name:
213            self.write(" as ")
214            self.write(t.name)
215        self.enter()
216        self.dispatch(t.body)
217        self.leave()
218
219    def _ClassDef(self, t):
220        self.write("\n")
221        for deco in t.decorator_list:
222            self.fill("@")
223            self.dispatch(deco)
224        self.fill("class "+t.name)
225        self.write("(")
226        comma = False
227        for e in t.bases:
228            if comma: self.write(", ")
229            else: comma = True
230            self.dispatch(e)
231        for e in t.keywords:
232            if comma: self.write(", ")
233            else: comma = True
234            self.dispatch(e)
235        self.write(")")
236
237        self.enter()
238        self.dispatch(t.body)
239        self.leave()
240
241    def _FunctionDef(self, t):
242        self.__FunctionDef_helper(t, "def")
243
244    def _AsyncFunctionDef(self, t):
245        self.__FunctionDef_helper(t, "async def")
246
247    def __FunctionDef_helper(self, t, fill_suffix):
248        self.write("\n")
249        for deco in t.decorator_list:
250            self.fill("@")
251            self.dispatch(deco)
252        def_str = fill_suffix+" "+t.name + "("
253        self.fill(def_str)
254        self.dispatch(t.args)
255        self.write(")")
256        if t.returns:
257            self.write(" -> ")
258            self.dispatch(t.returns)
259        self.enter()
260        self.dispatch(t.body)
261        self.leave()
262
263    def _For(self, t):
264        self.__For_helper("for ", t)
265
266    def _AsyncFor(self, t):
267        self.__For_helper("async for ", t)
268
269    def __For_helper(self, fill, t):
270        self.fill(fill)
271        self.dispatch(t.target)
272        self.write(" in ")
273        self.dispatch(t.iter)
274        self.enter()
275        self.dispatch(t.body)
276        self.leave()
277        if t.orelse:
278            self.fill("else")
279            self.enter()
280            self.dispatch(t.orelse)
281            self.leave()
282
283    def _If(self, t):
284        self.fill("if ")
285        self.dispatch(t.test)
286        self.enter()
287        self.dispatch(t.body)
288        self.leave()
289        # collapse nested ifs into equivalent elifs.
290        while (t.orelse and len(t.orelse) == 1 and
291               isinstance(t.orelse[0], ast.If)):
292            t = t.orelse[0]
293            self.fill("elif ")
294            self.dispatch(t.test)
295            self.enter()
296            self.dispatch(t.body)
297            self.leave()
298        # final else
299        if t.orelse:
300            self.fill("else")
301            self.enter()
302            self.dispatch(t.orelse)
303            self.leave()
304
305    def _While(self, t):
306        self.fill("while ")
307        self.dispatch(t.test)
308        self.enter()
309        self.dispatch(t.body)
310        self.leave()
311        if t.orelse:
312            self.fill("else")
313            self.enter()
314            self.dispatch(t.orelse)
315            self.leave()
316
317    def _With(self, t):
318        self.fill("with ")
319        interleave(lambda: self.write(", "), self.dispatch, t.items)
320        self.enter()
321        self.dispatch(t.body)
322        self.leave()
323
324    def _AsyncWith(self, t):
325        self.fill("async with ")
326        interleave(lambda: self.write(", "), self.dispatch, t.items)
327        self.enter()
328        self.dispatch(t.body)
329        self.leave()
330
331    # expr
332    def _Bytes(self, t):
333        self.write(repr(t.s))
334
335    def _Str(self, tree):
336        self.write(repr(tree.s))
337
338    def _JoinedStr(self, t):
339        self.write("f")
340        string = io.StringIO()
341        self._fstring_JoinedStr(t, string.write)
342        self.write(repr(string.getvalue()))
343
344    def _FormattedValue(self, t):
345        self.write("f")
346        string = io.StringIO()
347        self._fstring_FormattedValue(t, string.write)
348        self.write(repr(string.getvalue()))
349
350    def _fstring_JoinedStr(self, t, write):
351        for value in t.values:
352            meth = getattr(self, "_fstring_" + type(value).__name__)
353            meth(value, write)
354
355    def _fstring_Str(self, t, write):
356        value = t.s.replace("{", "{{").replace("}", "}}")
357        write(value)
358
359    def _fstring_Constant(self, t, write):
360        assert isinstance(t.value, str)
361        value = t.value.replace("{", "{{").replace("}", "}}")
362        write(value)
363
364    def _fstring_FormattedValue(self, t, write):
365        write("{")
366        expr = io.StringIO()
367        Unparser(t.value, expr)
368        expr = expr.getvalue().rstrip("\n")
369        if expr.startswith("{"):
370            write(" ")  # Separate pair of opening brackets as "{ {"
371        write(expr)
372        if t.conversion != -1:
373            conversion = chr(t.conversion)
374            assert conversion in "sra"
375            write(f"!{conversion}")
376        if t.format_spec:
377            write(":")
378            meth = getattr(self, "_fstring_" + type(t.format_spec).__name__)
379            meth(t.format_spec, write)
380        write("}")
381
382    def _Name(self, t):
383        self.write(t.id)
384
385    def _write_constant(self, value):
386        if isinstance(value, (float, complex)):
387            self.write(repr(value).replace("inf", INFSTR))
388        else:
389            self.write(repr(value))
390
391    def _Constant(self, t):
392        value = t.value
393        if isinstance(value, tuple):
394            self.write("(")
395            if len(value) == 1:
396                self._write_constant(value[0])
397                self.write(",")
398            else:
399                interleave(lambda: self.write(", "), self._write_constant, value)
400            self.write(")")
401        else:
402            self._write_constant(t.value)
403
404    def _NameConstant(self, t):
405        self.write(repr(t.value))
406
407    def _Num(self, t):
408        # Substitute overflowing decimal literal for AST infinities.
409        self.write(repr(t.n).replace("inf", INFSTR))
410
411    def _List(self, t):
412        self.write("[")
413        interleave(lambda: self.write(", "), self.dispatch, t.elts)
414        self.write("]")
415
416    def _ListComp(self, t):
417        self.write("[")
418        self.dispatch(t.elt)
419        for gen in t.generators:
420            self.dispatch(gen)
421        self.write("]")
422
423    def _GeneratorExp(self, t):
424        self.write("(")
425        self.dispatch(t.elt)
426        for gen in t.generators:
427            self.dispatch(gen)
428        self.write(")")
429
430    def _SetComp(self, t):
431        self.write("{")
432        self.dispatch(t.elt)
433        for gen in t.generators:
434            self.dispatch(gen)
435        self.write("}")
436
437    def _DictComp(self, t):
438        self.write("{")
439        self.dispatch(t.key)
440        self.write(": ")
441        self.dispatch(t.value)
442        for gen in t.generators:
443            self.dispatch(gen)
444        self.write("}")
445
446    def _comprehension(self, t):
447        if t.is_async:
448            self.write(" async for ")
449        else:
450            self.write(" for ")
451        self.dispatch(t.target)
452        self.write(" in ")
453        self.dispatch(t.iter)
454        for if_clause in t.ifs:
455            self.write(" if ")
456            self.dispatch(if_clause)
457
458    def _IfExp(self, t):
459        self.write("(")
460        self.dispatch(t.body)
461        self.write(" if ")
462        self.dispatch(t.test)
463        self.write(" else ")
464        self.dispatch(t.orelse)
465        self.write(")")
466
467    def _Set(self, t):
468        assert(t.elts) # should be at least one element
469        self.write("{")
470        interleave(lambda: self.write(", "), self.dispatch, t.elts)
471        self.write("}")
472
473    def _Dict(self, t):
474        self.write("{")
475        def write_key_value_pair(k, v):
476            self.dispatch(k)
477            self.write(": ")
478            self.dispatch(v)
479
480        def write_item(item):
481            k, v = item
482            if k is None:
483                # for dictionary unpacking operator in dicts {**{'y': 2}}
484                # see PEP 448 for details
485                self.write("**")
486                self.dispatch(v)
487            else:
488                write_key_value_pair(k, v)
489        interleave(lambda: self.write(", "), write_item, zip(t.keys, t.values))
490        self.write("}")
491
492    def _Tuple(self, t):
493        self.write("(")
494        if len(t.elts) == 1:
495            elt = t.elts[0]
496            self.dispatch(elt)
497            self.write(",")
498        else:
499            interleave(lambda: self.write(", "), self.dispatch, t.elts)
500        self.write(")")
501
502    unop = {"Invert":"~", "Not": "not", "UAdd":"+", "USub":"-"}
503    def _UnaryOp(self, t):
504        self.write("(")
505        self.write(self.unop[t.op.__class__.__name__])
506        self.write(" ")
507        self.dispatch(t.operand)
508        self.write(")")
509
510    binop = { "Add":"+", "Sub":"-", "Mult":"*", "MatMult":"@", "Div":"/", "Mod":"%",
511                    "LShift":"<<", "RShift":">>", "BitOr":"|", "BitXor":"^", "BitAnd":"&",
512                    "FloorDiv":"//", "Pow": "**"}
513    def _BinOp(self, t):
514        self.write("(")
515        self.dispatch(t.left)
516        self.write(" " + self.binop[t.op.__class__.__name__] + " ")
517        self.dispatch(t.right)
518        self.write(")")
519
520    cmpops = {"Eq":"==", "NotEq":"!=", "Lt":"<", "LtE":"<=", "Gt":">", "GtE":">=",
521                        "Is":"is", "IsNot":"is not", "In":"in", "NotIn":"not in"}
522    def _Compare(self, t):
523        self.write("(")
524        self.dispatch(t.left)
525        for o, e in zip(t.ops, t.comparators):
526            self.write(" " + self.cmpops[o.__class__.__name__] + " ")
527            self.dispatch(e)
528        self.write(")")
529
530    boolops = {ast.And: 'and', ast.Or: 'or'}
531    def _BoolOp(self, t):
532        self.write("(")
533        s = " %s " % self.boolops[t.op.__class__]
534        interleave(lambda: self.write(s), self.dispatch, t.values)
535        self.write(")")
536
537    def _Attribute(self,t):
538        self.dispatch(t.value)
539        # Special case: 3.__abs__() is a syntax error, so if t.value
540        # is an integer literal then we need to either parenthesize
541        # it or add an extra space to get 3 .__abs__().
542        if ((isinstance(t.value, ast.Num) and isinstance(t.value.n, int))
543           or (isinstance(t.value, ast.Constant) and isinstance(t.value.value, int))):
544            self.write(" ")
545        self.write(".")
546        self.write(t.attr)
547
548    def _Call(self, t):
549        self.dispatch(t.func)
550        self.write("(")
551        comma = False
552        for e in t.args:
553            if comma: self.write(", ")
554            else: comma = True
555            self.dispatch(e)
556        for e in t.keywords:
557            if comma: self.write(", ")
558            else: comma = True
559            self.dispatch(e)
560        self.write(")")
561
562    def _Subscript(self, t):
563        self.dispatch(t.value)
564        self.write("[")
565        self.dispatch(t.slice)
566        self.write("]")
567
568    def _Starred(self, t):
569        self.write("*")
570        self.dispatch(t.value)
571
572    # slice
573    def _Ellipsis(self, t):
574        self.write("...")
575
576    def _Index(self, t):
577        self.dispatch(t.value)
578
579    def _Slice(self, t):
580        if t.lower:
581            self.dispatch(t.lower)
582        self.write(":")
583        if t.upper:
584            self.dispatch(t.upper)
585        if t.step:
586            self.write(":")
587            self.dispatch(t.step)
588
589    def _ExtSlice(self, t):
590        interleave(lambda: self.write(', '), self.dispatch, t.dims)
591
592    # argument
593    def _arg(self, t):
594        self.write(t.arg)
595        if t.annotation:
596            self.write(": ")
597            self.dispatch(t.annotation)
598
599    # others
600    def _arguments(self, t):
601        first = True
602        # normal arguments
603        defaults = [None] * (len(t.args) - len(t.defaults)) + t.defaults
604        for a, d in zip(t.args, defaults):
605            if first:first = False
606            else: self.write(", ")
607            self.dispatch(a)
608            if d:
609                self.write("=")
610                self.dispatch(d)
611
612        # varargs, or bare '*' if no varargs but keyword-only arguments present
613        if t.vararg or t.kwonlyargs:
614            if first:first = False
615            else: self.write(", ")
616            self.write("*")
617            if t.vararg:
618                self.write(t.vararg.arg)
619                if t.vararg.annotation:
620                    self.write(": ")
621                    self.dispatch(t.vararg.annotation)
622
623        # keyword-only arguments
624        if t.kwonlyargs:
625            for a, d in zip(t.kwonlyargs, t.kw_defaults):
626                if first:first = False
627                else: self.write(", ")
628                self.dispatch(a),
629                if d:
630                    self.write("=")
631                    self.dispatch(d)
632
633        # kwargs
634        if t.kwarg:
635            if first:first = False
636            else: self.write(", ")
637            self.write("**"+t.kwarg.arg)
638            if t.kwarg.annotation:
639                self.write(": ")
640                self.dispatch(t.kwarg.annotation)
641
642    def _keyword(self, t):
643        if t.arg is None:
644            self.write("**")
645        else:
646            self.write(t.arg)
647            self.write("=")
648        self.dispatch(t.value)
649
650    def _Lambda(self, t):
651        self.write("(")
652        self.write("lambda ")
653        self.dispatch(t.args)
654        self.write(": ")
655        self.dispatch(t.body)
656        self.write(")")
657
658    def _alias(self, t):
659        self.write(t.name)
660        if t.asname:
661            self.write(" as "+t.asname)
662
663    def _withitem(self, t):
664        self.dispatch(t.context_expr)
665        if t.optional_vars:
666            self.write(" as ")
667            self.dispatch(t.optional_vars)
668
669def roundtrip(filename, output=sys.stdout):
670    with open(filename, "rb") as pyfile:
671        encoding = tokenize.detect_encoding(pyfile.readline)[0]
672    with open(filename, "r", encoding=encoding) as pyfile:
673        source = pyfile.read()
674    tree = compile(source, filename, "exec", ast.PyCF_ONLY_AST)
675    Unparser(tree, output)
676
677
678
679def testdir(a):
680    try:
681        names = [n for n in os.listdir(a) if n.endswith('.py')]
682    except OSError:
683        print("Directory not readable: %s" % a, file=sys.stderr)
684    else:
685        for n in names:
686            fullname = os.path.join(a, n)
687            if os.path.isfile(fullname):
688                output = io.StringIO()
689                print('Testing %s' % fullname)
690                try:
691                    roundtrip(fullname, output)
692                except Exception as e:
693                    print('  Failed to compile, exception is %s' % repr(e))
694            elif os.path.isdir(fullname):
695                testdir(fullname)
696
697def main(args):
698    if args[0] == '--testdir':
699        for a in args[1:]:
700            testdir(a)
701    else:
702        for a in args:
703            roundtrip(a)
704
705if __name__=='__main__':
706    main(sys.argv[1:])
707