Repository navigation
perf(docx): cache style chains and reuse paragraph content (1.6x faster) - #4867
Conversation
Signed-off-by: Afonso Ribeiro <afonso.diamantino2@gmail.com>
|
✅ DCO Check Passed Thanks @deadstrobe5, all your commits are properly signed off. 🎉 |
Merge Protections🟢 Merge protection satisfied — ready to merge. Show 1 satisfied protection🟢 Enforce conventional commitMake sure that we follow https://www.conventionalcommits.org/en/v1.0.0/
|
AI triage (advisory)Checked commit This PR caches the Topic labels added: CI safety: no concern found. A maintainer must still approve CI. 2 related pull request(s) in the same code
The |
There was a problem hiding this comment.
AI first review (advisory): no blocker found
This review covers the full diff at 424240460f. It read the code and did not run it.
Performance-only change to msword_backend.py. The two new caches are correctly scoped to the backend instance (per DOCX file) and use element identity checks. The paragraph-content cache is a size-1 memoizer that hits exactly when _get_paragraph_elements and _get_paragraph_text are called in sequence for the same paragraph inside _handle_text_elements. No public API or method signature changes. The one thing to verify is that the _get_style_chain cache key (a style XML element object) cannot be mutated or reused across different DOCX files, which the instance-level __init__ assignment guarantees.
A maintainer makes the merge decision. This review never approves the pull request.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Feel free to check if it can apply to other backends, like odt/odf |
|
I had a look at the OpenDocument backend. It already wraps |
|
If it’s a win, probs worth opening a PR, that’s just my opinion, I agree with you, another PR is better |
|
@ExtReMLapin it was a win after all, just not where I expected: style lookups on Word-exported ODTs, 1.6x-3.2x on real files. Opened #4881 for it. |
Converting a DOCX spends most of its time in python-docx lookups that repeat for every run. Two of them are fixed here.
Every
base_stylehop in the style chain is an XPath query over the styles part, and the bold check walks the chain for every run. The chain is now resolved once per style and cached on the backend._handle_text_elementsread a paragraph's content three times: once in_get_paragraph_textand twice in_get_paragraph_elements(the emptiness check and the grouping). Each read resolves the formatting of every run. The last paragraph's content is now remembered, so it's computed once. Both helper methods keep their signatures, so the existing tests that patch them still work.This doesn't overlap #4862, which memoizes
get_style; with both applied the gains stack.Checklist:
Review evidence:
Median of 5 fresh processes per side, Python 3.13, Apple M5:
export_to_markdown()andexport_to_dict()are byte-identical before and after for all 36 files intests/data/docx/sourcesand the generated documents, so no reference data changes.tests/test_backend_msword*.py: 290 passed;test_e2e_docx_conversionsandtest_chart_image_renderingfail the same way on main in my environment (drawingml chart ground truth). ruff check and ruff format are clean.