From 55dda07578142b532f74292ef136c4556863ff1a Mon Sep 17 00:00:00 2001 From: Anton Petnitsky <168552591+Mukller@users.noreply.github.com> Date: Wed, 29 Jul 2026 19:44:00 +0300 Subject: [PATCH 1/3] fix(parser): raise ParserMatchError instead of IndexError for malformed fmt (#1191) DateTimeParser.parse() iterates over fmt_tokens and calls match.group(token) for each one. When the format string is malformed, the generated regex may not define a named capture group for every token. In that case match.group(token) raises IndexError (not KeyError), which propagates up as an unhandled exception. Wrap the match.group() block in try/except IndexError and re-raise as ParserMatchError, consistent with how the no-match case is handled just above the loop. --- arrow/parser.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/arrow/parser.py b/arrow/parser.py index fc3774b09..d0d7abce2 100644 --- a/arrow/parser.py +++ b/arrow/parser.py @@ -422,12 +422,21 @@ def parse( parts: _Parts = {} for token in fmt_tokens: value: Union[Tuple[str, str, str], str] - if token == "Do": - value = match.group("value") - elif token == "W": - value = (match.group("year"), match.group("week"), match.group("day")) - else: - value = match.group(token) + try: + if token == "Do": + value = match.group("value") + elif token == "W": + value = ( + match.group("year"), + match.group("week"), + match.group("day"), + ) + else: + value = match.group(token) + except IndexError: + raise ParserMatchError( + f"Failed to match {fmt!r} when parsing {datetime_string!r}." + ) if value is None: raise ParserMatchError( From e51c0ddd8cecacad60726fa215625e93f0469f6b Mon Sep 17 00:00:00 2001 From: Anton Petnitsky <168552591+Mukller@users.noreply.github.com> Date: Wed, 29 Jul 2026 23:06:35 +0300 Subject: [PATCH 2/3] test(parser): add regression for #1191 (IndexError -> ParserMatchError for Do token) On locales whose ordinal_day_re does not expose a named value capture group (e.g. GermanLocale, which inherits the base r"(\d+)"), match.group("value") inside the Do-token branch previously propagated as a bare IndexError. After the fix it becomes ParserMatchError. --- tests/test_parser.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_parser.py b/tests/test_parser.py index 7038d880f..9a7facf1d 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -529,6 +529,14 @@ def test_parse_with_leading_and_trailing_whitespace(self): assert self.parser.parse(" 2016 ", "YYYY") == datetime(2016, 1, 1) + def test_parse_Do_indexerror_becomes_parser_match_error(self): + """Regression for #1191: On locales whose ordinal_day_re does not expose + a named 'value' capture group (e.g. GermanLocale), match.group('value') + previously raised a bare IndexError. It must raise ParserMatchError.""" + de_parser = DateTimeParser(locale="de") + with pytest.raises(ParserMatchError): + de_parser.parse("1", "Do") + assert self.parser.parse( " 2016-05-16 04:05:06.789120 ", "YYYY-MM-DD hh:mm:ss.S" ) == datetime(2016, 5, 16, 4, 5, 6, 789120) From 5aeb4c61ddc5a5ddb5ef4d301f9b691cd8fd0348 Mon Sep 17 00:00:00 2001 From: Anton Petnitsky <168552591+Mukller@users.noreply.github.com> Date: Thu, 13 Aug 2026 00:27:40 +0300 Subject: [PATCH 3/3] ci: retrigger CI to clear transient windows/pypy network failure