Skip to content

Commit 60a8541

Browse files
authored
pythongh-93649: Add Modules/_testcapi/function.c file (python#129521)
* 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.
1 parent fad36bf commit 60a8541

File tree

8 files changed

+476
-413
lines changed

8 files changed

+476
-413
lines changed

Lib/test/test_capi/test_function.py

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
import unittest
2+
from test.support import import_helper
3+
4+
5+
_testcapi = import_helper.import_module('_testcapi')
6+
7+
8+
class FunctionTest(unittest.TestCase):
9+
def test_function_get_code(self):
10+
# Test PyFunction_GetCode()
11+
import types
12+
13+
def some():
14+
pass
15+
16+
code = _testcapi.function_get_code(some)
17+
self.assertIsInstance(code, types.CodeType)
18+
self.assertEqual(code, some.__code__)
19+
20+
with self.assertRaises(SystemError):
21+
_testcapi.function_get_code(None) # not a function
22+
23+
def test_function_get_globals(self):
24+
# Test PyFunction_GetGlobals()
25+
def some():
26+
pass
27+
28+
globals_ = _testcapi.function_get_globals(some)
29+
self.assertIsInstance(globals_, dict)
30+
self.assertEqual(globals_, some.__globals__)
31+
32+
with self.assertRaises(SystemError):
33+
_testcapi.function_get_globals(None) # not a function
34+
35+
def test_function_get_module(self):
36+
# Test PyFunction_GetModule()
37+
def some():
38+
pass
39+
40+
module = _testcapi.function_get_module(some)
41+
self.assertIsInstance(module, str)
42+
self.assertEqual(module, some.__module__)
43+
44+
with self.assertRaises(SystemError):
45+
_testcapi.function_get_module(None) # not a function
46+
47+
def test_function_get_defaults(self):
48+
# Test PyFunction_GetDefaults()
49+
def some(
50+
pos_only1, pos_only2='p',
51+
/,
52+
zero=0, optional=None,
53+
*,
54+
kw1,
55+
kw2=True,
56+
):
57+
pass
58+
59+
defaults = _testcapi.function_get_defaults(some)
60+
self.assertEqual(defaults, ('p', 0, None))
61+
self.assertEqual(defaults, some.__defaults__)
62+
63+
with self.assertRaises(SystemError):
64+
_testcapi.function_get_defaults(None) # not a function
65+
66+
def test_function_set_defaults(self):
67+
# Test PyFunction_SetDefaults()
68+
def some(
69+
pos_only1, pos_only2='p',
70+
/,
71+
zero=0, optional=None,
72+
*,
73+
kw1,
74+
kw2=True,
75+
):
76+
pass
77+
78+
old_defaults = ('p', 0, None)
79+
self.assertEqual(_testcapi.function_get_defaults(some), old_defaults)
80+
self.assertEqual(some.__defaults__, old_defaults)
81+
82+
with self.assertRaises(SystemError):
83+
_testcapi.function_set_defaults(some, 1) # not tuple or None
84+
self.assertEqual(_testcapi.function_get_defaults(some), old_defaults)
85+
self.assertEqual(some.__defaults__, old_defaults)
86+
87+
with self.assertRaises(SystemError):
88+
_testcapi.function_set_defaults(1, ()) # not a function
89+
self.assertEqual(_testcapi.function_get_defaults(some), old_defaults)
90+
self.assertEqual(some.__defaults__, old_defaults)
91+
92+
new_defaults = ('q', 1, None)
93+
_testcapi.function_set_defaults(some, new_defaults)
94+
self.assertEqual(_testcapi.function_get_defaults(some), new_defaults)
95+
self.assertEqual(some.__defaults__, new_defaults)
96+
97+
# Empty tuple is fine:
98+
new_defaults = ()
99+
_testcapi.function_set_defaults(some, new_defaults)
100+
self.assertEqual(_testcapi.function_get_defaults(some), new_defaults)
101+
self.assertEqual(some.__defaults__, new_defaults)
102+
103+
class tuplesub(tuple): ... # tuple subclasses must work
104+
105+
new_defaults = tuplesub(((1, 2), ['a', 'b'], None))
106+
_testcapi.function_set_defaults(some, new_defaults)
107+
self.assertEqual(_testcapi.function_get_defaults(some), new_defaults)
108+
self.assertEqual(some.__defaults__, new_defaults)
109+
110+
# `None` is special, it sets `defaults` to `NULL`,
111+
# it needs special handling in `_testcapi`:
112+
_testcapi.function_set_defaults(some, None)
113+
self.assertEqual(_testcapi.function_get_defaults(some), None)
114+
self.assertEqual(some.__defaults__, None)
115+
116+
def test_function_get_kw_defaults(self):
117+
# Test PyFunction_GetKwDefaults()
118+
def some(
119+
pos_only1, pos_only2='p',
120+
/,
121+
zero=0, optional=None,
122+
*,
123+
kw1,
124+
kw2=True,
125+
):
126+
pass
127+
128+
defaults = _testcapi.function_get_kw_defaults(some)
129+
self.assertEqual(defaults, {'kw2': True})
130+
self.assertEqual(defaults, some.__kwdefaults__)
131+
132+
with self.assertRaises(SystemError):
133+
_testcapi.function_get_kw_defaults(None) # not a function
134+
135+
def test_function_set_kw_defaults(self):
136+
# Test PyFunction_SetKwDefaults()
137+
def some(
138+
pos_only1, pos_only2='p',
139+
/,
140+
zero=0, optional=None,
141+
*,
142+
kw1,
143+
kw2=True,
144+
):
145+
pass
146+
147+
old_defaults = {'kw2': True}
148+
self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults)
149+
self.assertEqual(some.__kwdefaults__, old_defaults)
150+
151+
with self.assertRaises(SystemError):
152+
_testcapi.function_set_kw_defaults(some, 1) # not dict or None
153+
self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults)
154+
self.assertEqual(some.__kwdefaults__, old_defaults)
155+
156+
with self.assertRaises(SystemError):
157+
_testcapi.function_set_kw_defaults(1, {}) # not a function
158+
self.assertEqual(_testcapi.function_get_kw_defaults(some), old_defaults)
159+
self.assertEqual(some.__kwdefaults__, old_defaults)
160+
161+
new_defaults = {'kw2': (1, 2, 3)}
162+
_testcapi.function_set_kw_defaults(some, new_defaults)
163+
self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults)
164+
self.assertEqual(some.__kwdefaults__, new_defaults)
165+
166+
# Empty dict is fine:
167+
new_defaults = {}
168+
_testcapi.function_set_kw_defaults(some, new_defaults)
169+
self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults)
170+
self.assertEqual(some.__kwdefaults__, new_defaults)
171+
172+
class dictsub(dict): ... # dict subclasses must work
173+
174+
new_defaults = dictsub({'kw2': None})
175+
_testcapi.function_set_kw_defaults(some, new_defaults)
176+
self.assertEqual(_testcapi.function_get_kw_defaults(some), new_defaults)
177+
self.assertEqual(some.__kwdefaults__, new_defaults)
178+
179+
# `None` is special, it sets `kwdefaults` to `NULL`,
180+
# it needs special handling in `_testcapi`:
181+
_testcapi.function_set_kw_defaults(some, None)
182+
self.assertEqual(_testcapi.function_get_kw_defaults(some), None)
183+
self.assertEqual(some.__kwdefaults__, None)
184+
185+
def test_function_get_closure(self):
186+
# Test PyFunction_GetClosure()
187+
from types import CellType
188+
189+
def regular_function(): ...
190+
def unused_one_level(arg1):
191+
def inner(arg2, arg3): ...
192+
return inner
193+
def unused_two_levels(arg1, arg2):
194+
def decorator(arg3, arg4):
195+
def inner(arg5, arg6): ...
196+
return inner
197+
return decorator
198+
def with_one_level(arg1):
199+
def inner(arg2, arg3):
200+
return arg1 + arg2 + arg3
201+
return inner
202+
def with_two_levels(arg1, arg2):
203+
def decorator(arg3, arg4):
204+
def inner(arg5, arg6):
205+
return arg1 + arg2 + arg3 + arg4 + arg5 + arg6
206+
return inner
207+
return decorator
208+
209+
# Functions without closures:
210+
self.assertIsNone(_testcapi.function_get_closure(regular_function))
211+
self.assertIsNone(regular_function.__closure__)
212+
213+
func = unused_one_level(1)
214+
closure = _testcapi.function_get_closure(func)
215+
self.assertIsNone(closure)
216+
self.assertIsNone(func.__closure__)
217+
218+
func = unused_two_levels(1, 2)(3, 4)
219+
closure = _testcapi.function_get_closure(func)
220+
self.assertIsNone(closure)
221+
self.assertIsNone(func.__closure__)
222+
223+
# Functions with closures:
224+
func = with_one_level(5)
225+
closure = _testcapi.function_get_closure(func)
226+
self.assertEqual(closure, func.__closure__)
227+
self.assertIsInstance(closure, tuple)
228+
self.assertEqual(len(closure), 1)
229+
self.assertEqual(len(closure), len(func.__code__.co_freevars))
230+
for cell in closure:
231+
self.assertIsInstance(cell, CellType)
232+
self.assertTrue(closure[0].cell_contents, 5)
233+
234+
func = with_two_levels(1, 2)(3, 4)
235+
closure = _testcapi.function_get_closure(func)
236+
self.assertEqual(closure, func.__closure__)
237+
self.assertIsInstance(closure, tuple)
238+
self.assertEqual(len(closure), 4)
239+
self.assertEqual(len(closure), len(func.__code__.co_freevars))
240+
for cell in closure:
241+
self.assertIsInstance(cell, CellType)
242+
self.assertEqual([cell.cell_contents for cell in closure],
243+
[1, 2, 3, 4])
244+
245+
def test_function_get_closure_error(self):
246+
# Test PyFunction_GetClosure()
247+
with self.assertRaises(SystemError):
248+
_testcapi.function_get_closure(1)
249+
with self.assertRaises(SystemError):
250+
_testcapi.function_get_closure(None)
251+
252+
def test_function_set_closure(self):
253+
# Test PyFunction_SetClosure()
254+
from types import CellType
255+
256+
def function_without_closure(): ...
257+
def function_with_closure(arg):
258+
def inner():
259+
return arg
260+
return inner
261+
262+
func = function_without_closure
263+
_testcapi.function_set_closure(func, (CellType(1), CellType(1)))
264+
closure = _testcapi.function_get_closure(func)
265+
self.assertEqual([c.cell_contents for c in closure], [1, 1])
266+
self.assertEqual([c.cell_contents for c in func.__closure__], [1, 1])
267+
268+
func = function_with_closure(1)
269+
_testcapi.function_set_closure(func,
270+
(CellType(1), CellType(2), CellType(3)))
271+
closure = _testcapi.function_get_closure(func)
272+
self.assertEqual([c.cell_contents for c in closure], [1, 2, 3])
273+
self.assertEqual([c.cell_contents for c in func.__closure__], [1, 2, 3])
274+
275+
def test_function_set_closure_none(self):
276+
# Test PyFunction_SetClosure()
277+
def function_without_closure(): ...
278+
def function_with_closure(arg):
279+
def inner():
280+
return arg
281+
return inner
282+
283+
_testcapi.function_set_closure(function_without_closure, None)
284+
self.assertIsNone(
285+
_testcapi.function_get_closure(function_without_closure))
286+
self.assertIsNone(function_without_closure.__closure__)
287+
288+
_testcapi.function_set_closure(function_with_closure, None)
289+
self.assertIsNone(
290+
_testcapi.function_get_closure(function_with_closure))
291+
self.assertIsNone(function_with_closure.__closure__)
292+
293+
def test_function_set_closure_errors(self):
294+
# Test PyFunction_SetClosure()
295+
def function_without_closure(): ...
296+
297+
with self.assertRaises(SystemError):
298+
_testcapi.function_set_closure(None, ()) # not a function
299+
300+
with self.assertRaises(SystemError):
301+
_testcapi.function_set_closure(function_without_closure, 1)
302+
self.assertIsNone(function_without_closure.__closure__) # no change
303+
304+
# NOTE: this works, but goes against the docs:
305+
_testcapi.function_set_closure(function_without_closure, (1, 2))
306+
self.assertEqual(
307+
_testcapi.function_get_closure(function_without_closure), (1, 2))
308+
self.assertEqual(function_without_closure.__closure__, (1, 2))
309+
310+
# TODO: test PyFunction_New()
311+
# TODO: test PyFunction_NewWithQualName()
312+
# TODO: test PyFunction_SetVectorcall()
313+
# TODO: test PyFunction_GetAnnotations()
314+
# TODO: test PyFunction_SetAnnotations()
315+
# TODO: test PyClassMethod_New()
316+
# TODO: test PyStaticMethod_New()
317+
#
318+
# PyFunction_AddWatcher() and PyFunction_ClearWatcher() are tested by
319+
# test_capi.test_watchers.
320+
321+
322+
if __name__ == "__main__":
323+
unittest.main()

0 commit comments

Comments
 (0)