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