mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +00:00 
			
		
		
		
	* Move PyFunction C API tests to a new file. * Add Lib/test/test_capi/test_function.py. * Move tests from test_capi.test_misc to test_capi.test_function.
		
			
				
	
	
		
			323 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			323 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import unittest
 | 
						|
from test.support import import_helper
 | 
						|
 | 
						|
 | 
						|
_testcapi = import_helper.import_module('_testcapi')
 | 
						|
 | 
						|
 | 
						|
class FunctionTest(unittest.TestCase):
 | 
						|
    def test_function_get_code(self):
 | 
						|
        # Test PyFunction_GetCode()
 | 
						|
        import types
 | 
						|
 | 
						|
        def some():
 | 
						|
            pass
 | 
						|
 | 
						|
        code = _testcapi.function_get_code(some)
 | 
						|
        self.assertIsInstance(code, types.CodeType)
 | 
						|
        self.assertEqual(code, some.__code__)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_get_code(None)  # not a function
 | 
						|
 | 
						|
    def test_function_get_globals(self):
 | 
						|
        # Test PyFunction_GetGlobals()
 | 
						|
        def some():
 | 
						|
            pass
 | 
						|
 | 
						|
        globals_ = _testcapi.function_get_globals(some)
 | 
						|
        self.assertIsInstance(globals_, dict)
 | 
						|
        self.assertEqual(globals_, some.__globals__)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_get_globals(None)  # not a function
 | 
						|
 | 
						|
    def test_function_get_module(self):
 | 
						|
        # Test PyFunction_GetModule()
 | 
						|
        def some():
 | 
						|
            pass
 | 
						|
 | 
						|
        module = _testcapi.function_get_module(some)
 | 
						|
        self.assertIsInstance(module, str)
 | 
						|
        self.assertEqual(module, some.__module__)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_get_module(None)  # not a function
 | 
						|
 | 
						|
    def test_function_get_defaults(self):
 | 
						|
        # Test PyFunction_GetDefaults()
 | 
						|
        def some(
 | 
						|
            pos_only1, pos_only2='p',
 | 
						|
            /,
 | 
						|
            zero=0, optional=None,
 | 
						|
            *,
 | 
						|
            kw1,
 | 
						|
            kw2=True,
 | 
						|
        ):
 | 
						|
            pass
 | 
						|
 | 
						|
        defaults = _testcapi.function_get_defaults(some)
 | 
						|
        self.assertEqual(defaults, ('p', 0, None))
 | 
						|
        self.assertEqual(defaults, some.__defaults__)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_get_defaults(None)  # not a function
 | 
						|
 | 
						|
    def test_function_set_defaults(self):
 | 
						|
        # Test PyFunction_SetDefaults()
 | 
						|
        def some(
 | 
						|
            pos_only1, pos_only2='p',
 | 
						|
            /,
 | 
						|
            zero=0, optional=None,
 | 
						|
            *,
 | 
						|
            kw1,
 | 
						|
            kw2=True,
 | 
						|
        ):
 | 
						|
            pass
 | 
						|
 | 
						|
        old_defaults = ('p', 0, None)
 | 
						|
        self.assertEqual(_testcapi.function_get_defaults(some), old_defaults)
 | 
						|
        self.assertEqual(some.__defaults__, old_defaults)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_set_defaults(some, 1)  # not tuple or None
 | 
						|
        self.assertEqual(_testcapi.function_get_defaults(some), old_defaults)
 | 
						|
        self.assertEqual(some.__defaults__, old_defaults)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_set_defaults(1, ())    # not a function
 | 
						|
        self.assertEqual(_testcapi.function_get_defaults(some), old_defaults)
 | 
						|
        self.assertEqual(some.__defaults__, old_defaults)
 | 
						|
 | 
						|
        new_defaults = ('q', 1, None)
 | 
						|
        _testcapi.function_set_defaults(some, new_defaults)
 | 
						|
        self.assertEqual(_testcapi.function_get_defaults(some), new_defaults)
 | 
						|
        self.assertEqual(some.__defaults__, new_defaults)
 | 
						|
 | 
						|
        # Empty tuple is fine:
 | 
						|
        new_defaults = ()
 | 
						|
        _testcapi.function_set_defaults(some, new_defaults)
 | 
						|
        self.assertEqual(_testcapi.function_get_defaults(some), new_defaults)
 | 
						|
        self.assertEqual(some.__defaults__, new_defaults)
 | 
						|
 | 
						|
        class tuplesub(tuple): ...  # tuple subclasses must work
 | 
						|
 | 
						|
        new_defaults = tuplesub(((1, 2), ['a', 'b'], None))
 | 
						|
        _testcapi.function_set_defaults(some, new_defaults)
 | 
						|
        self.assertEqual(_testcapi.function_get_defaults(some), new_defaults)
 | 
						|
        self.assertEqual(some.__defaults__, new_defaults)
 | 
						|
 | 
						|
        # `None` is special, it sets `defaults` to `NULL`,
 | 
						|
        # it needs special handling in `_testcapi`:
 | 
						|
        _testcapi.function_set_defaults(some, None)
 | 
						|
        self.assertEqual(_testcapi.function_get_defaults(some), None)
 | 
						|
        self.assertEqual(some.__defaults__, None)
 | 
						|
 | 
						|
    def test_function_get_kw_defaults(self):
 | 
						|
        # Test PyFunction_GetKwDefaults()
 | 
						|
        def some(
 | 
						|
            pos_only1, pos_only2='p',
 | 
						|
            /,
 | 
						|
            zero=0, optional=None,
 | 
						|
            *,
 | 
						|
            kw1,
 | 
						|
            kw2=True,
 | 
						|
        ):
 | 
						|
            pass
 | 
						|
 | 
						|
        defaults = _testcapi.function_get_kw_defaults(some)
 | 
						|
        self.assertEqual(defaults, {'kw2': True})
 | 
						|
        self.assertEqual(defaults, some.__kwdefaults__)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_get_kw_defaults(None)  # not a function
 | 
						|
 | 
						|
    def test_function_set_kw_defaults(self):
 | 
						|
        # Test PyFunction_SetKwDefaults()
 | 
						|
        def some(
 | 
						|
            pos_only1, pos_only2='p',
 | 
						|
            /,
 | 
						|
            zero=0, optional=None,
 | 
						|
            *,
 | 
						|
            kw1,
 | 
						|
            kw2=True,
 | 
						|
        ):
 | 
						|
            pass
 | 
						|
 | 
						|
        old_defaults = {'kw2': True}
 | 
						|
        self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults)
 | 
						|
        self.assertEqual(some.__kwdefaults__, old_defaults)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_set_kw_defaults(some, 1)  # not dict or None
 | 
						|
        self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults)
 | 
						|
        self.assertEqual(some.__kwdefaults__, old_defaults)
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_set_kw_defaults(1, {})    # not a function
 | 
						|
        self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults)
 | 
						|
        self.assertEqual(some.__kwdefaults__, old_defaults)
 | 
						|
 | 
						|
        new_defaults = {'kw2': (1, 2, 3)}
 | 
						|
        _testcapi.function_set_kw_defaults(some, new_defaults)
 | 
						|
        self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults)
 | 
						|
        self.assertEqual(some.__kwdefaults__, new_defaults)
 | 
						|
 | 
						|
        # Empty dict is fine:
 | 
						|
        new_defaults = {}
 | 
						|
        _testcapi.function_set_kw_defaults(some, new_defaults)
 | 
						|
        self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults)
 | 
						|
        self.assertEqual(some.__kwdefaults__, new_defaults)
 | 
						|
 | 
						|
        class dictsub(dict): ...  # dict subclasses must work
 | 
						|
 | 
						|
        new_defaults = dictsub({'kw2': None})
 | 
						|
        _testcapi.function_set_kw_defaults(some, new_defaults)
 | 
						|
        self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults)
 | 
						|
        self.assertEqual(some.__kwdefaults__, new_defaults)
 | 
						|
 | 
						|
        # `None` is special, it sets `kwdefaults` to `NULL`,
 | 
						|
        # it needs special handling in `_testcapi`:
 | 
						|
        _testcapi.function_set_kw_defaults(some, None)
 | 
						|
        self.assertEqual(_testcapi.function_get_kw_defaults(some), None)
 | 
						|
        self.assertEqual(some.__kwdefaults__, None)
 | 
						|
 | 
						|
    def test_function_get_closure(self):
 | 
						|
        # Test PyFunction_GetClosure()
 | 
						|
        from types import CellType
 | 
						|
 | 
						|
        def regular_function(): ...
 | 
						|
        def unused_one_level(arg1):
 | 
						|
            def inner(arg2, arg3): ...
 | 
						|
            return inner
 | 
						|
        def unused_two_levels(arg1, arg2):
 | 
						|
            def decorator(arg3, arg4):
 | 
						|
                def inner(arg5, arg6): ...
 | 
						|
                return inner
 | 
						|
            return decorator
 | 
						|
        def with_one_level(arg1):
 | 
						|
            def inner(arg2, arg3):
 | 
						|
                return arg1 + arg2 + arg3
 | 
						|
            return inner
 | 
						|
        def with_two_levels(arg1, arg2):
 | 
						|
            def decorator(arg3, arg4):
 | 
						|
                def inner(arg5, arg6):
 | 
						|
                    return arg1 + arg2 + arg3 + arg4 + arg5 + arg6
 | 
						|
                return inner
 | 
						|
            return decorator
 | 
						|
 | 
						|
        # Functions without closures:
 | 
						|
        self.assertIsNone(_testcapi.function_get_closure(regular_function))
 | 
						|
        self.assertIsNone(regular_function.__closure__)
 | 
						|
 | 
						|
        func = unused_one_level(1)
 | 
						|
        closure = _testcapi.function_get_closure(func)
 | 
						|
        self.assertIsNone(closure)
 | 
						|
        self.assertIsNone(func.__closure__)
 | 
						|
 | 
						|
        func = unused_two_levels(1, 2)(3, 4)
 | 
						|
        closure = _testcapi.function_get_closure(func)
 | 
						|
        self.assertIsNone(closure)
 | 
						|
        self.assertIsNone(func.__closure__)
 | 
						|
 | 
						|
        # Functions with closures:
 | 
						|
        func = with_one_level(5)
 | 
						|
        closure = _testcapi.function_get_closure(func)
 | 
						|
        self.assertEqual(closure, func.__closure__)
 | 
						|
        self.assertIsInstance(closure, tuple)
 | 
						|
        self.assertEqual(len(closure), 1)
 | 
						|
        self.assertEqual(len(closure), len(func.__code__.co_freevars))
 | 
						|
        for cell in closure:
 | 
						|
            self.assertIsInstance(cell, CellType)
 | 
						|
        self.assertTrue(closure[0].cell_contents, 5)
 | 
						|
 | 
						|
        func = with_two_levels(1, 2)(3, 4)
 | 
						|
        closure = _testcapi.function_get_closure(func)
 | 
						|
        self.assertEqual(closure, func.__closure__)
 | 
						|
        self.assertIsInstance(closure, tuple)
 | 
						|
        self.assertEqual(len(closure), 4)
 | 
						|
        self.assertEqual(len(closure), len(func.__code__.co_freevars))
 | 
						|
        for cell in closure:
 | 
						|
            self.assertIsInstance(cell, CellType)
 | 
						|
        self.assertEqual([cell.cell_contents for cell in closure],
 | 
						|
                         [1, 2, 3, 4])
 | 
						|
 | 
						|
    def test_function_get_closure_error(self):
 | 
						|
        # Test PyFunction_GetClosure()
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_get_closure(1)
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_get_closure(None)
 | 
						|
 | 
						|
    def test_function_set_closure(self):
 | 
						|
        # Test PyFunction_SetClosure()
 | 
						|
        from types import CellType
 | 
						|
 | 
						|
        def function_without_closure(): ...
 | 
						|
        def function_with_closure(arg):
 | 
						|
            def inner():
 | 
						|
                return arg
 | 
						|
            return inner
 | 
						|
 | 
						|
        func = function_without_closure
 | 
						|
        _testcapi.function_set_closure(func, (CellType(1), CellType(1)))
 | 
						|
        closure = _testcapi.function_get_closure(func)
 | 
						|
        self.assertEqual([c.cell_contents for c in closure], [1, 1])
 | 
						|
        self.assertEqual([c.cell_contents for c in func.__closure__], [1, 1])
 | 
						|
 | 
						|
        func = function_with_closure(1)
 | 
						|
        _testcapi.function_set_closure(func,
 | 
						|
                                       (CellType(1), CellType(2), CellType(3)))
 | 
						|
        closure = _testcapi.function_get_closure(func)
 | 
						|
        self.assertEqual([c.cell_contents for c in closure], [1, 2, 3])
 | 
						|
        self.assertEqual([c.cell_contents for c in func.__closure__], [1, 2, 3])
 | 
						|
 | 
						|
    def test_function_set_closure_none(self):
 | 
						|
        # Test PyFunction_SetClosure()
 | 
						|
        def function_without_closure(): ...
 | 
						|
        def function_with_closure(arg):
 | 
						|
            def inner():
 | 
						|
                return arg
 | 
						|
            return inner
 | 
						|
 | 
						|
        _testcapi.function_set_closure(function_without_closure, None)
 | 
						|
        self.assertIsNone(
 | 
						|
            _testcapi.function_get_closure(function_without_closure))
 | 
						|
        self.assertIsNone(function_without_closure.__closure__)
 | 
						|
 | 
						|
        _testcapi.function_set_closure(function_with_closure, None)
 | 
						|
        self.assertIsNone(
 | 
						|
            _testcapi.function_get_closure(function_with_closure))
 | 
						|
        self.assertIsNone(function_with_closure.__closure__)
 | 
						|
 | 
						|
    def test_function_set_closure_errors(self):
 | 
						|
        # Test PyFunction_SetClosure()
 | 
						|
        def function_without_closure(): ...
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_set_closure(None, ())  # not a function
 | 
						|
 | 
						|
        with self.assertRaises(SystemError):
 | 
						|
            _testcapi.function_set_closure(function_without_closure, 1)
 | 
						|
        self.assertIsNone(function_without_closure.__closure__)  # no change
 | 
						|
 | 
						|
        # NOTE: this works, but goes against the docs:
 | 
						|
        _testcapi.function_set_closure(function_without_closure, (1, 2))
 | 
						|
        self.assertEqual(
 | 
						|
            _testcapi.function_get_closure(function_without_closure), (1, 2))
 | 
						|
        self.assertEqual(function_without_closure.__closure__, (1, 2))
 | 
						|
 | 
						|
    # TODO: test PyFunction_New()
 | 
						|
    # TODO: test PyFunction_NewWithQualName()
 | 
						|
    # TODO: test PyFunction_SetVectorcall()
 | 
						|
    # TODO: test PyFunction_GetAnnotations()
 | 
						|
    # TODO: test PyFunction_SetAnnotations()
 | 
						|
    # TODO: test PyClassMethod_New()
 | 
						|
    # TODO: test PyStaticMethod_New()
 | 
						|
    #
 | 
						|
    # PyFunction_AddWatcher() and PyFunction_ClearWatcher() are tested by
 | 
						|
    # test_capi.test_watchers.
 | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__":
 | 
						|
    unittest.main()
 |