Catalogue tool: compile parses every catalogue twice and builds each .mo by repeated byte concatenation #1145
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#1145
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What exists today
src/postulo/core/messages_tool.py:cmd_compile()(957-978) parses everydjango.poof every set and writes its.mo; then, for Postulo itself,build_report()(1351-1371) parses all 2,352 catalogues again from disk to writelocale/status.json, callingpo_path()twice per language and set.compile_catalogue()(909-954) builds the GNU .mo without += struct.pack("<2I", …)in two loops (lines 943-950). Bytes are immutable, so each+=copies the whole buffer: quadratic in the entry count.Measurement, in memory with nothing written (a script in the audit folder,
work/w3_compile.py, imported the module, parsed and compiled every catalogue without writing, then ranbuild_report();compileitself was not run):catalogue_sets0.9 s; 2,352 catalogues: parse 8.0 s,compile_catalogue3.9 s (39.3 MB of .mo);build_report13.7 s, of which a profile puts about three quarters inparse(20.1 of 25.8 s profiled). So a compile is about 27 s and about 10 s of it is the second parse. The concatenation: 9,684 appends (the size of a fully translated core catalogue's two tables) take 25 ms with+=against 2.5 ms with a list andb"".join, about 1-2 s over all well-translated catalogues. The quadratic claim holds but is the smaller half of this finding.compileruns atci.yml:140, 262, 555,every-python.yml:112,dependencies.yml:104,release.yml:68, in the Dockerfile and in each plugin's CI. #1033 item 6 caches the CI output on a hash of the.pofiles: that skips the run when nothing changed but does not speed up a run when something did (every Weblate pull request), and covers neither the image build nor the plugins. Not a duplicate.What would change
cmd_compilekeeps the catalogues it parsed, per set and language, and passes them tobuild_reportso it does not read them again;statskeeps its own read.compile_cataloguebuilds the two offset tables once: a flat list of integers and onestruct.pack(f"<{2 * count}I", *flat), orarray.array("I"), as CPython's ownTools/i18n/msgfmt.pydoes.babel.messages.mofile.write_mowould be the library answer, but it would bring Babel in for some forty lines and the current writer leaves out drafts that failmessage_problems; keep the house code and fix the loop.What it saves or protects
small-medium: about 10 s of a 27 s compile (the status report re-parses every catalogue) and 1-2 s of byte copying; compile runs 4-6 times per CI push, in the image build and in each plugin's CI
Accessibility
none
Decisions
test_compile_writes_the_status_report_the_picker_reads.Found by the efficiency and reliability audit of 2026-10-30 (finding W3; index postulo/postulo#1108). Checked against
mainatdaee30b15(2026-10-30).