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 .. import fixer_base
     35 from ..fixer_util import Name, parenthesize
     36 
     37 
     38 class FixHasKey(fixer_base.BaseFix):
     39     BM_compatible = True
     40 
     41     PATTERN = """
     42     anchor=power<
     43         before=any+
     44         trailer< '.' 'has_key' >
     45         trailer<
     46             '('
     47             ( not(arglist | argument<any '=' any>) arg=any
     48             | arglist<(not argument<any '=' any>) arg=any ','>
     49             )
     50             ')'
     51         >
     52         after=any*
     53     >
     54     |
     55     negation=not_test<
     56         'not'
     57         anchor=power<
     58             before=any+
     59             trailer< '.' 'has_key' >
     60             trailer<
     61                 '('
     62                 ( not(arglist | argument<any '=' any>) arg=any
     63                 | arglist<(not argument<any '=' any>) arg=any ','>
     64                 )
     65                 ')'
     66             >
     67         >
     68     >
     69     """
     70 
     71     def transform(self, node, results):
     72         assert results
     73         syms = self.syms
     74         if (node.parent.type == syms.not_test and
     75             self.pattern.match(node.parent)):
     76             # Don't transform a node matching the first alternative of the
     77             # pattern when its parent matches the second alternative
     78             return None
     79         negation = results.get("negation")
     80         anchor = results["anchor"]
     81         prefix = node.prefix
     82         before = [n.clone() for n in results["before"]]
     83         arg = results["arg"].clone()
     84         after = results.get("after")
     85         if after:
     86             after = [n.clone() for n in after]
     87         if arg.type in (syms.comparison, syms.not_test, syms.and_test,
     88                         syms.or_test, syms.test, syms.lambdef, syms.argument):
     89             arg = parenthesize(arg)
     90         if len(before) == 1:
     91             before = before[0]
     92         else:
     93             before = pytree.Node(syms.power, before)
     94         before.prefix = " "
     95         n_op = Name("in", prefix=" ")
     96         if negation:
     97             n_not = Name("not", prefix=" ")
     98             n_op = pytree.Node(syms.comp_op, (n_not, n_op))
     99         new = pytree.Node(syms.comparison, (arg, n_op, before))
    100         if after:
    101             new = parenthesize(new)
    102             new = pytree.Node(syms.power, (new,) + tuple(after))
    103         if node.parent.type in (syms.comparison, syms.expr, syms.xor_expr,
    104                                 syms.and_expr, syms.shift_expr,
    105                                 syms.arith_expr, syms.term,
    106                                 syms.factor, syms.power):
    107             new = parenthesize(new)
    108         new.prefix = prefix
    109         return new
    110