Commit 033daa49 authored by Barry Warsaw's avatar Barry Warsaw

Test the new semantics for setting and deleting a function's __dict__

attribute.  Deleting it, or setting it to a non-dictionary result in a
TypeError.  Note that getting it the first time magically initializes
it to an empty dict so that func.__dict__ will always appear to be a
dictionary (never None).

Closes SF bug #446645.
parent 142865ca
...@@ -11,13 +11,11 @@ def b(): ...@@ -11,13 +11,11 @@ def b():
# setting attributes on functions # setting attributes on functions
try: try:
b.publish b.publish
except AttributeError: except AttributeError: pass
pass else: raise TestFailed, 'expected AttributeError'
else:
raise TestFailed, 'expected AttributeError'
if b.__dict__ <> None: if b.__dict__ <> {}:
raise TestFailed, 'expected unassigned func.__dict__ to be None' raise TestFailed, 'expected unassigned func.__dict__ to be {}'
b.publish = 1 b.publish = 1
if b.publish <> 1: if b.publish <> 1:
...@@ -31,41 +29,43 @@ if b.__doc__ <> docstring: ...@@ -31,41 +29,43 @@ if b.__doc__ <> docstring:
if 'publish' not in dir(b): if 'publish' not in dir(b):
raise TestFailed, 'attribute not in dir()' raise TestFailed, 'attribute not in dir()'
del b.__dict__ try:
if b.__dict__ <> None: del b.__dict__
raise TestFailed, 'del func.__dict__ did not result in __dict__ == None' except TypeError: pass
else: raise TestFailed, 'del func.__dict__ expected TypeError'
b.publish = 1 b.publish = 1
b.__dict__ = None try:
if b.__dict__ <> None: b.__dict__ = None
raise TestFailed, 'func.__dict__ = None did not result in __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() f1 = F()
f2 = F() f2 = F()
try: try:
F.a.publish F.a.publish
except AttributeError: except AttributeError: pass
pass else: raise TestFailed, 'expected AttributeError'
else:
raise TestFailed, 'expected AttributeError'
try: try:
f1.a.publish f1.a.publish
except AttributeError: except AttributeError: pass
pass else: raise TestFailed, 'expected AttributeError'
else:
raise TestFailed, 'expected AttributeError'
# In Python 2.1 beta 1, we disallowed setting attributes on unbound methods # 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. # (it was already disallowed on bound methods). See the PEP for details.
try: try:
F.a.publish = 1 F.a.publish = 1
except TypeError: except TypeError: pass
pass else: raise TestFailed, 'expected TypeError'
else:
raise TestFailed, 'expected TypeError'
# But setting it explicitly on the underlying function object is okay. # But setting it explicitly on the underlying function object is okay.
F.a.im_func.publish = 1 F.a.im_func.publish = 1
...@@ -84,18 +84,14 @@ if 'publish' not in dir(F.a): ...@@ -84,18 +84,14 @@ if 'publish' not in dir(F.a):
try: try:
f1.a.publish = 0 f1.a.publish = 0
except TypeError: except TypeError: pass
pass else: raise TestFailed, 'expected TypeError'
else:
raise TestFailed, 'expected TypeError'
# See the comment above about the change in semantics for Python 2.1b1 # See the comment above about the change in semantics for Python 2.1b1
try: try:
F.a.myclass = F F.a.myclass = F
except TypeError: except TypeError: pass
pass else: raise TestFailed, 'expected TypeError'
else:
raise TestFailed, 'expected TypeError'
F.a.im_func.myclass = F F.a.im_func.myclass = F
...@@ -105,16 +101,14 @@ f1.a.myclass ...@@ -105,16 +101,14 @@ f1.a.myclass
F.a.myclass F.a.myclass
if f1.a.myclass is not f2.a.myclass or \ if f1.a.myclass is not f2.a.myclass or \
f1.a.myclass is not F.a.myclass: f1.a.myclass is not F.a.myclass:
raise TestFailed, 'attributes were not the same' raise TestFailed, 'attributes were not the same'
# try setting __dict__ # try setting __dict__
try: try:
F.a.__dict__ = (1, 2, 3) F.a.__dict__ = (1, 2, 3)
except TypeError: except TypeError: pass
pass else: raise TestFailed, 'expected TypeError'
else:
raise TestFailed, 'expected TypeError'
F.a.im_func.__dict__ = {'one': 11, 'two': 22, 'three': 33} F.a.im_func.__dict__ = {'one': 11, 'two': 22, 'three': 33}
...@@ -126,10 +120,8 @@ d = UserDict({'four': 44, 'five': 55}) ...@@ -126,10 +120,8 @@ d = UserDict({'four': 44, 'five': 55})
try: try:
F.a.__dict__ = d F.a.__dict__ = d
except TypeError: except TypeError: pass
pass else: raise TestFailed
else:
raise TestFailed
if f2.a.one <> f1.a.one <> F.a.one <> 11: if f2.a.one <> f1.a.one <> F.a.one <> 11:
raise TestFailed raise TestFailed
...@@ -175,9 +167,21 @@ else: raise TestFailed ...@@ -175,9 +167,21 @@ else: raise TestFailed
# Regression test for a crash in pre-2.1a1 # Regression test for a crash in pre-2.1a1
def another(): def another():
pass pass
del another.__dict__
del another.func_dict try:
another.func_dict = None 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: try:
del another.bar del another.bar
...@@ -197,7 +201,8 @@ def bar(): ...@@ -197,7 +201,8 @@ def bar():
def temp(): def temp():
print 1 print 1
if foo==bar: raise TestFailed if foo==bar:
raise TestFailed
d={} d={}
d[foo] = 1 d[foo] = 1
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment