+++ /dev/null
-"""Fixer for function definitions with tuple parameters.\r
-\r
-def func(((a, b), c), d):\r
- ...\r
-\r
- ->\r
-\r
-def func(x, d):\r
- ((a, b), c) = x\r
- ...\r
-\r
-It will also support lambdas:\r
-\r
- lambda (x, y): x + y -> lambda t: t[0] + t[1]\r
-\r
- # The parens are a syntax error in Python 3\r
- lambda (x): x + y -> lambda x: x + y\r
-"""\r
-# Author: Collin Winter\r
-\r
-# Local imports\r
-from .. import pytree\r
-from ..pgen2 import token\r
-from .. import fixer_base\r
-from ..fixer_util import Assign, Name, Newline, Number, Subscript, syms\r
-\r
-def is_docstring(stmt):\r
- return isinstance(stmt, pytree.Node) and \\r
- stmt.children[0].type == token.STRING\r
-\r
-class FixTupleParams(fixer_base.BaseFix):\r
- run_order = 4 #use a lower order since lambda is part of other\r
- #patterns\r
- BM_compatible = True\r
-\r
- PATTERN = """\r
- funcdef< 'def' any parameters< '(' args=any ')' >\r
- ['->' any] ':' suite=any+ >\r
- |\r
- lambda=\r
- lambdef< 'lambda' args=vfpdef< '(' inner=any ')' >\r
- ':' body=any\r
- >\r
- """\r
-\r
- def transform(self, node, results):\r
- if "lambda" in results:\r
- return self.transform_lambda(node, results)\r
-\r
- new_lines = []\r
- suite = results["suite"]\r
- args = results["args"]\r
- # This crap is so "def foo(...): x = 5; y = 7" is handled correctly.\r
- # TODO(cwinter): suite-cleanup\r
- if suite[0].children[1].type == token.INDENT:\r
- start = 2\r
- indent = suite[0].children[1].value\r
- end = Newline()\r
- else:\r
- start = 0\r
- indent = u"; "\r
- end = pytree.Leaf(token.INDENT, u"")\r
-\r
- # We need access to self for new_name(), and making this a method\r
- # doesn't feel right. Closing over self and new_lines makes the\r
- # code below cleaner.\r
- def handle_tuple(tuple_arg, add_prefix=False):\r
- n = Name(self.new_name())\r
- arg = tuple_arg.clone()\r
- arg.prefix = u""\r
- stmt = Assign(arg, n.clone())\r
- if add_prefix:\r
- n.prefix = u" "\r
- tuple_arg.replace(n)\r
- new_lines.append(pytree.Node(syms.simple_stmt,\r
- [stmt, end.clone()]))\r
-\r
- if args.type == syms.tfpdef:\r
- handle_tuple(args)\r
- elif args.type == syms.typedargslist:\r
- for i, arg in enumerate(args.children):\r
- if arg.type == syms.tfpdef:\r
- # Without add_prefix, the emitted code is correct,\r
- # just ugly.\r
- handle_tuple(arg, add_prefix=(i > 0))\r
-\r
- if not new_lines:\r
- return\r
-\r
- # This isn't strictly necessary, but it plays nicely with other fixers.\r
- # TODO(cwinter) get rid of this when children becomes a smart list\r
- for line in new_lines:\r
- line.parent = suite[0]\r
-\r
- # TODO(cwinter) suite-cleanup\r
- after = start\r
- if start == 0:\r
- new_lines[0].prefix = u" "\r
- elif is_docstring(suite[0].children[start]):\r
- new_lines[0].prefix = indent\r
- after = start + 1\r
-\r
- for line in new_lines:\r
- line.parent = suite[0]\r
- suite[0].children[after:after] = new_lines\r
- for i in range(after+1, after+len(new_lines)+1):\r
- suite[0].children[i].prefix = indent\r
- suite[0].changed()\r
-\r
- def transform_lambda(self, node, results):\r
- args = results["args"]\r
- body = results["body"]\r
- inner = simplify_args(results["inner"])\r
-\r
- # Replace lambda ((((x)))): x with lambda x: x\r
- if inner.type == token.NAME:\r
- inner = inner.clone()\r
- inner.prefix = u" "\r
- args.replace(inner)\r
- return\r
-\r
- params = find_params(args)\r
- to_index = map_to_index(params)\r
- tup_name = self.new_name(tuple_name(params))\r
-\r
- new_param = Name(tup_name, prefix=u" ")\r
- args.replace(new_param.clone())\r
- for n in body.post_order():\r
- if n.type == token.NAME and n.value in to_index:\r
- subscripts = [c.clone() for c in to_index[n.value]]\r
- new = pytree.Node(syms.power,\r
- [new_param.clone()] + subscripts)\r
- new.prefix = n.prefix\r
- n.replace(new)\r
-\r
-\r
-### Helper functions for transform_lambda()\r
-\r
-def simplify_args(node):\r
- if node.type in (syms.vfplist, token.NAME):\r
- return node\r
- elif node.type == syms.vfpdef:\r
- # These look like vfpdef< '(' x ')' > where x is NAME\r
- # or another vfpdef instance (leading to recursion).\r
- while node.type == syms.vfpdef:\r
- node = node.children[1]\r
- return node\r
- raise RuntimeError("Received unexpected node %s" % node)\r
-\r
-def find_params(node):\r
- if node.type == syms.vfpdef:\r
- return find_params(node.children[1])\r
- elif node.type == token.NAME:\r
- return node.value\r
- return [find_params(c) for c in node.children if c.type != token.COMMA]\r
-\r
-def map_to_index(param_list, prefix=[], d=None):\r
- if d is None:\r
- d = {}\r
- for i, obj in enumerate(param_list):\r
- trailer = [Subscript(Number(unicode(i)))]\r
- if isinstance(obj, list):\r
- map_to_index(obj, trailer, d=d)\r
- else:\r
- d[obj] = prefix + trailer\r
- return d\r
-\r
-def tuple_name(param_list):\r
- l = []\r
- for obj in param_list:\r
- if isinstance(obj, list):\r
- l.append(tuple_name(obj))\r
- else:\r
- l.append(obj)\r
- return u"_".join(l)\r