Skip to content

feat: add pet-hatch locator for Hatch environments (Fixes #450)#460

Open
karthiknadig wants to merge 6 commits intomainfrom
feature/issue-450
Open

feat: add pet-hatch locator for Hatch environments (Fixes #450)#460
karthiknadig wants to merge 6 commits intomainfrom
feature/issue-450

Conversation

@karthiknadig
Copy link
Copy Markdown
Member

Fixes #450.

Adds a new pet-hatch crate so Hatch-managed virtual environments are no longer misclassified as plain Venv by downstream consumers.

Implementation

Matches Hatch's actual storage layout from src/hatch/env/virtual.py:

  • Default storage: <data_dir>/env/virtual/<project_name>/<project_id>/<venv_name> (3 levels deep, not 2).
  • HATCH_DATA_DIR is honoured and never silently falls back to the platform default when set.
  • Project-local discovery via [tool.hatch.dirs.env].virtual in pyproject.toml or [dirs.env] in hatch.toml. Handles the virtual = ".hatch" example called out in the issue.
  • Hatch locator is inserted before Venv so it claims envs first.
  • LocatorKind::Hatch registered as RefreshStatePersistence::ConfiguredOnly (workspace-driven discovery).

Tests

15 unit tests covering:

  • Default-storage layout matches at exactly 3 levels deep; rejects 2-deep and 4-deep prefixes.
  • HATCH_DATA_DIR semantics — used when set, no fallback to platform default.
  • Project-local discovery via both pyproject.toml and hatch.toml; rejected without dirs.env.virtual config.
  • Per-platform platformdirs defaults (Linux/macOS/Windows).

Replaces #451

This PR replaces #451, which had a fundamental layout bug: it assumed envs are stored 2 levels deep under <data_dir>/env/virtual (<project-hash>/<env-name>). Per Hatch's source, the actual layout is 3 levels deep (<project_name>/<project_id>/<venv_name>), so #451 would fail to detect any real Hatch env in the default storage location.

Adds a new pet-hatch crate that detects Hatch-managed virtual environments so they are no longer misclassified as plain Venv by downstream consumers.

Implementation matches Hatch's actual storage layout from src/hatch/env/virtual.py:

- Default storage: <data_dir>/env/virtual/<project_name>/<project_id>/<venv_name> (3 levels deep)

- HATCH_DATA_DIR env var honoured; never silently falls back to platform default when set

\ example from the issue)

- Locator inserted before Venv so Hatch claims its envs first

- 15 unit tests covering layout depth (rejects 2 / 4 levels), HATCH_DATA_DIR semantics, project-local config, and platform defaults
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Performance Report (Linux) ➖

Metric PR (P50) PR (P95) Baseline (P50) Delta Change
Server Startup 1ms 1ms 1ms 0ms 0%
Full Refresh 56ms 311ms 50ms 6ms 10.0%

Results based on 10 iterations. P50 = median, P95 = 95th percentile.


Legend
  • 🚀 Significant speedup (>100ms faster)
  • ✅ Faster than baseline
  • ➖ No significant change
  • 🔺 Slower than baseline (>100ms)
  • ⚠️ Significant slowdown (>500ms)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Test Coverage Report (Linux)

Metric Value
Current Coverage 77.2%
Base Branch Coverage 76.5%
Delta .7% ✅

Coverage increased! Great work!

@karthiknadig karthiknadig marked this pull request as ready for review May 8, 2026 21:28
@karthiknadig karthiknadig requested a review from Copilot May 8, 2026 21:28
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Performance Report (Windows) ✅

Metric PR (P50) PR (P95) Baseline (P50) Delta Change
Server Startup 9ms 14ms 9ms 0ms 0%
Full Refresh 142ms 2581ms 148ms -6ms -4.1%

Results based on 10 iterations. P50 = median, P95 = 95th percentile.


Legend
  • 🚀 Significant speedup (>100ms faster)
  • ✅ Faster than baseline
  • ➖ No significant change
  • 🔺 Slower than baseline (>100ms)
  • ⚠️ Significant slowdown (>500ms)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Test Coverage Report (Windows)

Metric Value
Current Coverage 75%
Base Branch Coverage 74.45%
Delta 0.55% ✅

Coverage increased! Great work!

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 8, 2026

Performance Report (macOS)

Metric PR (P50) PR (P95) Baseline (P50) Delta
Server Startup 51ms 481ms 71ms -20ms
Full Refresh 97ms 25730ms 147ms -50ms

Results based on 10 iterations. P50 = median, P95 = 95th percentile.


Legend
  • 🚀 Significant speedup (>100ms faster)
  • ✅ Faster than baseline
  • ➖ No significant change
  • 🔺 Slower than baseline (>100ms)
  • ⚠️ Significant slowdown (>500ms)

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds first-class Hatch environment detection to PET by introducing a new pet-hatch locator and wiring it into the existing locator chain so Hatch-managed venvs are classified as Hatch rather than the generic Venv.

Changes:

  • Add new pet-hatch crate implementing a Hatch locator with default-storage and workspace-configured discovery/identification.
  • Register LocatorKind::Hatch and PythonEnvironmentKind::Hatch, and insert Hatch into the locator chain before Venv.
  • Update refresh-state contract expectations to mark Hatch as RefreshStatePersistence::ConfiguredOnly.
Show a summary per file
File Description
crates/pet/src/locators.rs Adds the Hatch locator into the ordered locator chain before Venv.
crates/pet/src/jsonrpc.rs Updates the refresh-state contract test to include LocatorKind::Hatch.
crates/pet/Cargo.toml Adds pet-hatch as a dependency of the main pet crate.
crates/pet-hatch/src/lib.rs Implements Hatch detection (default storage + workspace-configured) and adds unit tests.
crates/pet-hatch/Cargo.toml Defines the new pet-hatch crate and its dependencies.
crates/pet-core/src/python_environment.rs Adds PythonEnvironmentKind::Hatch.
crates/pet-core/src/lib.rs Adds LocatorKind::Hatch.
Cargo.lock Records the new pet-hatch crate and dependency graph updates.

Copilot's findings

  • Files reviewed: 7/8 changed files
  • Comments generated: 2

Comment thread crates/pet-hatch/src/lib.rs Outdated
Comment thread crates/pet-hatch/src/lib.rs
- Cache resolved virtual dirs in configure() so try_from() does not re-parse pyproject.toml / hatch.toml on every executable identification attempt.

- Expand ~ (and \C:\Users\kanadig/\) in configured dirs.env.virtual values via pet_fs::path::expand_path so values like '~/.virtualenvs' resolve against the user home.

- Build the new cache outside the workspace_virtual_dirs lock to keep disk I/O out of the critical section.

- Serialize env-var-mutating tests via a per-binary mutex so cargo's default multi-threaded harness cannot race.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 7/8 changed files
  • Comments generated: 1

Comment thread crates/pet/src/locators.rs
dmitrivMS
dmitrivMS previously approved these changes May 8, 2026
A Hatch project can configure dirs.env.virtual = '~/.virtualenvs' (or any path that overlaps with WORKON_HOME). With Hatch placed after VirtualEnvWrapper, those envs would be claimed as VirtualEnvWrapper before Hatch ever saw them. Move Hatch ahead so it gets first claim on workspace-configured envs.
dmitrivMS
dmitrivMS previously approved these changes May 8, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 7/8 changed files
  • Comments generated: 3

Comment thread crates/pet-hatch/src/lib.rs Outdated
Comment thread crates/pet-hatch/src/lib.rs Outdated
Comment thread crates/pet-hatch/src/lib.rs Outdated
- get_default_virtual_dir(): no longer requires the path to exist on disk at construction time. The long-lived locator graph is built once at server startup; users may install Hatch (or create the first env) later in the same process. find() now re-checks existence at call time so newly-created envs are discovered without a restart.

- try_from(): do the cheap path-shape classification (default storage / configured workspace dirs) BEFORE reading pyvenv.cfg, so non-Hatch venvs flowing through the locator chain do not pay an extra filesystem read.

- try_from(): inspect the workspace cache under the lock and capture the match instead of cloning the entire cache on every identification attempt.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 7/8 changed files
  • Comments generated: 3

Comment thread crates/pet-hatch/src/lib.rs Outdated
Comment thread crates/pet-hatch/src/lib.rs Outdated
Comment thread crates/pet-hatch/src/lib.rs Outdated
- resolve_project_virtual_dirs(): cache configured paths regardless of whether the directory exists on disk yet. A user may configure 'virtual = .hatch' and create the env later in this process lifetime; we now recognise it without requiring the client to re-send 'configure'. find_envs_in_flat_dir() already handles missing dirs by returning empty.

- default_virtual_dir field doc: corrected to reflect that the path may not exist on disk; existence is rechecked at find() time.

- Module docs: aligned with implementation. All workspace-configured 'dirs.env.virtual' paths use the flat layout, regardless of whether they are relative, absolute, or use ~ expansion.
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 7/8 changed files
  • Comments generated: 1

Comment thread crates/pet-hatch/Cargo.toml Outdated
Use serde 1.0.152 to match the dominant version string used across the workspace. Both 1.0.152 and 1.0.226 are semver-compatible and resolve to the same compiled crate, but matching the workspace style avoids inconsistency.

Keep toml at 0.9.7 (already used by pet-uv).
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 7/8 changed files
  • Comments generated: 2

Comment on lines +241 to +250
fn get_default_virtual_dir(environment: &dyn Environment) -> Option<PathBuf> {
// If HATCH_DATA_DIR is set and non-empty, Hatch *only* uses that location
// — it never falls back to the platform default. Mirror that behaviour.
// Do not fall through to platform defaults, or we'd risk attributing
// platform-default envs to Hatch when the user has redirected Hatch
// elsewhere.
if let Some(custom) = environment.get_env_var("HATCH_DATA_DIR".to_string()) {
if !custom.is_empty() {
return Some(norm_case(append_virtual_subdir(PathBuf::from(custom))));
}
Comment on lines +388 to +401
fn resolve_project_virtual_dirs(workspace: &Path) -> Vec<PathBuf> {
let mut dirs = Vec::new();
for raw in read_configured_virtual_paths(workspace) {
// Expand ~ and ${HOME}/${USERNAME} so configured values like
// "~/.virtualenvs" resolve to the user home rather than being
// joined onto the workspace as a relative path.
let expanded = expand_path(PathBuf::from(&raw));
let resolved = if expanded.is_absolute() {
expanded
} else {
workspace.join(expanded)
};
dirs.push(norm_case(resolved));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Hatch environment detection (new pet-hatch locator)

4 participants