diff options
Diffstat (limited to 'Lib/lib2to3/fixes/fix_map.py')
-rw-r--r-- | Lib/lib2to3/fixes/fix_map.py | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/Lib/lib2to3/fixes/fix_map.py b/Lib/lib2to3/fixes/fix_map.py new file mode 100644 index 0000000..b5dcaa2 --- /dev/null +++ b/Lib/lib2to3/fixes/fix_map.py @@ -0,0 +1,126 @@ +# Copyright 2007 Google, Inc. All Rights Reserved. +# Licensed to PSF under a Contributor Agreement. + +"""Fixer that changes map(F, ...) into list(map(F, ...)) unless there +exists a 'from future_builtins import map' statement in the top-level +namespace. + +As a special case, map(None, X) is changed into list(X). (This is +necessary because the semantics are changed in this case -- the new +map(None, X) is equivalent to [(x,) for x in X].) + +We avoid the transformation (except for the special case mentioned +above) if the map() call is directly contained in iter(<>), list(<>), +tuple(<>), sorted(<>), ...join(<>), or for V in <>:. + +NOTE: This is still not correct if the original code was depending on +map(F, X, Y, ...) to go on until the longest argument is exhausted, +substituting None for missing values -- like zip(), it now stops as +soon as the shortest argument is exhausted. +""" + +# Local imports +from .. import pytree +from .. import patcomp +from ..pgen2 import token +from . import basefix +from .util import Name, Call, ListComp, attr_chain, does_tree_import +from ..pygram import python_symbols as syms + +class FixMap(basefix.BaseFix): + + PATTERN = """ + map_none=power< + 'map' + trailer< '(' arglist< 'None' ',' arg=any [','] > ')' > + > + | + map_lambda=power< + 'map' + trailer< + '(' + arglist< + lambdef< 'lambda' + (fp=NAME | vfpdef< '(' fp=NAME ')'> ) ':' xp=any + > + ',' + it=any + > + ')' + > + > + | + power< + 'map' + args=trailer< '(' [any] ')' > + > + """ + + def start_tree(self, *args): + super(FixMap, self).start_tree(*args) + self._future_map_found = None + + def has_future_map(self, node): + if self._future_map_found is not None: + return self._future_map_found + self._future_map_found = does_tree_import('future_builtins', 'map', node) + return self._future_map_found + + def transform(self, node, results): + if self.has_future_map(node): + # If a future map has been imported for this file, we won't + # be making any modifications + return + + if node.parent.type == syms.simple_stmt: + self.warning(node, "You should use a for loop here") + new = node.clone() + new.set_prefix("") + new = Call(Name("list"), [new]) + elif "map_lambda" in results: + new = ListComp(results.get("xp").clone(), + results.get("fp").clone(), + results.get("it").clone()) + else: + if "map_none" in results: + new = results["arg"].clone() + else: + if in_special_context(node): + return None + new = node.clone() + new.set_prefix("") + new = Call(Name("list"), [new]) + new.set_prefix(node.get_prefix()) + return new + +P0 = """for_stmt< 'for' any 'in' node=any ':' any* > + | comp_for< 'for' any 'in' node=any any* > + """ +p0 = patcomp.compile_pattern(P0) + +P1 = """ +power< + ( 'iter' | 'list' | 'tuple' | 'sorted' | 'set' | 'sum' | + 'any' | 'all' | (any* trailer< '.' 'join' >) ) + trailer< '(' node=any ')' > + any* +> +""" +p1 = patcomp.compile_pattern(P1) + +P2 = """ +power< + 'sorted' + trailer< '(' arglist<node=any any*> ')' > + any* +> +""" +p2 = patcomp.compile_pattern(P2) + +def in_special_context(node): + patterns = [p0, p1, p2] + for pattern, parent in zip(patterns, attr_chain(node, "parent")): + results = {} + if pattern.match(parent, results) and results["node"] is node: + return True + return False |