diff options
author | Christian Heimes <christian@cheimes.de> | 2007-11-25 09:39:14 (GMT) |
---|---|---|
committer | Christian Heimes <christian@cheimes.de> | 2007-11-25 09:39:14 (GMT) |
commit | 4a22b5dee77b6a3439e4a09362586c390bbdef02 (patch) | |
tree | 670472c02e788fe4d027f7967fbbd8253e18cb5f | |
parent | 91c77301bf0246deabcdcd80bc7bedb169e2f964 (diff) | |
download | cpython-4a22b5dee77b6a3439e4a09362586c390bbdef02.zip cpython-4a22b5dee77b6a3439e4a09362586c390bbdef02.tar.gz cpython-4a22b5dee77b6a3439e4a09362586c390bbdef02.tar.bz2 |
Patch from Georg Brandl and me for #1493
Remove unbound method objects
-rw-r--r-- | Lib/DocXMLRPCServer.py | 2 | ||||
-rw-r--r-- | Lib/inspect.py | 4 | ||||
-rw-r--r-- | Lib/test/inspect_fodder2.py | 2 | ||||
-rw-r--r-- | Lib/test/output/test_extcall | 2 | ||||
-rw-r--r-- | Lib/test/test_class.py | 2 | ||||
-rw-r--r-- | Lib/test/test_descr.py | 19 | ||||
-rw-r--r-- | Lib/test/test_descrtut.py | 4 | ||||
-rw-r--r-- | Lib/test/test_extcall.py | 14 | ||||
-rw-r--r-- | Lib/test/test_funcattrs.py | 61 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 5 | ||||
-rw-r--r-- | Lib/test/test_pyclbr.py | 22 | ||||
-rw-r--r-- | Lib/test/test_repr.py | 4 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 2 | ||||
-rw-r--r-- | Lib/test/test_typechecks.py | 12 | ||||
-rw-r--r-- | Lib/test/test_weakref.py | 5 | ||||
-rw-r--r-- | Lib/types.py | 1 | ||||
-rw-r--r-- | Lib/unittest.py | 11 | ||||
-rw-r--r-- | Lib/xml/dom/minicompat.py | 2 | ||||
-rw-r--r-- | 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 = '<a name="%s"><strong>%s</strong></a>' % (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("<bound method ")) + r = repr(E().foo) + verify(r.startswith("<bound method E.foo "), r) + r = repr(C.foo.__get__(C())) + verify(r.startswith("<bound method ?.foo "), r) def compattr(): if verbose: print("Testing computed attributes...") @@ -1685,8 +1687,9 @@ def methods(): vereq(d2.goo(), 1) class E(object): foo = C.foo - vereq(E().foo, C.foo) # i.e., unbound - verify(repr(C.foo.__get__(C(1))).startswith("<bound method ")) + vereq(E().foo.im_func, C.foo) # i.e., unbound + r = repr(C.foo.__get__(C(1))) + verify(r.startswith("<bound method "), r) def specials(): # Test operators like __hash__ for which a built-in default exists diff --git a/Lib/test/test_descrtut.py b/Lib/test/test_descrtut.py index ea75366..5ce2119 100644 --- a/Lib/test/test_descrtut.py +++ b/Lib/test/test_descrtut.py @@ -444,9 +444,7 @@ Backwards incompatibilities ... B.foo(self) >>> 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), - '<unbound method aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod>') + self.failUnless(repr(qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.amethod).startswith( + '<function amethod')) # Bound method next iqux = qux.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa() self.failUnless(repr(iqux.amethod).startswith( diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index dfad172..9a285c5 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -183,7 +183,7 @@ class SysModuleTest(unittest.TestCase): self.assertRaises(TypeError, sys._getframe, 42, 42) self.assertRaises(ValueError, sys._getframe, 2000000000) self.assert_( - SysModuleTest.test_getframe.im_func.__code__ \ + SysModuleTest.test_getframe.__code__ \ is sys._getframe().f_code ) diff --git a/Lib/test/test_typechecks.py b/Lib/test/test_typechecks.py index 632598c..1dcc82c 100644 --- a/Lib/test/test_typechecks.py +++ b/Lib/test/test_typechecks.py @@ -18,19 +18,13 @@ class ABC(type): class Integer(metaclass=ABC): - __subclass__ = {int} class SubInt(Integer): - pass -class Evil: - def __instancecheck__(self, inst): return False - - class TypeChecksTest(unittest.TestCase): def testIsSubclassInternal(self): @@ -60,12 +54,6 @@ class TypeChecksTest(unittest.TestCase): self.assertEqual(isinstance(SubInt(), SubInt), True) self.assertEqual(isinstance(42, SubInt), False) - def testInfiniteRecursionCaughtProperly(self): - e = Evil() - # This invokes isinstance() recursively, until the stack is exhausted. - self.assertRaises(RuntimeError, isinstance, e, Evil) - # XXX How to check the same situation for issubclass()? - def test_main(): test_support.run_unittest(TypeChecksTest) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 1a49aea..922b293 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -28,9 +28,6 @@ def create_function(): def create_bound_method(): return C().method -def create_unbound_method(): - return C.method - class TestBase(unittest.TestCase): @@ -47,7 +44,6 @@ class ReferencesTestCase(TestBase): self.check_basic_ref(C) self.check_basic_ref(create_function) self.check_basic_ref(create_bound_method) - self.check_basic_ref(create_unbound_method) # Just make sure the tp_repr handler doesn't raise an exception. # Live reference: @@ -62,7 +58,6 @@ class ReferencesTestCase(TestBase): self.check_basic_callback(C) self.check_basic_callback(create_function) self.check_basic_callback(create_bound_method) - self.check_basic_callback(create_unbound_method) def test_multiple_callbacks(self): o = C() diff --git a/Lib/types.py b/Lib/types.py index 5c1f249..402fa18 100644 --- a/Lib/types.py +++ b/Lib/types.py @@ -38,7 +38,6 @@ GeneratorType = type(_g()) class _C: def _m(self): pass ClassType = type -UnboundMethodType = type(_C._m) # Same as MethodType MethodType = type(_C()._m) BuiltinFunctionType = type(len) diff --git a/Lib/unittest.py b/Lib/unittest.py index c590558..c4b124b 100644 --- a/Lib/unittest.py +++ b/Lib/unittest.py @@ -559,13 +559,18 @@ class TestLoader: return self.loadTestsFromModule(obj) elif isinstance(obj, type) and issubclass(obj, TestCase): return self.loadTestsFromTestCase(obj) - elif (isinstance(obj, types.UnboundMethodType) and + elif (isinstance(obj, types.FunctionType) and isinstance(parent, type) and issubclass(parent, TestCase)): - return TestSuite([parent(obj.__name__)]) + name = obj.__name__ + inst = parent(name) + # static methods follow a different path + if not(isinstance(getattr(inst, name), types.FunctionType)): + return TestSuite([inst]) elif isinstance(obj, TestSuite): return obj - elif hasattr(obj, '__call__'): + + if hasattr(obj, '__call__'): test = obj() if isinstance(test, TestSuite): return test diff --git a/Lib/xml/dom/minicompat.py b/Lib/xml/dom/minicompat.py index c0a797e..2e6cc7e 100644 --- a/Lib/xml/dom/minicompat.py +++ b/Lib/xml/dom/minicompat.py @@ -95,7 +95,7 @@ class EmptyNodeList(tuple): def defproperty(klass, name, doc): - get = getattr(klass, ("_get_" + name)).im_func + get = getattr(klass, ("_get_" + name)) def set(self, value, name=name): raise xml.dom.NoModificationAllowedErr( "attempt to modify read-only attribute " + repr(name)) diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 408be4c..f9b0346 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -643,8 +643,10 @@ function_call(PyObject *func, PyObject *arg, PyObject *kw) static PyObject * func_descr_get(PyObject *func, PyObject *obj, PyObject *type) { - if (obj == Py_None) - obj = NULL; + if (obj == Py_None || obj == NULL) { + Py_INCREF(func); + return func; + } return PyMethod_New(func, obj, type); } |