]>
Commit | Line | Data |
---|---|---|
4710c53d | 1 | """Fixer for function definitions with tuple parameters.\r |
2 | \r | |
3 | def func(((a, b), c), d):\r | |
4 | ...\r | |
5 | \r | |
6 | ->\r | |
7 | \r | |
8 | def func(x, d):\r | |
9 | ((a, b), c) = x\r | |
10 | ...\r | |
11 | \r | |
12 | It will also support lambdas:\r | |
13 | \r | |
14 | lambda (x, y): x + y -> lambda t: t[0] + t[1]\r | |
15 | \r | |
16 | # The parens are a syntax error in Python 3\r | |
17 | lambda (x): x + y -> lambda x: x + y\r | |
18 | """\r | |
19 | # Author: Collin Winter\r | |
20 | \r | |
21 | # Local imports\r | |
22 | from .. import pytree\r | |
23 | from ..pgen2 import token\r | |
24 | from .. import fixer_base\r | |
25 | from ..fixer_util import Assign, Name, Newline, Number, Subscript, syms\r | |
26 | \r | |
27 | def is_docstring(stmt):\r | |
28 | return isinstance(stmt, pytree.Node) and \\r | |
29 | stmt.children[0].type == token.STRING\r | |
30 | \r | |
31 | class FixTupleParams(fixer_base.BaseFix):\r | |
32 | run_order = 4 #use a lower order since lambda is part of other\r | |
33 | #patterns\r | |
34 | BM_compatible = True\r | |
35 | \r | |
36 | PATTERN = """\r | |
37 | funcdef< 'def' any parameters< '(' args=any ')' >\r | |
38 | ['->' any] ':' suite=any+ >\r | |
39 | |\r | |
40 | lambda=\r | |
41 | lambdef< 'lambda' args=vfpdef< '(' inner=any ')' >\r | |
42 | ':' body=any\r | |
43 | >\r | |
44 | """\r | |
45 | \r | |
46 | def transform(self, node, results):\r | |
47 | if "lambda" in results:\r | |
48 | return self.transform_lambda(node, results)\r | |
49 | \r | |
50 | new_lines = []\r | |
51 | suite = results["suite"]\r | |
52 | args = results["args"]\r | |
53 | # This crap is so "def foo(...): x = 5; y = 7" is handled correctly.\r | |
54 | # TODO(cwinter): suite-cleanup\r | |
55 | if suite[0].children[1].type == token.INDENT:\r | |
56 | start = 2\r | |
57 | indent = suite[0].children[1].value\r | |
58 | end = Newline()\r | |
59 | else:\r | |
60 | start = 0\r | |
61 | indent = u"; "\r | |
62 | end = pytree.Leaf(token.INDENT, u"")\r | |
63 | \r | |
64 | # We need access to self for new_name(), and making this a method\r | |
65 | # doesn't feel right. Closing over self and new_lines makes the\r | |
66 | # code below cleaner.\r | |
67 | def handle_tuple(tuple_arg, add_prefix=False):\r | |
68 | n = Name(self.new_name())\r | |
69 | arg = tuple_arg.clone()\r | |
70 | arg.prefix = u""\r | |
71 | stmt = Assign(arg, n.clone())\r | |
72 | if add_prefix:\r | |
73 | n.prefix = u" "\r | |
74 | tuple_arg.replace(n)\r | |
75 | new_lines.append(pytree.Node(syms.simple_stmt,\r | |
76 | [stmt, end.clone()]))\r | |
77 | \r | |
78 | if args.type == syms.tfpdef:\r | |
79 | handle_tuple(args)\r | |
80 | elif args.type == syms.typedargslist:\r | |
81 | for i, arg in enumerate(args.children):\r | |
82 | if arg.type == syms.tfpdef:\r | |
83 | # Without add_prefix, the emitted code is correct,\r | |
84 | # just ugly.\r | |
85 | handle_tuple(arg, add_prefix=(i > 0))\r | |
86 | \r | |
87 | if not new_lines:\r | |
88 | return\r | |
89 | \r | |
90 | # This isn't strictly necessary, but it plays nicely with other fixers.\r | |
91 | # TODO(cwinter) get rid of this when children becomes a smart list\r | |
92 | for line in new_lines:\r | |
93 | line.parent = suite[0]\r | |
94 | \r | |
95 | # TODO(cwinter) suite-cleanup\r | |
96 | after = start\r | |
97 | if start == 0:\r | |
98 | new_lines[0].prefix = u" "\r | |
99 | elif is_docstring(suite[0].children[start]):\r | |
100 | new_lines[0].prefix = indent\r | |
101 | after = start + 1\r | |
102 | \r | |
103 | for line in new_lines:\r | |
104 | line.parent = suite[0]\r | |
105 | suite[0].children[after:after] = new_lines\r | |
106 | for i in range(after+1, after+len(new_lines)+1):\r | |
107 | suite[0].children[i].prefix = indent\r | |
108 | suite[0].changed()\r | |
109 | \r | |
110 | def transform_lambda(self, node, results):\r | |
111 | args = results["args"]\r | |
112 | body = results["body"]\r | |
113 | inner = simplify_args(results["inner"])\r | |
114 | \r | |
115 | # Replace lambda ((((x)))): x with lambda x: x\r | |
116 | if inner.type == token.NAME:\r | |
117 | inner = inner.clone()\r | |
118 | inner.prefix = u" "\r | |
119 | args.replace(inner)\r | |
120 | return\r | |
121 | \r | |
122 | params = find_params(args)\r | |
123 | to_index = map_to_index(params)\r | |
124 | tup_name = self.new_name(tuple_name(params))\r | |
125 | \r | |
126 | new_param = Name(tup_name, prefix=u" ")\r | |
127 | args.replace(new_param.clone())\r | |
128 | for n in body.post_order():\r | |
129 | if n.type == token.NAME and n.value in to_index:\r | |
130 | subscripts = [c.clone() for c in to_index[n.value]]\r | |
131 | new = pytree.Node(syms.power,\r | |
132 | [new_param.clone()] + subscripts)\r | |
133 | new.prefix = n.prefix\r | |
134 | n.replace(new)\r | |
135 | \r | |
136 | \r | |
137 | ### Helper functions for transform_lambda()\r | |
138 | \r | |
139 | def simplify_args(node):\r | |
140 | if node.type in (syms.vfplist, token.NAME):\r | |
141 | return node\r | |
142 | elif node.type == syms.vfpdef:\r | |
143 | # These look like vfpdef< '(' x ')' > where x is NAME\r | |
144 | # or another vfpdef instance (leading to recursion).\r | |
145 | while node.type == syms.vfpdef:\r | |
146 | node = node.children[1]\r | |
147 | return node\r | |
148 | raise RuntimeError("Received unexpected node %s" % node)\r | |
149 | \r | |
150 | def find_params(node):\r | |
151 | if node.type == syms.vfpdef:\r | |
152 | return find_params(node.children[1])\r | |
153 | elif node.type == token.NAME:\r | |
154 | return node.value\r | |
155 | return [find_params(c) for c in node.children if c.type != token.COMMA]\r | |
156 | \r | |
157 | def map_to_index(param_list, prefix=[], d=None):\r | |
158 | if d is None:\r | |
159 | d = {}\r | |
160 | for i, obj in enumerate(param_list):\r | |
161 | trailer = [Subscript(Number(unicode(i)))]\r | |
162 | if isinstance(obj, list):\r | |
163 | map_to_index(obj, trailer, d=d)\r | |
164 | else:\r | |
165 | d[obj] = prefix + trailer\r | |
166 | return d\r | |
167 | \r | |
168 | def tuple_name(param_list):\r | |
169 | l = []\r | |
170 | for obj in param_list:\r | |
171 | if isinstance(obj, list):\r | |
172 | l.append(tuple_name(obj))\r | |
173 | else:\r | |
174 | l.append(obj)\r | |
175 | return u"_".join(l)\r |