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