Summary
Catalog.is_identical() returns False for any catalog that contains a
context-specific message — including when compared against itself. Because
pybabel update --check decides by is_identical, that flag reports every
such catalog as out of date on every run, no matter how current it is.
Any project that uses pgettext/npgettext — that is, any project that
disambiguates a homonym, which is what contexts are for — cannot use --check
as the CI staleness gate it was added for in #831.
Reproduction
>>> from babel.messages.catalog import Catalog
>>> c = Catalog(locale="ja")
>>> c.add("Guide", "ガイド", context="navigation")
<Message 'Guide' (flags: [])>
>>> c.is_identical(c)
False
End to end, on a catalog pybabel init produced seconds earlier:
$ cat src/app.py
from gettext import pgettext
pgettext("navigation", "Guide")
$ pybabel extract -F babel.cfg -o messages.pot src
$ pybabel init -i messages.pot -d locales -l ja
$ pybabel update -i messages.pot -d locales --check
...
BaseError: Some catalogs are out of date.
Tested on 2.18.0 and on main (95d2f60).
Cause
Catalog._key_for() stores a context-specific message under a
(msgid, msgctxt) tuple, and a pluralizable message under its singular id:
key = id
if isinstance(key, (list, tuple)):
key = id[0]
if context is not None:
key = (key, context)
Catalog.is_identical() iterates those internal keys but resolves each one
through Catalog.get():
for key in self._messages.keys() | other._messages.keys():
message_1 = self.get(key)
message_2 = other.get(key)
get() calls _key_for(key) again. Handed the (msgid, msgctxt) tuple, that
branch cannot tell it from a pluralizable message's (singular, plural) id, so
it takes key[0] and looks up the bare msgid — a key no message is stored
under. get() returns None, and is_identical returns False.
Context-free messages are unaffected, which is why the existing coverage
(test_enclosed_filenames_in_location_comment) does not catch it.
Fix
The keys being iterated are _messages' own, so they should index _messages
directly rather than being re-derived. PR to follow.
Summary
Catalog.is_identical()returnsFalsefor any catalog that contains acontext-specific message — including when compared against itself. Because
pybabel update --checkdecides byis_identical, that flag reports everysuch catalog as out of date on every run, no matter how current it is.
Any project that uses
pgettext/npgettext— that is, any project thatdisambiguates a homonym, which is what contexts are for — cannot use
--checkas the CI staleness gate it was added for in #831.
Reproduction
End to end, on a catalog
pybabel initproduced seconds earlier:Tested on 2.18.0 and on
main(95d2f60).Cause
Catalog._key_for()stores a context-specific message under a(msgid, msgctxt)tuple, and a pluralizable message under its singular id:Catalog.is_identical()iterates those internal keys but resolves each onethrough
Catalog.get():get()calls_key_for(key)again. Handed the(msgid, msgctxt)tuple, thatbranch cannot tell it from a pluralizable message's
(singular, plural)id, soit takes
key[0]and looks up the baremsgid— a key no message is storedunder.
get()returnsNone, andis_identicalreturnsFalse.Context-free messages are unaffected, which is why the existing coverage
(
test_enclosed_filenames_in_location_comment) does not catch it.Fix
The keys being iterated are
_messages' own, so they should index_messagesdirectly rather than being re-derived. PR to follow.