Skip to content

Commit b8e29d9

Browse files
authored
Merge pull request #181 from martindemello/six-dict
Fix for dict.viewitems(), dict.iteritems() etc in chained calls.
2 parents 3b69bc1 + f41303b commit b8e29d9

File tree

2 files changed

+61
-5
lines changed

2 files changed

+61
-5
lines changed

libmodernize/fixes/fix_dict_six.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,46 @@
33

44
# Local imports
55
from lib2to3 import fixer_util
6+
from lib2to3 import pytree
67
from lib2to3.fixes import fix_dict
78
import libmodernize
89

910

1011
class FixDictSix(fix_dict.FixDict):
1112

12-
def transform_iter(self, method_name, node, base):
13+
def transform_iter(self, node, results):
1314
"""Call six.(iter|view)items() and friends."""
15+
# Make sure six is imported.
1416
libmodernize.touch_import(None, u'six', node)
15-
new_node = [n.clone() for n in base]
16-
new_node[0].prefix = u''
17+
18+
# Copy of self.transform() from lib2to3.fix_dict with some changes to
19+
# use the six.* methods.
20+
21+
head = results['head']
22+
method = results['method'][0] # Extract node for method name
23+
tail = results['tail']
24+
syms = self.syms
25+
method_name = method.value
1726
name = fixer_util.Name(u'six.' + method_name, prefix=node.prefix)
18-
node.replace(fixer_util.Call(name, new_node))
27+
assert method_name.startswith((u'iter', u'view')), repr(method)
28+
assert method_name[4:] in (u'keys', u'items', u'values'), repr(method)
29+
head = [n.clone() for n in head]
30+
tail = [n.clone() for n in tail]
31+
new = pytree.Node(syms.power, head)
32+
new.prefix = u''
33+
new = fixer_util.Call(name, [new])
34+
if tail:
35+
new = pytree.Node(syms.power, [new] + tail)
36+
new.prefix = node.prefix
37+
return new
1938

2039
def transform(self, node, results):
2140
method = results['method'][0]
2241
method_name = method.value
2342
if method_name in ('keys', 'items', 'values'):
2443
return super(FixDictSix, self).transform(node, results)
2544
else:
26-
return self.transform_iter(method_name, node, results['head'])
45+
return self.transform_iter(node, results)
2746

2847
def in_special_context(self, node, isiter):
2948
# Redefined from parent class to make "for x in d.items()" count as

tests/test_fix_dict_six.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,34 @@
3535
pass
3636
""")
3737

38+
DICT_ITER_IN_LOOP = ("""\
39+
for k in x.iter{type}():
40+
pass
41+
""", """\
42+
from __future__ import absolute_import
43+
import six
44+
for k in six.iter{type}(x):
45+
pass
46+
""")
47+
48+
DICT_ITER_IN_LIST = ("""\
49+
for k in list(x.iter{type}()):
50+
pass
51+
""", """\
52+
from __future__ import absolute_import
53+
import six
54+
for k in list(six.iter{type}(x)):
55+
pass
56+
""")
57+
58+
CHAINED_CALLS = ("""\
59+
(x + y).foo().iter{type}().bar()
60+
""", """\
61+
from __future__ import absolute_import
62+
import six
63+
six.iter{type}((x + y).foo()).bar()
64+
""")
65+
3866

3967
def check_all_types(input, output):
4068
for type_ in TYPES:
@@ -51,3 +79,12 @@ def test_dict_plain():
5179

5280
def test_dict_in_loop():
5381
check_on_input(*DICT_IN_LOOP)
82+
83+
def test_dict_iter_in_loop():
84+
check_all_types(*DICT_ITER_IN_LOOP)
85+
86+
def test_dict_iter_in_list():
87+
check_all_types(*DICT_ITER_IN_LIST)
88+
89+
def test_chained_calls():
90+
check_all_types(*CHAINED_CALLS)

0 commit comments

Comments
 (0)