1# Copyright 2007 Google, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
3
4"""Fixer that changes map(F, ...) into list(map(F, ...)) unless there
5exists a 'from future_builtins import map' statement in the top-level
6namespace.
7
8As a special case, map(None, X) is changed into list(X).  (This is
9necessary because the semantics are changed in this case -- the new
10map(None, X) is equivalent to [(x,) for x in X].)
11
12We avoid the transformation (except for the special case mentioned
13above) if the map() call is directly contained in iter(<>), list(<>),
14tuple(<>), sorted(<>), ...join(<>), or for V in <>:.
15
16NOTE: This is still not correct if the original code was depending on
17map(F, X, Y, ...) to go on until the longest argument is exhausted,
18substituting None for missing values -- like zip(), it now stops as
19soon as the shortest argument is exhausted.
20"""
21
22# Local imports
23from ..pgen2 import token
24from .. import fixer_base
25from ..fixer_util import Name, Call, ListComp, in_special_context
26from ..pygram import python_symbols as syms
27
28class FixMap(fixer_base.ConditionalFix):
29    BM_compatible = True
30
31    PATTERN = """
32    map_none=power<
33        'map'
34        trailer< '(' arglist< 'None' ',' arg=any [','] > ')' >
35    >
36    |
37    map_lambda=power<
38        'map'
39        trailer<
40            '('
41            arglist<
42                lambdef< 'lambda'
43                         (fp=NAME | vfpdef< '(' fp=NAME ')'> ) ':' xp=any
44                >
45                ','
46                it=any
47            >
48            ')'
49        >
50    >
51    |
52    power<
53        'map' trailer< '(' [arglist=any] ')' >
54    >
55    """
56
57    skip_on = 'future_builtins.map'
58
59    def transform(self, node, results):
60        if self.should_skip(node):
61            return
62
63        if node.parent.type == syms.simple_stmt:
64            self.warning(node, "You should use a for loop here")
65            new = node.clone()
66            new.prefix = u""
67            new = Call(Name(u"list"), [new])
68        elif "map_lambda" in results:
69            new = ListComp(results["xp"].clone(),
70                           results["fp"].clone(),
71                           results["it"].clone())
72        else:
73            if "map_none" in results:
74                new = results["arg"].clone()
75            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":
81                        self.warning(node, "cannot convert map(None, ...) "
82                                     "with multiple arguments because map() "
83                                     "now truncates to the shortest sequence")
84                        return
85                if in_special_context(node):
86                    return None
87                new = node.clone()
88            new.prefix = u""
89            new = Call(Name(u"list"), [new])
90        new.prefix = node.prefix
91        return new
92