diff --git a/babel/messages/pofile.py b/babel/messages/pofile.py index 27f113ddf..f72ddd9d7 100644 --- a/babel/messages/pofile.py +++ b/babel/messages/pofile.py @@ -276,7 +276,15 @@ def _process_keyword_line(self, lineno, line, obsolete=False) -> None: self.in_msgid = False self.in_msgstr = True kwarg, has_bracket, idxarg = keyword.partition('[') - idx = int(idxarg[:-1]) if has_bracket else 0 + try: + idx = int(idxarg[:-1]) if has_bracket else 0 + except ValueError: + # Nothing was appended to `self.translations`, so leave `in_msgstr` off: a + # continuation line after this one would otherwise index an empty list. + # This puts the parser in the same state the unknown-keyword path below does. + self.in_msgstr = False + self._invalid_pofile(line, lineno, f"Invalid plural index in keyword {keyword!r}") + return s = _NormalizedString(arg) if arg != '""' else _NormalizedString() self.translations.append([idx, s]) return diff --git a/tests/messages/test_pofile.py b/tests/messages/test_pofile.py index 0f4b483cf..dedad0c8c 100644 --- a/tests/messages/test_pofile.py +++ b/tests/messages/test_pofile.py @@ -177,3 +177,30 @@ def test_issue_1134(case: str, abort_invalid: bool): output = pofile.read_po(buf) assert len(output) == 1 assert output["foo"].string in ((''), ('', '')) + + +@pytest.mark.parametrize("abort_invalid", [False, True]) +def test_invalid_msgstr_index_issue_1209(abort_invalid: bool): + # Regression test for #1209: a non-integer plural index in msgstr[...] must be reported + # through the normal invalid-pofile handling, not leak a bare ValueError from int(). + buf = StringIO('msgstr[\x0c]') + + if abort_invalid: + with pytest.raises(pofile.PoFileError): + pofile.read_po(buf, abort_invalid=True) + else: + # No crash: an invalid entry is skipped with a warning. + pofile.read_po(buf) + + +@pytest.mark.parametrize("abort_invalid", [False, True]) +def test_invalid_msgstr_index_continuation_issue_1209(abort_invalid: bool): + # The skipped keyword must not leave the parser inside a msgstr: a continuation line + # after it would index an empty translations list. + buf = StringIO('msgid "foo"\nmsgstr[\x0c] "x"\n"more"\n') + + if abort_invalid: + with pytest.raises(pofile.PoFileError): + pofile.read_po(buf, abort_invalid=True) + else: + pofile.read_po(buf)