MAINT: Split PdfDocCommon._flatten - #4070
Conversation
_flatten mixed argument-defaulting, page-tree-root resolution, node-type classification, /Pages recursion and /Page construction in one ~135-line method (cyclomatic complexity 30). Extract three private helpers, leaving _flatten as a small orchestrator: - _page_tree_node_type: classify a node as "/Pages" or "/Page", including the no-/Type strict-mode heuristic. - _flatten_page_tree_node: propagate inheritable attributes and recurse into /Kids, with the cycle checks and entry-limit enforcement. - _flatten_leaf_page: build the PageObject and append it to flattened_pages. Behaviour is unchanged. Radon complexity drops from D(30) to _flatten B(10), _flatten_page_tree_node C(13), _page_tree_node_type B(6), _flatten_leaf_page A(4). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> (cherry picked from commit b45900b)
Three behaviours of PdfDocCommon._flatten that are wrong today and are marked xfail until the traversal is reworked: - A page tree deeper than the interpreter recursion limit raises RecursionError before Configuration.page_tree_maximum_depth can apply. - A document catalog without /Pages raises AttributeError from .get_object() on None instead of PdfReadError. - A mid-traversal error leaves self.flattened_pages as a truncated, non-None list, so a later page access silently serves the wrong count instead of re-raising. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The changes are a mechanical refactor plus xfail regression tests, with no functional behavior changes introduced in the page-tree processing logic.
Pull request overview
This PR is a maintenance-focused refactor of PdfDocCommon._flatten, splitting portions of its logic into smaller helper methods to make subsequent changes (notably the iterative-flatten work in #4061) easier to review and implement without changing current traversal behavior.
Changes:
- Split
_flattenby extracting page-tree node classification into_page_tree_node_type. - Extract
/Pagesnode handling into_flatten_page_tree_nodeand leaf/Pageconstruction into_flatten_leaf_page. - Add three
xfailregression tests documenting known current limitations/bugs in recursive flattening and error handling.
File summaries
| File | Description |
|---|---|
tests/test_doc_common.py |
Adds xfail tests capturing current recursive-depth and error-handling shortcomings to lock in expected fixes for the follow-up work. |
pypdf/_doc_common.py |
Refactors _flatten into helper methods without changing the current recursive traversal approach. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4070 +/- ##
=======================================
Coverage 98.11% 98.12%
=======================================
Files 59 59
Lines 11489 11495 +6
Branches 2150 2150
=======================================
+ Hits 11273 11280 +7
Misses 122 122
+ Partials 94 93 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🟡 Changes recommended
Address the outstanding refactoring and Ruff formatting comments before approval.
Get a fresh assessment by requesting another Copilot review.
Review details
Suppressed comments (1)
pypdf/_doc_common.py:1383
_flattenalready retrieves the configuration at line 1311 for the depth check, so this adds a secondContextVar.get()for every/Pagesnode. Pass the existing configuration into this helper (or otherwise share it) to avoid the per-node lookup introduced by the refactor.
configuration = get_configuration()
- Files reviewed: 2/2 changed files
- Comments generated: 4
- Review effort level: Lite
There was a problem hiding this comment.
🔵 Needs a closer look
Fix the test’s lint violation by adding spaces around the arithmetic operator.
Review details
Suppressed comments (1)
tests/test_doc_common.py:776
- This expression omits the whitespace required by the repository's Ruff configuration (
select = ["ALL"]inpyproject.toml:151-153), so the new test fails linting. Add spaces around the arithmetic operator.
with apply_configuration(page_tree_maximum_depth=depth+1):
- Files reviewed: 2/2 changed files
- Comments generated: 0 new
- Review effort level: Lite
Split _flatten into three functions to make it easier to read:
_flatten_page_tree_node_flatten_leaf_pageThis makes it easier to understand the function.
Meta
This PR is a first step towards #4061 which is easier to review as it doesn't change the way data is processed, only the structure of the code by splitting _flatten