Skip to content

♻️ refactor(core): reduce duplication and migrate to ty#548

Merged
gaborbernat merged 2 commits intotox-dev:mainfrom
gaborbernat:refactor/reduce-duplication-migrate-ty
Mar 3, 2026
Merged

♻️ refactor(core): reduce duplication and migrate to ty#548
gaborbernat merged 2 commits intotox-dev:mainfrom
gaborbernat:refactor/reduce-duplication-migrate-ty

Conversation

@gaborbernat
Copy link
Copy Markdown
Member

@gaborbernat gaborbernat commented Mar 3, 2026

Several renderers had duplicated logic that made changes error-prone and maintenance harder. The freeze renderer reimplemented the same recursive traversal as the non-unicode text renderer, differing only in the frozen flag and bullet string. The graphviz forward and reverse graph builders each contained their own BFS depth computation with mirrored if/else branches for depth-limited vs unlimited traversal. Edge label derivation (version_spec or "any") was inlined identically across graphviz and mermaid renderers. These duplications meant any bug fix or behavior change had to be applied in multiple places, risking divergence.

♻️ The text/freeze unification extracts _render_text_simple with frozen and bullet parameters, letting freeze.py drop from 40 lines to a single function call. The graphviz cleanup pulls BFS into a shared _compute_reachable_depths helper and collapses each builder's branching into a single iteration that checks visited membership when depth-limiting is active. edge_label properties on DistPackage and ReqPackage replace the repeated inline patterns across graphviz and mermaid. The reverse() methods in PackageDAG and ReversedPackageDAG used next(p for p in m if p.key == v.key) for node deduplication — an O(n) linear scan per edge — replaced with a dict index for O(1) lookups.

PackageDAG.__getitem__ previously used dict.get(), silently returning None on missing keys and violating the Mapping protocol contract. It now delegates to dict.__getitem__ which raises KeyError, matching what callers expect through Mapping.get(). The type checker has been migrated from mypy to ty, and stale type: ignore comments that ty no longer needs have been cleaned up.

The text and freeze renderers duplicated the same recursive traversal
logic. Extracting _render_text_simple with frozen/bullet parameters
lets freeze.py reuse text.py instead of maintaining its own copy.
Similarly, both graphviz graph builders duplicated BFS depth computation
and had mirrored if/else branches for depth-limited vs unlimited; a
shared _compute_reachable_depths helper eliminates that.

Edge label derivation was repeated inline across graphviz and mermaid
renderers. Adding edge_label properties to DistPackage and ReqPackage
centralizes this logic.

The reverse() methods used O(n) linear scans via next(p for p in m ...)
to deduplicate nodes. A dict index makes this O(1) per edge.

__getitem__ returned None on missing keys via dict.get(), violating the
Mapping protocol. Changed to dict.__getitem__ which raises KeyError as
callers expect via Mapping.get().

Replaced mypy with ty following the tox-dev/tox pattern, cleaning up
stale type: ignore comments that ty no longer needs.

Signed-off-by: Bernát Gábor <bgabor8@bloomberg.net>
Use setdefault to collapse the walrus+if node deduplication pattern in
both reverse() methods. Remove the _render_text_without_unicode wrapper
now that _render_text_simple handles both cases. Inline the dep_keys
helper into a set comprehension and rename it to _get_root_keys since
that better describes what callers actually need.

Reorder module-level functions and methods so callers appear before
callees — public entry points at the top, helpers below — matching
topological usage order.
@gaborbernat gaborbernat enabled auto-merge (squash) March 3, 2026 05:35
@gaborbernat gaborbernat merged commit eaa364b into tox-dev:main Mar 3, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant