• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import types
2import unittest
3
4
5def global_function():
6    def inner_function():
7        class LocalClass:
8            pass
9        global inner_global_function
10        def inner_global_function():
11            def inner_function2():
12                pass
13            return inner_function2
14        return LocalClass
15    return lambda: inner_function
16
17
18class FuncAttrsTest(unittest.TestCase):
19    def setUp(self):
20        class F:
21            def a(self):
22                pass
23        def b():
24            return 3
25        self.fi = F()
26        self.F = F
27        self.b = b
28
29    def cannot_set_attr(self, obj, name, value, exceptions):
30        try:
31            setattr(obj, name, value)
32        except exceptions:
33            pass
34        else:
35            self.fail("shouldn't be able to set %s to %r" % (name, value))
36        try:
37            delattr(obj, name)
38        except exceptions:
39            pass
40        else:
41            self.fail("shouldn't be able to del %s" % name)
42
43
44class FunctionPropertiesTest(FuncAttrsTest):
45    # Include the external setUp method that is common to all tests
46    def test_module(self):
47        self.assertEqual(self.b.__module__, __name__)
48
49    def test_dir_includes_correct_attrs(self):
50        self.b.known_attr = 7
51        self.assertIn('known_attr', dir(self.b),
52            "set attributes not in dir listing of method")
53        # Test on underlying function object of method
54        self.F.a.known_attr = 7
55        self.assertIn('known_attr', dir(self.fi.a), "set attribute on function "
56                     "implementations, should show up in next dir")
57
58    def test_duplicate_function_equality(self):
59        # Body of `duplicate' is the exact same as self.b
60        def duplicate():
61            'my docstring'
62            return 3
63        self.assertNotEqual(self.b, duplicate)
64
65    def test_copying___code__(self):
66        def test(): pass
67        self.assertEqual(test(), None)
68        test.__code__ = self.b.__code__
69        self.assertEqual(test(), 3) # self.b always returns 3, arbitrarily
70
71    def test___globals__(self):
72        self.assertIs(self.b.__globals__, globals())
73        self.cannot_set_attr(self.b, '__globals__', 2,
74                             (AttributeError, TypeError))
75
76    def test___closure__(self):
77        a = 12
78        def f(): print(a)
79        c = f.__closure__
80        self.assertIsInstance(c, tuple)
81        self.assertEqual(len(c), 1)
82        # don't have a type object handy
83        self.assertEqual(c[0].__class__.__name__, "cell")
84        self.cannot_set_attr(f, "__closure__", c, AttributeError)
85
86    def test_empty_cell(self):
87        def f(): print(a)
88        try:
89            f.__closure__[0].cell_contents
90        except ValueError:
91            pass
92        else:
93            self.fail("shouldn't be able to read an empty cell")
94        a = 12
95
96    def test_set_cell(self):
97        a = 12
98        def f(): return a
99        c = f.__closure__
100        c[0].cell_contents = 9
101        self.assertEqual(c[0].cell_contents, 9)
102        self.assertEqual(f(), 9)
103        self.assertEqual(a, 9)
104        del c[0].cell_contents
105        try:
106            c[0].cell_contents
107        except ValueError:
108            pass
109        else:
110            self.fail("shouldn't be able to read an empty cell")
111        with self.assertRaises(NameError):
112            f()
113        with self.assertRaises(UnboundLocalError):
114            print(a)
115
116    def test___name__(self):
117        self.assertEqual(self.b.__name__, 'b')
118        self.b.__name__ = 'c'
119        self.assertEqual(self.b.__name__, 'c')
120        self.b.__name__ = 'd'
121        self.assertEqual(self.b.__name__, 'd')
122        # __name__ and __name__ must be a string
123        self.cannot_set_attr(self.b, '__name__', 7, TypeError)
124        # __name__ must be available when in restricted mode. Exec will raise
125        # AttributeError if __name__ is not available on f.
126        s = """def f(): pass\nf.__name__"""
127        exec(s, {'__builtins__': {}})
128        # Test on methods, too
129        self.assertEqual(self.fi.a.__name__, 'a')
130        self.cannot_set_attr(self.fi.a, "__name__", 'a', AttributeError)
131
132    def test___qualname__(self):
133        # PEP 3155
134        self.assertEqual(self.b.__qualname__, 'FuncAttrsTest.setUp.<locals>.b')
135        self.assertEqual(FuncAttrsTest.setUp.__qualname__, 'FuncAttrsTest.setUp')
136        self.assertEqual(global_function.__qualname__, 'global_function')
137        self.assertEqual(global_function().__qualname__,
138                         'global_function.<locals>.<lambda>')
139        self.assertEqual(global_function()().__qualname__,
140                         'global_function.<locals>.inner_function')
141        self.assertEqual(global_function()()().__qualname__,
142                         'global_function.<locals>.inner_function.<locals>.LocalClass')
143        self.assertEqual(inner_global_function.__qualname__, 'inner_global_function')
144        self.assertEqual(inner_global_function().__qualname__, 'inner_global_function.<locals>.inner_function2')
145        self.b.__qualname__ = 'c'
146        self.assertEqual(self.b.__qualname__, 'c')
147        self.b.__qualname__ = 'd'
148        self.assertEqual(self.b.__qualname__, 'd')
149        # __qualname__ must be a string
150        self.cannot_set_attr(self.b, '__qualname__', 7, TypeError)
151
152    def test___code__(self):
153        num_one, num_two = 7, 8
154        def a(): pass
155        def b(): return 12
156        def c(): return num_one
157        def d(): return num_two
158        def e(): return num_one, num_two
159        for func in [a, b, c, d, e]:
160            self.assertEqual(type(func.__code__), types.CodeType)
161        self.assertEqual(c(), 7)
162        self.assertEqual(d(), 8)
163        d.__code__ = c.__code__
164        self.assertEqual(c.__code__, d.__code__)
165        self.assertEqual(c(), 7)
166        # self.assertEqual(d(), 7)
167        try:
168            b.__code__ = c.__code__
169        except ValueError:
170            pass
171        else:
172            self.fail("__code__ with different numbers of free vars should "
173                      "not be possible")
174        try:
175            e.__code__ = d.__code__
176        except ValueError:
177            pass
178        else:
179            self.fail("__code__ with different numbers of free vars should "
180                      "not be possible")
181
182    def test_blank_func_defaults(self):
183        self.assertEqual(self.b.__defaults__, None)
184        del self.b.__defaults__
185        self.assertEqual(self.b.__defaults__, None)
186
187    def test_func_default_args(self):
188        def first_func(a, b):
189            return a+b
190        def second_func(a=1, b=2):
191            return a+b
192        self.assertEqual(first_func.__defaults__, None)
193        self.assertEqual(second_func.__defaults__, (1, 2))
194        first_func.__defaults__ = (1, 2)
195        self.assertEqual(first_func.__defaults__, (1, 2))
196        self.assertEqual(first_func(), 3)
197        self.assertEqual(first_func(3), 5)
198        self.assertEqual(first_func(3, 5), 8)
199        del second_func.__defaults__
200        self.assertEqual(second_func.__defaults__, None)
201        try:
202            second_func()
203        except TypeError:
204            pass
205        else:
206            self.fail("__defaults__ does not update; deleting it does not "
207                      "remove requirement")
208
209
210class InstancemethodAttrTest(FuncAttrsTest):
211
212    def test___class__(self):
213        self.assertEqual(self.fi.a.__self__.__class__, self.F)
214        self.cannot_set_attr(self.fi.a, "__class__", self.F, TypeError)
215
216    def test___func__(self):
217        self.assertEqual(self.fi.a.__func__, self.F.a)
218        self.cannot_set_attr(self.fi.a, "__func__", self.F.a, AttributeError)
219
220    def test___self__(self):
221        self.assertEqual(self.fi.a.__self__, self.fi)
222        self.cannot_set_attr(self.fi.a, "__self__", self.fi, AttributeError)
223
224    def test___func___non_method(self):
225        # Behavior should be the same when a method is added via an attr
226        # assignment
227        self.fi.id = types.MethodType(id, self.fi)
228        self.assertEqual(self.fi.id(), id(self.fi))
229        # Test usage
230        try:
231            self.fi.id.unknown_attr
232        except AttributeError:
233            pass
234        else:
235            self.fail("using unknown attributes should raise AttributeError")
236        # Test assignment and deletion
237        self.cannot_set_attr(self.fi.id, 'unknown_attr', 2, AttributeError)
238
239
240class ArbitraryFunctionAttrTest(FuncAttrsTest):
241    def test_set_attr(self):
242        self.b.known_attr = 7
243        self.assertEqual(self.b.known_attr, 7)
244        try:
245            self.fi.a.known_attr = 7
246        except AttributeError:
247            pass
248        else:
249            self.fail("setting attributes on methods should raise error")
250
251    def test_delete_unknown_attr(self):
252        try:
253            del self.b.unknown_attr
254        except AttributeError:
255            pass
256        else:
257            self.fail("deleting unknown attribute should raise TypeError")
258
259    def test_unset_attr(self):
260        for func in [self.b, self.fi.a]:
261            try:
262                func.non_existent_attr
263            except AttributeError:
264                pass
265            else:
266                self.fail("using unknown attributes should raise "
267                          "AttributeError")
268
269
270class FunctionDictsTest(FuncAttrsTest):
271    def test_setting_dict_to_invalid(self):
272        self.cannot_set_attr(self.b, '__dict__', None, TypeError)
273        from collections import UserDict
274        d = UserDict({'known_attr': 7})
275        self.cannot_set_attr(self.fi.a.__func__, '__dict__', d, TypeError)
276
277    def test_setting_dict_to_valid(self):
278        d = {'known_attr': 7}
279        self.b.__dict__ = d
280        # Test assignment
281        self.assertIs(d, self.b.__dict__)
282        # ... and on all the different ways of referencing the method's func
283        self.F.a.__dict__ = d
284        self.assertIs(d, self.fi.a.__func__.__dict__)
285        self.assertIs(d, self.fi.a.__dict__)
286        # Test value
287        self.assertEqual(self.b.known_attr, 7)
288        self.assertEqual(self.b.__dict__['known_attr'], 7)
289        # ... and again, on all the different method's names
290        self.assertEqual(self.fi.a.__func__.known_attr, 7)
291        self.assertEqual(self.fi.a.known_attr, 7)
292
293    def test_delete___dict__(self):
294        try:
295            del self.b.__dict__
296        except TypeError:
297            pass
298        else:
299            self.fail("deleting function dictionary should raise TypeError")
300
301    def test_unassigned_dict(self):
302        self.assertEqual(self.b.__dict__, {})
303
304    def test_func_as_dict_key(self):
305        value = "Some string"
306        d = {}
307        d[self.b] = value
308        self.assertEqual(d[self.b], value)
309
310
311class FunctionDocstringTest(FuncAttrsTest):
312    def test_set_docstring_attr(self):
313        self.assertEqual(self.b.__doc__, None)
314        docstr = "A test method that does nothing"
315        self.b.__doc__ = docstr
316        self.F.a.__doc__ = docstr
317        self.assertEqual(self.b.__doc__, docstr)
318        self.assertEqual(self.fi.a.__doc__, docstr)
319        self.cannot_set_attr(self.fi.a, "__doc__", docstr, AttributeError)
320
321    def test_delete_docstring(self):
322        self.b.__doc__ = "The docstring"
323        del self.b.__doc__
324        self.assertEqual(self.b.__doc__, None)
325
326
327def cell(value):
328    """Create a cell containing the given value."""
329    def f():
330        print(a)
331    a = value
332    return f.__closure__[0]
333
334def empty_cell(empty=True):
335    """Create an empty cell."""
336    def f():
337        print(a)
338    # the intent of the following line is simply "if False:";  it's
339    # spelt this way to avoid the danger that a future optimization
340    # might simply remove an "if False:" code block.
341    if not empty:
342        a = 1729
343    return f.__closure__[0]
344
345
346class CellTest(unittest.TestCase):
347    def test_comparison(self):
348        # These tests are here simply to exercise the comparison code;
349        # their presence should not be interpreted as providing any
350        # guarantees about the semantics (or even existence) of cell
351        # comparisons in future versions of CPython.
352        self.assertTrue(cell(2) < cell(3))
353        self.assertTrue(empty_cell() < cell('saturday'))
354        self.assertTrue(empty_cell() == empty_cell())
355        self.assertTrue(cell(-36) == cell(-36.0))
356        self.assertTrue(cell(True) > empty_cell())
357
358
359class StaticMethodAttrsTest(unittest.TestCase):
360    def test_func_attribute(self):
361        def f():
362            pass
363
364        c = classmethod(f)
365        self.assertTrue(c.__func__ is f)
366
367        s = staticmethod(f)
368        self.assertTrue(s.__func__ is f)
369
370
371class BuiltinFunctionPropertiesTest(unittest.TestCase):
372    # XXX Not sure where this should really go since I can't find a
373    # test module specifically for builtin_function_or_method.
374
375    def test_builtin__qualname__(self):
376        import time
377
378        # builtin function:
379        self.assertEqual(len.__qualname__, 'len')
380        self.assertEqual(time.time.__qualname__, 'time')
381
382        # builtin classmethod:
383        self.assertEqual(dict.fromkeys.__qualname__, 'dict.fromkeys')
384        self.assertEqual(float.__getformat__.__qualname__,
385                         'float.__getformat__')
386
387        # builtin staticmethod:
388        self.assertEqual(str.maketrans.__qualname__, 'str.maketrans')
389        self.assertEqual(bytes.maketrans.__qualname__, 'bytes.maketrans')
390
391        # builtin bound instance method:
392        self.assertEqual([1, 2, 3].append.__qualname__, 'list.append')
393        self.assertEqual({'foo': 'bar'}.pop.__qualname__, 'dict.pop')
394
395
396if __name__ == "__main__":
397    unittest.main()
398