mirror of
https://github.com/astral-sh/uv.git
synced 2025-08-04 10:58:28 +00:00
Merge 9e5c16d833
into efc361223c
This commit is contained in:
commit
153f7d0159
4 changed files with 510 additions and 13 deletions
|
@ -1261,7 +1261,7 @@ impl Lock {
|
|||
build_constraints: &[Requirement],
|
||||
dependency_groups: &BTreeMap<GroupName, Vec<Requirement>>,
|
||||
dependency_metadata: &DependencyMetadata,
|
||||
indexes: Option<&IndexLocations>,
|
||||
indexes: Option<Cow<'_, IndexLocations>>,
|
||||
tags: &Tags,
|
||||
hasher: &HashStrategy,
|
||||
index: &InMemoryIndex,
|
||||
|
@ -1427,7 +1427,7 @@ impl Lock {
|
|||
}
|
||||
|
||||
// Collect the set of available indexes (both `--index-url` and `--find-links` entries).
|
||||
let remotes = indexes.map(|locations| {
|
||||
let remotes = indexes.as_ref().map(|locations| {
|
||||
locations
|
||||
.allowed_indexes()
|
||||
.into_iter()
|
||||
|
@ -1440,7 +1440,7 @@ impl Lock {
|
|||
.collect::<BTreeSet<_>>()
|
||||
});
|
||||
|
||||
let locals = indexes.map(|locations| {
|
||||
let locals = indexes.as_ref().map(|locations| {
|
||||
locations
|
||||
.allowed_indexes()
|
||||
.into_iter()
|
||||
|
@ -1473,6 +1473,8 @@ impl Lock {
|
|||
queue.push_back(root);
|
||||
}
|
||||
|
||||
// Unlike path dependencies, Git dependencies are immutable. Their sources cannot change
|
||||
// without the hashes changing, so we know their indexes are still present.
|
||||
while let Some(package) = queue.pop_front() {
|
||||
// If the lockfile references an index that was not provided, we can't validate it.
|
||||
if let Source::Registry(index) = &package.id.source {
|
||||
|
|
|
@ -20,7 +20,7 @@ use uv_warnings::warn_user_once;
|
|||
|
||||
use crate::dependency_groups::{DependencyGroupError, FlatDependencyGroup, FlatDependencyGroups};
|
||||
use crate::pyproject::{
|
||||
Project, PyProjectToml, PyprojectTomlError, Sources, ToolUvSources, ToolUvWorkspace,
|
||||
Project, PyProjectToml, PyprojectTomlError, Source, Sources, ToolUvSources, ToolUvWorkspace,
|
||||
};
|
||||
|
||||
type WorkspaceMembers = Arc<BTreeMap<PackageName, WorkspaceMember>>;
|
||||
|
@ -769,9 +769,7 @@ impl Workspace {
|
|||
// project. If it is the current project, it is added as such in the next step.
|
||||
if let Some(project) = &workspace_pyproject_toml.project {
|
||||
let pyproject_path = workspace_root.join("pyproject.toml");
|
||||
let contents = fs_err::read_to_string(&pyproject_path)?;
|
||||
let pyproject_toml = PyProjectToml::from_string(contents)
|
||||
.map_err(|err| WorkspaceError::Toml(pyproject_path.clone(), Box::new(err)))?;
|
||||
let pyproject_toml = pyproject_toml_from_path(pyproject_path.clone())?;
|
||||
|
||||
debug!(
|
||||
"Adding root workspace member: `{}`",
|
||||
|
@ -935,6 +933,85 @@ impl Workspace {
|
|||
}
|
||||
Ok(workspace_members)
|
||||
}
|
||||
|
||||
/// Collects indexes provided as sources in (transitive) path dependencies that
|
||||
/// have not already been defined in the workspace.
|
||||
pub fn collect_path_dependency_source_indexes(&self) -> Vec<Index> {
|
||||
let mut dependency_indexes = FxHashSet::default();
|
||||
let mut seen = FxHashSet::default();
|
||||
|
||||
// We will only add indexes if we have not already seen the URLs.
|
||||
let known_urls: FxHashSet<_> = self.indexes.iter().map(Index::url).collect();
|
||||
|
||||
let mut pyprojects = std::collections::VecDeque::new();
|
||||
pyprojects.push_back((self.install_path.clone(), self.pyproject_toml.clone()));
|
||||
|
||||
while let Some((base_path, pyproject)) = pyprojects.pop_front() {
|
||||
if let Some(tool_uv_sources) = pyproject
|
||||
.tool
|
||||
.as_ref()
|
||||
.and_then(|tool| tool.uv.as_ref())
|
||||
.and_then(|uv| uv.sources.as_ref())
|
||||
{
|
||||
for sources in tool_uv_sources.inner().values() {
|
||||
for source in sources.iter() {
|
||||
if let Source::Path { path, .. } = source {
|
||||
let dep_path = if path.as_ref().is_absolute() {
|
||||
path.as_ref().to_path_buf()
|
||||
} else {
|
||||
base_path.join(path)
|
||||
};
|
||||
|
||||
// Canonicalize path to compare symlinks and relative paths correctly
|
||||
let Ok(canonical_path) = dep_path.canonicalize() else {
|
||||
debug!(
|
||||
"Failed to canonicalize path dependency path: {}",
|
||||
dep_path.display()
|
||||
);
|
||||
continue;
|
||||
};
|
||||
|
||||
// Prevent infinite loops from circular dependencies
|
||||
if !seen.insert(canonical_path.clone()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let dep_pyproject_path = canonical_path.join("pyproject.toml");
|
||||
|
||||
match pyproject_toml_from_path(dep_pyproject_path.clone()) {
|
||||
Ok(dep_pyproject) => {
|
||||
if let Some(dep_indexes) = dep_pyproject
|
||||
.tool
|
||||
.as_ref()
|
||||
.and_then(|tool| tool.uv.as_ref())
|
||||
.and_then(|uv| uv.index.as_ref())
|
||||
{
|
||||
dependency_indexes.extend(
|
||||
dep_indexes
|
||||
.iter()
|
||||
.filter(|idx| !known_urls.contains(idx.url()))
|
||||
.cloned(),
|
||||
);
|
||||
}
|
||||
|
||||
pyprojects.push_back((canonical_path, dep_pyproject));
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Failed to read `pyproject.toml` in path dependency `{}`: {}",
|
||||
dep_pyproject_path.display(),
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependency_indexes.into_iter().collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
/// A project in a workspace.
|
||||
|
@ -1606,6 +1683,13 @@ impl VirtualProject {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parses a `pyproject.toml` file from a path.
|
||||
fn pyproject_toml_from_path(pyproject_path: PathBuf) -> Result<PyProjectToml, WorkspaceError> {
|
||||
let contents = fs_err::read_to_string(&pyproject_path)?;
|
||||
PyProjectToml::from_string(contents)
|
||||
.map_err(|err| WorkspaceError::Toml(pyproject_path, Box::new(err)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(unix)] // Avoid path escaping for the unit tests
|
||||
mod tests {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(clippy::single_match_else)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
|
@ -685,7 +686,7 @@ async fn do_lock(
|
|||
let existing_lock = if let Some(existing_lock) = existing_lock {
|
||||
match ValidatedLock::validate(
|
||||
existing_lock,
|
||||
target.install_path(),
|
||||
target,
|
||||
packages,
|
||||
&members,
|
||||
&requirements,
|
||||
|
@ -898,7 +899,7 @@ impl ValidatedLock {
|
|||
/// Validate a [`Lock`] against the workspace requirements.
|
||||
async fn validate<Context: BuildContext>(
|
||||
lock: Lock,
|
||||
install_path: &Path,
|
||||
target: LockTarget<'_>,
|
||||
packages: &BTreeMap<PackageName, WorkspaceMember>,
|
||||
members: &[PackageName],
|
||||
requirements: &[Requirement],
|
||||
|
@ -1100,16 +1101,32 @@ impl ValidatedLock {
|
|||
// However, if _no_ indexes were provided, we assume that the user wants to reuse the existing
|
||||
// distributions, even though a failure to reuse the lockfile will result in re-resolving
|
||||
// against PyPI by default.
|
||||
let indexes = if index_locations.is_none() {
|
||||
let validation_indexes = if index_locations.is_none() {
|
||||
None
|
||||
} else {
|
||||
Some(index_locations)
|
||||
// If indexes were defined as sources in path dependencies, add them to the
|
||||
// index locations to use for validation.
|
||||
if let LockTarget::Workspace(workspace) = target {
|
||||
let path_dependency_source_indexes =
|
||||
workspace.collect_path_dependency_source_indexes();
|
||||
if path_dependency_source_indexes.is_empty() {
|
||||
Some(Cow::Borrowed(index_locations))
|
||||
} else {
|
||||
Some(Cow::Owned(index_locations.clone().combine(
|
||||
path_dependency_source_indexes,
|
||||
Vec::new(),
|
||||
false,
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Some(Cow::Borrowed(index_locations))
|
||||
}
|
||||
};
|
||||
|
||||
// Determine whether the lockfile satisfies the workspace requirements.
|
||||
match lock
|
||||
.satisfies(
|
||||
install_path,
|
||||
target.install_path(),
|
||||
packages,
|
||||
members,
|
||||
requirements,
|
||||
|
@ -1118,7 +1135,7 @@ impl ValidatedLock {
|
|||
build_constraints,
|
||||
dependency_groups,
|
||||
dependency_metadata,
|
||||
indexes,
|
||||
validation_indexes,
|
||||
interpreter.tags()?,
|
||||
hasher,
|
||||
index,
|
||||
|
|
|
@ -28257,3 +28257,397 @@ fn lock_conflict_for_disjoint_platform() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that lockfile validation includes explicit indexes from path dependencies.
|
||||
/// <https://github.com/astral-sh/uv/issues/11419>
|
||||
#[test]
|
||||
fn lock_path_dependency_explicit_index() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create the path dependency with explicit index
|
||||
let pkg_a = context.temp_dir.child("pkg_a");
|
||||
fs_err::create_dir_all(&pkg_a)?;
|
||||
|
||||
let pyproject_toml = pkg_a.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-a"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["iniconfig"]
|
||||
|
||||
[tool.uv.sources]
|
||||
iniconfig = { index = "inner-index" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "inner-index"
|
||||
url = "https://pypi-proxy.fly.dev/simple"
|
||||
explicit = true
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create a project that depends on pkg_a
|
||||
let pkg_b = context.temp_dir.child("pkg_b");
|
||||
fs_err::create_dir_all(&pkg_b)?;
|
||||
|
||||
let pyproject_toml = pkg_b.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-b"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["pkg-a"]
|
||||
|
||||
[tool.uv.sources]
|
||||
pkg-a = { path = "../pkg_a/", editable = true }
|
||||
black = { index = "outer-index" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "outer-index"
|
||||
url = "https://outer-index.com/simple"
|
||||
explicit = true
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().current_dir(&pkg_b), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 3 packages in [TIME]
|
||||
");
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--check").current_dir(&pkg_b), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 3 packages in [TIME]
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that lockfile validation works correctly when path dependency has
|
||||
/// both explicit and non-explicit indexes.
|
||||
#[test]
|
||||
fn lock_path_dependency_mixed_indexes() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create the path dependency with both explicit and non-explicit indexes
|
||||
let pkg_a = context.temp_dir.child("pkg_a");
|
||||
fs_err::create_dir_all(&pkg_a)?;
|
||||
|
||||
let pyproject_toml = pkg_a.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-a"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["iniconfig", "anyio"]
|
||||
|
||||
[tool.uv.sources]
|
||||
iniconfig = { index = "explicit-index" }
|
||||
anyio = { index = "non-explicit-index" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "non-explicit-index"
|
||||
url = "https://pypi-proxy.fly.dev/simple"
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "explicit-index"
|
||||
url = "https://pypi.org/simple"
|
||||
explicit = true
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create a project that depends on pkg_a
|
||||
let pkg_b = context.temp_dir.child("pkg_b");
|
||||
fs_err::create_dir_all(&pkg_b)?;
|
||||
|
||||
let pyproject_toml = pkg_b.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-b"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["pkg-a"]
|
||||
|
||||
[tool.uv.sources]
|
||||
pkg-a = { path = "../pkg_a/", editable = true }
|
||||
black = { index = "outer-index" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "outer-index"
|
||||
url = "https://outer-index.com/simple"
|
||||
explicit = true
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().current_dir(&pkg_b), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 6 packages in [TIME]
|
||||
");
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--check").current_dir(&pkg_b), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 6 packages in [TIME]
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that path dependencies without an index don't affect validation.
|
||||
#[test]
|
||||
fn lock_path_dependency_no_index() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create the path dependency without explicit indexes
|
||||
let pkg_a = context.temp_dir.child("pkg_a");
|
||||
fs_err::create_dir_all(&pkg_a)?;
|
||||
|
||||
let pyproject_toml = pkg_a.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-a"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["requests"]
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create a project that depends on pkg_a
|
||||
let pkg_b = context.temp_dir.child("pkg_b");
|
||||
fs_err::create_dir_all(&pkg_b)?;
|
||||
|
||||
let pyproject_toml = pkg_b.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-b"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["pkg-a"]
|
||||
|
||||
[tool.uv.sources]
|
||||
pkg-a = { path = "../pkg_a/", editable = true }
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().current_dir(&pkg_b), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 7 packages in [TIME]
|
||||
");
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--check").current_dir(&pkg_b), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 7 packages in [TIME]
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that a nested path dependency with an explicit index validates correctly.
|
||||
#[test]
|
||||
fn lock_nested_path_dependency_explicit_index() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create the inner dependency with explicit index
|
||||
let pkg_a = context.temp_dir.child("pkg_a");
|
||||
fs_err::create_dir_all(&pkg_a)?;
|
||||
|
||||
let pyproject_toml = pkg_a.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-a"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["iniconfig"]
|
||||
|
||||
[tool.uv.sources]
|
||||
iniconfig = { index = "inner-index" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "inner-index"
|
||||
url = "https://pypi-proxy.fly.dev/simple"
|
||||
explicit = true
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create intermediate dependency that depends on pkg_a
|
||||
let pkg_b = context.temp_dir.child("pkg_b");
|
||||
fs_err::create_dir_all(&pkg_b)?;
|
||||
|
||||
let pyproject_toml = pkg_b.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-b"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["pkg-a"]
|
||||
|
||||
[tool.uv.sources]
|
||||
pkg-a = { path = "../pkg_a/", editable = true }
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create a project that depends on intermediate dependency
|
||||
let pkg_c = context.temp_dir.child("pkg_c");
|
||||
fs_err::create_dir_all(&pkg_c)?;
|
||||
|
||||
let pyproject_toml = pkg_c.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-c"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = ["pkg-b"]
|
||||
|
||||
[tool.uv.sources]
|
||||
pkg-b = { path = "../pkg_b/", editable = true }
|
||||
black = { index = "outer-index" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "outer-index"
|
||||
url = "https://outer-index.com/simple"
|
||||
explicit = true
|
||||
"#,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().current_dir(&pkg_c), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 4 packages in [TIME]
|
||||
");
|
||||
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--check").current_dir(&pkg_c), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 4 packages in [TIME]
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that validating circular path dependency indexes doesn't cause an infinite loop.
|
||||
// TODO(john): Validation doesn't hang but the `uv lock --check` step fails to validate
|
||||
// in the circular path dependency case (same behavior as prior to the fix for path
|
||||
// dependency index lock validation).
|
||||
#[test]
|
||||
fn lock_circular_path_dependency_explicit_index() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create pkg_a (with explicit index) that depends on pkg_b
|
||||
let pkg_a = context.temp_dir.child("pkg_a");
|
||||
fs_err::create_dir_all(&pkg_a)?;
|
||||
|
||||
let pyproject_toml = pkg_a.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-a"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = ["pkg-b", "iniconfig"]
|
||||
|
||||
[tool.uv.sources]
|
||||
pkg-b = { path = "../pkg_b/", editable = true }
|
||||
iniconfig = { index = "index-a" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "index-a"
|
||||
url = "https://pypi-proxy.fly.dev/simple"
|
||||
explicit = true
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// Create pkg_b that depends on pkg_a. This is a circular dependency.
|
||||
let pkg_b = context.temp_dir.child("pkg_b");
|
||||
fs_err::create_dir_all(&pkg_b)?;
|
||||
|
||||
let pyproject_toml = pkg_b.child("pyproject.toml");
|
||||
pyproject_toml.write_str(
|
||||
r#"
|
||||
[project]
|
||||
name = "pkg-b"
|
||||
version = "0.1.0"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = ["pkg-a", "anyio"]
|
||||
|
||||
[tool.uv.sources]
|
||||
pkg-a = { path = "../pkg_a/", editable = true }
|
||||
anyio = { index = "index-b" }
|
||||
|
||||
[[tool.uv.index]]
|
||||
name = "index-b"
|
||||
url = "https://pypi.org/simple"
|
||||
explicit = true
|
||||
default = true
|
||||
"#,
|
||||
)?;
|
||||
|
||||
// This should not hang or crash due to the circular dependency
|
||||
uv_snapshot!(context.filters(), context.lock().current_dir(&pkg_a), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 8 packages in [TIME]
|
||||
");
|
||||
|
||||
// TODO(john): This currently fails due to circular dependency validation issues
|
||||
uv_snapshot!(context.filters(), context.lock().arg("--check").current_dir(&pkg_a), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||
Resolved 8 packages in [TIME]
|
||||
error: The lockfile at `uv.lock` needs to be updated, but `--locked` was provided. To update the lockfile, run `uv lock`.
|
||||
");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue