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