summaryrefslogtreecommitdiff
path: root/Lib/packaging/tests/fixer/fix_idioms.py
blob: 64f5ea051628e9ec910922ec5e56857c0729c39b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
"""Adjust some old Python 2 idioms to their modern counterparts.

* Change some type comparisons to isinstance() calls:
    type(x) == T -> isinstance(x, T)
    type(x) is T -> isinstance(x, T)
    type(x) != T -> not isinstance(x, T)
    type(x) is not T -> not isinstance(x, T)

* Change "while 1:" into "while True:".

* Change both

    v = list(EXPR)
    v.sort()
    foo(v)

and the more general

    v = EXPR
    v.sort()
    foo(v)

into

    v = sorted(EXPR)
    foo(v)
"""
# Author: Jacques Frechet, Collin Winter

# Local imports
from lib2to3 import fixer_base
from lib2to3.fixer_util import Call, Comma, Name, Node, syms

CMP = "(n='!=' | '==' | 'is' | n=comp_op< 'is' 'not' >)"
TYPE = "power< 'type' trailer< '(' x=any ')' > >"

class FixIdioms(fixer_base.BaseFix):

    explicit = False # The user must ask for this fixer

    PATTERN = r"""
        isinstance=comparison< %s %s T=any >
        |
        isinstance=comparison< T=any %s %s >
        |
        while_stmt< 'while' while='1' ':' any+ >
        |
        sorted=any<
            any*
            simple_stmt<
              expr_stmt< id1=any '='
                         power< list='list' trailer< '(' (not arglist<any+>) any ')' > >
              >
              '\n'
            >
            sort=
            simple_stmt<
              power< id2=any
                     trailer< '.' 'sort' > trailer< '(' ')' >
              >
              '\n'
            >
            next=any*
        >
        |
        sorted=any<
            any*
            simple_stmt< expr_stmt< id1=any '=' expr=any > '\n' >
            sort=
            simple_stmt<
              power< id2=any
                     trailer< '.' 'sort' > trailer< '(' ')' >
              >
              '\n'
            >
            next=any*
        >
    """ % (TYPE, CMP, CMP, TYPE)

    def match(self, node):
        r = super(FixIdioms, self).match(node)
        # If we've matched one of the sort/sorted subpatterns above, we
        # want to reject matches where the initial assignment and the
        # subsequent .sort() call involve different identifiers.
        if r and "sorted" in r:
            if r["id1"] == r["id2"]:
                return r
            return None
        return r

    def transform(self, node, results):
        if "isinstance" in results:
            return self.transform_isinstance(node, results)
        elif "while" in results:
            return self.transform_while(node, results)
        elif "sorted" in results:
            return self.transform_sort(node, results)
        else:
            raise RuntimeError("Invalid match")

    def transform_isinstance(self, node, results):
        x = results["x"].clone() # The thing inside of type()
        T = results["T"].clone() # The type being compared against
        x.prefix = ""
        T.prefix = " "
        test = Call(Name("isinstance"), [x, Comma(), T])
        if "n" in results:
            test.prefix = " "
            test = Node(syms.not_test, [Name("not"), test])
        test.prefix = node.prefix
        return test

    def transform_while(self, node, results):
        one = results["while"]
        one.replace(Name("True", prefix=one.prefix))

    def transform_sort(self, node, results):
        sort_stmt = results["sort"]
        next_stmt = results["next"]
        list_call = results.get("list")
        simple_expr = results.get("expr")

        if list_call:
            list_call.replace(Name("sorted", prefix=list_call.prefix))
        elif simple_expr:
            new = simple_expr.clone()
            new.prefix = ""
            simple_expr.replace(Call(Name("sorted"), [new],
                                     prefix=simple_expr.prefix))
        else:
            raise RuntimeError("should not have reached here")
        sort_stmt.remove()
        if next_stmt:
            next_stmt[0].prefix = sort_stmt._prefix