mirror of
https://github.com/python/cpython.git
synced 2025-07-23 19:25:40 +00:00

There really isn't a good reason for instance method objects to have their own __dict__, __doc__ and __name__ properties that just delegate the request to the function (callable); the default attribute behavior already does this. The test suite had to be fixed because the error changes from TypeError to AttributeError.
379 lines
8.5 KiB
Python
379 lines
8.5 KiB
Python
from test_support import verbose, TestFailed, verify
|
|
import types
|
|
|
|
class F:
|
|
def a(self):
|
|
pass
|
|
|
|
def b():
|
|
'my docstring'
|
|
pass
|
|
|
|
# setting attributes on functions
|
|
try:
|
|
b.publish
|
|
except AttributeError: pass
|
|
else: raise TestFailed, 'expected AttributeError'
|
|
|
|
if b.__dict__ <> {}:
|
|
raise TestFailed, 'expected unassigned func.__dict__ to be {}'
|
|
|
|
b.publish = 1
|
|
if b.publish <> 1:
|
|
raise TestFailed, 'function attribute not set to expected value'
|
|
|
|
docstring = 'its docstring'
|
|
b.__doc__ = docstring
|
|
if b.__doc__ <> docstring:
|
|
raise TestFailed, 'problem with setting __doc__ attribute'
|
|
|
|
if 'publish' not in dir(b):
|
|
raise TestFailed, 'attribute not in dir()'
|
|
|
|
try:
|
|
del b.__dict__
|
|
except TypeError: pass
|
|
else: raise TestFailed, 'del func.__dict__ expected TypeError'
|
|
|
|
b.publish = 1
|
|
try:
|
|
b.__dict__ = None
|
|
except TypeError: pass
|
|
else: raise TestFailed, 'func.__dict__ = None expected TypeError'
|
|
|
|
d = {'hello': 'world'}
|
|
b.__dict__ = d
|
|
if b.func_dict is not d:
|
|
raise TestFailed, 'func.__dict__ assignment to dictionary failed'
|
|
if b.hello <> 'world':
|
|
raise TestFailed, 'attribute after func.__dict__ assignment failed'
|
|
|
|
f1 = F()
|
|
f2 = F()
|
|
|
|
try:
|
|
F.a.publish
|
|
except AttributeError: pass
|
|
else: raise TestFailed, 'expected AttributeError'
|
|
|
|
try:
|
|
f1.a.publish
|
|
except AttributeError: pass
|
|
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
|
|
|
|
if F.a.publish <> 1:
|
|
raise TestFailed, 'unbound method attribute not set to expected value'
|
|
|
|
if f1.a.publish <> 1:
|
|
raise TestFailed, 'bound method attribute access did not work'
|
|
|
|
if f2.a.publish <> 1:
|
|
raise TestFailed, 'bound method attribute access did not work'
|
|
|
|
if 'publish' not in dir(F.a):
|
|
raise TestFailed, 'attribute not in dir()'
|
|
|
|
try:
|
|
f1.a.publish = 0
|
|
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}
|
|
|
|
if f1.a.two <> 22:
|
|
raise TestFailed, 'setting __dict__'
|
|
|
|
from UserDict import UserDict
|
|
d = UserDict({'four': 44, 'five': 55})
|
|
|
|
try:
|
|
F.a.__dict__ = d
|
|
except (AttributeError, TypeError): pass
|
|
else: raise TestFailed
|
|
|
|
if f2.a.one <> f1.a.one <> F.a.one <> 11:
|
|
raise TestFailed
|
|
|
|
# im_func may not be a Python method!
|
|
import new
|
|
F.id = new.instancemethod(id, None, F)
|
|
|
|
eff = F()
|
|
if eff.id() <> id(eff):
|
|
raise TestFailed
|
|
|
|
try:
|
|
F.id.foo
|
|
except AttributeError: pass
|
|
else: raise TestFailed
|
|
|
|
try:
|
|
F.id.foo = 12
|
|
except (AttributeError, TypeError): pass
|
|
else: raise TestFailed
|
|
|
|
try:
|
|
F.id.foo
|
|
except AttributeError: pass
|
|
else: raise TestFailed
|
|
|
|
try:
|
|
eff.id.foo
|
|
except AttributeError: pass
|
|
else: raise TestFailed
|
|
|
|
try:
|
|
eff.id.foo = 12
|
|
except (AttributeError, TypeError): pass
|
|
else: raise TestFailed
|
|
|
|
try:
|
|
eff.id.foo
|
|
except AttributeError: pass
|
|
else: raise TestFailed
|
|
|
|
# Regression test for a crash in pre-2.1a1
|
|
def another():
|
|
pass
|
|
|
|
try:
|
|
del another.__dict__
|
|
except TypeError: pass
|
|
else: raise TestFailed
|
|
|
|
try:
|
|
del another.func_dict
|
|
except TypeError: pass
|
|
else: raise TestFailed
|
|
|
|
try:
|
|
another.func_dict = None
|
|
except TypeError: pass
|
|
else: raise TestFailed
|
|
|
|
try:
|
|
del another.bar
|
|
except AttributeError: pass
|
|
else: raise TestFailed
|
|
|
|
# This isn't specifically related to function attributes, but it does test a
|
|
# core dump regression in funcobject.c
|
|
del another.func_defaults
|
|
|
|
def foo():
|
|
pass
|
|
|
|
def bar():
|
|
pass
|
|
|
|
def temp():
|
|
print 1
|
|
|
|
if foo==bar:
|
|
raise TestFailed
|
|
|
|
d={}
|
|
d[foo] = 1
|
|
|
|
foo.func_code = temp.func_code
|
|
|
|
d[foo]
|
|
|
|
# Test all predefined function attributes systematically
|
|
|
|
def cantset(obj, name, value):
|
|
verify(hasattr(obj, name)) # Otherwise it's probably a typo
|
|
try:
|
|
setattr(obj, name, value)
|
|
except (AttributeError, TypeError):
|
|
pass
|
|
else:
|
|
raise TestFailed, "shouldn't be able to set %s to %r" % (name, value)
|
|
try:
|
|
delattr(obj, name)
|
|
except (AttributeError, TypeError):
|
|
pass
|
|
else:
|
|
raise TestFailed, "shouldn't be able to del %s" % name
|
|
|
|
def test_func_closure():
|
|
a = 12
|
|
def f(): print a
|
|
c = f.func_closure
|
|
verify(isinstance(c, tuple))
|
|
verify(len(c) == 1)
|
|
verify(c[0].__class__.__name__ == "cell") # don't have a type object handy
|
|
cantset(f, "func_closure", c)
|
|
|
|
def test_func_doc():
|
|
def f(): pass
|
|
verify(f.__doc__ is None)
|
|
verify(f.func_doc is None)
|
|
f.__doc__ = "hello"
|
|
verify(f.__doc__ == "hello")
|
|
verify(f.func_doc == "hello")
|
|
del f.__doc__
|
|
verify(f.__doc__ is None)
|
|
verify(f.func_doc is None)
|
|
f.func_doc = "world"
|
|
verify(f.__doc__ == "world")
|
|
verify(f.func_doc == "world")
|
|
del f.func_doc
|
|
verify(f.func_doc is None)
|
|
verify(f.__doc__ is None)
|
|
|
|
def test_func_globals():
|
|
def f(): pass
|
|
verify(f.func_globals is globals())
|
|
cantset(f, "func_globals", globals())
|
|
|
|
def test_func_name():
|
|
def f(): pass
|
|
verify(f.__name__ == "f")
|
|
verify(f.func_name == "f")
|
|
cantset(f, "func_name", "f")
|
|
cantset(f, "__name__", "f")
|
|
|
|
def test_func_code():
|
|
def f(): pass
|
|
def g(): print 12
|
|
verify(type(f.func_code) is types.CodeType)
|
|
f.func_code = g.func_code
|
|
cantset(f, "func_code", None)
|
|
|
|
def test_func_defaults():
|
|
def f(a, b): return (a, b)
|
|
verify(f.func_defaults is None)
|
|
f.func_defaults = (1, 2)
|
|
verify(f.func_defaults == (1, 2))
|
|
verify(f(10) == (10, 2))
|
|
def g(a=1, b=2): return (a, b)
|
|
verify(g.func_defaults == (1, 2))
|
|
del g.func_defaults
|
|
verify(g.func_defaults is None)
|
|
try:
|
|
g()
|
|
except TypeError:
|
|
pass
|
|
else:
|
|
raise TestFailed, "shouldn't be allowed to call g() w/o defaults"
|
|
|
|
def test_func_dict():
|
|
def f(): pass
|
|
a = f.__dict__
|
|
b = f.func_dict
|
|
verify(a == {})
|
|
verify(a is b)
|
|
f.hello = 'world'
|
|
verify(a == {'hello': 'world'})
|
|
verify(f.func_dict is a is f.__dict__)
|
|
f.func_dict = {}
|
|
verify(not hasattr(f, "hello"))
|
|
f.__dict__ = {'world': 'hello'}
|
|
verify(f.world == "hello")
|
|
verify(f.__dict__ is f.func_dict == {'world': 'hello'})
|
|
cantset(f, "func_dict", None)
|
|
cantset(f, "__dict__", None)
|
|
|
|
def test_im_class():
|
|
class C:
|
|
def foo(self): pass
|
|
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)
|
|
|
|
def test_im_func():
|
|
def foo(self): pass
|
|
class C:
|
|
pass
|
|
C.foo = 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)
|
|
|
|
def test_im_self():
|
|
class C:
|
|
def foo(self): pass
|
|
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)
|
|
|
|
def test_im_dict():
|
|
class C:
|
|
def foo(self): pass
|
|
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__)
|
|
|
|
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")
|
|
|
|
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")
|
|
|
|
def testmore():
|
|
test_func_closure()
|
|
test_func_doc()
|
|
test_func_globals()
|
|
test_func_name()
|
|
test_func_code()
|
|
test_func_defaults()
|
|
test_func_dict()
|
|
# Tests for instance method attributes
|
|
test_im_class()
|
|
test_im_func()
|
|
test_im_self()
|
|
test_im_dict()
|
|
test_im_doc()
|
|
test_im_name()
|
|
|
|
testmore()
|