From d592ffae76c49890f16620987d14a39177981077 Mon Sep 17 00:00:00 2001 From: Peter Hawkins Date: Fri, 26 Sep 2025 20:25:34 +0000 Subject: [PATCH] Fix __new__ overloads with variadic args but not keyword args. We had confused has_var_kwargs with has_var_args, and this made us think that such a __new__ overload was not compatible with a nullary call. --- src/nb_func.cpp | 4 ++-- tests/test_classes.cpp | 7 +++++++ tests/test_classes.py | 4 ++++ tests/test_classes_ext.pyi.ref | 6 ++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/nb_func.cpp b/src/nb_func.cpp index 0bebfcb29..d9fd15506 100644 --- a/src/nb_func.cpp +++ b/src/nb_func.cpp @@ -201,8 +201,8 @@ PyObject *nb_func_new(const func_data_prelim_base *f) noexcept { bool has_scope = f->flags & (uint32_t) func_flags::has_scope, has_name = f->flags & (uint32_t) func_flags::has_name, has_args = f->flags & (uint32_t) func_flags::has_args, - has_var_args = f->flags & (uint32_t) func_flags::has_var_kwargs, - has_var_kwargs = f->flags & (uint32_t) func_flags::has_var_args, + has_var_args = f->flags & (uint32_t) func_flags::has_var_args, + has_var_kwargs = f->flags & (uint32_t) func_flags::has_var_kwargs, can_mutate_args = f->flags & (uint32_t) func_flags::can_mutate_args, has_doc = f->flags & (uint32_t) func_flags::has_doc, has_signature = f->flags & (uint32_t) func_flags::has_signature, diff --git a/tests/test_classes.cpp b/tests/test_classes.cpp index caaa9ec1e..cf460a7fc 100644 --- a/tests/test_classes.cpp +++ b/tests/test_classes.cpp @@ -680,6 +680,7 @@ NB_MODULE(test_classes_ext, m) { // issue #786 struct NewNone {}; struct NewDflt { int value; }; + struct NewStarPosOnly { size_t value; }; struct NewStar { size_t value; }; nb::class_(m, "NewNone") .def(nb::new_([]() { return NewNone(); })); @@ -687,6 +688,12 @@ NB_MODULE(test_classes_ext, m) { .def(nb::new_([](int value) { return NewDflt{value}; }), "value"_a = 42) .def_ro("value", &NewDflt::value); + nb::class_(m, "NewStarPosOnly") + .def(nb::new_([](nb::args a, int value) { + return NewStarPosOnly{nb::len(a) + value}; + }), + "args"_a, "value"_a = 42) + .def_ro("value", &NewStarPosOnly::value); nb::class_(m, "NewStar") .def(nb::new_([](nb::args a, int value, nb::kwargs k) { return NewStar{nb::len(a) + value + 10 * nb::len(k)}; diff --git a/tests/test_classes.py b/tests/test_classes.py index a34fa65a0..42cf12aad 100644 --- a/tests/test_classes.py +++ b/tests/test_classes.py @@ -895,6 +895,10 @@ def test46_custom_new(): t.NewNone() assert t.NewDflt().value == 42 assert t.NewDflt(10).value == 10 + assert t.NewStarPosOnly().value == 42 + assert t.NewStarPosOnly("hi").value == 43 + assert t.NewStarPosOnly(value=10).value == 10 + assert t.NewStarPosOnly("hi", "lo", value=10).value == 12 assert t.NewStar().value == 42 assert t.NewStar("hi").value == 43 assert t.NewStar(value=10).value == 10 diff --git a/tests/test_classes_ext.pyi.ref b/tests/test_classes_ext.pyi.ref index d9b859353..e06eb41be 100644 --- a/tests/test_classes_ext.pyi.ref +++ b/tests/test_classes_ext.pyi.ref @@ -335,6 +335,12 @@ class NewDflt: @property def value(self) -> int: ... +class NewStarPosOnly: + def __init__(self, *args, value: int = 42) -> None: ... + + @property + def value(self) -> int: ... + class NewStar: def __init__(self, *args, value: int = 42, **kwargs) -> None: ...