8000 gh-93649: Add Modules/_testcapi/function.c file (#129521) · python/cpython@60a8541 · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 60a8541

Browse files
authored
gh-93649: Add Modules/_testcapi/function.c file (#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)
0