1 # Copyright 2007 Google, Inc. All Rights Reserved. 2 # Licensed to PSF under a Contributor Agreement. 3 4 """Fixer for dict methods. 5 6 d.keys() -> list(d.keys()) 7 d.items() -> list(d.items()) 8 d.values() -> list(d.values()) 9 10 d.iterkeys() -> iter(d.keys()) 11 d.iteritems() -> iter(d.items()) 12 d.itervalues() -> iter(d.values()) 13 14 d.viewkeys() -> d.keys() 15 d.viewitems() -> d.items() 16 d.viewvalues() -> d.values() 17 18 Except in certain very specific contexts: the iter() can be dropped 19 when the context is list(), sorted(), iter() or for...in; the list() 20 can be dropped when the context is list() or sorted() (but not iter() 21 or for...in!). Special contexts that apply to both: list(), sorted(), tuple() 22 set(), any(), all(), sum(). 23 24 Note: iter(d.keys()) could be written as iter(d) but since the 25 original d.iterkeys() was also redundant we don't fix this. And there 26 are (rare) contexts where it makes a difference (e.g. when passing it 27 as an argument to a function that introspects the argument). 28 """ 29 30 # Local imports 31 from .. import pytree 32 from .. import patcomp 33 from ..pgen2 import token 34 from .. import fixer_base 35 from ..fixer_util import Name, Call, LParen, RParen, ArgList, Dot 36 from .. import fixer_util 37 38 39 iter_exempt = fixer_util.consuming_calls | set(["iter"]) 40 41 42 class FixDict(fixer_base.BaseFix): 43 BM_compatible = True 44 45 PATTERN = """ 46 power< head=any+ 47 trailer< '.' method=('keys'|'items'|'values'| 48 'iterkeys'|'iteritems'|'itervalues'| 49 'viewkeys'|'viewitems'|'viewvalues') > 50 parens=trailer< '(' ')' > 51 tail=any* 52 > 53 """ 54 55 def transform(self, node, results): 56 head = results["head"] 57 method = results["method"][0] # Extract node for method name 58 tail = results["tail"] 59 syms = self.syms 60 method_name = method.value 61 isiter = method_name.startswith(u"iter") 62 isview = method_name.startswith(u"view") 63 if isiter or isview: 64 method_name = method_name[4:] 65 assert method_name in (u"keys", u"items", u"values"), repr(method) 66 head = [n.clone() for n in head] 67 tail = [n.clone() for n in tail] 68 special = not tail and self.in_special_context(node, isiter) 69 args = head + [pytree.Node(syms.trailer, 70 [Dot(), 71 Name(method_name, 72 prefix=method.prefix)]), 73 results["parens"].clone()] 74 new = pytree.Node(syms.power, args) 75 if not (special or isview): 76 new.prefix = u"" 77 new = Call(Name(u"iter" if isiter else u"list"), [new]) 78 if tail: 79 new = pytree.Node(syms.power, [new] + tail) 80 new.prefix = node.prefix 81 return new 82 83 P1 = "power< func=NAME trailer< '(' node=any ')' > any* >" 84 p1 = patcomp.compile_pattern(P1) 85 86 P2 = """for_stmt< 'for' any 'in' node=any ':' any* > 87 | comp_for< 'for' any 'in' node=any any* > 88 """ 89 p2 = patcomp.compile_pattern(P2) 90 91 def in_special_context(self, node, isiter): 92 if node.parent is None: 93 return False 94 results = {} 95 if (node.parent.parent is not None and 96 self.p1.match(node.parent.parent, results) and 97 results["node"] is node): 98 if isiter: 99 # iter(d.iterkeys()) -> iter(d.keys()), etc. 100 return results["func"].value in iter_exempt 101 else: 102 # list(d.keys()) -> list(d.keys()), etc. 103 return results["func"].value in fixer_util.consuming_calls 104 if not isiter: 105 return False 106 # for ... in d.iterkeys() -> for ... in d.keys(), etc. 107 return self.p2.match(node.parent, results) and results["node"] is node 108