From fc4a7c65dab5382bf12e9ad71dc98d416a11f3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Afonso=20Janu=C3=A1rio?= Date: Sat, 29 Aug 2026 18:16:00 +0100 Subject: [PATCH 1/2] Fix allcaptures()/allspans() return type in regex stubs Both were typed as fixed one-element tuples, tuple[list[AnyStr]] and tuple[list[tuple[int, int]]], even though the runtime always returns one entry per capture group, a count that varies with the pattern. A type checker refuses to unpack or index past the first element of a fixed-length tuple, so any code doing that against the real, variable return value failed to type-check even though it ran correctly. Changed both to the variable-length tuple form, matching what the neighboring captures() method already uses for the same shape. Fixes #566 --- .../regex/@tests/test_cases/check_allcaptures.py | 15 +++++++++++++++ stubs/regex/regex/_main.pyi | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 stubs/regex/@tests/test_cases/check_allcaptures.py diff --git a/stubs/regex/@tests/test_cases/check_allcaptures.py b/stubs/regex/@tests/test_cases/check_allcaptures.py new file mode 100644 index 000000000000..f3cff18c7423 --- /dev/null +++ b/stubs/regex/@tests/test_cases/check_allcaptures.py @@ -0,0 +1,15 @@ +from __future__ import annotations + +from typing import List, Tuple +from typing_extensions import assert_type + +import regex + +# Regression test for #566: allcaptures()/allspans() were typed as fixed +# 1-tuples, which made a type checker refuse to unpack or index past the +# first element even though the runtime always returns one entry per group, +# a count that depends on the pattern rather than being fixed at one. +m = regex.match(r"(\w+) (\w+)", "hello world") +assert m is not None +assert_type(m.allcaptures(), Tuple[List[str], ...]) +assert_type(m.allspans(), Tuple[List[Tuple[int, int]], ...]) diff --git a/stubs/regex/regex/_main.pyi b/stubs/regex/regex/_main.pyi index 2a77bafd9fa0..0e38ec3cad27 100644 --- a/stubs/regex/regex/_main.pyi +++ b/stubs/regex/regex/_main.pyi @@ -746,8 +746,8 @@ class Match(Generic[AnyStr]): def capturesdict(self) -> dict[str, list[AnyStr]]: ... def detach_string(self) -> None: ... - def allcaptures(self) -> tuple[list[AnyStr]]: ... - def allspans(self) -> tuple[list[tuple[int, int]]]: ... + def allcaptures(self) -> tuple[list[AnyStr], ...]: ... + def allspans(self) -> tuple[list[tuple[int, int]], ...]: ... @overload def __getitem__(self, key: Literal[0], /) -> AnyStr: ... From a9ae8feb1fdf7cee9cef51a4cb52126dbd2d7d59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Afonso=20Janu=C3=A1rio?= Date: Sat, 29 Aug 2026 22:13:47 +0100 Subject: [PATCH 2/2] Remove regression test per maintainer feedback Per REGRESSION.md, typeshed only adds regression tests for functions known to have caused complex problems in the past. This stub change doesn't meet that bar, so the test case is unnecessary. --- .../regex/@tests/test_cases/check_allcaptures.py | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 stubs/regex/@tests/test_cases/check_allcaptures.py diff --git a/stubs/regex/@tests/test_cases/check_allcaptures.py b/stubs/regex/@tests/test_cases/check_allcaptures.py deleted file mode 100644 index f3cff18c7423..000000000000 --- a/stubs/regex/@tests/test_cases/check_allcaptures.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import annotations - -from typing import List, Tuple -from typing_extensions import assert_type - -import regex - -# Regression test for #566: allcaptures()/allspans() were typed as fixed -# 1-tuples, which made a type checker refuse to unpack or index past the -# first element even though the runtime always returns one entry per group, -# a count that depends on the pattern rather than being fixed at one. -m = regex.match(r"(\w+) (\w+)", "hello world") -assert m is not None -assert_type(m.allcaptures(), Tuple[List[str], ...]) -assert_type(m.allspans(), Tuple[List[Tuple[int, int]], ...])