defer hatch toml parsing out of configure step#469
Merged
Conversation
…gure() Hatch::configure() previously parsed pyproject.toml and hatch.toml for every workspace folder on every call. Combined with the configuration write lock, this imposed Hatch's per-workspace TOML cost on every user — including the majority who do not use Hatch. Switch to the same lazy-parse pattern pet-poetry and pet-uv already use: - configure() now records the workspace list and clears the parsed cache. It performs no filesystem I/O and is O(workspace_count) PathBuf clones. - Parsing happens lazily inside try_from()/find() on first access per workspace, behind a single state mutex. Parses run outside the lock so concurrent try_from() calls on other workspaces are not serialized by a slow TOML read. - Cache is invalidated on every configure() so TOML edits are picked up on the next workspace/settings change (matching prior behaviour). Result: non-Hatch workspaces never trigger a TOML read at all. Hatch workspaces pay the parse cost once per workspace per process lifetime, inside refresh — where the cost is amortized against the rest of discovery and does not affect configure latency. Tests: - configure_does_no_toml_io_and_defers_parsing - configure_clears_parsed_cache_so_toml_edits_are_picked_up - try_from_lazily_populates_parsed_cache_on_first_call - All existing Hatch tests continue to pass.
Performance Report (Linux) ✅
Legend
|
Performance Report (macOS)
Legend
|
Test Coverage Report (Linux)
Coverage increased! Great work! |
Test Coverage Report (Windows)
Coverage increased! Great work! |
Performance Report (Windows) ➖
Legend
|
There was a problem hiding this comment.
Pull request overview
This PR defers Hatch workspace TOML parsing from configure() to lazy access during environment identification/discovery, aligning Hatch with the locator pattern used by Poetry/Uv and reducing configure-time overhead.
Changes:
- Replaces eager Hatch workspace parsing with a lazily populated
HatchStatecache. - Updates Hatch tests to validate deferred parsing and cache invalidation on configure.
- Adds a Bash
rust-precommit.shscript mirroring the existing PowerShell pre-commit checks.
Show a summary per file
| File | Description |
|---|---|
crates/pet-hatch/src/lib.rs |
Moves Hatch TOML parsing out of configure() and into lazy workspace_entry() access with updated tests. |
scripts/rust-precommit.sh |
Adds macOS/Linux script for running cargo fmt --all and cargo clippy --all -- -D warnings. |
Copilot's findings
- Files reviewed: 1/2 changed files
- Comments generated: 1
…oncurrent configure() Address Copilot review on PR #469. workspace_entry() previously parsed TOMLs outside the lock and re-acquired only to insert via or_insert_with. If configure() landed between the cache miss and the re-acquire, the stale parse would be written back into the just-cleared cache, silently undoing the invalidation. Add a generation counter to HatchState that configure() bumps on every call. workspace_entry() snapshots the generation before parsing and discards its result if the generation changed by the time it re-acquires the lock — looping to re-read the current on-disk state instead. Tests: - configure_bumps_generation_so_workspace_entry_can_detect_invalidation pins the generation-bump invariant. - workspace_entry_concurrent_configure_does_not_leak_stale_parse drives the race with multiple threads and verifies the cache reflects the final on-disk state after the concurrent configures stop.
rchiodo
reviewed
May 28, 2026
rchiodo
reviewed
May 28, 2026
dmitrivMS
previously approved these changes
May 28, 2026
bryanchen-d
previously approved these changes
May 28, 2026
…rkspaces Address rchiodo's verified review comment. The generation guard alone catches the race where configure() runs *during* a parse, but misses the case where configure() removes a workspace *before* workspace_entry snapshots the generation: T1: try_from snapshots state.workspaces = [W, X] T2: configure removes W, generation = N+1 T1: workspace_entry(W) misses cache, snapshots generation = N+1 T1: parses W, re-acquires lock, generation still matches, inserts W W now lives in state.parsed as an orphan until the next configure() clears it. Add a membership check before insert: if the workspace is no longer in state.workspaces, return the parsed value (so the in-flight caller can finish) without caching it. Impact of the prior behaviour was low (cache gets cleared on next configure and new callers never re-iterate the removed workspace), but the membership check keeps the cache invariant clean: parsed only contains entries for currently configured workspaces. Test: workspace_entry_does_not_cache_for_unconfigured_workspace pins the contract directly without needing threads.
pwang347
reviewed
May 28, 2026
pwang347
approved these changes
May 28, 2026
dmitrivMS
approved these changes
May 28, 2026
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 #462
Hatch::configure() previously parsed pyproject.toml and hatch.toml for every workspace folder on every call. That imposed Hatch's per-workspace TOML cost on every user — including the majority who never use Hatch.
This PR switches Hatch to the same lazy-parse pattern pet-poetry and pet-uv already use