Skip to content

Commit

Permalink
gh-112640: Add kwdefaults parameter to types.FunctionType.__new__ (
Browse files Browse the repository at this point in the history
  • Loading branch information
sobolevn authored Jan 11, 2024
1 parent f653caa commit 2ac4cf4
Show file tree
Hide file tree
Showing 8 changed files with 76 additions and 13 deletions.
1 change: 1 addition & 0 deletions Include/internal/pycore_global_objects_fini_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Include/internal/pycore_global_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(kw)
STRUCT_FOR_ID(kw1)
STRUCT_FOR_ID(kw2)
STRUCT_FOR_ID(kwdefaults)
STRUCT_FOR_ID(lambda)
STRUCT_FOR_ID(last)
STRUCT_FOR_ID(last_exc)
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_runtime_init_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Include/internal/pycore_unicodeobject_generated.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 33 additions & 0 deletions Lib/test/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -2263,5 +2263,38 @@ def coro():
'close', 'throw'}))


class FunctionTests(unittest.TestCase):
def test_function_type_defaults(self):
def ex(a, /, b, *, c):
return a + b + c

func = types.FunctionType(
ex.__code__, {}, "func", (1, 2), None, {'c': 3},
)

self.assertEqual(func(), 6)
self.assertEqual(func.__defaults__, (1, 2))
self.assertEqual(func.__kwdefaults__, {'c': 3})

func = types.FunctionType(
ex.__code__, {}, "func", None, None, None,
)
self.assertEqual(func.__defaults__, None)
self.assertEqual(func.__kwdefaults__, None)

def test_function_type_wrong_defaults(self):
def ex(a, /, b, *, c):
return a + b + c

with self.assertRaisesRegex(TypeError, 'arg 4'):
types.FunctionType(
ex.__code__, {}, "func", 1, None, {'c': 3},
)
with self.assertRaisesRegex(TypeError, 'arg 6'):
types.FunctionType(
ex.__code__, {}, "func", None, None, 3,
)


if __name__ == '__main__':
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add ``kwdefaults`` parameter to :data:`types.FunctionType` to set
default keyword argument values.
33 changes: 22 additions & 11 deletions Objects/clinic/funcobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -809,14 +809,17 @@ function.__new__ as func_new
a tuple that specifies the default argument values
closure: object = None
a tuple that supplies the bindings for free variables
kwdefaults: object = None
a dictionary that specifies the default keyword argument values
Create a function object.
[clinic start generated code]*/

static PyObject *
func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
PyObject *name, PyObject *defaults, PyObject *closure)
/*[clinic end generated code: output=99c6d9da3a24e3be input=93611752fc2daf11]*/
PyObject *name, PyObject *defaults, PyObject *closure,
PyObject *kwdefaults)
/*[clinic end generated code: output=de72f4c22ac57144 input=20c9c9f04ad2d3f2]*/
{
PyFunctionObject *newfunc;
Py_ssize_t nclosure;
Expand All @@ -843,6 +846,11 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
return NULL;
}
}
if (kwdefaults != Py_None && !PyDict_Check(kwdefaults)) {
PyErr_SetString(PyExc_TypeError,
"arg 6 (kwdefaults) must be None or dict");
return NULL;
}

/* check that the closure is well-formed */
nclosure = closure == Py_None ? 0 : PyTuple_GET_SIZE(closure);
Expand Down Expand Up @@ -879,6 +887,9 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
if (closure != Py_None) {
newfunc->func_closure = Py_NewRef(closure);
}
if (kwdefaults != Py_None) {
newfunc->func_kwdefaults = Py_NewRef(kwdefaults);
}

return (PyObject *)newfunc;
}
Expand Down

0 comments on commit 2ac4cf4

Please sign in to comment.