Fix KeyError in diff_for_humans for the zh locale - #1001
Open
dylanpulver wants to merge 1 commit into
Open
Conversation
zh/custom.py used a NAMED placeholder in "after"/"before" ("{time}后",
"{time}前") while DifferenceFormatter.format() substitutes positionally
(difference_formatter.py:115,159 call locale.get(key).format(time)).
So the two-datetime form raises KeyError: 'time' for every unit:
a.diff_for_humans(b, locale="zh") -> KeyError: 'time'
All 28 other locales use "{0}". zh is switched to match; no code change.
The relative-to-now form was unaffected because it reads the CLDR
translations table rather than these custom entries, and there was no
tests/localization/test_zh.py at all, so nothing exercised this path.
Adds that test file, covering the relative-to-now form and the
two-datetime form that was crashing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request Check List
diff_for_humans()raisesKeyError: 'time'for thezhlocale whenever it is given asecond datetime (the non-relative-to-now form), for every unit:
zh/custom.py:9-10holds"after": "{time}后"/"before": "{time}前"— named placeholders —while
DifferenceFormatter.format()substitutes positionally(
difference_formatter.py:115and:159, bothlocale.get(key).format(time)). All 28 otherlocales use
{0}, so this changes only thezhdata; no code change.Base is
masterper the template note (bug fix, not a behaviour change).Why nothing caught it: the relative-to-now form reads
translations.relative.*(thegenerated CLDR table) and never touches these two custom entries, and there is no
tests/localization/test_zh.py—zh,en*andpt_brare the locales with no test file.Added one for
zh, modelled ontest_ja.py, covering both forms.How it was found, since it's a reasonable question: a sweep, not a bug report from real
use. I called
diff_for_humansacross all 29 locales x 8 durations x both signs xabsoluteon/off (928 calls) and looked for exceptions or unsubstituted{.zhwas theonly locale that failed, 16/32 of its calls — exactly the
is_now=False, absolute=Falsepath.{time}appears nowhere else insrc/ortests/.Measured:
pytest tests1840 passed / 3 skipped before, 1841 / 3 after (same command,same venv). Mutants: reverting the two strings and keeping the new test re-raises all 16
sweep failures and fails
test_zh.py.Honest limitation: a second mutant — keeping
{time}and making the formatter call.format(time, time=time)— also passes the sweep and the new test. So the test does not byitself pick between the two remedies. I chose the data fix because the alternative sanctions
two placeholder conventions in the locale files; if you would rather the formatter be
tolerant, say so and I'll switch it.
Not tested: I did not add test files for
en,en_gb,en_usorpt_br, which alsohave none.
ruff format --checkreports a pre-existing blank-line diff in 28 of the 29locales/*/custom.pyfiles including this one; I left it alone.Disclosure: the diff, the test and this description were drafted with Claude Opus 4.8.