Skip to content

Commit 2ac4cf4

Browse files
authored
gh-112640: Add kwdefaults parameter to types.FunctionType.__new__ (#112641)
1 parent f653caa commit 2ac4cf4

8 files changed

+76
-13
lines changed

Include/internal/pycore_global_objects_fini_generated.h

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

+1
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ struct _Py_global_strings {
504504
STRUCT_FOR_ID(kw)
505505
STRUCT_FOR_ID(kw1)
506506
STRUCT_FOR_ID(kw2)
507+
STRUCT_FOR_ID(kwdefaults)
507508
STRUCT_FOR_ID(lambda)
508509
STRUCT_FOR_ID(last)
509510
STRUCT_FOR_ID(last_exc)

Include/internal/pycore_runtime_init_generated.h

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_types.py

+33
Original file line numberDiff line numberDiff line change
@@ -2263,5 +2263,38 @@ def coro():
22632263
'close', 'throw'}))
22642264

22652265

2266+
class FunctionTests(unittest.TestCase):
2267+
def test_function_type_defaults(self):
2268+
def ex(a, /, b, *, c):
2269+
return a + b + c
2270+
2271+
func = types.FunctionType(
2272+
ex.__code__, {}, "func", (1, 2), None, {'c': 3},
2273+
)
2274+
2275+
self.assertEqual(func(), 6)
2276+
self.assertEqual(func.__defaults__, (1, 2))
2277+
self.assertEqual(func.__kwdefaults__, {'c': 3})
2278+
2279+
func = types.FunctionType(
2280+
ex.__code__, {}, "func", None, None, None,
2281+
)
2282+
self.assertEqual(func.__defaults__, None)
2283+
self.assertEqual(func.__kwdefaults__, None)
2284+
2285+
def test_function_type_wrong_defaults(self):
2286+
def ex(a, /, b, *, c):
2287+
return a + b + c
2288+
2289+
with self.assertRaisesRegex(TypeError, 'arg 4'):
2290+
types.FunctionType(
2291+
ex.__code__, {}, "func", 1, None, {'c': 3},
2292+
)
2293+
with self.assertRaisesRegex(TypeError, 'arg 6'):
2294+
types.FunctionType(
2295+
ex.__code__, {}, "func", None, None, 3,
2296+
)
2297+
2298+
22662299
if __name__ == '__main__':
22672300
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add ``kwdefaults`` parameter to :data:`types.FunctionType` to set
2+
default keyword argument values.

Objects/clinic/funcobject.c.h

+22-11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Objects/funcobject.c

+13-2
Original file line numberDiff line numberDiff line change
@@ -809,14 +809,17 @@ function.__new__ as func_new
809809
a tuple that specifies the default argument values
810810
closure: object = None
811811
a tuple that supplies the bindings for free variables
812+
kwdefaults: object = None
813+
a dictionary that specifies the default keyword argument values
812814
813815
Create a function object.
814816
[clinic start generated code]*/
815817

816818
static PyObject *
817819
func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
818-
PyObject *name, PyObject *defaults, PyObject *closure)
819-
/*[clinic end generated code: output=99c6d9da3a24e3be input=93611752fc2daf11]*/
820+
PyObject *name, PyObject *defaults, PyObject *closure,
821+
PyObject *kwdefaults)
822+
/*[clinic end generated code: output=de72f4c22ac57144 input=20c9c9f04ad2d3f2]*/
820823
{
821824
PyFunctionObject *newfunc;
822825
Py_ssize_t nclosure;
@@ -843,6 +846,11 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
843846
return NULL;
844847
}
845848
}
849+
if (kwdefaults != Py_None && !PyDict_Check(kwdefaults)) {
850+
PyErr_SetString(PyExc_TypeError,
851+
"arg 6 (kwdefaults) must be None or dict");
852+
return NULL;
853+
}
846854

847855
/* check that the closure is well-formed */
848856
nclosure = closure == Py_None ? 0 : PyTuple_GET_SIZE(closure);
@@ -879,6 +887,9 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
879887
if (closure != Py_None) {
880888
newfunc->func_closure = Py_NewRef(closure);
881889
}
890+
if (kwdefaults != Py_None) {
891+
newfunc->func_kwdefaults = Py_NewRef(kwdefaults);
892+
}
882893

883894
return (PyObject *)newfunc;
884895
}

0 commit comments

Comments
 (0)