1# Copyright 2006 Google, Inc. All Rights Reserved.
2# Licensed to PSF under a Contributor Agreement.
3
4"""Fixer for has_key().
5
6Calls to .has_key() methods are expressed in terms of the 'in'
7operator:
8
9    d.has_key(k) -> k in d
10
11CAVEATS:
121) While the primary target of this fixer is dict.has_key(), the
13   fixer will change any has_key() method call, regardless of its
14   class.
15
162) Cases like this will not be converted:
17
18    m = d.has_key
19    if m(k):
20        ...
21
22   Only *calls* to has_key() are converted. While it is possible to
23   convert the above to something like
24
25    m = d.__contains__
26    if m(k):
27        ...
28
29   this is currently not done.
30"""
31
32# Local imports
33from .. import pytree
34from ..pgen2 import token
35from .. import fixer_base
36from ..fixer_util import Name, parenthesize
37
38
39class FixHasKey(fixer_base.BaseFix):
40    BM_compatible = True
41
42    PATTERN = """
43    anchor=power<
44        before=any+
45        trailer< '.' 'has_key' >
46        trailer<
47            '('
48            ( not(arglist | argument<any '=' any>) arg=any
49            | arglist<(not argument<any '=' any>) arg=any ','>
50            )
51            ')'
52        >
53        after=any*
54    >
55    |
56    negation=not_test<
57        'not'
58        anchor=power<
59            before=any+
60            trailer< '.' 'has_key' >
61            trailer<
62                '('
63                ( not(arglist | argument<any '=' any>) arg=any
64                | arglist<(not argument<any '=' any>) arg=any ','>
65                )
66                ')'
67            >
68        >
69    >
70    """
71
72    def transform(self, node, results):
73        assert results
74        syms = self.syms
75        if (node.parent.type == syms.not_test and
76            self.pattern.match(node.parent)):
77            # Don't transform a node matching the first alternative of the
78            # pattern when its parent matches the second alternative
79            return None
80        negation = results.get("negation")
81        anchor = results["anchor"]
82        prefix = node.prefix
83        before = [n.clone() for n in results["before"]]
84        arg = results["arg"].clone()
85        after = results.get("after")
86        if after:
87            after = [n.clone() for n in after]
88        if arg.type in (syms.comparison, syms.not_test, syms.and_test,
89                        syms.or_test, syms.test, syms.lambdef, syms.argument):
90            arg = parenthesize(arg)
91        if len(before) == 1:
92            before = before[0]
93        else:
94            before = pytree.Node(syms.power, before)
95        before.prefix = u" "
96        n_op = Name(u"in", prefix=u" ")
97        if negation:
98            n_not = Name(u"not", prefix=u" ")
99            n_op = pytree.Node(syms.comp_op, (n_not, n_op))
100        new = pytree.Node(syms.comparison, (arg, n_op, before))
101        if after:
102            new = parenthesize(new)
103            new = pytree.Node(syms.power, (new,) + tuple(after))
104        if node.parent.type in (syms.comparison, syms.expr, syms.xor_expr,
105                                syms.and_expr, syms.shift_expr,
106                                syms.arith_expr, syms.term,
107                                syms.factor, syms.power):
108            new = parenthesize(new)
109        new.prefix = prefix
110        return new
111