From 4a22b5dee77b6a3439e4a09362586c390bbdef02 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Sun, 25 Nov 2007 09:39:14 +0000 Subject: Patch from Georg Brandl and me for #1493 Remove unbound method objects --- Lib/DocXMLRPCServer.py | 2 +- Lib/inspect.py | 4 +-- Lib/test/inspect_fodder2.py | 2 +- Lib/test/output/test_extcall | 2 ++ Lib/test/test_class.py | 2 +- Lib/test/test_descr.py | 19 ++++++++------ Lib/test/test_descrtut.py | 4 +-- Lib/test/test_extcall.py | 14 ++-------- Lib/test/test_funcattrs.py | 61 ++++++++++++-------------------------------- Lib/test/test_inspect.py | 5 ++-- Lib/test/test_pyclbr.py | 22 ++++++---------- Lib/test/test_repr.py | 4 +-- Lib/test/test_sys.py | 2 +- Lib/test/test_typechecks.py | 12 --------- Lib/test/test_weakref.py | 5 ---- Lib/types.py | 1 - Lib/unittest.py | 11 +++++--- Lib/xml/dom/minicompat.py | 2 +- Objects/funcobject.c | 6 +++-- 19 files changed, 65 insertions(+), 115 deletions(-) diff --git a/Lib/DocXMLRPCServer.py b/Lib/DocXMLRPCServer.py index 111e5f6..08e1f10 100644 --- a/Lib/DocXMLRPCServer.py +++ b/Lib/DocXMLRPCServer.py @@ -74,7 +74,7 @@ class ServerHTMLDoc(pydoc.HTMLDoc): title = '%s' % (anchor, name) if inspect.ismethod(object): - args, varargs, varkw, defaults = inspect.getargspec(object.im_func) + args, varargs, varkw, defaults = inspect.getargspec(object) # exclude the argument bound to the instance, it will be # confusing to the non-Python user argspec = inspect.formatargspec ( diff --git a/Lib/inspect.py b/Lib/inspect.py index d0608d7..3a95796 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -57,7 +57,7 @@ def ismethod(object): __name__ name with which this method was defined im_class class object in which this method belongs im_func function object containing implementation of method - im_self instance to which this method is bound, or None""" + im_self instance to which this method is bound""" return isinstance(object, types.MethodType) def ismethoddescriptor(object): @@ -269,7 +269,7 @@ def classify_class_attrs(cls): kind = "class method" elif isinstance(obj, property): kind = "property" - elif (ismethod(obj_via_getattr) or + elif (isfunction(obj_via_getattr) or ismethoddescriptor(obj_via_getattr)): kind = "method" else: diff --git a/Lib/test/inspect_fodder2.py b/Lib/test/inspect_fodder2.py index 7a9f84e..d244935 100644 --- a/Lib/test/inspect_fodder2.py +++ b/Lib/test/inspect_fodder2.py @@ -96,7 +96,7 @@ def f(): "doc" return 42 return X -method_in_dynamic_class = f().g.im_func +method_in_dynamic_class = f().g #line 101 def keyworded(*arg1, arg2=1): diff --git a/Lib/test/output/test_extcall b/Lib/test/output/test_extcall index 323fe7a..63f5b71 100644 --- a/Lib/test/output/test_extcall +++ b/Lib/test/output/test_extcall @@ -38,6 +38,8 @@ dir() got multiple values for keyword argument 'b' 3 512 True 3 3 +5 +5 za () {} -> za() takes exactly 1 positional argument (0 given) za () {'a': 'aa'} -> ok za aa B D E V a za () {'d': 'dd'} -> za() got an unexpected keyword argument 'd' diff --git a/Lib/test/test_class.py b/Lib/test/test_class.py index 76b30a3..ab44a4c 100644 --- a/Lib/test/test_class.py +++ b/Lib/test/test_class.py @@ -552,7 +552,7 @@ class ClassTests(unittest.TestCase): self.assertEquals(hash(B.f), hash(A.f)) # the following triggers a SystemError in 2.4 - a = A(hash(A.f.im_func)^(-1)) + a = A(hash(A.f)^(-1)) hash(a.f) def test_main(): diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py index 1ea93bb..e093ce8 100644 --- a/Lib/test/test_descr.py +++ b/Lib/test/test_descr.py @@ -280,12 +280,12 @@ def test_dir(): c = C() vereq(interesting(dir(c)), cstuff) - verify('im_self' in dir(C.Cmethod)) + #verify('im_self' in dir(C.Cmethod)) c.cdata = 2 c.cmethod = lambda self: 0 vereq(interesting(dir(c)), cstuff + ['cdata', 'cmethod']) - verify('im_self' in dir(c.Cmethod)) + #verify('im_self' in dir(c.Cmethod)) class A(C): Adata = 1 @@ -293,13 +293,13 @@ def test_dir(): astuff = ['Adata', 'Amethod'] + cstuff vereq(interesting(dir(A)), astuff) - verify('im_self' in dir(A.Amethod)) + #verify('im_self' in dir(A.Amethod)) a = A() vereq(interesting(dir(a)), astuff) a.adata = 42 a.amethod = lambda self: 3 vereq(interesting(dir(a)), astuff + ['adata', 'amethod']) - verify('im_self' in dir(a.Amethod)) + #verify('im_self' in dir(a.Amethod)) # Try a module subclass. import sys @@ -1504,8 +1504,10 @@ def classic(): vereq(D.foo(d, 1), (d, 1)) class E: # *not* subclassing from C foo = C.foo - vereq(E().foo, C.foo) # i.e., unbound - verify(repr(C.foo.__get__(C())).startswith(">> C().foo() -Traceback (most recent call last): - ... -TypeError: unbound method foo() must be called with B instance as first argument (got C instance instead) +called A.foo() >>> class C(A): ... def foo(self): diff --git a/Lib/test/test_extcall.py b/Lib/test/test_extcall.py index 56a207a..611f4ab 100644 --- a/Lib/test/test_extcall.py +++ b/Lib/test/test_extcall.py @@ -231,18 +231,8 @@ class Foo: x = Foo() print(Foo.method(*(x, 1, 2))) print(Foo.method(x, *(1, 2))) -try: - print(Foo.method(*(1, 2, 3))) -except TypeError as err: - pass -else: - print('expected a TypeError for unbound method call') -try: - print(Foo.method(1, *(2, 3))) -except TypeError as err: - pass -else: - print('expected a TypeError for unbound method call') +print(Foo.method(*(1, 2, 3))) +print(Foo.method(1, *(2, 3))) # A PyCFunction that takes only positional parameters should allow an # empty keyword dictionary to pass without a complaint, but raise a diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 66ba9cb..bd9caff 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -67,13 +67,8 @@ else: raise TestFailed('expected AttributeError') # In Python 2.1 beta 1, we disallowed setting attributes on unbound methods # (it was already disallowed on bound methods). See the PEP for details. -try: - F.a.publish = 1 -except (AttributeError, TypeError): pass -else: raise TestFailed('expected AttributeError or TypeError') - -# But setting it explicitly on the underlying function object is okay. -F.a.im_func.publish = 1 +# In Python 3.0 unbound methods are gone. +F.a.publish = 1 if F.a.publish != 1: raise TestFailed('unbound method attribute not set to expected value') @@ -92,30 +87,8 @@ try: except (AttributeError, TypeError): pass else: raise TestFailed('expected AttributeError or TypeError') -# See the comment above about the change in semantics for Python 2.1b1 -try: - F.a.myclass = F -except (AttributeError, TypeError): pass -else: raise TestFailed('expected AttributeError or TypeError') - -F.a.im_func.myclass = F - -f1.a.myclass -f2.a.myclass -f1.a.myclass -F.a.myclass - -if f1.a.myclass is not f2.a.myclass or \ - f1.a.myclass is not F.a.myclass: - raise TestFailed('attributes were not the same') - # try setting __dict__ -try: - F.a.__dict__ = (1, 2, 3) -except (AttributeError, TypeError): pass -else: raise TestFailed('expected TypeError or AttributeError') - -F.a.im_func.__dict__ = {'one': 11, 'two': 22, 'three': 33} +F.a.__dict__ = {'one': 11, 'two': 22, 'three': 33} if f1.a.two != 22: raise TestFailed('setting __dict__') @@ -315,9 +288,9 @@ def test_func_dict(): def test_im_class(): class C: def foo(self): pass - verify(C.foo.im_class is C) + #verify(C.foo.im_class is C) verify(C().foo.im_class is C) - cantset(C.foo, "im_class", C) + #cantset(C.foo, "im_class", C) cantset(C().foo, "im_class", C) def test_im_func(): @@ -325,19 +298,19 @@ def test_im_func(): class C: pass C.foo = foo - verify(C.foo.im_func is foo) + #verify(C.foo.im_func is foo) verify(C().foo.im_func is foo) - cantset(C.foo, "im_func", foo) + #cantset(C.foo, "im_func", foo) cantset(C().foo, "im_func", foo) def test_im_self(): class C: def foo(self): pass - verify(C.foo.im_self is None) + #verify(C.foo.im_self is None) c = C() - verify(c.foo.im_self is c) - cantset(C.foo, "im_self", None) - cantset(c.foo, "im_self", c) + #verify(c.foo.im_self is c) + #cantset(C.foo, "im_self", None) + #cantset(c.foo, "im_self", c) def test_im_dict(): class C: @@ -345,24 +318,24 @@ def test_im_dict(): foo.bar = 42 verify(C.foo.__dict__ == {'bar': 42}) verify(C().foo.__dict__ == {'bar': 42}) - cantset(C.foo, "__dict__", C.foo.__dict__) - cantset(C().foo, "__dict__", C.foo.__dict__) + #cantset(C.foo, "__dict__", C.foo.__dict__) + #cantset(C().foo, "__dict__", C.foo.__dict__) def test_im_doc(): class C: def foo(self): "hello" verify(C.foo.__doc__ == "hello") verify(C().foo.__doc__ == "hello") - cantset(C.foo, "__doc__", "hello") - cantset(C().foo, "__doc__", "hello") + #cantset(C.foo, "__doc__", "hello") + #cantset(C().foo, "__doc__", "hello") def test_im_name(): class C: def foo(self): pass verify(C.foo.__name__ == "foo") verify(C().foo.__name__ == "foo") - cantset(C.foo, "__name__", "foo") - cantset(C().foo, "__name__", "foo") + #cantset(C.foo, "__name__", "foo") + #cantset(C().foo, "__name__", "foo") def testmore(): test_func_closure() diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 1858372..3752810 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -65,7 +65,7 @@ class TestPredicates(IsTestBase): self.istest(inspect.iscode, 'mod.spam.__code__') self.istest(inspect.isframe, 'tb.tb_frame') self.istest(inspect.isfunction, 'mod.spam') - self.istest(inspect.ismethod, 'mod.StupidGit.abuse') + self.istest(inspect.isfunction, 'mod.StupidGit.abuse') self.istest(inspect.ismethod, 'git.argue') self.istest(inspect.ismodule, 'mod') self.istest(inspect.istraceback, 'tb') @@ -395,7 +395,8 @@ class TestClassesAndFunctions(unittest.TestCase): self.assert_(('s', 'static method', A) in attrs, 'missing static method') self.assert_(('c', 'class method', A) in attrs, 'missing class method') self.assert_(('p', 'property', A) in attrs, 'missing property') - self.assert_(('m', 'method', A) in attrs, 'missing plain method') + self.assert_(('m', 'method', A) in attrs, + 'missing plain method: %r' % attrs) self.assert_(('m1', 'method', A) in attrs, 'missing plain method') self.assert_(('datablob', 'data', A) in attrs, 'missing data') diff --git a/Lib/test/test_pyclbr.py b/Lib/test/test_pyclbr.py index caccf11..bcb7988 100644 --- a/Lib/test/test_pyclbr.py +++ b/Lib/test/test_pyclbr.py @@ -64,23 +64,17 @@ class PyclbrTest(TestCase): def ismethod(oclass, obj, name): classdict = oclass.__dict__ - if isinstance(obj, FunctionType): - if not isinstance(classdict[name], StaticMethodType): + if isinstance(obj, MethodType): + # could be a classmethod + if (not isinstance(classdict[name], ClassMethodType) or + obj.im_self is not oclass): return False - else: - if not isinstance(obj, MethodType): - return False - if obj.im_self is not None: - if (not isinstance(classdict[name], ClassMethodType) or - obj.im_self is not oclass): - return False - else: - if not isinstance(classdict[name], FunctionType): - return False + elif not isinstance(obj, FunctionType): + return False objname = obj.__name__ if objname.startswith("__") and not objname.endswith("__"): - objname = "_%s%s" % (obj.im_class.__name__, objname) + objname = "_%s%s" % (oclass.__name__, objname) return objname == name # Make sure the toplevel functions and classes are the same. @@ -154,7 +148,7 @@ class PyclbrTest(TestCase): # XXX: See comment in pyclbr_input.py for a test that would fail # if it were not commented out. # - self.checkModule('test.pyclbr_input') + self.checkModule('test.pyclbr_input', ignore=['om']) def test_others(self): cm = self.checkModule diff --git a/Lib/test/test_repr.py b/Lib/test/test_repr.py index 8d9e99d..af66e97 100644 --- a/Lib/test/test_repr.py +++ b/Lib/test/test_repr.py @@ -280,8 +280,8 @@ class aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa ''') from areallylongpackageandmodulenametotestreprtruncation.areallylongpackageandmodulenametotestreprtruncation import qux # Unbound methods first - eq(repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod), - '') + self.failUnless(repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod).startswith( + '