From 5cdd2dbf2c8107f3b01cfa1a59930cbc365d88e0 Mon Sep 17 00:00:00 2001 From: Stuart Berg Date: Wed, 18 Jan 2017 23:32:43 -0500 Subject: [PATCH] Issue #28837: Fix lib2to3 handling of map/zip/filter calls when followed with a 'trailer', e.g. zip()[x] --- Lib/lib2to3/fixes/fix_filter.py | 21 ++++++++++-- Lib/lib2to3/fixes/fix_map.py | 37 ++++++++++++++++----- Lib/lib2to3/fixes/fix_zip.py | 21 +++++++++--- Lib/lib2to3/tests/test_fixers.py | 56 +++++++++++++++++++++++++++----- Misc/ACKS | 1 + 5 files changed, 110 insertions(+), 26 deletions(-) diff --git a/Lib/lib2to3/fixes/fix_filter.py b/Lib/lib2to3/fixes/fix_filter.py index bb6718cbf770f5..a7a5a154f6fb11 100644 --- a/Lib/lib2to3/fixes/fix_filter.py +++ b/Lib/lib2to3/fixes/fix_filter.py @@ -15,7 +15,10 @@ # Local imports from .. import fixer_base -from ..fixer_util import Name, Call, ListComp, in_special_context +from ..pytree import Node +from ..pygram import python_symbols as syms +from ..fixer_util import Name, ArgList, ListComp, in_special_context + class FixFilter(fixer_base.ConditionalFix): BM_compatible = True @@ -34,16 +37,19 @@ class FixFilter(fixer_base.ConditionalFix): > ')' > + [extra_trailers=trailer*] > | power< 'filter' trailer< '(' arglist< none='None' ',' seq=any > ')' > + [extra_trailers=trailer*] > | power< 'filter' args=trailer< '(' [any] ')' > + [extra_trailers=trailer*] > """ @@ -53,23 +59,32 @@ def transform(self, node, results): if self.should_skip(node): return + trailers = [] + if 'extra_trailers' in results: + for t in results['extra_trailers']: + trailers.append(t.clone()) + if "filter_lambda" in results: new = ListComp(results.get("fp").clone(), results.get("fp").clone(), results.get("it").clone(), results.get("xp").clone()) + new = Node(syms.power, [new] + trailers, prefix="") elif "none" in results: new = ListComp(Name("_f"), Name("_f"), results["seq"].clone(), Name("_f")) + new = Node(syms.power, [new] + trailers, prefix="") else: if in_special_context(node): return None - new = node.clone() + + args = results['args'].clone() + new = Node(syms.power, [Name("filter"), args], prefix="") + new = Node(syms.power, [Name("list"), ArgList([new])] + trailers) new.prefix = "" - new = Call(Name("list"), [new]) new.prefix = node.prefix return new diff --git a/Lib/lib2to3/fixes/fix_map.py b/Lib/lib2to3/fixes/fix_map.py index 9f966feedea5aa..78cf81c6f94098 100644 --- a/Lib/lib2to3/fixes/fix_map.py +++ b/Lib/lib2to3/fixes/fix_map.py @@ -22,8 +22,10 @@ # Local imports from ..pgen2 import token from .. import fixer_base -from ..fixer_util import Name, Call, ListComp, in_special_context +from ..fixer_util import Name, ArgList, Call, ListComp, in_special_context from ..pygram import python_symbols as syms +from ..pytree import Node + class FixMap(fixer_base.ConditionalFix): BM_compatible = True @@ -32,6 +34,7 @@ class FixMap(fixer_base.ConditionalFix): map_none=power< 'map' trailer< '(' arglist< 'None' ',' arg=any [','] > ')' > + [extra_trailers=trailer*] > | map_lambda=power< @@ -47,10 +50,12 @@ class FixMap(fixer_base.ConditionalFix): > ')' > + [extra_trailers=trailer*] > | power< - 'map' trailer< '(' [arglist=any] ')' > + 'map' args=trailer< '(' [any] ')' > + [extra_trailers=trailer*] > """ @@ -60,6 +65,11 @@ def transform(self, node, results): if self.should_skip(node): return + trailers = [] + if 'extra_trailers' in results: + for t in results['extra_trailers']: + trailers.append(t.clone()) + if node.parent.type == syms.simple_stmt: self.warning(node, "You should use a for loop here") new = node.clone() @@ -69,23 +79,32 @@ def transform(self, node, results): new = ListComp(results["xp"].clone(), results["fp"].clone(), results["it"].clone()) + new = Node(syms.power, [new] + trailers, prefix="") + else: if "map_none" in results: new = results["arg"].clone() + new.prefix = "" else: - if "arglist" in results: - args = results["arglist"] - if args.type == syms.arglist and \ - args.children[0].type == token.NAME and \ - args.children[0].value == "None": + if "args" in results: + args = results["args"] + if args.type == syms.trailer and \ + args.children[1].type == syms.arglist and \ + args.children[1].children[0].type == token.NAME and \ + args.children[1].children[0].value == "None": self.warning(node, "cannot convert map(None, ...) " "with multiple arguments because map() " "now truncates to the shortest sequence") return + + new = Node(syms.power, [Name("map"), args.clone()]) + new.prefix = "" + if in_special_context(node): return None - new = node.clone() + + new = Node(syms.power, [Name("list"), ArgList([new])] + trailers) new.prefix = "" - new = Call(Name("list"), [new]) + new.prefix = node.prefix return new diff --git a/Lib/lib2to3/fixes/fix_zip.py b/Lib/lib2to3/fixes/fix_zip.py index 8f36a94fb4f550..52c28df6aab411 100644 --- a/Lib/lib2to3/fixes/fix_zip.py +++ b/Lib/lib2to3/fixes/fix_zip.py @@ -9,13 +9,16 @@ # Local imports from .. import fixer_base -from ..fixer_util import Name, Call, in_special_context +from ..pytree import Node +from ..pygram import python_symbols as syms +from ..fixer_util import Name, ArgList, in_special_context + class FixZip(fixer_base.ConditionalFix): BM_compatible = True PATTERN = """ - power< 'zip' args=trailer< '(' [any] ')' > + power< 'zip' args=trailer< '(' [any] ')' > [trailers=trailer*] > """ @@ -28,8 +31,16 @@ def transform(self, node, results): if in_special_context(node): return None - new = node.clone() - new.prefix = "" - new = Call(Name("list"), [new]) + args = results['args'].clone() + args.prefix = "" + + trailers = [] + if 'trailers' in results: + trailers = [n.clone() for n in results['trailers']] + for n in trailers: + n.prefix = "" + + new = Node(syms.power, [Name("zip"), args], prefix="") + new = Node(syms.power, [Name("list"), ArgList([new])] + trailers) new.prefix = node.prefix return new diff --git a/Lib/lib2to3/tests/test_fixers.py b/Lib/lib2to3/tests/test_fixers.py index b3f2680725961e..3e1a255737ec4b 100644 --- a/Lib/lib2to3/tests/test_fixers.py +++ b/Lib/lib2to3/tests/test_fixers.py @@ -2954,10 +2954,23 @@ def test_filter_basic(self): a = """x = [x for x in range(10) if x%2 == 0]""" self.check(b, a) - # XXX This (rare) case is not supported -## b = """x = filter(f, 'abc')[0]""" -## a = """x = list(filter(f, 'abc'))[0]""" -## self.check(b, a) + def test_filter_trailers(self): + b = """x = filter(None, 'abc')[0]""" + a = """x = [_f for _f in 'abc' if _f][0]""" + self.check(b, a) + + b = """x = len(filter(f, 'abc')[0])""" + a = """x = len(list(filter(f, 'abc'))[0])""" + self.check(b, a) + + b = """x = filter(lambda x: x%2 == 0, range(10))[0]""" + a = """x = [x for x in range(10) if x%2 == 0][0]""" + self.check(b, a) + + # Note the parens around x + b = """x = filter(lambda (x): x%2 == 0, range(10))[0]""" + a = """x = [x for x in range(10) if x%2 == 0][0]""" + self.check(b, a) def test_filter_nochange(self): a = """b.join(filter(f, 'abc'))""" @@ -3022,6 +3035,23 @@ def test_prefix_preservation(self): a = """x = list(map( f, 'abc' ))""" self.check(b, a) + def test_map_trailers(self): + b = """x = map(f, 'abc')[0]""" + a = """x = list(map(f, 'abc'))[0]""" + self.check(b, a) + + b = """x = map(None, l)[0]""" + a = """x = list(l)[0]""" + self.check(b, a) + + b = """x = map(lambda x:x, l)[0]""" + a = """x = [x for x in l][0]""" + self.check(b, a) + + b = """x = map(f, 'abc')[0][1]""" + a = """x = list(map(f, 'abc'))[0][1]""" + self.check(b, a) + def test_trailing_comment(self): b = """x = map(f, 'abc') # foo""" a = """x = list(map(f, 'abc')) # foo""" @@ -3066,11 +3096,6 @@ def test_map_basic(self): """ self.warns(b, a, "You should use a for loop here") - # XXX This (rare) case is not supported -## b = """x = map(f, 'abc')[0]""" -## a = """x = list(map(f, 'abc'))[0]""" -## self.check(b, a) - def test_map_nochange(self): a = """b.join(map(f, 'abc'))""" self.unchanged(a) @@ -3130,6 +3155,10 @@ def check(self, b, a): super(Test_zip, self).check(b, a) def test_zip_basic(self): + b = """x = zip()""" + a = """x = list(zip())""" + self.check(b, a) + b = """x = zip(a, b, c)""" a = """x = list(zip(a, b, c))""" self.check(b, a) @@ -3138,6 +3167,15 @@ def test_zip_basic(self): a = """x = len(list(zip(a, b)))""" self.check(b, a) + def test_zip_trailers(self): + b = """x = zip(a, b, c)[0]""" + a = """x = list(zip(a, b, c))[0]""" + self.check(b, a) + + b = """x = zip(a, b, c)[0][1]""" + a = """x = list(zip(a, b, c))[0][1]""" + self.check(b, a) + def test_zip_nochange(self): a = """b.join(zip(a, b))""" self.unchanged(a) diff --git a/Misc/ACKS b/Misc/ACKS index 319128c9e9a4d4..5b9fbf9c02d627 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -128,6 +128,7 @@ Andrew Bennetts Andy Bensky Bennett Benson Ezra Berch +Stuart Berg Michel Van den Bergh Julian Berman Brice Berna pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy