Skip to content

Commit 5820f05

Browse files
sobolevngvanrossum
authored andcommitted
pythongh-94808: cover PyFunction_GetDefaults and PyFunction_SetDefaults (python#98449)
1 parent 5215027 commit 5820f05

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

Lib/test/test_capi.py

+42
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,48 @@ def some():
942942
with self.assertRaises(SystemError):
943943
_testcapi.function_get_module(None) # not a function
944944

945+
def test_function_get_defaults(self):
946+
def some(pos_only='p', zero=0, optional=None):
947+
pass
948+
949+
defaults = _testcapi.function_get_defaults(some)
950+
self.assertEqual(defaults, ('p', 0, None))
951+
self.assertEqual(defaults, some.__defaults__)
952+
953+
with self.assertRaises(SystemError):
954+
_testcapi.function_get_module(None) # not a function
955+
956+
def test_function_set_defaults(self):
957+
def some(pos_only='p', zero=0, optional=None):
958+
pass
959+
960+
old_defaults = ('p', 0, None)
961+
self.assertEqual(_testcapi.function_get_defaults(some), old_defaults)
962+
self.assertEqual(some.__defaults__, old_defaults)
963+
964+
with self.assertRaises(SystemError):
965+
_testcapi.function_set_defaults(some, 1) # not tuple or None
966+
self.assertEqual(_testcapi.function_get_defaults(some), old_defaults)
967+
self.assertEqual(some.__defaults__, old_defaults)
968+
969+
new_defaults = ('q', 1, None)
970+
_testcapi.function_set_defaults(some, new_defaults)
971+
self.assertEqual(_testcapi.function_get_defaults(some), new_defaults)
972+
self.assertEqual(some.__defaults__, new_defaults)
973+
974+
class tuplesub(tuple): ... # tuple subclasses must work
975+
976+
new_defaults = tuplesub(((1, 2), ['a', 'b'], None))
977+
_testcapi.function_set_defaults(some, new_defaults)
978+
self.assertEqual(_testcapi.function_get_defaults(some), new_defaults)
979+
self.assertEqual(some.__defaults__, new_defaults)
980+
981+
# `None` is special, it sets `defaults` to `NULL`,
982+
# it needs special handling in `_testcapi`:
983+
_testcapi.function_set_defaults(some, None)
984+
self.assertEqual(_testcapi.function_get_defaults(some), None)
985+
self.assertEqual(some.__defaults__, None)
986+
945987

946988
class TestPendingCalls(unittest.TestCase):
947989

Modules/_testcapimodule.c

+29
Original file line numberDiff line numberDiff line change
@@ -5788,6 +5788,33 @@ function_get_module(PyObject *self, PyObject *func)
57885788
}
57895789
}
57905790

5791+
static PyObject *
5792+
function_get_defaults(PyObject *self, PyObject *func)
5793+
{
5794+
PyObject *defaults = PyFunction_GetDefaults(func);
5795+
if (defaults != NULL) {
5796+
Py_INCREF(defaults);
5797+
return defaults;
5798+
} else if (PyErr_Occurred()) {
5799+
return NULL;
5800+
} else {
5801+
Py_RETURN_NONE; // This can happen when `defaults` are set to `None`
5802+
}
5803+
}
5804+
5805+
static PyObject *
5806+
function_set_defaults(PyObject *self, PyObject *args)
5807+
{
5808+
PyObject *func = NULL, *defaults = NULL;
5809+
if (!PyArg_ParseTuple(args, "OO", &func, &defaults)) {
5810+
return NULL;
5811+
}
5812+
int result = PyFunction_SetDefaults(func, defaults);
5813+
if (result == -1)
5814+
return NULL;
5815+
Py_RETURN_NONE;
5816+
}
5817+
57915818

57925819
// type watchers
57935820

@@ -6202,6 +6229,8 @@ static PyMethodDef TestMethods[] = {
62026229
{"function_get_code", function_get_code, METH_O, NULL},
62036230
{"function_get_globals", function_get_globals, METH_O, NULL},
62046231
{"function_get_module", function_get_module, METH_O, NULL},
6232+
{"function_get_defaults", function_get_defaults, METH_O, NULL},
6233+
{"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
62056234
{"add_type_watcher", add_type_watcher, METH_O, NULL},
62066235
{"clear_type_watcher", clear_type_watcher, METH_O, NULL},
62076236
{"watch_type", watch_type, METH_VARARGS, NULL},

0 commit comments

Comments
 (0)