Home | History | Annotate | Download | only in fixes
      1 # Copyright 2006 Google, Inc. All Rights Reserved.
      2 # Licensed to PSF under a Contributor Agreement.
      3 
      4 """Fixer for has_key().
      5 
      6 Calls to .has_key() methods are expressed in terms of the 'in'
      7 operator:
      8 
      9     d.has_key(k) -> k in d
     10 
     11 CAVEATS:
     12 1) 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 
     16 2) 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
     33 from .. import pytree
     34 from ..pgen2 import token
     35 from .. import fixer_base
     36 from ..fixer_util import Name, parenthesize
     37 
     38 
     39 class 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