test_funcattrs.py revision d9d1d4ac6fda7f4c898b55194be37b03b89450e9
1from test_support import verbose, TestFailed, verify
2import types
3
4class F:
5    def a(self):
6        pass
7
8def b():
9    'my docstring'
10    pass
11
12# setting attributes on functions
13try:
14    b.publish
15except AttributeError: pass
16else: raise TestFailed, 'expected AttributeError'
17
18if b.__dict__ <> {}:
19    raise TestFailed, 'expected unassigned func.__dict__ to be {}'
20
21b.publish = 1
22if b.publish <> 1:
23    raise TestFailed, 'function attribute not set to expected value'
24
25docstring = 'its docstring'
26b.__doc__ = docstring
27if b.__doc__ <> docstring:
28    raise TestFailed, 'problem with setting __doc__ attribute'
29
30if 'publish' not in dir(b):
31    raise TestFailed, 'attribute not in dir()'
32
33try:
34    del b.__dict__
35except TypeError: pass
36else: raise TestFailed, 'del func.__dict__ expected TypeError'
37
38b.publish = 1
39try:
40    b.__dict__ = None
41except TypeError: pass
42else: raise TestFailed, 'func.__dict__ = None expected TypeError'
43
44d = {'hello': 'world'}
45b.__dict__ = d
46if b.func_dict is not d:
47    raise TestFailed, 'func.__dict__ assignment to dictionary failed'
48if b.hello <> 'world':
49    raise TestFailed, 'attribute after func.__dict__ assignment failed'
50
51f1 = F()
52f2 = F()
53
54try:
55    F.a.publish
56except AttributeError: pass
57else: raise TestFailed, 'expected AttributeError'
58
59try:
60    f1.a.publish
61except AttributeError: pass
62else: raise TestFailed, 'expected AttributeError'
63
64# In Python 2.1 beta 1, we disallowed setting attributes on unbound methods
65# (it was already disallowed on bound methods).  See the PEP for details.
66try:
67    F.a.publish = 1
68except TypeError: pass
69else: raise TestFailed, 'expected TypeError'
70
71# But setting it explicitly on the underlying function object is okay.
72F.a.im_func.publish = 1
73
74if F.a.publish <> 1:
75    raise TestFailed, 'unbound method attribute not set to expected value'
76
77if f1.a.publish <> 1:
78    raise TestFailed, 'bound method attribute access did not work'
79
80if f2.a.publish <> 1:
81    raise TestFailed, 'bound method attribute access did not work'
82
83if 'publish' not in dir(F.a):
84    raise TestFailed, 'attribute not in dir()'
85
86try:
87    f1.a.publish = 0
88except TypeError: pass
89else: raise TestFailed, 'expected TypeError'
90
91# See the comment above about the change in semantics for Python 2.1b1
92try:
93    F.a.myclass = F
94except TypeError: pass
95else: raise TestFailed, 'expected TypeError'
96
97F.a.im_func.myclass = F
98
99f1.a.myclass
100f2.a.myclass
101f1.a.myclass
102F.a.myclass
103
104if f1.a.myclass is not f2.a.myclass or \
105       f1.a.myclass is not F.a.myclass:
106    raise TestFailed, 'attributes were not the same'
107
108# try setting __dict__
109try:
110    F.a.__dict__ = (1, 2, 3)
111except TypeError: pass
112else: raise TestFailed, 'expected TypeError'
113
114F.a.im_func.__dict__ = {'one': 11, 'two': 22, 'three': 33}
115
116if f1.a.two <> 22:
117    raise TestFailed, 'setting __dict__'
118
119from UserDict import UserDict
120d = UserDict({'four': 44, 'five': 55})
121
122try:
123    F.a.__dict__ = d
124except TypeError: pass
125else: raise TestFailed
126
127if f2.a.one <> f1.a.one <> F.a.one <> 11:
128    raise TestFailed
129
130# im_func may not be a Python method!
131import new
132F.id = new.instancemethod(id, None, F)
133
134eff = F()
135if eff.id() <> id(eff):
136    raise TestFailed
137
138try:
139    F.id.foo
140except AttributeError: pass
141else: raise TestFailed
142
143try:
144    F.id.foo = 12
145except TypeError: pass
146else: raise TestFailed
147
148try:
149    F.id.foo
150except AttributeError: pass
151else: raise TestFailed
152
153try:
154    eff.id.foo
155except AttributeError: pass
156else: raise TestFailed
157
158try:
159    eff.id.foo = 12
160except TypeError: pass
161else: raise TestFailed
162
163try:
164    eff.id.foo
165except AttributeError: pass
166else: raise TestFailed
167
168# Regression test for a crash in pre-2.1a1
169def another():
170    pass
171
172try:
173    del another.__dict__
174except TypeError: pass
175else: raise TestFailed
176
177try:
178    del another.func_dict
179except TypeError: pass
180else: raise TestFailed
181
182try:
183    another.func_dict = None
184except TypeError: pass
185else: raise TestFailed
186
187try:
188    del another.bar
189except AttributeError: pass
190else: raise TestFailed
191
192# This isn't specifically related to function attributes, but it does test a
193# core dump regression in funcobject.c
194del another.func_defaults
195
196def foo():
197    pass
198
199def bar():
200    pass
201
202def temp():
203    print 1
204
205if foo==bar:
206    raise TestFailed
207
208d={}
209d[foo] = 1
210
211foo.func_code = temp.func_code
212
213d[foo]
214
215# Test all predefined function attributes systematically
216
217def test_func_closure():
218    a = 12
219    def f(): print a
220    c = f.func_closure
221    verify(isinstance(c, tuple))
222    verify(len(c) == 1)
223    verify(c[0].__class__.__name__ == "cell") # don't have a type object handy
224    try:
225        f.func_closure = c
226    except (AttributeError, TypeError):
227        pass
228    else:
229        raise TestFailed, "shouldn't be allowed to set func_closure"
230    try:
231        del a.func_closure
232    except (AttributeError, TypeError):
233        pass
234    else:
235        raise TestFailed, "shouldn't be allowed to del func_closure"
236
237def test_func_doc():
238    def f(): pass
239    verify(f.__doc__ is None)
240    verify(f.func_doc is None)
241    f.__doc__ = "hello"
242    verify(f.__doc__ == "hello")
243    verify(f.func_doc == "hello")
244    del f.__doc__
245    verify(f.__doc__ is None)
246    verify(f.func_doc is None)
247    f.func_doc = "world"
248    verify(f.__doc__ == "world")
249    verify(f.func_doc == "world")
250    del f.func_doc
251    verify(f.func_doc is None)
252    verify(f.__doc__ is None)
253
254def test_func_globals():
255    def f(): pass
256    verify(f.func_globals is globals())
257    try:
258        f.func_globals = globals()
259    except (AttributeError, TypeError):
260        pass
261    else:
262        raise TestFailed, "shouldn't be allowed to set func_globals"
263    try:
264        del f.func_globals
265    except (AttributeError, TypeError):
266        pass
267    else:
268        raise TestFailed, "shouldn't be allowed to del func_globals"
269
270def test_func_name():
271    def f(): pass
272    verify(f.__name__ == "f")
273    verify(f.func_name == "f")
274    try:
275        f.func_name = "f"
276    except (AttributeError, TypeError):
277        pass
278    else:
279        raise TestFailed, "shouldn't be allowed to set func_name"
280    try:
281        f.__name__ = "f"
282    except (AttributeError, TypeError):
283        pass
284    else:
285        raise TestFailed, "shouldn't be allowed to set __name__"
286    try:
287        del f.func_name
288    except (AttributeError, TypeError):
289        pass
290    else:
291        raise TestFailed, "shouldn't be allowed to del func_name"
292    try:
293        del f.__name__
294    except (AttributeError, TypeError):
295        pass
296    else:
297        raise TestFailed, "shouldn't be allowed to del __name__"
298
299def test_func_code():
300    def f(): pass
301    def g(): print 12
302    verify(type(f.func_code) is types.CodeType)
303    f.func_code = g.func_code
304    try:
305        del f.func_code
306    except (AttributeError, TypeError):
307        pass
308    else:
309        raise TestFailed, "shouldn't be allowed to del func_code"
310
311def test_func_defaults():
312    def f(a, b): return (a, b)
313    verify(f.func_defaults is None)
314    f.func_defaults = (1, 2)
315    verify(f.func_defaults == (1, 2))
316    verify(f(10) == (10, 2))
317    def g(a=1, b=2): return (a, b)
318    verify(g.func_defaults == (1, 2))
319    del g.func_defaults
320    verify(g.func_defaults is None)
321    try:
322        g()
323    except TypeError:
324        pass
325    else:
326        raise TestFailed, "shouldn't be allowed to call g() w/o defaults"
327
328def test_func_dict():
329    def f(): pass
330    a = f.__dict__
331    b = f.func_dict
332    verify(a == {})
333    verify(a is b)
334    f.hello = 'world'
335    verify(a == {'hello': 'world'})
336    verify(f.func_dict is a is f.__dict__)
337    f.func_dict = {}
338    try:
339        f.hello
340    except (AttributeError, TypeError):
341        pass
342    else:
343        raise TestFailed, "hello attribute should have disappeared"
344    f.__dict__ = {'world': 'hello'}
345    verify(f.world == "hello")
346    verify(f.__dict__ is f.func_dict == {'world': 'hello'})
347    try:
348        del f.func_dict
349    except (AttributeError, TypeError):
350        pass
351    else:
352        raise TestFailed, "shouldn't be allowed to delete func_dict"
353    try:
354        del f.__dict__
355    except (AttributeError, TypeError):
356        pass
357    else:
358        raise TestFailed, "shouldn't be allowed to delete __dict__"
359
360def testmore():
361    test_func_closure()
362    test_func_doc()
363    test_func_globals()
364    test_func_name()
365    test_func_code()
366    test_func_defaults()
367    test_func_dict()
368
369testmore()
370