Patch from Georg Brandl and me for #1493

Remove unbound method objects
This commit is contained in:
Christian Heimes 2007-11-25 09:39:14 +00:00
parent 91c77301bf
commit 4a22b5dee7
19 changed files with 65 additions and 115 deletions

View File

@ -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 (

View File

@ -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:

View File

@ -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):

View File

@ -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'

View File

@ -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():

View File

@ -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

View File

@ -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):

View File

@ -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

View File

@ -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()

View File

@ -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')

View File

@ -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

View File

@ -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(

View File

@ -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
)

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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))

View File

@ -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);
}