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
|
from .. import fixer_base
from ..fixer_util import Call
from ..fixer_util import Comma
from ..fixer_util import KeywordArg
from ..fixer_util import Name
from ..fixer_util import Node
from ..fixer_util import touch_import
from ..pgen2 import token
from ..pygram import python_symbols as symbols
try:
from itertools import filterfalse
except ImportError:
from itertools import ifilterfalse as filterfalse
class FixSorted(fixer_base.BaseFix):
PATTERN = """
power< "sorted" trailer< "(" not arglist ")" > any* >
|
power< "sorted" trailer< "(" arglist< any "," func_args=any+ > ")" > any* >
|
power<any* trailer< any* >* trailer<"." "sort" > trailer<"(" arglist< func_args=any+ > ")"> any* >
|
power<any* trailer< any* >* trailer<"." "sort" > trailer<"(" func_arg=any+ ")"> any* >
"""
def _transform_keyword(self, nodes, result):
"""Transform second and later position argument into keyword argument."""
if not nodes:
return
if not isinstance(nodes, list):
nodes = [nodes]
parent = nodes[0].parent
# transform positional arguments
positional_args = list(
filterfalse(lambda arg: arg.type in (token.COMMA, symbols.argument), nodes)
)
template = ["cmp", "key", "reverse"]
new_args = []
for arg, key in zip(positional_args, template):
arg_ = arg.clone()
arg_.prefix = ""
new_arg = KeywordArg(Name(key), arg_)
new_arg.prefix = arg.prefix
new_args.append(new_arg)
arg.remove()
for child in list(parent.children):
if child.type == token.COMMA and (
child.next_sibling is None or child.next_sibling.type == token.COMMA
):
child.remove()
# update keyword argument list
keyword_args = list(filter(lambda arg: arg.type == symbols.argument, nodes))
keywords = [arg.children[0].value for arg in keyword_args]
assert parent.type == symbols.arglist
for arg in new_args:
if arg.children[0].value not in keywords:
if len(parent.children) > 0:
parent.append_child(Comma())
parent.append_child(arg)
# update result mapping
return parent.children
def _transform_cmp(self, nodes, result):
"""transform argument `cmp` into `key`"""
arglist = list(filter(lambda arg: arg.type == symbols.argument, nodes))
cmp_node = None
key_node = None
for arg in arglist:
if arg.type == symbols.argument:
if arg.children[0].value == "cmp":
cmp_node = arg
if arg.children[0].value == "key":
key_node = arg
if cmp_node:
if key_node:
return # Do nothing when it have both cmp and key argument
cmp_node.children[0].value = "key"
cmp_node.children[2].replace(
Call(Name("cmp_to_key"), args=[cmp_node.children[2].clone()])
)
touch_import("functools", "cmp_to_key", cmp_node)
def transform(self, node, result):
if result.get("func_arg"):
a = Node(symbols.arglist, [r.clone() for r in result["func_arg"]])
result["func_arg"][0].replace(a)
result["func_args"] = a.children
if result.get("func_args"):
ret = self._transform_keyword(result["func_args"], result)
self._transform_cmp(ret, result)
|