Index: lib2to3/tests/test_fixers.py =================================================================== --- lib2to3/tests/test_fixers.py (revision 77044) +++ lib2to3/tests/test_fixers.py (working copy) @@ -2725,18 +2725,79 @@ def test_prefix_preservation(self): b = """callable( x)""" - a = """hasattr( x, '__call__')""" + a = """from collections import Callable\nisinstance( x, Callable)""" self.check(b, a) b = """if callable(x): pass""" - a = """if hasattr(x, '__call__'): pass""" + a = """from collections import Callable\nif isinstance(x, Callable): pass""" self.check(b, a) def test_callable_call(self): b = """callable(x)""" - a = """hasattr(x, '__call__')""" + a = """from collections import Callable\nisinstance(x, Callable)""" self.check(b, a) + def test_global_import(self): + b = """ +def spam(foo): + callable(foo)"""[1:] + a = """ +from collections import Callable +def spam(foo): + isinstance(foo, Callable)"""[1:] + self.check(b, a) + + b = """ +from collections import Callable +def spam(foo): + callable(foo)"""[1:] + # same output if it was already imported + self.check(b, a) + + b = """ +from collections import * +def spam(foo): + callable(foo)"""[1:] + a = """ +from collections import * +def spam(foo): + isinstance(foo, Callable)"""[1:] + self.check(b, a) + + b = """ +do_stuff() +do_some_other_stuff() +assert callable(do_stuff)"""[1:] + a = """ +from collections import Callable +do_stuff() +do_some_other_stuff() +assert isinstance(do_stuff, Callable)"""[1:] + self.check(b, a) + + b = """ +if isinstance(do_stuff, Callable): + assert callable(do_stuff) + do_stuff(do_stuff) + if not callable(do_stuff): + exit(1) + else: + assert callable(do_stuff) +else: + assert not callable(do_stuff)"""[1:] + a = """ +from collections import Callable +if isinstance(do_stuff, Callable): + assert isinstance(do_stuff, Callable) + do_stuff(do_stuff) + if not isinstance(do_stuff, Callable): + exit(1) + else: + assert isinstance(do_stuff, Callable) +else: + assert not isinstance(do_stuff, Callable)"""[1:] + self.check(b, a) + def test_callable_should_not_change(self): a = """callable(*x)""" self.unchanged(a) Index: lib2to3/fixes/fix_callable.py =================================================================== --- lib2to3/fixes/fix_callable.py (revision 77044) +++ lib2to3/fixes/fix_callable.py (working copy) @@ -3,12 +3,12 @@ """Fixer for callable(). -This converts callable(obj) into hasattr(obj, '__call__').""" +This converts callable(obj) into isinstance(obj, collections.Callable).""" # Local imports from .. import pytree from .. import fixer_base -from ..fixer_util import Call, Name, String +from ..fixer_util import Call, Name, String, Attr, touch_import class FixCallable(fixer_base.BaseFix): @@ -25,7 +25,9 @@ """ def transform(self, node, results): - func = results["func"] + func = results['func'] - args = [func.clone(), String(u', '), String(u"'__call__'")] - return Call(Name(u"hasattr"), args, prefix=node.prefix) + touch_import(u'collections', u'Callable', node) + + args = [func.clone(), String(u', '), Name(u'Callable')] + return Call(Name(u'isinstance'), args, prefix=node.prefix)