Enable strict mypy across eucare package - #4
Open
imagirom wants to merge 4 commits into
Open
Conversation
All eucare modules now pass mypy with strict settings (`disallow_untyped_defs = true`, `check_untyped_defs = true`, `no_implicit_optional = true`), so the transitional per-module override mechanism in pyproject.toml can go away. Key changes: - Type annotations added across ~40 modules. Foundational DCEL classes (HalfEdge / Vertex / Face) declare structural links (rev/nex/pre/orig/ dest, any_outgoing, any_side) as non-Optional with a `cast(...)` in __init__, matching the invariant that they are always linked before being read — this cascade fixed hundreds of downstream errors. - Added `@overload` to HalfEdgeGraph.copy and EHEG_from_nx so callers get precise return types instead of tuple-or-not unions. - pyproject.toml: replace permissive transitional block with strict defaults, exclude tests/docs, plus a small follow_imports=skip override for tifffile (uses py3.12 `type` syntax that mypy parses under py3.10). - Added types-PyYAML, types-networkx, types-tqdm, scipy-stubs to dev extras so CI installs the needed stubs. - Fixed a latent bug in eucare.base.nearest_neighbor: the conditional return mixed operator precedence with the tuple so the `return_index=False` branch could not actually be taken; rewrote as two distinct return statements. - Fixed a latent docs example bug in eucare.example_graphs (referenced example_graphs.curved_platonic where example_tilesets.curved_platonic was intended). All 392 existing tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`tests/test_image_to_graph.py` and the intersecting-cylinders tests need `mahotas`/`scikit-image`/`rdp`/`plotly` to import. Previously CI only installed `--extra dev`, so collection failed at import time. Add the necessary extras so the test suite can run. Note: `--extra torch` is intentionally not added; the torch-dependent tests in `test_alternating_flagstones.py` already gate themselves with `pytest.mark.skipif(not HAS_TORCH, ...)`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cuts every cast() in the eucare package without loosening mypy strictness. Three mechanical patterns did most of the work: * HalfEdgeGraph.copy() and Conway operators now use TypeVar so the return type matches the input. ~17 cast() call sites drop away — callers get EuclideanPositionHEG / GeometricHEG back directly. * HalfEdge / Vertex / Face structural links (rev, nex, pre, orig, dest, any_outgoing, any_side) are typed as non-Optional at class level, with __init__ doing `if x is not None: self.x = x`. The attribute literally doesn't exist until linked, so reads before set fail fast at AttributeError instead of silently lying. * CairoRenderer's surface/dc move to the same "set on first use" pattern; width/height fallback rewritten as straight `if None` branches that mypy can narrow on its own. Remaining one-offs: * subdivide_face's `f: Face` was wrong — its body handles `f is None` for border edges; signature now reflects that. * apply_mobius / MobiusTransform.__call__ overloaded on complex vs NDArray so scalar callers don't need to widen-and-cast. * example_graphs.from_tiles overloaded on `add_positions` so callers get GeometricHEG (the True case) without casting. * io.py drops the cast(Any, ...) around float() by switching to a precise isinstance(int|float|np.floating|np.integer) gate. Removed two structurally-unnecessary `assert isinstance(...)`s inside the Conway operator __call__s: they narrowed the TypeVar away and forced callers to re-cast. The runtime invariant they enforced was already implied by the input type. Mypy: clean under the existing strict settings (no rollback). Tests: 388 passed, 1 skipped, 4 deselected (slow). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Merges the Conway shorthand methods from #28/PR#3. Conflict in conway/operators.py was on the __call__ signature: kept our TypeVar G return type, picked up the `set[Face] | Callable[[Face], bool] | None` filter from main. Typed the new conway/methods.py: `_shorthand` returns `Callable[..., GeometricHEG]`; the inner `method` annotated as `(self: GeometricHEG, *args: Any, **kwargs: Any) -> GeometricHEG`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes imagirom/eucare#34.
Summary
eucare/and switches mypy to strict defaults (disallow_untyped_defs = true,check_untyped_defs = true,no_implicit_optional = true,ignore_missing_imports = true) — the transitional per-module override mechanism inpyproject.tomlis gone.HalfEdge/Vertex/Facenow declare structural links (rev/nex/pre/orig/dest,any_outgoing,any_side) as non-Optional with acast(...)in__init__, matching the data-structure invariant that they are linked before being read. This cascade removed hundreds of downstream errors at read sites.@overloadtoHalfEdgeGraph.copyandEHEG_from_nxso callers get a precise return type instead of unpacking a union.tifffile(transitively pulled in byskimage; uses py3.12typesyntax that mypy parses under py3.10).devextra:types-PyYAML,types-networkx,types-tqdm,scipy-stubs.Bugs fixed along the way
eucare.base.nearest_neighborhad a precedence bug — the conditional return mixed,andif/elseso thereturn_index=Falsebranch could not actually be taken. Rewritten as two distinct return statements.eucare.example_graphsreferencedexample_graphs.curved_platonicwhereexample_tilesets.curved_platonicwas intended; would have raised at runtime if the fallback path was taken.Test plan
mypy eucare→Success: no issues found in 42 source filespytest -q --ignore=docs→392 passed, 1 skipped🤖 Generated with Claude Code