summaryrefslogtreecommitdiffstats
path: root/Lib/lib2to3/fixes/fix_map.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/lib2to3/fixes/fix_map.py')
-rw-r--r--Lib/lib2to3/fixes/fix_map.py126
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