Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 93b4b47

Browse filesBrowse files
stuartebergbenjaminp
authored andcommitted
bpo-28837: Fix lib2to3 handling of map/zip/filter calls when followed with a 'trailer', e.g. zip()[x] (#24)
1 parent 01fa9ae commit 93b4b47
Copy full SHA for 93b4b47

File tree

5 files changed

+110
-26
lines changed
Filter options

5 files changed

+110
-26
lines changed

‎Lib/lib2to3/fixes/fix_filter.py

Copy file name to clipboardExpand all lines: Lib/lib2to3/fixes/fix_filter.py
+18-3Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515

1616
# Local imports
1717
from .. import fixer_base
18-
from ..fixer_util import Name, Call, ListComp, in_special_context
18+
from ..pytree import Node
19+
from ..pygram import python_symbols as syms
20+
from ..fixer_util import Name, ArgList, ListComp, in_special_context
21+
1922

2023
class FixFilter(fixer_base.ConditionalFix):
2124
BM_compatible = True
@@ -34,16 +37,19 @@ class FixFilter(fixer_base.ConditionalFix):
3437
>
3538
')'
3639
>
40+
[extra_trailers=trailer*]
3741
>
3842
|
3943
power<
4044
'filter'
4145
trailer< '(' arglist< none='None' ',' seq=any > ')' >
46+
[extra_trailers=trailer*]
4247
>
4348
|
4449
power<
4550
'filter'
4651
args=trailer< '(' [any] ')' >
52+
[extra_trailers=trailer*]
4753
>
4854
"""
4955

@@ -53,23 +59,32 @@ def transform(self, node, results):
5359
if self.should_skip(node):
5460
return
5561

62+
trailers = []
63+
if 'extra_trailers' in results:
64+
for t in results['extra_trailers']:
65+
trailers.append(t.clone())
66+
5667
if "filter_lambda" in results:
5768
new = ListComp(results.get("fp").clone(),
5869
results.get("fp").clone(),
5970
results.get("it").clone(),
6071
results.get("xp").clone())
72+
new = Node(syms.power, [new] + trailers, prefix="")
6173

6274
elif "none" in results:
6375
new = ListComp(Name("_f"),
6476
Name("_f"),
6577
results["seq"].clone(),
6678
Name("_f"))
79+
new = Node(syms.power, [new] + trailers, prefix="")
6780

6881
else:
6982
if in_special_context(node):
7083
return None
71-
new = node.clone()
84+
85+
args = results['args'].clone()
86+
new = Node(syms.power, [Name("filter"), args], prefix="")
87+
new = Node(syms.power, [Name("list"), ArgList([new])] + trailers)
7288
new.prefix = ""
73-
new = Call(Name("list"), [new])
7489
new.prefix = node.prefix
7590
return new

‎Lib/lib2to3/fixes/fix_map.py

Copy file name to clipboardExpand all lines: Lib/lib2to3/fixes/fix_map.py
+28-9Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
# Local imports
2323
from ..pgen2 import token
2424
from .. import fixer_base
25-
from ..fixer_util import Name, Call, ListComp, in_special_context
25+
from ..fixer_util import Name, ArgList, Call, ListComp, in_special_context
2626
from ..pygram import python_symbols as syms
27+
from ..pytree import Node
28+
2729

2830
class FixMap(fixer_base.ConditionalFix):
2931
BM_compatible = True
@@ -32,6 +34,7 @@ class FixMap(fixer_base.ConditionalFix):
3234
map_none=power<
3335
'map'
3436
trailer< '(' arglist< 'None' ',' arg=any [','] > ')' >
37+
[extra_trailers=trailer*]
3538
>
3639
|
3740
map_lambda=power<
@@ -47,10 +50,12 @@ class FixMap(fixer_base.ConditionalFix):
4750
>
4851
')'
4952
>
53+
[extra_trailers=trailer*]
5054
>
5155
|
5256
power<
53-
'map' trailer< '(' [arglist=any] ')' >
57+
'map' args=trailer< '(' [any] ')' >
58+
[extra_trailers=trailer*]
5459
>
5560
"""
5661

@@ -60,6 +65,11 @@ def transform(self, node, results):
6065
if self.should_skip(node):
6166
return
6267

68+
trailers = []
69+
if 'extra_trailers' in results:
70+
for t in results['extra_trailers']:
71+
trailers.append(t.clone())
72+
6373
if node.parent.type == syms.simple_stmt:
6474
self.warning(node, "You should use a for loop here")
6575
new = node.clone()
@@ -69,23 +79,32 @@ def transform(self, node, results):
6979
new = ListComp(results["xp"].clone(),
7080
results["fp"].clone(),
7181
results["it"].clone())
82+
new = Node(syms.power, [new] + trailers, prefix="")
83+
7284
else:
7385
if "map_none" in results:
7486
new = results["arg"].clone()
87+
new.prefix = ""
7588
else:
76-
if "arglist" in results:
77-
args = results["arglist"]
78-
if args.type == syms.arglist and \
79-
args.children[0].type == token.NAME and \
80-
args.children[0].value == "None":
89+
if "args" in results:
90+
args = results["args"]
91+
if args.type == syms.trailer and \
92+
args.children[1].type == syms.arglist and \
93+
args.children[1].children[0].type == token.NAME and \
94+
args.children[1].children[0].value == "None":
8195
self.warning(node, "cannot convert map(None, ...) "
8296
"with multiple arguments because map() "
8397
"now truncates to the shortest sequence")
8498
return
99+
100+
new = Node(syms.power, [Name("map"), args.clone()])
101+
new.prefix = ""
102+
85103
if in_special_context(node):
86104
return None
87-
new = node.clone()
105+
106+
new = Node(syms.power, [Name("list"), ArgList([new])] + trailers)
88107
new.prefix = ""
89-
new = Call(Name("list"), [new])
108+
90109
new.prefix = node.prefix
91110
return new

‎Lib/lib2to3/fixes/fix_zip.py

Copy file name to clipboardExpand all lines: Lib/lib2to3/fixes/fix_zip.py
+16-5Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@
99

1010
# Local imports
1111
from .. import fixer_base
12-
from ..fixer_util import Name, Call, in_special_context
12+
from ..pytree import Node
13+
from ..pygram import python_symbols as syms
14+
from ..fixer_util import Name, ArgList, in_special_context
15+
1316

1417
class FixZip(fixer_base.ConditionalFix):
1518

1619
BM_compatible = True
1720
PATTERN = """
18-
power< 'zip' args=trailer< '(' [any] ')' >
21+
power< 'zip' args=trailer< '(' [any] ')' > [trailers=trailer*]
1922
>
2023
"""
2124

@@ -28,8 +31,16 @@ def transform(self, node, results):
2831
if in_special_context(node):
2932
return None
3033

31-
new = node.clone()
32-
new.prefix = ""
33-
new = Call(Name("list"), [new])
34+
args = results['args'].clone()
35+
args.prefix = ""
36+
37+
trailers = []
38+
if 'trailers' in results:
39+
trailers = [n.clone() for n in results['trailers']]
40+
for n in trailers:
41+
n.prefix = ""
42+
43+
new = Node(syms.power, [Name("zip"), args], prefix="")
44+
new = Node(syms.power, [Name("list"), ArgList([new])] + trailers)
3445
new.prefix = node.prefix
3546
return new

‎Lib/lib2to3/tests/test_fixers.py

Copy file name to clipboardExpand all lines: Lib/lib2to3/tests/test_fixers.py
+47-9Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2954,10 +2954,23 @@ def test_filter_basic(self):
29542954
a = """x = [x for x in range(10) if x%2 == 0]"""
29552955
self.check(b, a)
29562956

2957-
# XXX This (rare) case is not supported
2958-
## b = """x = filter(f, 'abc')[0]"""
2959-
## a = """x = list(filter(f, 'abc'))[0]"""
2960-
## self.check(b, a)
2957+
def test_filter_trailers(self):
2958+
b = """x = filter(None, 'abc')[0]"""
2959+
a = """x = [_f for _f in 'abc' if _f][0]"""
2960+
self.check(b, a)
2961+
2962+
b = """x = len(filter(f, 'abc')[0])"""
2963+
a = """x = len(list(filter(f, 'abc'))[0])"""
2964+
self.check(b, a)
2965+
2966+
b = """x = filter(lambda x: x%2 == 0, range(10))[0]"""
2967+
a = """x = [x for x in range(10) if x%2 == 0][0]"""
2968+
self.check(b, a)
2969+
2970+
# Note the parens around x
2971+
b = """x = filter(lambda (x): x%2 == 0, range(10))[0]"""
2972+
a = """x = [x for x in range(10) if x%2 == 0][0]"""
2973+
self.check(b, a)
29612974

29622975
def test_filter_nochange(self):
29632976
a = """b.join(filter(f, 'abc'))"""
@@ -3022,6 +3035,23 @@ def test_prefix_preservation(self):
30223035
a = """x = list(map( f, 'abc' ))"""
30233036
self.check(b, a)
30243037

3038+
def test_map_trailers(self):
3039+
b = """x = map(f, 'abc')[0]"""
3040+
a = """x = list(map(f, 'abc'))[0]"""
3041+
self.check(b, a)
3042+
3043+
b = """x = map(None, l)[0]"""
3044+
a = """x = list(l)[0]"""
3045+
self.check(b, a)
3046+
3047+
b = """x = map(lambda x:x, l)[0]"""
3048+
a = """x = [x for x in l][0]"""
3049+
self.check(b, a)
3050+
3051+
b = """x = map(f, 'abc')[0][1]"""
3052+
a = """x = list(map(f, 'abc'))[0][1]"""
3053+
self.check(b, a)
3054+
30253055
def test_trailing_comment(self):
30263056
b = """x = map(f, 'abc') # foo"""
30273057
a = """x = list(map(f, 'abc')) # foo"""
@@ -3066,11 +3096,6 @@ def test_map_basic(self):
30663096
"""
30673097
self.warns(b, a, "You should use a for loop here")
30683098

3069-
# XXX This (rare) case is not supported
3070-
## b = """x = map(f, 'abc')[0]"""
3071-
## a = """x = list(map(f, 'abc'))[0]"""
3072-
## self.check(b, a)
3073-
30743099
def test_map_nochange(self):
30753100
a = """b.join(map(f, 'abc'))"""
30763101
self.unchanged(a)
@@ -3130,6 +3155,10 @@ def check(self, b, a):
31303155
super(Test_zip, self).check(b, a)
31313156

31323157
def test_zip_basic(self):
3158+
b = """x = zip()"""
3159+
a = """x = list(zip())"""
3160+
self.check(b, a)
3161+
31333162
b = """x = zip(a, b, c)"""
31343163
a = """x = list(zip(a, b, c))"""
31353164
self.check(b, a)
@@ -3138,6 +3167,15 @@ def test_zip_basic(self):
31383167
a = """x = len(list(zip(a, b)))"""
31393168
self.check(b, a)
31403169

3170+
def test_zip_trailers(self):
3171+
b = """x = zip(a, b, c)[0]"""
3172+
a = """x = list(zip(a, b, c))[0]"""
3173+
self.check(b, a)
3174+
3175+
b = """x = zip(a, b, c)[0][1]"""
3176+
a = """x = list(zip(a, b, c))[0][1]"""
3177+
self.check(b, a)
3178+
31413179
def test_zip_nochange(self):
31423180
a = """b.join(zip(a, b))"""
31433181
self.unchanged(a)

‎Misc/ACKS

Copy file name to clipboardExpand all lines: Misc/ACKS
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ Andrew Bennetts
128128
Andy Bensky
129129
Bennett Benson
130130
Ezra Berch
131+
Stuart Berg
131132
Michel Van den Bergh
132133
Julian Berman
133134
Brice Berna

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.