test_funcattrs.py revision 5c8da86f3a515ce1a6d5f27fd15e3c5f4d8e931e
1from test import test_support
2import types
3import unittest
4
5class FuncAttrsTest(unittest.TestCase):
6    def setUp(self):
7        class F:
8            def a(self):
9                pass
10        def b():
11            return 3
12        self.f = F
13        self.fi = F()
14        self.b = b
15
16    def cannot_set_attr(self,obj, name, value, exceptions):
17        # This method is not called as a test (name doesn't start with 'test'),
18        # but may be used by other tests.
19        try: setattr(obj, name, value)
20        except exceptions: pass
21        else: self.fail("shouldn't be able to set %s to %r" % (name, value))
22        try: delattr(obj, name)
23        except exceptions: pass
24        else: self.fail("shouldn't be able to del %s" % name)
25
26
27class FunctionPropertiesTest(FuncAttrsTest):
28    # Include the external setUp method that is common to all tests
29    def test_module(self):
30        self.assertEqual(self.b.__module__, __name__)
31
32    def test_dir_includes_correct_attrs(self):
33        self.b.known_attr = 7
34        self.assertTrue('known_attr' in dir(self.b),
35            "set attributes not in dir listing of method")
36        # Test on underlying function object of method
37        self.f.a.im_func.known_attr = 7
38        self.assertTrue('known_attr' in dir(self.f.a),
39            "set attribute on unbound method implementation in class not in "
40                     "dir")
41        self.assertTrue('known_attr' in dir(self.fi.a),
42            "set attribute on unbound method implementations, should show up"
43                     " in next dir")
44
45    def test_duplicate_function_equality(self):
46        # Body of `duplicate' is the exact same as self.b
47        def duplicate():
48            'my docstring'
49            return 3
50        self.assertNotEqual(self.b, duplicate)
51
52    def test_copying_func_code(self):
53        def test(): pass
54        self.assertEqual(test(), None)
55        test.func_code = self.b.func_code
56        self.assertEqual(test(), 3) # self.b always returns 3, arbitrarily
57
58    def test_func_globals(self):
59        self.assertEqual(self.b.func_globals, globals())
60        self.cannot_set_attr(self.b, 'func_globals', 2, TypeError)
61
62    def test_func_name(self):
63        self.assertEqual(self.b.__name__, 'b')
64        self.assertEqual(self.b.func_name, 'b')
65        self.b.__name__ = 'c'
66        self.assertEqual(self.b.__name__, 'c')
67        self.assertEqual(self.b.func_name, 'c')
68        self.b.func_name = 'd'
69        self.assertEqual(self.b.__name__, 'd')
70        self.assertEqual(self.b.func_name, 'd')
71        # __name__ and func_name must be a string
72        self.cannot_set_attr(self.b, '__name__', 7, TypeError)
73        self.cannot_set_attr(self.b, 'func_name', 7, TypeError)
74        # __name__ must be available when in restricted mode. Exec will raise
75        # AttributeError if __name__ is not available on f.
76        s = """def f(): pass\nf.__name__"""
77        exec s in {'__builtins__': {}}
78        # Test on methods, too
79        self.assertEqual(self.f.a.__name__, 'a')
80        self.assertEqual(self.fi.a.__name__, 'a')
81        self.cannot_set_attr(self.f.a, "__name__", 'a', AttributeError)
82        self.cannot_set_attr(self.fi.a, "__name__", 'a', AttributeError)
83
84    def test_func_code(self):
85        num_one, num_two = 7, 8
86        def a(): pass
87        def b(): return 12
88        def c(): return num_one
89        def d(): return num_two
90        def e(): return num_one, num_two
91        for func in [a, b, c, d, e]:
92            self.assertEqual(type(func.func_code), types.CodeType)
93        self.assertEqual(c(), 7)
94        self.assertEqual(d(), 8)
95        d.func_code = c.func_code
96        self.assertEqual(c.func_code, d.func_code)
97        self.assertEqual(c(), 7)
98        # self.assertEqual(d(), 7)
99        try: b.func_code = c.func_code
100        except ValueError: pass
101        else: self.fail(
102            "func_code with different numbers of free vars should not be "
103            "possible")
104        try: e.func_code = d.func_code
105        except ValueError: pass
106        else: self.fail(
107            "func_code with different numbers of free vars should not be "
108            "possible")
109
110    def test_blank_func_defaults(self):
111        self.assertEqual(self.b.func_defaults, None)
112        del self.b.func_defaults
113        self.assertEqual(self.b.func_defaults, None)
114
115    def test_func_default_args(self):
116        def first_func(a, b):
117            return a+b
118        def second_func(a=1, b=2):
119            return a+b
120        self.assertEqual(first_func.func_defaults, None)
121        self.assertEqual(second_func.func_defaults, (1, 2))
122        first_func.func_defaults = (1, 2)
123        self.assertEqual(first_func.func_defaults, (1, 2))
124        self.assertEqual(first_func(), 3)
125        self.assertEqual(first_func(3), 5)
126        self.assertEqual(first_func(3, 5), 8)
127        del second_func.func_defaults
128        self.assertEqual(second_func.func_defaults, None)
129        try: second_func()
130        except TypeError: pass
131        else: self.fail(
132            "func_defaults does not update; deleting it does not remove "
133            "requirement")
134
135class ImplicitReferencesTest(FuncAttrsTest):
136    def test_im_class(self):
137        self.assertEqual(self.f.a.im_class, self.f)
138        self.assertEqual(self.fi.a.im_class, self.f)
139        self.cannot_set_attr(self.f.a, "im_class", self.f, TypeError)
140        self.cannot_set_attr(self.fi.a, "im_class", self.f, TypeError)
141
142    def test_im_func(self):
143        self.f.b = self.b
144        self.assertEqual(self.f.b.im_func, self.b)
145        self.assertEqual(self.fi.b.im_func, self.b)
146        self.cannot_set_attr(self.f.b, "im_func", self.b, TypeError)
147        self.cannot_set_attr(self.fi.b, "im_func", self.b, TypeError)
148
149    def test_im_self(self):
150        self.assertEqual(self.f.a.im_self, None)
151        self.assertEqual(self.fi.a.im_self, self.fi)
152        self.cannot_set_attr(self.f.a, "im_self", None, TypeError)
153        self.cannot_set_attr(self.fi.a, "im_self", self.fi, TypeError)
154
155    def test_im_func_non_method(self):
156        # Behavior should be the same when a method is added via an attr
157        # assignment
158        self.f.id = types.MethodType(id, None, self.f)
159        self.assertEqual(self.fi.id(), id(self.fi))
160        self.assertNotEqual(self.fi.id(), id(self.f))
161        # Test usage
162        try: self.f.id.unknown_attr
163        except AttributeError: pass
164        else: self.fail("using unknown attributes should raise AttributeError")
165        # Test assignment and deletion
166        self.cannot_set_attr(self.f.id, 'unknown_attr', 2, AttributeError)
167        self.cannot_set_attr(self.fi.id, 'unknown_attr', 2, AttributeError)
168
169    def test_implicit_method_properties(self):
170        self.f.a.im_func.known_attr = 7
171        self.assertEqual(self.f.a.known_attr, 7)
172        self.assertEqual(self.fi.a.known_attr, 7)
173
174class ArbitraryFunctionAttrTest(FuncAttrsTest):
175    def test_set_attr(self):
176        self.b.known_attr = 7
177        self.assertEqual(self.b.known_attr, 7)
178        for func in [self.f.a, self.fi.a]:
179            try: func.known_attr = 7
180            except AttributeError: pass
181            else: self.fail("setting attributes on methods should raise error")
182
183    def test_delete_unknown_attr(self):
184        try: del self.b.unknown_attr
185        except AttributeError: pass
186        else: self.fail("deleting unknown attribute should raise TypeError")
187
188    def test_setting_attrs_duplicates(self):
189        try: self.f.a.klass = self.f
190        except AttributeError: pass
191        else: self.fail("setting arbitrary attribute in unbound function "
192                        " should raise AttributeError")
193        self.f.a.im_func.klass = self.f
194        for method in [self.f.a, self.fi.a, self.fi.a.im_func]:
195            self.assertEqual(method.klass, self.f)
196
197    def test_unset_attr(self):
198        for func in [self.b, self.f.a, self.fi.a]:
199            try:  func.non_existent_attr
200            except AttributeError: pass
201            else: self.fail("using unknown attributes should raise "
202                            "AttributeError")
203
204class FunctionDictsTest(FuncAttrsTest):
205    def test_setting_dict_to_invalid(self):
206        self.cannot_set_attr(self.b, '__dict__', None, TypeError)
207        self.cannot_set_attr(self.b, 'func_dict', None, TypeError)
208        from UserDict import UserDict
209        d = UserDict({'known_attr': 7})
210        self.cannot_set_attr(self.f.a.im_func, '__dict__', d, TypeError)
211        self.cannot_set_attr(self.fi.a.im_func, '__dict__', d, TypeError)
212
213    def test_setting_dict_to_valid(self):
214        d = {'known_attr': 7}
215        self.b.__dict__ = d
216        # Setting dict is only possible on the underlying function objects
217        self.f.a.im_func.__dict__ = d
218        # Test assignment
219        self.assertEqual(d, self.b.__dict__)
220        self.assertEqual(d, self.b.func_dict)
221        # ... and on all the different ways of referencing the method's func
222        self.assertEqual(d, self.f.a.im_func.__dict__)
223        self.assertEqual(d, self.f.a.__dict__)
224        self.assertEqual(d, self.fi.a.im_func.__dict__)
225        self.assertEqual(d, self.fi.a.__dict__)
226        # Test value
227        self.assertEqual(self.b.known_attr, 7)
228        self.assertEqual(self.b.__dict__['known_attr'], 7)
229        self.assertEqual(self.b.func_dict['known_attr'], 7)
230        # ... and again, on all the different method's names
231        self.assertEqual(self.f.a.im_func.known_attr, 7)
232        self.assertEqual(self.f.a.known_attr, 7)
233        self.assertEqual(self.fi.a.im_func.known_attr, 7)
234        self.assertEqual(self.fi.a.known_attr, 7)
235
236    def test_delete_func_dict(self):
237        try: del self.b.__dict__
238        except TypeError: pass
239        else: self.fail("deleting function dictionary should raise TypeError")
240        try: del self.b.func_dict
241        except TypeError: pass
242        else: self.fail("deleting function dictionary should raise TypeError")
243
244    def test_unassigned_dict(self):
245        self.assertEqual(self.b.__dict__, {})
246
247    def test_func_as_dict_key(self):
248        value = "Some string"
249        d = {}
250        d[self.b] = value
251        self.assertEqual(d[self.b], value)
252
253class FunctionDocstringTest(FuncAttrsTest):
254    def test_set_docstring_attr(self):
255        self.assertEqual(self.b.__doc__, None)
256        self.assertEqual(self.b.func_doc, None)
257        docstr = "A test method that does nothing"
258        self.b.__doc__ = self.f.a.im_func.__doc__ = docstr
259        self.assertEqual(self.b.__doc__, docstr)
260        self.assertEqual(self.b.func_doc, docstr)
261        self.assertEqual(self.f.a.__doc__, docstr)
262        self.assertEqual(self.fi.a.__doc__, docstr)
263        self.cannot_set_attr(self.f.a, "__doc__", docstr, AttributeError)
264        self.cannot_set_attr(self.fi.a, "__doc__", docstr, AttributeError)
265
266    def test_delete_docstring(self):
267        self.b.__doc__ = "The docstring"
268        del self.b.__doc__
269        self.assertEqual(self.b.__doc__, None)
270        self.assertEqual(self.b.func_doc, None)
271        self.b.func_doc = "The docstring"
272        del self.b.func_doc
273        self.assertEqual(self.b.__doc__, None)
274        self.assertEqual(self.b.func_doc, None)
275
276class StaticMethodAttrsTest(unittest.TestCase):
277    def test_func_attribute(self):
278        def f():
279            pass
280
281        c = classmethod(f)
282        self.assertTrue(c.__func__ is f)
283
284        s = staticmethod(f)
285        self.assertTrue(s.__func__ is f)
286
287
288def test_main():
289    test_support.run_unittest(FunctionPropertiesTest, ImplicitReferencesTest,
290                              ArbitraryFunctionAttrTest, FunctionDictsTest,
291                              FunctionDocstringTest,
292                              StaticMethodAttrsTest)
293
294if __name__ == "__main__":
295    test_main()
296