Index: lib2to3/tests/test_fixers.py =================================================================== --- lib2to3/tests/test_fixers.py (revision 83457) +++ lib2to3/tests/test_fixers.py (working copy) @@ -4307,15 +4307,91 @@ a = "operator.contains(x, y)" self.check(b, a) + b = "operator .sequenceIncludes(x, y)" + a = "operator .contains(x, y)" + self.check(b, a) + + b = "operator. sequenceIncludes(x, y)" + a = "operator. contains(x, y)" + self.check(b, a) + + def test_operator_isSequenceType(self): + b = "operator.isSequenceType(x)" + a = "import collections\nisinstance(x, collections.Sequence)" + self.check(b, a) + + def test_operator_isMappingType(self): + b = "operator.isMappingType(x)" + a = "import collections\nisinstance(x, collections.Mapping)" + self.check(b, a) + + def test_operator_isNumberType(self): + b = "operator.isNumberType(x)" + a = "import numbers\nisinstance(x, numbers.Number)" + self.check(b, a) + + def test_operator_repeat(self): + b = "operator.repeat(x, n)" + a = "operator.mul(x, n)" + self.check(b, a) + + b = "operator .repeat(x, n)" + a = "operator .mul(x, n)" + self.check(b, a) + + b = "operator. repeat(x, n)" + a = "operator. mul(x, n)" + self.check(b, a) + + def test_operator_irepeat(self): + b = "operator.irepeat(x, n)" + a = "operator.imul(x, n)" + self.check(b, a) + + b = "operator .irepeat(x, n)" + a = "operator .imul(x, n)" + self.check(b, a) + + b = "operator. irepeat(x, n)" + a = "operator. imul(x, n)" + self.check(b, a) + def test_bare_isCallable(self): s = "isCallable(x)" - self.warns_unchanged(s, "You should use hasattr(x, '__call__') here.") + t = "You should use 'hasattr(x, '__call__')' here." + self.warns_unchanged(s, t) def test_bare_sequenceIncludes(self): s = "sequenceIncludes(x, y)" - self.warns_unchanged(s, "You should use operator.contains here.") + t = "You should use 'operator.contains(x, y)' here." + self.warns_unchanged(s, t) + def test_bare_operator_isSequenceType(self): + s = "isSequenceType(z)" + t = "You should use 'isinstance(z, collections.Sequence)' here." + self.warns_unchanged(s, t) + def test_bare_operator_isMappingType(self): + s = "isMappingType(x)" + t = "You should use 'isinstance(x, collections.Mapping)' here." + self.warns_unchanged(s, t) + + def test_bare_operator_isNumberType(self): + s = "isNumberType(y)" + t = "You should use 'isinstance(y, numbers.Number)' here." + self.warns_unchanged(s, t) + + def test_bare_operator_repeat(self): + s = "repeat(x, n)" + t = "You should use 'operator.mul(x, n)' here." + self.warns_unchanged(s, t) + + def test_bare_operator_irepeat(self): + s = "irepeat(y, 187)" + t = "You should use 'operator.imul(y, 187)' here." + self.warns_unchanged(s, t) + + class Test_exitfunc(FixerTestCase): fixer = "exitfunc" Index: lib2to3/fixes/fix_operator.py =================================================================== --- lib2to3/fixes/fix_operator.py (revision 83457) +++ lib2to3/fixes/fix_operator.py (working copy) @@ -1,40 +1,87 @@ -"""Fixer for operator.{isCallable,sequenceIncludes} +"""Fixer for operator functions. -operator.isCallable(obj) -> hasattr(obj, '__call__') +operator.isCallable(obj) -> hasattr(obj, '__call__') operator.sequenceIncludes(obj) -> operator.contains(obj) +operator.isSequenceType(obj) -> isinstance(obj, collections.Sequence) +operator.isMappingType(obj) -> isinstance(obj, collections.Mapping) +operator.isNumberType(obj) -> isinstance(obj, numbers.Number) +operator.repeat(obj, n) -> operator.mul(obj, n) +operator.irepeat(obj, n) -> operator.imul(obj, n) """ # Local imports from .. import fixer_base -from ..fixer_util import Call, Name, String +from ..fixer_util import Call, Name, String, touch_import class FixOperator(fixer_base.BaseFix): - methods = "method=('isCallable'|'sequenceIncludes')" - func = "'(' func=any ')'" + methods = """ + method=('isCallable'|'sequenceIncludes' + |'isSequenceType'|'isMappingType'|'isNumberType' + |'repeat'|'irepeat') + """ + obj = "'(' obj=any ')'" PATTERN = """ power< module='operator' - trailer< '.' %(methods)s > trailer< %(func)s > > + trailer< '.' %(methods)s > trailer< %(obj)s > > | - power< %(methods)s trailer< %(func)s > > - """ % dict(methods=methods, func=func) + power< %(methods)s trailer< %(obj)s > > + """ % dict(methods=methods, obj=obj) def transform(self, node, results): + if self._check_method(node, results): + return self._get_method(results)(node, results) + + def _sequenceIncludes(self, node, results): + """operator.contains(%s)""" + return self._handle_rename(node, results, u"contains") + + def _isCallable(self, node, results): + """hasattr(%s, '__call__')""" + obj = results["obj"] + args = [obj.clone(), String(u", "), String(u"'__call__'")] + return Call(Name(u"hasattr"), args, prefix=node.prefix) + + def _repeat(self, node, results): + """operator.mul(%s)""" + return self._handle_rename(node, results, u"mul") + + def _irepeat(self, node, results): + """operator.imul(%s)""" + return self._handle_rename(node, results, u"imul") + + def _isSequenceType(self, node, results): + """isinstance(%s, collections.Sequence)""" + return self._handle_type2abc(node, results, "collections", "Sequence") + + def _isMappingType(self, node, results): + """isinstance(%s, collections.Mapping)""" + return self._handle_type2abc(node, results, "collections", "Mapping") + + def _isNumberType(self, node, results): + """isinstance(%s, numbers.Number)""" + return self._handle_type2abc(node, results, "numbers", "Number") + + def _handle_rename(self, node, results, name): method = results["method"][0] + method.value = name + method.changed() - if method.value == u"sequenceIncludes": - if "module" not in results: - # operator may not be in scope, so we can't make a change. - self.warning(node, "You should use operator.contains here.") + def _handle_type2abc(self, node, results, module, abc): + touch_import(None, module, node) + obj = results['obj'] + args = [obj.clone(), String(', ' + ".".join([module, abc]))] + return Call(Name('isinstance'), args, prefix=node.get_prefix()) + + def _get_method(self, results): + return getattr(self, "_" + results["method"][0].value) + + def _check_method(self, node, results): + method = self._get_method(results) + if callable(method): + if "module" in results: + return True else: - method.value = u"contains" - method.changed() - elif method.value == u"isCallable": - if "module" not in results: - self.warning(node, - "You should use hasattr(%s, '__call__') here." % - results["func"].value) - else: - func = results["func"] - args = [func.clone(), String(u", "), String(u"'__call__'")] - return Call(Name(u"hasattr"), args, prefix=node.prefix) + invocation_str = method.__doc__ % str(results["obj"]) + self.warning(node, "You should use '%s' here." % invocation_str) + return False