diff --git a/arrow/parser.py b/arrow/parser.py index fc3774b09..8be285e4d 100644 --- a/arrow/parser.py +++ b/arrow/parser.py @@ -422,12 +422,24 @@ 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: + # A malformed format can leave a token in ``fmt_tokens`` without a + # corresponding capture group in the compiled pattern. Surface this + # as a ParserError rather than letting a raw IndexError escape. + raise ParserMatchError( + f"Unable to find a match group for the specified token {token!r}." + ) if value is None: raise ParserMatchError( diff --git a/tests/test_parser.py b/tests/test_parser.py index 7038d880f..0779c776d 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -191,6 +191,14 @@ def test_parse_parse_no_match(self): with pytest.raises(ParserError): self.parser.parse("01-01", "YYYY-MM-DD") + def test_parse_malformed_fmt_no_match_group(self): + # A malformed format string can leave a token without a corresponding + # capture group in the compiled pattern; this must raise a ParserError + # rather than leaking a raw IndexError from match.group(). + # Regression test for https://github.com/arrow-py/arrow/issues/1191 + with pytest.raises(ParserMatchError): + self.parser.parse("foo", "[|(\\]s") + def test_parse_separators(self): with pytest.raises(ParserError): self.parser.parse("1403549231", "YYYY-MM-DD")