]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """Adjust some old Python 2 idioms to their modern counterparts.\r |
2 | \r | |
3 | * Change some type comparisons to isinstance() calls:\r | |
4 | type(x) == T -> isinstance(x, T)\r | |
5 | type(x) is T -> isinstance(x, T)\r | |
6 | type(x) != T -> not isinstance(x, T)\r | |
7 | type(x) is not T -> not isinstance(x, T)\r | |
8 | \r | |
9 | * Change "while 1:" into "while True:".\r | |
10 | \r | |
11 | * Change both\r | |
12 | \r | |
13 | v = list(EXPR)\r | |
14 | v.sort()\r | |
15 | foo(v)\r | |
16 | \r | |
17 | and the more general\r | |
18 | \r | |
19 | v = EXPR\r | |
20 | v.sort()\r | |
21 | foo(v)\r | |
22 | \r | |
23 | into\r | |
24 | \r | |
25 | v = sorted(EXPR)\r | |
26 | foo(v)\r | |
27 | """\r | |
28 | # Author: Jacques Frechet, Collin Winter\r | |
29 | \r | |
30 | # Local imports\r | |
31 | from .. import fixer_base\r | |
32 | from ..fixer_util import Call, Comma, Name, Node, BlankLine, syms\r | |
33 | \r | |
34 | CMP = "(n='!=' | '==' | 'is' | n=comp_op< 'is' 'not' >)"\r | |
35 | TYPE = "power< 'type' trailer< '(' x=any ')' > >"\r | |
36 | \r | |
37 | class FixIdioms(fixer_base.BaseFix):\r | |
38 | explicit = True # The user must ask for this fixer\r | |
39 | \r | |
40 | PATTERN = r"""\r | |
41 | isinstance=comparison< %s %s T=any >\r | |
42 | |\r | |
43 | isinstance=comparison< T=any %s %s >\r | |
44 | |\r | |
45 | while_stmt< 'while' while='1' ':' any+ >\r | |
46 | |\r | |
47 | sorted=any<\r | |
48 | any*\r | |
49 | simple_stmt<\r | |
50 | expr_stmt< id1=any '='\r | |
51 | power< list='list' trailer< '(' (not arglist<any+>) any ')' > >\r | |
52 | >\r | |
53 | '\n'\r | |
54 | >\r | |
55 | sort=\r | |
56 | simple_stmt<\r | |
57 | power< id2=any\r | |
58 | trailer< '.' 'sort' > trailer< '(' ')' >\r | |
59 | >\r | |
60 | '\n'\r | |
61 | >\r | |
62 | next=any*\r | |
63 | >\r | |
64 | |\r | |
65 | sorted=any<\r | |
66 | any*\r | |
67 | simple_stmt< expr_stmt< id1=any '=' expr=any > '\n' >\r | |
68 | sort=\r | |
69 | simple_stmt<\r | |
70 | power< id2=any\r | |
71 | trailer< '.' 'sort' > trailer< '(' ')' >\r | |
72 | >\r | |
73 | '\n'\r | |
74 | >\r | |
75 | next=any*\r | |
76 | >\r | |
77 | """ % (TYPE, CMP, CMP, TYPE)\r | |
78 | \r | |
79 | def match(self, node):\r | |
80 | r = super(FixIdioms, self).match(node)\r | |
81 | # If we've matched one of the sort/sorted subpatterns above, we\r | |
82 | # want to reject matches where the initial assignment and the\r | |
83 | # subsequent .sort() call involve different identifiers.\r | |
84 | if r and "sorted" in r:\r | |
85 | if r["id1"] == r["id2"]:\r | |
86 | return r\r | |
87 | return None\r | |
88 | return r\r | |
89 | \r | |
90 | def transform(self, node, results):\r | |
91 | if "isinstance" in results:\r | |
92 | return self.transform_isinstance(node, results)\r | |
93 | elif "while" in results:\r | |
94 | return self.transform_while(node, results)\r | |
95 | elif "sorted" in results:\r | |
96 | return self.transform_sort(node, results)\r | |
97 | else:\r | |
98 | raise RuntimeError("Invalid match")\r | |
99 | \r | |
100 | def transform_isinstance(self, node, results):\r | |
101 | x = results["x"].clone() # The thing inside of type()\r | |
102 | T = results["T"].clone() # The type being compared against\r | |
103 | x.prefix = u""\r | |
104 | T.prefix = u" "\r | |
105 | test = Call(Name(u"isinstance"), [x, Comma(), T])\r | |
106 | if "n" in results:\r | |
107 | test.prefix = u" "\r | |
108 | test = Node(syms.not_test, [Name(u"not"), test])\r | |
109 | test.prefix = node.prefix\r | |
110 | return test\r | |
111 | \r | |
112 | def transform_while(self, node, results):\r | |
113 | one = results["while"]\r | |
114 | one.replace(Name(u"True", prefix=one.prefix))\r | |
115 | \r | |
116 | def transform_sort(self, node, results):\r | |
117 | sort_stmt = results["sort"]\r | |
118 | next_stmt = results["next"]\r | |
119 | list_call = results.get("list")\r | |
120 | simple_expr = results.get("expr")\r | |
121 | \r | |
122 | if list_call:\r | |
123 | list_call.replace(Name(u"sorted", prefix=list_call.prefix))\r | |
124 | elif simple_expr:\r | |
125 | new = simple_expr.clone()\r | |
126 | new.prefix = u""\r | |
127 | simple_expr.replace(Call(Name(u"sorted"), [new],\r | |
128 | prefix=simple_expr.prefix))\r | |
129 | else:\r | |
130 | raise RuntimeError("should not have reached here")\r | |
131 | sort_stmt.remove()\r | |
132 | \r | |
133 | btwn = sort_stmt.prefix\r | |
134 | # Keep any prefix lines between the sort_stmt and the list_call and\r | |
135 | # shove them right after the sorted() call.\r | |
136 | if u"\n" in btwn:\r | |
137 | if next_stmt:\r | |
138 | # The new prefix should be everything from the sort_stmt's\r | |
139 | # prefix up to the last newline, then the old prefix after a new\r | |
140 | # line.\r | |
141 | prefix_lines = (btwn.rpartition(u"\n")[0], next_stmt[0].prefix)\r | |
142 | next_stmt[0].prefix = u"\n".join(prefix_lines)\r | |
143 | else:\r | |
144 | assert list_call.parent\r | |
145 | assert list_call.next_sibling is None\r | |
146 | # Put a blank line after list_call and set its prefix.\r | |
147 | end_line = BlankLine()\r | |
148 | list_call.parent.append_child(end_line)\r | |
149 | assert list_call.next_sibling is end_line\r | |
150 | # The new prefix should be everything up to the first new line\r | |
151 | # of sort_stmt's prefix.\r | |
152 | end_line.prefix = btwn.rpartition(u"\n")[0]\r |