]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-94808: cover `PyFunction_GetDefaults` and `PyFunction_SetDefaults` (#98449)
authorNikita Sobolev <mail@sobolevn.me>
Thu, 27 Oct 2022 00:47:29 +0000 (03:47 +0300)
committerGitHub <noreply@github.com>
Thu, 27 Oct 2022 00:47:29 +0000 (17:47 -0700)
Lib/test/test_capi.py
Modules/_testcapimodule.c

index 9446d472addd41f8c11828eb5763de8955d063f8..2a35576fc57e36b0e625a1291ad44a284a433864 100644 (file)
@@ -942,6 +942,48 @@ class CAPITest(unittest.TestCase):
         with self.assertRaises(SystemError):
             _testcapi.function_get_module(None)  # not a function
 
+    def test_function_get_defaults(self):
+        def some(pos_only='p', zero=0, optional=None):
+            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_module(None)  # not a function
+
+    def test_function_set_defaults(self):
+        def some(pos_only='p', zero=0, optional=None):
+            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)
+
+        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)
+
+        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)
+
 
 class TestPendingCalls(unittest.TestCase):
 
index fdf2f204acb29b3ee9b877be96dd21db33f307b3..bade0db64f5193db32854c59ca64818a3778b998 100644 (file)
@@ -5788,6 +5788,33 @@ function_get_module(PyObject *self, PyObject *func)
     }
 }
 
+static PyObject *
+function_get_defaults(PyObject *self, PyObject *func)
+{
+    PyObject *defaults = PyFunction_GetDefaults(func);
+    if (defaults != NULL) {
+        Py_INCREF(defaults);
+        return defaults;
+    } else if (PyErr_Occurred()) {
+        return NULL;
+    } else {
+        Py_RETURN_NONE;  // This can happen when `defaults` are set to `None`
+    }
+}
+
+static PyObject *
+function_set_defaults(PyObject *self, PyObject *args)
+{
+    PyObject *func = NULL, *defaults = NULL;
+    if (!PyArg_ParseTuple(args, "OO", &func, &defaults)) {
+        return NULL;
+    }
+    int result = PyFunction_SetDefaults(func, defaults);
+    if (result == -1)
+        return NULL;
+    Py_RETURN_NONE;
+}
+
 
 // type watchers
 
@@ -6202,6 +6229,8 @@ static PyMethodDef TestMethods[] = {
     {"function_get_code", function_get_code, METH_O, NULL},
     {"function_get_globals", function_get_globals, METH_O, NULL},
     {"function_get_module", function_get_module, METH_O, NULL},
+    {"function_get_defaults", function_get_defaults, METH_O, NULL},
+    {"function_set_defaults", function_set_defaults, METH_VARARGS, NULL},
     {"add_type_watcher", add_type_watcher, METH_O, NULL},
     {"clear_type_watcher", clear_type_watcher, METH_O, NULL},
     {"watch_type", watch_type, METH_VARARGS, NULL},