Skip to content

Conversation

@nichmor
Copy link
Contributor

@nichmor nichmor commented Dec 19, 2025

Replace hash-based validation for path-based PyPI packages with metadata-based validation. Instead of computing and storing a hash of pyproject.toml, setup.py, and setup.cfg, we now:

  1. Don't store any hash in the lock file for path-based packages
  2. During satisfiability checks, parse pyproject.toml and compare the name and version with the locked metadata

This approach is similar to how PR #5011 removed input_hash for conda packages. The metadata comparison is more explicit about what changed (name vs version) rather than just indicating "something changed via hash mismatch".

Changes:

  • pypi.rs: Stop computing and storing hash for SourceDist::Path and SourceDist::Directory
  • satisfiability/mod.rs: Replace PypiSourceTreeHashable hash comparison with metadata parsing and comparison
  • Add PypiSourceMetadataMismatch and PypiSourceMetadataError types
  • Remove SourceTreeHashMismatch type and related imports

Description

Fixes #{issue}

How Has This Been Tested?

AI Disclosure

  • This PR contains AI-generated content.
    • I have tested any AI-generated content in my PR.
    • I take responsibility for any AI-generated content in my PR.

Tools: {e.g., Claude, Codex, GitHub Copilot, ChatGPT, etc.}

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added sufficient tests to cover my changes.
  • I have verified that changes that would impact the JSON schema have been made in schema/model.py.

Copy link
Contributor

@baszalmstra baszalmstra left a comment

Choose a reason for hiding this comment

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

Nice! This is a good start but its not enough, we need to also compare the requirements, required python version optional dependencies, etc. Furthermore, some fields can be dynamic which means we need to invoke python, and we might also have to deal with setup.py only sources.

@tdejager
Copy link
Contributor

Nice! This is a good start but its not enough, we need to also compare the requirements, required python version optional dependencies, etc. Furthermore, some fields can be dynamic which means we need to invoke python, and we might also have to deal with setup.py only sources.

Indeed! It might be good to let Claude explore the uv codebase as well for inspiration, although if I recall their satisfiability check is less extensive, they regenerate more often.

@nichmor nichmor force-pushed the claude/remove-pypi-input-hash-9zwnN branch from 2b7191e to 08b5efe Compare January 5, 2026 15:55
@nichmor
Copy link
Contributor Author

nichmor commented Jan 6, 2026

Attaching benchmarks here:

Dynamic Metadata (minimal-project with hatch_build.py, setup-project with setup.py)

Main branch:

Command Mean [s] Min [s] Max [s] Relative
/tmp/pixi-main install --locked 1.070 ± 0.017 1.042 1.089 1.00

This branch:

Command Mean [s] Min [s] Max [s] Relative
/tmp/pixi-branch install --locked 1.140 ± 0.048 1.076 1.199 1.00

Static Metadata (minimal-project without hatch_build.py)

Main branch:

Command Mean [s] Min [s] Max [s] Relative
/tmp/pixi-main install --locked 1.196 ± 0.134 1.074 1.365 1.00

This branch:

Command Mean [s] Min [s] Max [s] Relative
/tmp/pixi-branch install --locked 1.309 ± 0.242 1.085 1.668 1.00

@nichmor nichmor added the test:extra_slow Run the extra slow tests label Jan 7, 2026
@nichmor nichmor marked this pull request as ready for review January 7, 2026 11:07
Comment on lines 33 to 48
pub struct EnvironmentBuildCache {
/// Lazily initialized build dispatch dependencies (interpreter, env, etc.)
pub lazy_build_dispatch_deps: LazyBuildDispatchDependencies,
/// Optional conda prefix updater (created during satisfiability checking)
pub conda_prefix_updater: OnceCell<CondaPrefixUpdater>,
/// Cached registry client (with Online connectivity)
pub registry_client: OnceCell<Arc<RegistryClient>>,
/// Cached index locations
pub index_locations: OnceCell<IndexLocations>,
/// Cached build options
pub build_options: OnceCell<BuildOptions>,
/// Cached flat index (populated lazily when needed, async initialization)
pub flat_index: AsyncOnceCell<FlatIndex>,
/// Cached dependency metadata
pub dependency_metadata: OnceCell<DependencyMetadata>,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure we need to cache most of these structs, as the most interesting things are chached to disk. I think there are non in-memory caches, only the InFlight which is not here and maybe in the RegistryClient. I'd rather not introduce another struct containing too many OnceCell's. Maybe we can figure out what we need to keep? I think the LazyBuildDispatch is something we should cache?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

removed all other fields except conda_prefix_updater. I think it's ok to keep it, because it contains an in-memory result ( OnceCell field) of installing a prefix environment.

"**/*.ipynb",
"**/docs_hooks.py",
"scripts/test_native_certs.py",
"examples/dynamic-source-deps/minimal-project/hatch_build.py",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this will be removed when we remove dynamic-source-deps

@@ -0,0 +1,19 @@
[workspace]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's added just to show how to test it

@ruben-arts
Copy link
Contributor

Test plan:

  • Run all examples pixi run run-all-examples
  • Run all the pypi path examples:
    • with a clean env
    • not rebuilding on a second install
    • rebuilding on a file change
    • not rebuilding on editable mode file change, unless pyproject.toml/pixi.toml change
  • Try with real world project like py-rattler or boltons etc.
  • Start with an environment build by a previous version of pixi and check if the rebuild logic still works on that "dirty" environment.

If all of these cases succeed next to the integration and unit tests I would say it is ready to merge, I did a similar thing for the conda input-hash removal.

@nichmor
Copy link
Contributor Author

nichmor commented Jan 7, 2026

added some stuff to cache reading static metadata, these are the new results ( with cache clean on every run )

Dynamic Metadata (minimal-project with hatch_build.py, setup-project with setup.py)

Main branch:

Command Mean [s] Min [s] Max [s] Relative
/tmp/pixi-main install --locked 5.646 ± 0.469 5.184 6.173 1.00

This branch:

Command Mean [s] Min [s] Max [s] Relative
/tmp/pixi-branch install --locked 6.260 ± 0.192 6.019 6.544 1.00

Static Metadata (minimal-project without hatch_build.py)

Main branch:

Command Mean [s] Min [s] Max [s] Relative
/tmp/pixi-main install --locked 5.468 ± 0.249 5.234 5.805 1.00

This branch:

Command Mean [s] Min [s] Max [s] Relative
/tmp/pixi-branch install --locked 6.523 ± 0.884 5.987 8.082 1.00

Comment on lines +102 to +110
if !current.is_version_dynamic
&& let Some(current_version) = &current.version
&& &locked.version != current_version
{
return Some(MetadataMismatch::Version {
locked: locked.version.clone(),
current: current_version.clone(),
});
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This needs some extra logic to work correctly when switching between dynamic and non-dynamic versions.

/// Cache for build-related resources that can be shared between
/// satisfiability checking and PyPI resolution.
#[derive(Default)]
pub struct EnvironmentBuildCache {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe name it something with PyPI to differentiate from pixi build?

@tdejager
Copy link
Contributor

tdejager commented Jan 8, 2026

Might be good to user-test if hatch-vcs is working as expected :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New features lock file pypi Issue related to PyPI dependencies test:extra_slow Run the extra slow tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants