Skip to content

Don't crash on blank PO-Revision-Date / POT-Creation-Date headers - #1293

Merged
akx merged 2 commits into
python-babel:masterfrom
agu2347:fix-blank-date-headers
Nov 4, 2026
Merged

akx merged 2 commits into
python-babel:masterfrom
agu2347:fix-blank-date-headers

Conversation

@agu2347

@agu2347 agu2347 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Fixes #1219.

Some tools (e.g. Poedit) can leave the PO-Revision-Date header blank in a generated .po file instead of omitting it or using the conventional YEAR-MO-DA HO:MI+ZONE placeholder. Parsing such a file crashes with:

ValueError: time data '' does not match format '%Y-%m-%d %H:%M'

because _parse_datetime_header() is called unconditionally with the blank value, as reported in the issue.

Fix: guard both call sites (po-revision-date and, for the same reason, pot-creation-date) to skip parsing when the header value is blank, keeping the previous/default value instead -- the same treatment already given to the YEAR-MO-DA HO:MI+ZONE placeholder value right above it, and to a blank Language header elsewhere in this same method (self._set_locale(value or None)).

Testing: added a regression test that parses a message block with both headers blank and asserts: no exception is raised, creation_date/revision_date fall back to their defaults rather than being corrupted, and no literal None leaks into the round-tripped mime_headers output. I confirmed the test fails with the original ValueError without the fix, and passes with it.

I also verified end-to-end with an actual .po file parse + write round-trip (using an installed release build with CLDR data, since a shallow clone here can't build that data on its own), confirming no None leaks into the output and that normal (non-blank) header values are completely unaffected. Ran the full existing test_catalog.py and test_pofile.py suites: all pass, no regressions.

@akx

akx commented Jul 30, 2026

Copy link
Copy Markdown
Member

See #1222 for previous discussion.

@agu2347

agu2347 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the pointer -- I read through the #1222 discussion. To make sure this PR doesn't repeat the concern raised there ("I don't think we should be silently ignoring malformed dates" / silent data loss on unparseable values): #1222 was about any unparseable date string being silently swallowed, discarding a value the user actually wrote. This PR is narrower and only triggers on a genuinely blank header value (POT-Creation-Date: / PO-Revision-Date: with nothing after the colon) -- something Poedit itself sometimes writes. In that case there's no user-authored value to lose; falling back to the existing default (or leaving the previous value, for revision-date) doesn't discard any real information the way silently accepting-and-mangling an actually malformed non-blank date would. Any other malformed-but-non-blank value still goes through _parse_datetime_header unchanged and will raise/behave exactly as before -- this PR doesn't touch that path. Happy to adjust the approach further if this distinction doesn't fully address the earlier concern.

@Mukller

Mukller commented Aug 22, 2026

Copy link
Copy Markdown

Heads up for maintainers: there are currently three open PRs addressing this exact problem (blank POT-Creation-Date/PO-Revision-Date headers from tools like Poedit, crash in _parse_datetime_header, ref. #1219):

PR Approach Blank value Malformed non-blank value
#1293 (this one) guard at call site (value.strip()) skipped, default kept still raises ValueError (current strict behavior)
#1311 with suppress(ValueError) at call site skipped also silently ignored — broader behavior change than the stated goal
#1317 parser returns datetime | None None returned, caller skips raises

Notes on the differences:

It would be great if @akx (or whoever picks this up) can choose a preferred approach so two of the three can be closed — happy to re-review whichever direction wins. All three reference the same motivating case, and all include tests, which is nice to see.

agu2347 and others added 2 commits November 4, 2026 12:55
Some tools (e.g. Poedit) can leave the PO-Revision-Date header blank
in a generated .po file instead of omitting it or using the
conventional 'YEAR-MO-DA HO:MI+ZONE' placeholder. Parsing such a file
crashed with:

  ValueError: time data '' does not match format '%Y-%m-%d %H:%M'

because _parse_datetime_header() was called unconditionally with the
blank value.

Guard both call sites (po-revision-date and, for the same reason,
pot-creation-date) to skip parsing when the header value is blank,
keeping the previous/default value instead -- the same treatment
already given to the 'YEAR-MO-DA HO:MI+ZONE' placeholder value just
above, and to a blank Language header elsewhere in this same method.

Added a regression test that parses a message block with both headers
blank and asserts: no exception is raised, the catalog's
creation_date/revision_date fall back to their defaults rather than
being corrupted, and no literal 'None' leaks into the round-tripped
mime_headers output. Confirmed the test fails with the original
ValueError without the fix, and passes with it. Also verified
end-to-end with an actual .po file parse + write round-trip (using an
installed release build with CLDR data, since a shallow clone here
can't build that data), and confirmed normal (non-blank) header values
are completely unaffected. Ran the full existing test_catalog.py and
test_pofile.py suites: all pass, no regressions.

Fixes python-babel#1219

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active issues are reported, and the change includes targeted regression coverage for blank date headers and preservation of existing defaults.

No open issues 🎉

What changed in this PR

This pull request prevents Babel from crashing when .po files contain blank POT-Creation-Date or PO-Revision-Date headers. Blank values are ignored so existing catalog defaults remain intact.

Changes:

  • Guard date parsing against blank creation and revision date headers.
  • Add regression coverage for blank headers and default preservation.
  • Verify that round-tripped MIME headers do not contain None.
File Description
tests/​messages/​test_catalog.py Adds regression coverage for blank date headers and default preservation.
babel/​messages/​catalog.py Skips parsing blank creation and revision date headers.

Review effort: Lite

@codecov

codecov Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.66%. Comparing base (344a3b4) to head (3735f07).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1293   +/-   ##
=======================================
  Coverage   93.66%   93.66%           
=======================================
  Files          27       27           
  Lines        4970     4971    +1     
=======================================
+ Hits         4655     4656    +1     
  Misses        315      315           
Flag Coverage Δ
macos-14-3.10 92.69% <100.00%> (+<0.01%) ⬆️
macos-14-3.11 92.63% <100.00%> (+<0.01%) ⬆️
macos-14-3.12 92.89% <100.00%> (+<0.01%) ⬆️
macos-14-3.13 92.89% <100.00%> (+<0.01%) ⬆️
macos-14-3.14 92.87% <100.00%> (+<0.01%) ⬆️
macos-14-3.15 92.87% <100.00%> (+<0.01%) ⬆️
macos-14-3.8 92.57% <100.00%> (+<0.01%) ⬆️
macos-14-3.9 92.63% <100.00%> (+<0.01%) ⬆️
macos-14-pypy3.10 92.69% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-3.10 92.71% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-3.11 92.65% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-3.12 92.91% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-3.13 92.91% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-3.14 92.89% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-3.15 92.89% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-3.8 92.59% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-3.9 92.65% <100.00%> (+<0.01%) ⬆️
ubuntu-24.04-pypy3.10 92.71% <100.00%> (+<0.01%) ⬆️
windows-2022-3.10 92.71% <100.00%> (+<0.01%) ⬆️
windows-2022-3.11 92.65% <100.00%> (+<0.01%) ⬆️
windows-2022-3.12 92.91% <100.00%> (+<0.01%) ⬆️
windows-2022-3.13 92.91% <100.00%> (+<0.01%) ⬆️
windows-2022-3.14 92.89% <100.00%> (+<0.01%) ⬆️
windows-2022-3.15 92.89% <100.00%> (+<0.01%) ⬆️
windows-2022-3.8 92.68% <100.00%> (+<0.01%) ⬆️
windows-2022-3.9 92.64% <100.00%> (+<0.01%) ⬆️
windows-2022-pypy3.10 92.71% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@codspeed

codspeed Bot commented Nov 4, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 88 untouched benchmarks


Comparing agu2347:fix-blank-date-headers (3735f07) with master (344a3b4)

Open in CodSpeed

@akx
akx merged commit 63c168e into python-babel:master Nov 4, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Exception raised if the revision date is blank

4 participants