• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""Fixer for operator functions.
2
3operator.isCallable(obj)       -> hasattr(obj, '__call__')
4operator.sequenceIncludes(obj) -> operator.contains(obj)
5operator.isSequenceType(obj)   -> isinstance(obj, collections.Sequence)
6operator.isMappingType(obj)    -> isinstance(obj, collections.Mapping)
7operator.isNumberType(obj)     -> isinstance(obj, numbers.Number)
8operator.repeat(obj, n)        -> operator.mul(obj, n)
9operator.irepeat(obj, n)       -> operator.imul(obj, n)
10"""
11
12# Local imports
13from lib2to3 import fixer_base
14from lib2to3.fixer_util import Call, Name, String, touch_import
15
16
17def invocation(s):
18    def dec(f):
19        f.invocation = s
20        return f
21    return dec
22
23
24class FixOperator(fixer_base.BaseFix):
25    BM_compatible = True
26    order = "pre"
27
28    methods = """
29              method=('isCallable'|'sequenceIncludes'
30                     |'isSequenceType'|'isMappingType'|'isNumberType'
31                     |'repeat'|'irepeat')
32              """
33    obj = "'(' obj=any ')'"
34    PATTERN = """
35              power< module='operator'
36                trailer< '.' %(methods)s > trailer< %(obj)s > >
37              |
38              power< %(methods)s trailer< %(obj)s > >
39              """ % dict(methods=methods, obj=obj)
40
41    def transform(self, node, results):
42        method = self._check_method(node, results)
43        if method is not None:
44            return method(node, results)
45
46    @invocation("operator.contains(%s)")
47    def _sequenceIncludes(self, node, results):
48        return self._handle_rename(node, results, u"contains")
49
50    @invocation("hasattr(%s, '__call__')")
51    def _isCallable(self, node, results):
52        obj = results["obj"]
53        args = [obj.clone(), String(u", "), String(u"'__call__'")]
54        return Call(Name(u"hasattr"), args, prefix=node.prefix)
55
56    @invocation("operator.mul(%s)")
57    def _repeat(self, node, results):
58        return self._handle_rename(node, results, u"mul")
59
60    @invocation("operator.imul(%s)")
61    def _irepeat(self, node, results):
62        return self._handle_rename(node, results, u"imul")
63
64    @invocation("isinstance(%s, collections.Sequence)")
65    def _isSequenceType(self, node, results):
66        return self._handle_type2abc(node, results, u"collections", u"Sequence")
67
68    @invocation("isinstance(%s, collections.Mapping)")
69    def _isMappingType(self, node, results):
70        return self._handle_type2abc(node, results, u"collections", u"Mapping")
71
72    @invocation("isinstance(%s, numbers.Number)")
73    def _isNumberType(self, node, results):
74        return self._handle_type2abc(node, results, u"numbers", u"Number")
75
76    def _handle_rename(self, node, results, name):
77        method = results["method"][0]
78        method.value = name
79        method.changed()
80
81    def _handle_type2abc(self, node, results, module, abc):
82        touch_import(None, module, node)
83        obj = results["obj"]
84        args = [obj.clone(), String(u", " + u".".join([module, abc]))]
85        return Call(Name(u"isinstance"), args, prefix=node.prefix)
86
87    def _check_method(self, node, results):
88        method = getattr(self, "_" + results["method"][0].value.encode("ascii"))
89        if callable(method):
90            if "module" in results:
91                return method
92            else:
93                sub = (unicode(results["obj"]),)
94                invocation_str = unicode(method.invocation) % sub
95                self.warning(node, u"You should use '%s' here." % invocation_str)
96        return None
97