forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pythongh-93649: Add Modules/_testcapi/function.c file
* 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.
- Loading branch information
Showing
8 changed files
with
465 additions
and
413 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,312 @@ | ||
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)) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
Oops, something went wrong.