From c102973b3ed6d102ec351a77004193e5258791c6 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 21:54:54 -0700 Subject: [PATCH 01/14] add pos_only test --- test-data/pybind11_fixtures/src/main.cpp | 76 ++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/test-data/pybind11_fixtures/src/main.cpp b/test-data/pybind11_fixtures/src/main.cpp index 4d275ab1fd70..7e42f9c1433d 100644 --- a/test-data/pybind11_fixtures/src/main.cpp +++ b/test-data/pybind11_fixtures/src/main.cpp @@ -265,6 +265,82 @@ void bind_demo(py::module& m) { // Module-level attributes m.attr("PI") = std::acos(-1); m.attr("__version__") = "0.0.1"; + + // test_keyword_only_args + m.def( + "kw_only_all", + [](int i, int j) { return py::make_tuple(i, j); }, + py::kw_only(), + py::arg("i"), + py::arg("j")); + m.def( + "kw_only_some", + [](int i, int j, int k) { return py::make_tuple(i, j, k); }, + py::arg(), + py::kw_only(), + py::arg("j"), + py::arg("k")); + m.def( + "kw_only_with_defaults", + [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); }, + py::arg() = 3, + "j"_a = 4, + py::kw_only(), + "k"_a = 5, + "z"_a); + m.def( + "kw_only_mixed", + [](int i, int j) { return py::make_tuple(i, j); }, + "i"_a, + py::kw_only(), + "j"_a); + m.def( + "kw_only_plus_more", + [](int i, int j, int k, const py::kwargs &kwargs) { + return py::make_tuple(i, j, k, kwargs); + }, + py::arg() /* positional */, + py::arg("j") = -1 /* both */, + py::kw_only(), + py::arg("k") /* kw-only */); + + m.def("register_invalid_kw_only", [](py::module_ m) { + m.def( + "bad_kw_only", + [](int i, int j) { return py::make_tuple(i, j); }, + py::kw_only(), + py::arg() /* invalid unnamed argument */, + "j"_a); + }); + + // test_positional_only_args + m.def( + "pos_only_all", + [](int i, int j) { return py::make_tuple(i, j); }, + py::arg("i"), + py::arg("j"), + py::pos_only()); + m.def( + "pos_only_mix", + [](int i, int j) { return py::make_tuple(i, j); }, + py::arg("i"), + py::pos_only(), + py::arg("j")); + m.def( + "pos_kw_only_mix", + [](int i, int j, int k) { return py::make_tuple(i, j, k); }, + py::arg("i"), + py::pos_only(), + py::arg("j"), + py::kw_only(), + py::arg("k")); + m.def( + "pos_only_def_mix", + [](int i, int j, int k) { return py::make_tuple(i, j, k); }, + py::arg("i"), + py::arg("j") = 2, + py::pos_only(), + py::arg("k") = 3); } // ---------------------------------------------------------------------------- From 1dcda4153cbc2cd24e88e87e63551846313c5fa0 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 21:57:30 -0700 Subject: [PATCH 02/14] fix workflow --- .github/workflows/test_stubgenc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 4676acf8695b..54ba79e80e78 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -11,7 +11,7 @@ on: - 'mypy/stubgenc.py' - 'mypy/stubdoc.py' - 'mypy/stubutil.py' - - 'test-data/stubgen/**' + - 'test-data/pybind11_features/**' permissions: contents: read From f4e847851c9118d3f9ddbdaf4e9539180bb742d4 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 21:58:45 -0700 Subject: [PATCH 03/14] fix spelling --- .github/workflows/test_stubgenc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_stubgenc.yml b/.github/workflows/test_stubgenc.yml index 54ba79e80e78..6cf3cb71c3ff 100644 --- a/.github/workflows/test_stubgenc.yml +++ b/.github/workflows/test_stubgenc.yml @@ -11,7 +11,7 @@ on: - 'mypy/stubgenc.py' - 'mypy/stubdoc.py' - 'mypy/stubutil.py' - - 'test-data/pybind11_features/**' + - 'test-data/pybind11_fixtures/**' permissions: contents: read From b21b2aba135f4c4ec71e1c5b61e6ef1f5be4fcbf Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 22:00:17 -0700 Subject: [PATCH 04/14] simplify test --- test-data/pybind11_fixtures/src/main.cpp | 47 ------------------------ 1 file changed, 47 deletions(-) diff --git a/test-data/pybind11_fixtures/src/main.cpp b/test-data/pybind11_fixtures/src/main.cpp index 7e42f9c1433d..fde1b391a222 100644 --- a/test-data/pybind11_fixtures/src/main.cpp +++ b/test-data/pybind11_fixtures/src/main.cpp @@ -266,53 +266,6 @@ void bind_demo(py::module& m) { m.attr("PI") = std::acos(-1); m.attr("__version__") = "0.0.1"; - // test_keyword_only_args - m.def( - "kw_only_all", - [](int i, int j) { return py::make_tuple(i, j); }, - py::kw_only(), - py::arg("i"), - py::arg("j")); - m.def( - "kw_only_some", - [](int i, int j, int k) { return py::make_tuple(i, j, k); }, - py::arg(), - py::kw_only(), - py::arg("j"), - py::arg("k")); - m.def( - "kw_only_with_defaults", - [](int i, int j, int k, int z) { return py::make_tuple(i, j, k, z); }, - py::arg() = 3, - "j"_a = 4, - py::kw_only(), - "k"_a = 5, - "z"_a); - m.def( - "kw_only_mixed", - [](int i, int j) { return py::make_tuple(i, j); }, - "i"_a, - py::kw_only(), - "j"_a); - m.def( - "kw_only_plus_more", - [](int i, int j, int k, const py::kwargs &kwargs) { - return py::make_tuple(i, j, k, kwargs); - }, - py::arg() /* positional */, - py::arg("j") = -1 /* both */, - py::kw_only(), - py::arg("k") /* kw-only */); - - m.def("register_invalid_kw_only", [](py::module_ m) { - m.def( - "bad_kw_only", - [](int i, int j) { return py::make_tuple(i, j); }, - py::kw_only(), - py::arg() /* invalid unnamed argument */, - "j"_a); - }); - // test_positional_only_args m.def( "pos_only_all", From 1572b619c9213b031ba44926ea62fd096dfa0964 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 22:05:32 -0700 Subject: [PATCH 05/14] bump pybind --- test-data/pybind11_fixtures/pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/pybind11_fixtures/pyproject.toml b/test-data/pybind11_fixtures/pyproject.toml index 773d036e62f5..ea3ed4734feb 100644 --- a/test-data/pybind11_fixtures/pyproject.toml +++ b/test-data/pybind11_fixtures/pyproject.toml @@ -4,7 +4,7 @@ requires = [ "wheel", # Officially supported pybind11 version. This is pinned to guarantee 100% reproducible CI. # As a result, the version needs to be bumped manually at will. - "pybind11==2.9.2", + "pybind11==2.13.6", ] build-backend = "setuptools.build_meta" From aa56a2f7daa6bbc6f5b31c03420d95088a4ecc76 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 22:41:49 -0700 Subject: [PATCH 06/14] test pos_only_index --- mypy/stubdoc.py | 9 ++++++++- mypy/test/teststubgen.py | 11 ++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 89db6cb3378f..e1140ce90058 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -77,6 +77,8 @@ class FunctionSig(NamedTuple): name: str args: list[ArgSig] ret_type: str | None + pos_only_index: int | None = None + kwarg_only_index: int | None = None type_args: str = "" # TODO implement in stubgenc and remove the default docstring: str | None = None @@ -139,6 +141,11 @@ def format_sig( args.append(arg_def) + if self.pos_only_index: + args.insert(self.pos_only_index, '/') + if self.kwarg_only_index: + args.insert(self.kwarg_only_index, '*') + retfield = "" ret_type = self.ret_type if self.ret_type else any_val if ret_type is not None: @@ -342,7 +349,7 @@ def add_token(self, token: tokenize.TokenInfo) -> None: if self.found: self.signatures.append( - FunctionSig(name=self.function_name, args=self.args, ret_type=self.ret_type) + FunctionSig(name=self.function_name, args=self.args, pos_only_index=self.pos_only, kwarg_only_index=self.keyword_only, ret_type=self.ret_type) ) self.found = False self.args = [] diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index 43974cf8ec68..e9af0da93d6b 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -477,17 +477,17 @@ def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: assert_equal( infer_sig_from_docstring("func(*, x) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x")], ret_type="str")], + [FunctionSig(name="func", args=[ArgSig(name="x")], ret_type="str", pos_only_index=0)], ) assert_equal( infer_sig_from_docstring("func(x, *, y) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", pos_only_index=1)], ) assert_equal( infer_sig_from_docstring("func(*, x, y) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", pos_only_index=0)], ) assert_equal( @@ -497,6 +497,7 @@ def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: name="func", args=[ArgSig(name="x"), ArgSig(name="kwonly"), ArgSig("**kwargs")], ret_type="str", + pos_only_index=1, ) ], ) @@ -514,6 +515,8 @@ def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments(self) -> N name="func", args=[ArgSig(name="x"), ArgSig(name="y"), ArgSig(name="z")], ret_type="str", + pos_only_index=1, + kwarg_only_index=3 ) ], ) @@ -530,6 +533,8 @@ def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments(self) -> N ArgSig("**kwargs"), ], ret_type="str", + pos_only_index=1, + kwarg_only_index=3 ) ], ) From fe5e404ceab40404090c129a32f049d2e9d37a13 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 22:45:26 -0700 Subject: [PATCH 07/14] fix order --- mypy/stubdoc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index e1140ce90058..fd5e39b9dd99 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -77,10 +77,10 @@ class FunctionSig(NamedTuple): name: str args: list[ArgSig] ret_type: str | None - pos_only_index: int | None = None - kwarg_only_index: int | None = None type_args: str = "" # TODO implement in stubgenc and remove the default docstring: str | None = None + pos_only_index: int | None = None + kwarg_only_index: int | None = None def is_special_method(self) -> bool: return bool( From a6d174938f1392377242d50f207ed3b37985663e Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 22:57:12 -0700 Subject: [PATCH 08/14] update .pyi --- mypy/stubdoc.py | 3 ++- .../pybind11_fixtures/demo.pyi | 4 ++++ .../pybind11_fixtures/__init__.pyi | 4 ++-- .../pybind11_fixtures/demo.pyi | 15 ++++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index fd5e39b9dd99..114ead0855dd 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -144,7 +144,8 @@ def format_sig( if self.pos_only_index: args.insert(self.pos_only_index, '/') if self.kwarg_only_index: - args.insert(self.kwarg_only_index, '*') + pos_offset = 1 if self.pos_only_index else 0 + args.insert(self.kwarg_only_index + pos_offset, '*') retfield = "" ret_type = self.ret_type if self.ret_type else any_val diff --git a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi index 87b8ec0e4ad6..268514a72c84 100644 --- a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi @@ -57,5 +57,9 @@ class Point: def answer() -> int: ... def midpoint(left: float, right: float) -> float: ... +def pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple: +def pos_only_all(i: int, j: int, /) -> tuple: +def pos_only_def_mix(i: int, j: int = ..., /, k: int = ...) -> tuple: +def pos_only_mix(i: int, /, j: int) -> tuple: def sum(arg0: int, arg1: int) -> int: ... def weighted_midpoint(left: float, right: float, alpha: float = ...) -> float: ... diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi index 0eeb788d4278..3c97d03938d7 100644 --- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/__init__.pyi @@ -48,8 +48,8 @@ def func_incomplete_signature(*args, **kwargs): def func_returning_optional() -> int | None: """func_returning_optional() -> Optional[int]""" def func_returning_pair() -> tuple[int, float]: - """func_returning_pair() -> Tuple[int, float]""" + """func_returning_pair() -> tuple[int, float]""" def func_returning_path() -> os.PathLike: """func_returning_path() -> os.PathLike""" def func_returning_vector() -> list[float]: - """func_returning_vector() -> List[float]""" + """func_returning_vector() -> list[float]""" diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi index 6e285f202f1a..9c62284a29d8 100644 --- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi @@ -96,7 +96,7 @@ class Point: 2. __init__(self: pybind11_fixtures.demo.Point, x: float, y: float) -> None """ def as_list(self) -> list[float]: - """as_list(self: pybind11_fixtures.demo.Point) -> List[float]""" + """as_list(self: pybind11_fixtures.demo.Point) -> list[float]""" @overload def distance_to(self, x: float, y: float) -> float: """distance_to(*args, **kwargs) @@ -126,6 +126,19 @@ def answer() -> int: ''' def midpoint(left: float, right: float) -> float: """midpoint(left: float, right: float) -> float""" + +def pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple: + """pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple""" + +def pos_only_all(i: int, j: int, /) -> tuple: + """pos_only_all(i: int, j: int, /) -> tuple""" + +def pos_only_def_mix(i: int, j: int = ..., /, k: int = ...) -> tuple: + """pos_only_def_mix(i: int, j: int = 2, /, k: int = 3) -> tuple""" + +def pos_only_mix(i: int, /, j: int) -> tuple: + """pos_only_mix(i: int, /, j: int) -> tuple""" + def sum(arg0: int, arg1: int) -> int: '''sum(arg0: int, arg1: int) -> int From 6653fbd017bb7ddd1cfb77e81cec1149a5f129ee Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 23:07:08 -0700 Subject: [PATCH 09/14] update kwarg only --- mypy/stubdoc.py | 7 ++++--- .../pybind11_fixtures/demo.pyi | 13 ++++--------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 114ead0855dd..2a0315845fa7 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -144,8 +144,7 @@ def format_sig( if self.pos_only_index: args.insert(self.pos_only_index, '/') if self.kwarg_only_index: - pos_offset = 1 if self.pos_only_index else 0 - args.insert(self.kwarg_only_index + pos_offset, '*') + args.insert(self.kwarg_only_index, '*') retfield = "" ret_type = self.ret_type if self.ret_type else any_val @@ -190,6 +189,7 @@ def __init__(self, function_name: str) -> None: self.args: list[ArgSig] = [] self.pos_only: int | None = None self.keyword_only: int | None = None + self.keyword_only_index: int | None = None # Valid signatures found so far. self.signatures: list[FunctionSig] = [] @@ -273,6 +273,7 @@ def add_token(self, token: tokenize.TokenInfo) -> None: self.reset() return self.keyword_only = len(self.args) + self.keyword_only_index = self.keyword_only self.accumulator = "" else: if self.accumulator.startswith("*"): @@ -350,7 +351,7 @@ def add_token(self, token: tokenize.TokenInfo) -> None: if self.found: self.signatures.append( - FunctionSig(name=self.function_name, args=self.args, pos_only_index=self.pos_only, kwarg_only_index=self.keyword_only, ret_type=self.ret_type) + FunctionSig(name=self.function_name, args=self.args, pos_only_index=self.pos_only, kwarg_only_index=self.keyword_only_index, ret_type=self.ret_type) ) self.found = False self.args = [] diff --git a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi index 9c62284a29d8..2e8bbf21801a 100644 --- a/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_with_docs/pybind11_fixtures/demo.pyi @@ -28,9 +28,9 @@ class Point: """__ne__(self: object, other: object) -> bool""" @property def name(self) -> str: - """name(self: handle) -> str + """name(self: object) -> str - name(self: handle) -> str + name(self: object) -> str """ @property def value(self) -> int: @@ -63,9 +63,9 @@ class Point: """__ne__(self: object, other: object) -> bool""" @property def name(self) -> str: - """name(self: handle) -> str + """name(self: object) -> str - name(self: handle) -> str + name(self: object) -> str """ @property def value(self) -> int: @@ -126,19 +126,14 @@ def answer() -> int: ''' def midpoint(left: float, right: float) -> float: """midpoint(left: float, right: float) -> float""" - def pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple: """pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple""" - def pos_only_all(i: int, j: int, /) -> tuple: """pos_only_all(i: int, j: int, /) -> tuple""" - def pos_only_def_mix(i: int, j: int = ..., /, k: int = ...) -> tuple: """pos_only_def_mix(i: int, j: int = 2, /, k: int = 3) -> tuple""" - def pos_only_mix(i: int, /, j: int) -> tuple: """pos_only_mix(i: int, /, j: int) -> tuple""" - def sum(arg0: int, arg1: int) -> int: '''sum(arg0: int, arg1: int) -> int From a31eabdc7986deb82179cb8dbb7608767b97deca Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 23:20:33 -0700 Subject: [PATCH 10/14] update teststubgen --- mypy/test/teststubgen.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index e9af0da93d6b..ec8453aae4bc 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -437,28 +437,28 @@ def test_infer_sig_from_docstring_args_kwargs_errors(self) -> None: def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: assert_equal( infer_sig_from_docstring("func(self, /) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="self")], ret_type="str")], + [FunctionSig(name="func", args=[ArgSig(name="self")], ret_type="str", pos_only_index=1)], ) assert_equal( infer_sig_from_docstring("func(self, x, /) -> str", "func"), [ FunctionSig( - name="func", args=[ArgSig(name="self"), ArgSig(name="x")], ret_type="str" + name="func", args=[ArgSig(name="self"), ArgSig(name="x")], ret_type="str", pos_only_index=2 ) ], ) assert_equal( infer_sig_from_docstring("func(x, /, y) -> int", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="int")], + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="int", pos_only_index=1)], ) assert_equal( infer_sig_from_docstring("func(x, /, *args) -> str", "func"), [ FunctionSig( - name="func", args=[ArgSig(name="x"), ArgSig(name="*args")], ret_type="str" + name="func", args=[ArgSig(name="x"), ArgSig(name="*args")], ret_type="str", pos_only_index=1 ) ], ) @@ -470,6 +470,8 @@ def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: name="func", args=[ArgSig(name="x"), ArgSig(name="kwonly"), ArgSig(name="**kwargs")], ret_type="str", + pos_only_index=1, + kwarg_only_index=2 ) ], ) @@ -477,17 +479,17 @@ def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: assert_equal( infer_sig_from_docstring("func(*, x) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x")], ret_type="str", pos_only_index=0)], + [FunctionSig(name="func", args=[ArgSig(name="x")], ret_type="str", kwarg_only_index=0)], ) assert_equal( infer_sig_from_docstring("func(x, *, y) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", pos_only_index=1)], + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", kwarg_only_index=1)], ) assert_equal( infer_sig_from_docstring("func(*, x, y) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", pos_only_index=0)], + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", kwarg_only_index=0)], ) assert_equal( @@ -497,7 +499,7 @@ def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: name="func", args=[ArgSig(name="x"), ArgSig(name="kwonly"), ArgSig("**kwargs")], ret_type="str", - pos_only_index=1, + kwarg_only_index=1, ) ], ) @@ -505,7 +507,7 @@ def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments(self) -> None: assert_equal( infer_sig_from_docstring("func(x, /, *, y) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str")], + [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", pos_only_index=1, kwarg_only_index=2)], ) assert_equal( From 27a41e30ad3065054f80488f838946a148fe2526 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 23:21:05 -0700 Subject: [PATCH 11/14] add ... --- .../expected_stubs_no_docs/pybind11_fixtures/demo.pyi | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi index 268514a72c84..a7318b38908f 100644 --- a/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi +++ b/test-data/pybind11_fixtures/expected_stubs_no_docs/pybind11_fixtures/demo.pyi @@ -57,9 +57,9 @@ class Point: def answer() -> int: ... def midpoint(left: float, right: float) -> float: ... -def pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple: -def pos_only_all(i: int, j: int, /) -> tuple: -def pos_only_def_mix(i: int, j: int = ..., /, k: int = ...) -> tuple: -def pos_only_mix(i: int, /, j: int) -> tuple: +def pos_kw_only_mix(i: int, /, j: int, *, k: int) -> tuple: ... +def pos_only_all(i: int, j: int, /) -> tuple: ... +def pos_only_def_mix(i: int, j: int = ..., /, k: int = ...) -> tuple: ... +def pos_only_mix(i: int, /, j: int) -> tuple: ... def sum(arg0: int, arg1: int) -> int: ... def weighted_midpoint(left: float, right: float, alpha: float = ...) -> float: ... From e8277e1ddb46d739c492caf78f4a6c0aec8f13e5 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 23:23:13 -0700 Subject: [PATCH 12/14] fix kwarg index --- mypy/stubdoc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index 2a0315845fa7..f1fa9f9704f8 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -273,7 +273,7 @@ def add_token(self, token: tokenize.TokenInfo) -> None: self.reset() return self.keyword_only = len(self.args) - self.keyword_only_index = self.keyword_only + self.keyword_only_index = self.keyword_only + 1 if self.pos_only is not None else 0 self.accumulator = "" else: if self.accumulator.startswith("*"): From b656ae345363fa73279b8784ce8fb45bbf6a1d3e Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 23:30:43 -0700 Subject: [PATCH 13/14] lint --- mypy/stubdoc.py | 16 +++++++--- mypy/test/teststubgen.py | 65 +++++++++++++++++++++++++++++++++------- 2 files changed, 66 insertions(+), 15 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index f1fa9f9704f8..e3f06906ad1d 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -142,9 +142,9 @@ def format_sig( args.append(arg_def) if self.pos_only_index: - args.insert(self.pos_only_index, '/') + args.insert(self.pos_only_index, "/") if self.kwarg_only_index: - args.insert(self.kwarg_only_index, '*') + args.insert(self.kwarg_only_index, "*") retfield = "" ret_type = self.ret_type if self.ret_type else any_val @@ -273,7 +273,9 @@ def add_token(self, token: tokenize.TokenInfo) -> None: self.reset() return self.keyword_only = len(self.args) - self.keyword_only_index = self.keyword_only + 1 if self.pos_only is not None else 0 + self.keyword_only_index = ( + self.keyword_only + 1 if self.pos_only is not None else 0 + ) self.accumulator = "" else: if self.accumulator.startswith("*"): @@ -351,7 +353,13 @@ def add_token(self, token: tokenize.TokenInfo) -> None: if self.found: self.signatures.append( - FunctionSig(name=self.function_name, args=self.args, pos_only_index=self.pos_only, kwarg_only_index=self.keyword_only_index, ret_type=self.ret_type) + FunctionSig( + name=self.function_name, + args=self.args, + pos_only_index=self.pos_only, + kwarg_only_index=self.keyword_only_index, + ret_type=self.ret_type, + ) ) self.found = False self.args = [] diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py index ec8453aae4bc..8ee76547d4a2 100644 --- a/mypy/test/teststubgen.py +++ b/mypy/test/teststubgen.py @@ -437,28 +437,45 @@ def test_infer_sig_from_docstring_args_kwargs_errors(self) -> None: def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: assert_equal( infer_sig_from_docstring("func(self, /) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="self")], ret_type="str", pos_only_index=1)], + [ + FunctionSig( + name="func", args=[ArgSig(name="self")], ret_type="str", pos_only_index=1 + ) + ], ) assert_equal( infer_sig_from_docstring("func(self, x, /) -> str", "func"), [ FunctionSig( - name="func", args=[ArgSig(name="self"), ArgSig(name="x")], ret_type="str", pos_only_index=2 + name="func", + args=[ArgSig(name="self"), ArgSig(name="x")], + ret_type="str", + pos_only_index=2, ) ], ) assert_equal( infer_sig_from_docstring("func(x, /, y) -> int", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="int", pos_only_index=1)], + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="y")], + ret_type="int", + pos_only_index=1, + ) + ], ) assert_equal( infer_sig_from_docstring("func(x, /, *args) -> str", "func"), [ FunctionSig( - name="func", args=[ArgSig(name="x"), ArgSig(name="*args")], ret_type="str", pos_only_index=1 + name="func", + args=[ArgSig(name="x"), ArgSig(name="*args")], + ret_type="str", + pos_only_index=1, ) ], ) @@ -471,7 +488,7 @@ def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: args=[ArgSig(name="x"), ArgSig(name="kwonly"), ArgSig(name="**kwargs")], ret_type="str", pos_only_index=1, - kwarg_only_index=2 + kwarg_only_index=2, ) ], ) @@ -479,17 +496,35 @@ def test_infer_sig_from_docstring_positional_only_arguments(self) -> None: def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: assert_equal( infer_sig_from_docstring("func(*, x) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x")], ret_type="str", kwarg_only_index=0)], + [ + FunctionSig( + name="func", args=[ArgSig(name="x")], ret_type="str", kwarg_only_index=0 + ) + ], ) assert_equal( infer_sig_from_docstring("func(x, *, y) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", kwarg_only_index=1)], + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="y")], + ret_type="str", + kwarg_only_index=1, + ) + ], ) assert_equal( infer_sig_from_docstring("func(*, x, y) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", kwarg_only_index=0)], + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="y")], + ret_type="str", + kwarg_only_index=0, + ) + ], ) assert_equal( @@ -507,7 +542,15 @@ def test_infer_sig_from_docstring_keyword_only_arguments(self) -> None: def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments(self) -> None: assert_equal( infer_sig_from_docstring("func(x, /, *, y) -> str", "func"), - [FunctionSig(name="func", args=[ArgSig(name="x"), ArgSig(name="y")], ret_type="str", pos_only_index=1, kwarg_only_index=2)], + [ + FunctionSig( + name="func", + args=[ArgSig(name="x"), ArgSig(name="y")], + ret_type="str", + pos_only_index=1, + kwarg_only_index=2, + ) + ], ) assert_equal( @@ -518,7 +561,7 @@ def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments(self) -> N args=[ArgSig(name="x"), ArgSig(name="y"), ArgSig(name="z")], ret_type="str", pos_only_index=1, - kwarg_only_index=3 + kwarg_only_index=3, ) ], ) @@ -536,7 +579,7 @@ def test_infer_sig_from_docstring_pos_only_and_keyword_only_arguments(self) -> N ], ret_type="str", pos_only_index=1, - kwarg_only_index=3 + kwarg_only_index=3, ) ], ) From 6c51281b1b2b2e9cf618d8b3538a709281396711 Mon Sep 17 00:00:00 2001 From: Michael Carlstrom Date: Tue, 24 Jun 2025 23:50:03 -0700 Subject: [PATCH 14/14] fix offset --- mypy/stubdoc.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mypy/stubdoc.py b/mypy/stubdoc.py index e3f06906ad1d..c5e918c01225 100644 --- a/mypy/stubdoc.py +++ b/mypy/stubdoc.py @@ -273,9 +273,8 @@ def add_token(self, token: tokenize.TokenInfo) -> None: self.reset() return self.keyword_only = len(self.args) - self.keyword_only_index = ( - self.keyword_only + 1 if self.pos_only is not None else 0 - ) + pos_offset = 1 if self.pos_only is not None else 0 + self.keyword_only_index = self.keyword_only + pos_offset self.accumulator = "" else: if self.accumulator.startswith("*"):