Index: example.py =================================================================== --- example.py (revision 61549) +++ example.py (working copy) @@ -362,5 +362,14 @@ def buffer_examples(): x = buffer(y) +def operator_examples(): + import operator + operator.isCallable(foo) + operator.sequenceIncludes(foo, bar) + from operator import isCallable, sequenceIncludes + isCallable(foo) + # This should produce a warning. + sequenceIncludes(foo, bar) + # This is the last line. Index: README =================================================================== --- README (revision 61549) +++ README (working copy) @@ -84,6 +84,9 @@ * **fix_numliterals** - tweak certain numeric literals to be 3.0-compliant. +* **fix_operator** - "operator.isCallable(x)" -> "hasattr(x, '__call__')", + "operator.sequenceIncludes(x, y)" -> "operator.contains(a, b)" + * **fix_print** - convert "print" statements to print() function calls. * **fix_raise** - convert "raise" statements to Python 3 syntax (PEP 3109). Index: lib2to3/tests/test_fixers.py =================================================================== --- lib2to3/tests/test_fixers.py (revision 61549) +++ lib2to3/tests/test_fixers.py (working copy) @@ -2908,7 +2908,24 @@ a = """ itertools.filterfalse(a, b)""" self.check(b, a) +class Test_operator(FixerTestCase): + fixer = "operator" + def test_0(self): + b = "operator.isCallable(x)" + a = "hasattr(x, '__call__')" + self.check(b, a) + + def test_1(self): + b = "operator.sequenceIncludes(x, y)" + a = "operator.contains(x, y)" + self.check(b, a) + + def test_2(self): + b = "isCallable(x)" + a = "hasattr(x, '__call__')" + self.check(b, a) + if __name__ == "__main__": import __main__ support.run_all_tests(__main__) Index: lib2to3/fixes/fix_operator.py =================================================================== --- lib2to3/fixes/fix_operator.py (revision 0) +++ lib2to3/fixes/fix_operator.py (revision 0) @@ -0,0 +1,36 @@ +"""Fixer for operator.{isCallable,sequenceIncludes} + +operator.isCallable(obj) -> hasattr(obj, '__call__') +operator.sequenceIncludes(obj) -> operator.contains(obj) +""" + +# Local imports +from . import basefix +from .util import Call, Name, String + +class FixOperator(basefix.BaseFix): + methods = "method=('isCallable'|'sequenceIncludes')" + func = "'(' func=any ')'" + PATTERN = """ + power< module='operator' trailer< '.' %(methods)s + > trailer< %(func)s > > + | + power< %(methods)s trailer< %(func)s > > + """ % locals() + + def transform(self, node, results): + method = results["method"][0] + + if method.value == "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.") + else: + method.value = "contains" + method.changed() + return node + else: + func = results["func"] + args = [func.clone(), String(", "), String("'__call__'")] + return Call(Name("hasattr"), args, prefix=node.get_prefix()) +