is_identical() iterates the internal _messages keys, but looked each one
up through Catalog.get(). For a context-specific message that key is a
(msgid, msgctxt) tuple, and get() re-runs _key_for() on it, which takes a
tuple for a pluralizable message's id and reduces it to msgid alone --
a key no message is stored under. The lookup returned None, so any
catalog containing a msgctxt never compared equal, not even to itself.
The visible consequence is that pybabel update --check reports every
catalog using pgettext/npgettext as out of date on every run, which
makes it unusable as the CI staleness gate it was added for (python-babel#831).
Index _messages directly, since the keys being iterated are its own.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fixes #1307.
is_identical() iterates the internal _messages keys but looked each one up
through Catalog.get(). For a context-specific message that key is a
(msgid, msgctxt) tuple, and get() re-runs _key_for() on it — which cannot
distinguish that tuple from a pluralizable message's (singular, plural) id,
so it reduces the key to msgid alone, a key no message is stored under. The
lookup returned None, so a catalog containing any msgctxt never compared
equal, not even to itself.
The user-visible consequence is that pybabel update --check reports every
catalog using pgettext/npgettext as out of date on every run, which makes
it unusable as the CI staleness gate it was added for in #831.
The keys are _messages' own, so this indexes _messages directly.
Two regression tests are added, covering a context-specific message and a
context-specific pluralizable message. Both fail on main and pass with the
change; the rest of the suite is unaffected (6405 passed, 1162 skipped).
🤖 Generated with Claude Code