Skip to content

When validating lockfile, take account of indexes defined as sources in path dependencies#14003

Closed
jtfmumm wants to merge 4 commits intomainfrom
jtfm/custom-indexes-lockfile
Closed

When validating lockfile, take account of indexes defined as sources in path dependencies#14003
jtfmumm wants to merge 4 commits intomainfrom
jtfm/custom-indexes-lockfile

Conversation

@jtfmumm
Copy link
Contributor

@jtfmumm jtfmumm commented Jun 12, 2025

As described in #11419, when an index was defined as a source in a path dependency (and that index ended up in the lockfile), it would cause lockfile validation to incorrectly determine that the file had changed. This PR adds those indexes to the list uv uses to validate.

This adds those path dependency source indexes as a new field on Workspace (including from transitive path dependencies). It includes cycle detection to handle the case of circular path dependencies.

This includes test for explicit indexes in a path dependency, a combination of explicit and non-explicit, path dependencies without indexes defined, nested (transitive) path dependencies, and circular path dependencies. The circular path dependency test does not hang (indicating successful cycle detection) but lockfile validation still indicates the file has changed. This is not a change from main so I have marked this as a TODO.

Closes #11419

@jtfmumm jtfmumm added the bug Something isn't working label Jun 12, 2025

// Canonicalize path to compare symlinks and relative paths correctly
let Ok(canonical_path) = dep_path.canonicalize() else {
debug!(
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've taken a permissive approach here since we are checking for pyproject.toml files defined in path dependencies, and only for the purposes of validation. That's why I've chosen to log problems canonicalizing and parsing them and then to continue.

}
Err(e) => {
debug!(
"Failed to read `pyproject.toml` in path dependency `{}`: {}",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

See comment above

false,
))
})
.or(Some(Cow::Borrowed(index_locations)))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm using a Cow here because the common case will probably be to have no path dependencies. This avoids cloning in that common case

@jtfmumm jtfmumm force-pushed the jtfm/custom-indexes-lockfile branch from 956031d to b11727c Compare June 12, 2025 18:32
@jtfmumm jtfmumm marked this pull request as ready for review June 12, 2025 19:31
@jtfmumm jtfmumm force-pushed the jtfm/custom-indexes-lockfile branch from b11727c to fbd3700 Compare June 13, 2025 08:07
@zanieb zanieb requested a review from konstin June 13, 2025 11:41
{
for source_list in sources.inner().values() {
for source in source_list.iter() {
if let Source::Path { path, .. } = source {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm currently only handling the Path case from the issue. We could also fetch Source::Git and Source::Url pyprojects but I wasn't sure if we wanted to do that here.

@jtfmumm jtfmumm force-pushed the jtfm/custom-indexes-lockfile branch from fbd3700 to f2d707b Compare June 15, 2025 10:38
}

/// Parses a `pyproject.toml` file from a path.
fn pyproject_toml_from_path(pyproject_path: PathBuf) -> Result<PyProjectToml, WorkspaceError> {
Copy link
Member

Choose a reason for hiding this comment

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

We should avoid reparsing pyproject.toml, this is an expensive operation when it runs as part of a noop uv run. See #12096 for context and benchmarking instructions.

Can we reuse the parsed file through using the workspace cache around collect_members_only, or alternatively collect this information after workspace discovery so we can read from a warm cache of workspace members?

@jtfmumm jtfmumm temporarily deployed to uv-test-registries June 19, 2025 11:42 — with GitHub Actions Inactive
@jtfmumm jtfmumm temporarily deployed to uv-test-publish June 19, 2025 11:43 — with GitHub Actions Inactive
@jtfmumm jtfmumm force-pushed the jtfm/custom-indexes-lockfile branch from 9db74dd to 9e5c16d Compare June 19, 2025 13:07
@jtfmumm jtfmumm temporarily deployed to uv-test-registries June 19, 2025 13:10 — with GitHub Actions Inactive
@charliermarsh
Copy link
Member

Could we instead solve this by adding any explicit index assignments to the lockfile? We already write the normalized requirements (requires-dev), so this would just be an extension of that behavior. Taking the indexes into account in the way it's outlined here seems like an abstraction leak.

@jtfmumm jtfmumm closed this Jul 25, 2025
charliermarsh added a commit that referenced this pull request Jul 25, 2025
…e explicit indexes (#14876)

## Summary

This is an alternative to #14003 that takes advantage of the fact that
we already validate that the requirements are up-to-date when validating
the lockfile, and the requirements for pinned requirements include the
index itself -- so rather than collecting all the explicit indexes
upfront, we can just add them to the available list as we iterate over
the lockfile's dependency graph.

This gets all the tests passing from that PR, but with ~no performance
impact and a much less invasive change. It also gets the "circular
dependency" test passing, which is marked with a TODO in that PR.

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dependencies with custom indexes lead to lockfile always being outdated

3 participants