From: Aarni Koskela Date: Mon, 17 Mar 2025 12:37:31 +0000 (+0200) Subject: Avoid hot `isinstance`ing in PO file parse loop X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7a8b587cdca438396fd5aca9cf3b1a0977138eed;p=thirdparty%2Fbabel.git Avoid hot `isinstance`ing in PO file parse loop --- diff --git a/babel/messages/pofile.py b/babel/messages/pofile.py index 987193e9..8220637b 100644 --- a/babel/messages/pofile.py +++ b/babel/messages/pofile.py @@ -300,16 +300,25 @@ class PoFileParser: def parse(self, fileobj: IO[AnyStr] | Iterable[AnyStr]) -> None: """ - Reads from the file-like object `fileobj` and adds any po file - units found in it to the `Catalog` supplied to the constructor. + Reads from the file-like object (or iterable of string-likes) `fileobj` + and adds any po file units found in it to the `Catalog` + supplied to the constructor. + + All of the items in the iterable must be the same type; either `str` + or `bytes` (decoded with the catalog charset), but not a mixture. """ + needs_decode = None for lineno, line in enumerate(fileobj): line = line.strip() - if not isinstance(line, str): - line = line.decode(self.catalog.charset) + if needs_decode is None: + # If we don't yet know whether we need to decode, + # let's find out now. + needs_decode = not isinstance(line, str) if not line: continue + if needs_decode: + line = line.decode(self.catalog.charset) if line[0] == '#': if line[:2] == '#~': self._process_message_line(lineno, line[2:].lstrip(), obsolete=True) diff --git a/tests/messages/test_pofile.py b/tests/messages/test_pofile.py index 2bcc3df8..ffc95295 100644 --- a/tests/messages/test_pofile.py +++ b/tests/messages/test_pofile.py @@ -1068,11 +1068,18 @@ def test_iterable_of_strings(): """ Test we can parse from an iterable of strings. """ - catalog = pofile.read_po(['msgid "foo"', b'msgstr "Voh"'], locale="en_US") + catalog = pofile.read_po(['msgid "foo"', 'msgstr "Voh"'], locale="en_US") assert catalog.locale == Locale("en", "US") assert catalog.get("foo").string == "Voh" +@pytest.mark.parametrize("order", [1, -1]) +def test_iterable_of_mismatching_strings(order): + # Mixing and matching byteses and strs in the same read_po call is not allowed. + with pytest.raises(Exception): # noqa: B017 (will raise either TypeError or AttributeError) + pofile.read_po(['msgid "foo"', b'msgstr "Voh"'][::order]) + + def test_issue_1087(): buf = StringIO(r''' msgid ""