mirror of https://github.com/astral-sh/uv
Allow `[project]` to be missing from a `pyproject.toml` (#14113)
Closes #8666 Closes https://github.com/astral-sh/uv/issues/6838 --------- Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
parent
fa50a5cede
commit
1943aba150
|
|
@ -77,6 +77,7 @@ impl SourcedDependencyGroups {
|
||||||
SourceStrategy::Enabled => MemberDiscovery::default(),
|
SourceStrategy::Enabled => MemberDiscovery::default(),
|
||||||
SourceStrategy::Disabled => MemberDiscovery::None,
|
SourceStrategy::Disabled => MemberDiscovery::None,
|
||||||
},
|
},
|
||||||
|
..DiscoveryOptions::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// The subsequent API takes an absolute path to the dir the pyproject is in
|
// The subsequent API takes an absolute path to the dir the pyproject is in
|
||||||
|
|
@ -84,7 +85,7 @@ impl SourcedDependencyGroups {
|
||||||
let absolute_pyproject_path =
|
let absolute_pyproject_path =
|
||||||
std::path::absolute(pyproject_path).map_err(WorkspaceError::Normalize)?;
|
std::path::absolute(pyproject_path).map_err(WorkspaceError::Normalize)?;
|
||||||
let project_dir = absolute_pyproject_path.parent().unwrap_or(&empty);
|
let project_dir = absolute_pyproject_path.parent().unwrap_or(&empty);
|
||||||
let project = VirtualProject::discover_defaulted(project_dir, &discovery, cache).await?;
|
let project = VirtualProject::discover(project_dir, &discovery, cache).await?;
|
||||||
|
|
||||||
// Collect the dependency groups.
|
// Collect the dependency groups.
|
||||||
let dependency_groups =
|
let dependency_groups =
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ impl RequiresDist {
|
||||||
SourceStrategy::Enabled => MemberDiscovery::default(),
|
SourceStrategy::Enabled => MemberDiscovery::default(),
|
||||||
SourceStrategy::Disabled => MemberDiscovery::None,
|
SourceStrategy::Disabled => MemberDiscovery::None,
|
||||||
},
|
},
|
||||||
|
..DiscoveryOptions::default()
|
||||||
};
|
};
|
||||||
let Some(project_workspace) =
|
let Some(project_workspace) =
|
||||||
ProjectWorkspace::from_maybe_project_root(install_path, &discovery, cache).await?
|
ProjectWorkspace::from_maybe_project_root(install_path, &discovery, cache).await?
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
pub use workspace::{
|
pub use workspace::{
|
||||||
DiscoveryOptions, Editability, MemberDiscovery, ProjectWorkspace, RequiresPythonSources,
|
DiscoveryOptions, Editability, MemberDiscovery, ProjectDiscovery, ProjectWorkspace,
|
||||||
VirtualProject, Workspace, WorkspaceCache, WorkspaceError, WorkspaceMember,
|
RequiresPythonSources, VirtualProject, Workspace, WorkspaceCache, WorkspaceError,
|
||||||
|
WorkspaceMember,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod dependency_groups;
|
pub mod dependency_groups;
|
||||||
|
|
|
||||||
|
|
@ -94,12 +94,51 @@ pub enum MemberDiscovery {
|
||||||
Ignore(BTreeSet<PathBuf>),
|
Ignore(BTreeSet<PathBuf>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a "project" must be defined via a `[project]` table.
|
||||||
|
#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub enum ProjectDiscovery {
|
||||||
|
/// The `[project]` table is optional; when missing, the target is treated as virtual.
|
||||||
|
#[default]
|
||||||
|
Optional,
|
||||||
|
/// A `[project]` table must be defined, unless `[tool.uv.workspace]` is present indicating a
|
||||||
|
/// legacy non-project workspace root.
|
||||||
|
///
|
||||||
|
/// If neither is defined, discovery will fail.
|
||||||
|
Legacy,
|
||||||
|
/// A `[project]` table must be defined.
|
||||||
|
///
|
||||||
|
/// If not defined, discovery will fail.
|
||||||
|
Required,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProjectDiscovery {
|
||||||
|
/// Whether a `[project]` table is required.
|
||||||
|
pub fn allows_implicit_workspace(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Optional => true,
|
||||||
|
Self::Legacy => false,
|
||||||
|
Self::Required => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether a legacy workspace root is allowed.
|
||||||
|
pub fn allows_legacy_workspace(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::Optional => true,
|
||||||
|
Self::Legacy => true,
|
||||||
|
Self::Required => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Default, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct DiscoveryOptions {
|
pub struct DiscoveryOptions {
|
||||||
/// The path to stop discovery at.
|
/// The path to stop discovery at.
|
||||||
pub stop_discovery_at: Option<PathBuf>,
|
pub stop_discovery_at: Option<PathBuf>,
|
||||||
/// The strategy to use when discovering workspace members.
|
/// The strategy to use when discovering workspace members.
|
||||||
pub members: MemberDiscovery,
|
pub members: MemberDiscovery,
|
||||||
|
/// The strategy to use when discovering the project.
|
||||||
|
pub project: ProjectDiscovery,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type RequiresPythonSources = BTreeMap<(PackageName, Option<GroupName>), VersionSpecifiers>;
|
pub type RequiresPythonSources = BTreeMap<(PackageName, Option<GroupName>), VersionSpecifiers>;
|
||||||
|
|
@ -1561,13 +1600,13 @@ fn is_included_in_workspace(
|
||||||
|
|
||||||
/// A project that can be discovered.
|
/// A project that can be discovered.
|
||||||
///
|
///
|
||||||
/// The project could be a package within a workspace, a real workspace root, or a (legacy)
|
/// The project could be a package within a workspace, a real workspace root, or a non-project
|
||||||
/// non-project workspace root, which can define its own dev dependencies.
|
/// workspace root, which can define its own dev dependencies.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum VirtualProject {
|
pub enum VirtualProject {
|
||||||
/// A project (which could be a workspace root or member).
|
/// A project (which could be a workspace root or member).
|
||||||
Project(ProjectWorkspace),
|
Project(ProjectWorkspace),
|
||||||
/// A (legacy) non-project workspace root.
|
/// A non-project workspace root.
|
||||||
NonProject(Workspace),
|
NonProject(Workspace),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1583,33 +1622,6 @@ impl VirtualProject {
|
||||||
path: &Path,
|
path: &Path,
|
||||||
options: &DiscoveryOptions,
|
options: &DiscoveryOptions,
|
||||||
cache: &WorkspaceCache,
|
cache: &WorkspaceCache,
|
||||||
) -> Result<Self, WorkspaceError> {
|
|
||||||
Self::discover_impl(path, options, cache, false).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Equivalent to [`VirtualProject::discover`] but consider it acceptable for
|
|
||||||
/// both `[project]` and `[tool.uv.workspace]` to be missing.
|
|
||||||
///
|
|
||||||
/// If they are, we act as if an empty `[tool.uv.workspace]` was found.
|
|
||||||
pub async fn discover_defaulted(
|
|
||||||
path: &Path,
|
|
||||||
options: &DiscoveryOptions,
|
|
||||||
cache: &WorkspaceCache,
|
|
||||||
) -> Result<Self, WorkspaceError> {
|
|
||||||
Self::discover_impl(path, options, cache, true).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Find the current project or virtual workspace root, given the current directory.
|
|
||||||
///
|
|
||||||
/// Similar to calling [`ProjectWorkspace::discover`] with a fallback to [`Workspace::discover`],
|
|
||||||
/// but avoids rereading the `pyproject.toml` (and relying on error-handling as control flow).
|
|
||||||
///
|
|
||||||
/// This method requires an absolute path and panics otherwise.
|
|
||||||
async fn discover_impl(
|
|
||||||
path: &Path,
|
|
||||||
options: &DiscoveryOptions,
|
|
||||||
cache: &WorkspaceCache,
|
|
||||||
default_missing_workspace: bool,
|
|
||||||
) -> Result<Self, WorkspaceError> {
|
) -> Result<Self, WorkspaceError> {
|
||||||
assert!(
|
assert!(
|
||||||
path.is_absolute(),
|
path.is_absolute(),
|
||||||
|
|
@ -1656,6 +1668,7 @@ impl VirtualProject {
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.and_then(|tool| tool.uv.as_ref())
|
.and_then(|tool| tool.uv.as_ref())
|
||||||
.and_then(|uv| uv.workspace.as_ref())
|
.and_then(|uv| uv.workspace.as_ref())
|
||||||
|
.filter(|_| options.project.allows_legacy_workspace())
|
||||||
{
|
{
|
||||||
// Otherwise, if it contains a `tool.uv.workspace` table, it's a non-project workspace
|
// Otherwise, if it contains a `tool.uv.workspace` table, it's a non-project workspace
|
||||||
// root.
|
// root.
|
||||||
|
|
@ -1674,7 +1687,7 @@ impl VirtualProject {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(Self::NonProject(workspace))
|
Ok(Self::NonProject(workspace))
|
||||||
} else if default_missing_workspace {
|
} else if options.project.allows_implicit_workspace() {
|
||||||
// Otherwise it's a pyproject.toml that maybe contains dependency-groups
|
// Otherwise it's a pyproject.toml that maybe contains dependency-groups
|
||||||
// that we want to treat like a project/workspace to handle those uniformly
|
// that we want to treat like a project/workspace to handle those uniformly
|
||||||
let project_path = std::path::absolute(project_root)
|
let project_path = std::path::absolute(project_root)
|
||||||
|
|
|
||||||
|
|
@ -358,7 +358,10 @@ async fn find_target(
|
||||||
VirtualProject::Project(
|
VirtualProject::Project(
|
||||||
Workspace::discover(
|
Workspace::discover(
|
||||||
project_dir,
|
project_dir,
|
||||||
&DiscoveryOptions::default(),
|
&DiscoveryOptions {
|
||||||
|
project: uv_workspace::ProjectDiscovery::Required,
|
||||||
|
..DiscoveryOptions::default()
|
||||||
|
},
|
||||||
&WorkspaceCache::default(),
|
&WorkspaceCache::default(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
@ -369,7 +372,10 @@ async fn find_target(
|
||||||
} else {
|
} else {
|
||||||
VirtualProject::discover(
|
VirtualProject::discover(
|
||||||
project_dir,
|
project_dir,
|
||||||
&DiscoveryOptions::default(),
|
&DiscoveryOptions {
|
||||||
|
project: uv_workspace::ProjectDiscovery::Required,
|
||||||
|
..DiscoveryOptions::default()
|
||||||
|
},
|
||||||
&WorkspaceCache::default(),
|
&WorkspaceCache::default(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
|
|
@ -4795,6 +4795,306 @@ fn add_non_project() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_virtual_empty() -> Result<()> {
|
||||||
|
// testing how `uv add` reacts to a pyproject with no `[project]` and nothing useful to it
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Add normal dep (doesn't make sense)
|
||||||
|
uv_snapshot!(context.filters(), context.add()
|
||||||
|
.arg("sortedcontainers"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Project is missing a `[project]` table; add a `[project]` table to use production dependencies, or run `uv add --dev` instead
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add dependency-group (can make sense!)
|
||||||
|
uv_snapshot!(context.filters(), context.add()
|
||||||
|
.arg("sortedcontainers")
|
||||||
|
.arg("--group").arg("dev"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 1 package in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ sortedcontainers==2.4.0
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"sortedcontainers>=2.4.0",
|
||||||
|
]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_virtual_dependency_group() -> Result<()> {
|
||||||
|
// testing basic `uv add --group` functionality
|
||||||
|
// when the pyproject.toml is fully virtual (no `[project]`)
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Add to existing group
|
||||||
|
uv_snapshot!(context.filters(), context.add()
|
||||||
|
.arg("sortedcontainers")
|
||||||
|
.arg("--group").arg("dev"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Prepared 2 packages in [TIME]
|
||||||
|
Installed 2 packages in [TIME]
|
||||||
|
+ sniffio==1.3.1
|
||||||
|
+ sortedcontainers==2.4.0
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = [
|
||||||
|
"sniffio",
|
||||||
|
"sortedcontainers>=2.4.0",
|
||||||
|
]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add to new group
|
||||||
|
uv_snapshot!(context.filters(), context.add()
|
||||||
|
.arg("sortedcontainers")
|
||||||
|
.arg("--group").arg("baz"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Audited 2 packages in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = [
|
||||||
|
"sniffio",
|
||||||
|
"sortedcontainers>=2.4.0",
|
||||||
|
]
|
||||||
|
baz = [
|
||||||
|
"sortedcontainers>=2.4.0",
|
||||||
|
]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remove_virtual_empty() -> Result<()> {
|
||||||
|
// testing how `uv remove` reacts to a pyproject with no `[project]` and nothing useful to it
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(
|
||||||
|
r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
|
||||||
|
"#,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Remove normal dep (doesn't make sense)
|
||||||
|
uv_snapshot!(context.filters(), context.remove()
|
||||||
|
.arg("sortedcontainers"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: The dependency `sortedcontainers` could not be found in `project.dependencies`
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove dependency-group (can make sense, but nothing there!)
|
||||||
|
uv_snapshot!(context.filters(), context.remove()
|
||||||
|
.arg("sortedcontainers")
|
||||||
|
.arg("--group").arg("dev"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: The dependency `sortedcontainers` could not be found in `tool.uv.dev-dependencies` or `tool.uv.dependency-groups.dev`
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn remove_virtual_dependency_group() -> Result<()> {
|
||||||
|
// testing basic `uv remove --group` functionality
|
||||||
|
// when the pyproject.toml is fully virtual (no `[project]`)
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Remove from group
|
||||||
|
uv_snapshot!(context.filters(), context.remove()
|
||||||
|
.arg("sortedcontainers")
|
||||||
|
.arg("--group").arg("foo"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ sniffio==1.3.1
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = []
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove from non-existent group
|
||||||
|
uv_snapshot!(context.filters(), context.remove()
|
||||||
|
.arg("sortedcontainers")
|
||||||
|
.arg("--group").arg("baz"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: The dependency `sortedcontainers` could not be found in `dependency-groups.baz`
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = []
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Add the same requirement multiple times.
|
/// Add the same requirement multiple times.
|
||||||
#[test]
|
#[test]
|
||||||
fn add_repeat() -> Result<()> {
|
fn add_repeat() -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -1145,6 +1145,102 @@ fn requirements_txt_non_project() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_empty() -> Result<()> {
|
||||||
|
// testing how `uv export` reacts to a pyproject with no `[project]` and nothing useful to it
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.export(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv export --cache-dir [CACHE_DIR]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_dependency_group() -> Result<()> {
|
||||||
|
// testing basic `uv export --group` functionality
|
||||||
|
// when the pyproject.toml is fully virtual (no `[project]`)
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// default groups
|
||||||
|
uv_snapshot!(context.filters(), context.export(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv export --cache-dir [CACHE_DIR]
|
||||||
|
sniffio==1.3.1 \
|
||||||
|
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||||
|
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
// explicit --group
|
||||||
|
uv_snapshot!(context.filters(), context.export()
|
||||||
|
.arg("--group").arg("bar"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv export --cache-dir [CACHE_DIR] --group bar
|
||||||
|
iniconfig==2.0.0 \
|
||||||
|
--hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \
|
||||||
|
--hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374
|
||||||
|
sniffio==1.3.1 \
|
||||||
|
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
|
||||||
|
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
// explicit --only-group
|
||||||
|
uv_snapshot!(context.filters(), context.export()
|
||||||
|
.arg("--only-group").arg("foo"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
# This file was autogenerated by uv via the following command:
|
||||||
|
# uv export --cache-dir [CACHE_DIR] --only-group foo
|
||||||
|
sortedcontainers==2.4.0 \
|
||||||
|
--hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
|
||||||
|
--hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "git")]
|
#[cfg(feature = "git")]
|
||||||
#[test]
|
#[test]
|
||||||
fn requirements_txt_https_git_credentials() -> Result<()> {
|
fn requirements_txt_https_git_credentials() -> Result<()> {
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,180 @@ fn python_find_project() {
|
||||||
");
|
");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_empty() {
|
||||||
|
// testing how `uv python find` reacts to a pyproject with no `[project]` and nothing useful to it
|
||||||
|
let context = TestContext::new_with_versions(&["3.10", "3.11", "3.12"]);
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml
|
||||||
|
.write_str(indoc! {r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Ask for the python
|
||||||
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.10]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// Ask for the python (--no-project)
|
||||||
|
uv_snapshot!(context.filters(), context.python_find()
|
||||||
|
.arg("--no-project"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.10]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// Ask for specific python (3.11)
|
||||||
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.11]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create a pin
|
||||||
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Pinned `.python-version` to `3.12`
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Ask for the python
|
||||||
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.12]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Ask for specific python (3.11)
|
||||||
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.11]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// Ask for the python (--no-project)
|
||||||
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.12]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_dependency_group() {
|
||||||
|
// testing basic `uv python find` functionality
|
||||||
|
// when the pyproject.toml is fully virtual (no `[project]`, but `[dependency-groups]` defined,
|
||||||
|
// which really shouldn't matter)
|
||||||
|
let context = TestContext::new_with_versions(&["3.10", "3.11", "3.12"]);
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml
|
||||||
|
.write_str(indoc! {r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Ask for the python
|
||||||
|
uv_snapshot!(context.filters(), context.python_find(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.10]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// Ask for the python (--no-project)
|
||||||
|
uv_snapshot!(context.filters(), context.python_find()
|
||||||
|
.arg("--no-project"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.10]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// Ask for specific python (3.11)
|
||||||
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.11]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// Create a pin
|
||||||
|
uv_snapshot!(context.filters(), context.python_pin().arg("3.12"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Pinned `.python-version` to `3.12`
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Ask for the python
|
||||||
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.12]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
|
// Ask for specific python (3.11)
|
||||||
|
uv_snapshot!(context.filters(), context.python_find().arg("3.11"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.11]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// Ask for the python (--no-project)
|
||||||
|
uv_snapshot!(context.filters(), context.python_find(), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[PYTHON-3.12]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn python_find_venv() {
|
fn python_find_venv() {
|
||||||
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"])
|
let context: TestContext = TestContext::new_with_versions(&["3.11", "3.12"])
|
||||||
|
|
|
||||||
|
|
@ -3313,9 +3313,9 @@ fn run_module_stdin() {
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When the `pyproject.toml` file is invalid.
|
/// Test for how run reacts to a pyproject.toml without a `[project]`
|
||||||
#[test]
|
#[test]
|
||||||
fn run_project_toml_error() -> Result<()> {
|
fn virtual_empty() -> Result<()> {
|
||||||
let context = TestContext::new("3.12")
|
let context = TestContext::new("3.12")
|
||||||
.with_filtered_python_names()
|
.with_filtered_python_names()
|
||||||
.with_filtered_virtualenv_bin()
|
.with_filtered_virtualenv_bin()
|
||||||
|
|
@ -3331,26 +3331,29 @@ fn run_project_toml_error() -> Result<()> {
|
||||||
let init = src.child("__init__.py");
|
let init = src.child("__init__.py");
|
||||||
init.touch()?;
|
init.touch()?;
|
||||||
|
|
||||||
// `run` should fail
|
// `run` should work fine
|
||||||
uv_snapshot!(context.filters(), context.run().arg("python").arg("-c").arg("import sys; print(sys.executable)"), @r"
|
uv_snapshot!(context.filters(), context.run().arg("python").arg("-c").arg("import sys; print(sys.executable)"), @r"
|
||||||
success: false
|
|
||||||
exit_code: 2
|
|
||||||
----- stdout -----
|
|
||||||
|
|
||||||
----- stderr -----
|
|
||||||
error: No `project` table found in: `[TEMP_DIR]/pyproject.toml`
|
|
||||||
");
|
|
||||||
|
|
||||||
// `run --no-project` should not
|
|
||||||
uv_snapshot!(context.filters(), context.run().arg("--no-project").arg("python").arg("-c").arg("import sys; print(sys.executable)"), @r"
|
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
[VENV]/[BIN]/[PYTHON]
|
[VENV]/[BIN]/[PYTHON]
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved in [TIME]
|
||||||
|
Audited in [TIME]
|
||||||
");
|
");
|
||||||
|
|
||||||
|
// `run --no-project` should also work fine
|
||||||
|
uv_snapshot!(context.filters(), context.run().arg("--no-project").arg("python").arg("-c").arg("import sys; print(sys.executable)"), @r###"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
[VENV]/[BIN]/[PYTHON]
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
"###);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5616,6 +5616,95 @@ fn virtual_no_build() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_empty() -> Result<()> {
|
||||||
|
// testing how `uv sync` reacts to a pyproject with no `[project]` and nothing useful to it
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved in [TIME]
|
||||||
|
Audited in [TIME]
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_dependency_group() -> Result<()> {
|
||||||
|
// testing basic `uv sync --group` functionality
|
||||||
|
// when the pyproject.toml is fully virtual (no `[project]`)
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// default groups
|
||||||
|
uv_snapshot!(context.filters(), context.sync(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ sniffio==1.3.1
|
||||||
|
");
|
||||||
|
|
||||||
|
// explicit --group
|
||||||
|
uv_snapshot!(context.filters(), context.sync()
|
||||||
|
.arg("--group").arg("bar"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
");
|
||||||
|
|
||||||
|
// explicit --only-group
|
||||||
|
uv_snapshot!(context.filters(), context.sync()
|
||||||
|
.arg("--only-group").arg("foo"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`.
|
||||||
|
Resolved 3 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Uninstalled 2 packages in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
- iniconfig==2.0.0
|
||||||
|
- sniffio==1.3.1
|
||||||
|
+ sortedcontainers==2.4.0
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn virtual_no_build_dynamic_cached() -> Result<()> {
|
fn virtual_no_build_dynamic_cached() -> Result<()> {
|
||||||
let context = TestContext::new("3.12");
|
let context = TestContext::new("3.12");
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,61 @@ fn create_venv_project_environment() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_empty() -> Result<()> {
|
||||||
|
// testing how `uv venv` reacts to a pyproject with no `[project]` and nothing useful to it
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.venv(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
|
Creating virtual environment at: .venv
|
||||||
|
warning: A virtual environment already exists at `.venv`. In the future, uv will require `--clear` to replace it
|
||||||
|
Activate with: source .venv/[BIN]/activate
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_dependency_group() -> Result<()> {
|
||||||
|
// testing basic `uv venv` functionality
|
||||||
|
// when the pyproject.toml is fully virtual (no `[project]`, but `[dependency-groups]` defined)
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.venv(), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Using CPython 3.12.[X] interpreter at: [PYTHON-3.12]
|
||||||
|
Creating virtual environment at: .venv
|
||||||
|
warning: A virtual environment already exists at `.venv`. In the future, uv will require `--clear` to replace it
|
||||||
|
Activate with: source .venv/[BIN]/activate
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create_venv_defaults_to_cwd() {
|
fn create_venv_defaults_to_cwd() {
|
||||||
let context = TestContext::new_with_versions(&["3.12"]);
|
let context = TestContext::new_with_versions(&["3.12"]);
|
||||||
|
|
|
||||||
|
|
@ -1867,7 +1867,7 @@ fn version_get_workspace() -> Result<()> {
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
error: Missing `project.name` field in: pyproject.toml
|
error: No `project` table found in: `[TEMP_DIR]/pyproject.toml`
|
||||||
");
|
");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -2462,6 +2462,137 @@ fn version_set_evil_constraints() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn virtual_empty() -> Result<()> {
|
||||||
|
// testing how `uv version` reacts to a pyproject with no `[project]` and nothing useful to it
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Get version (doesn't make sense)
|
||||||
|
uv_snapshot!(context.filters(), context.version()
|
||||||
|
.arg("sortedcontainers"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: No `project` table found in: `[TEMP_DIR]/pyproject.toml`
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set version (can make sense, but we should still refuse?)
|
||||||
|
uv_snapshot!(context.filters(), context.version()
|
||||||
|
.arg("1.0.0"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: No `project` table found in: `[TEMP_DIR]/pyproject.toml`
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[tool.mycooltool]
|
||||||
|
wow = "someconfig"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn add_virtual_dependency_group() -> Result<()> {
|
||||||
|
// testing basic `uv version` functionality
|
||||||
|
// when the pyproject.toml is fully virtual (no `[project]`)
|
||||||
|
// But at least has some dependency-group tables (shouldn't matter to this command)
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Get the version (doesn't make sense)
|
||||||
|
uv_snapshot!(context.filters(), context.version(), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: No `project` table found in: `[TEMP_DIR]/pyproject.toml`
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set the version (can make sense, we should refuse?)
|
||||||
|
uv_snapshot!(context.filters(), context.version()
|
||||||
|
.arg("1.0.0"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: No `project` table found in: `[TEMP_DIR]/pyproject.toml`
|
||||||
|
");
|
||||||
|
|
||||||
|
let pyproject_toml = context.read("pyproject.toml");
|
||||||
|
|
||||||
|
insta::with_settings!({
|
||||||
|
filters => context.filters(),
|
||||||
|
}, {
|
||||||
|
assert_snapshot!(
|
||||||
|
pyproject_toml, @r#"
|
||||||
|
[dependency-groups]
|
||||||
|
foo = ["sortedcontainers"]
|
||||||
|
bar = ["iniconfig"]
|
||||||
|
dev = ["sniffio"]
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Bump the version with conflicting extras, to ensure we're activating the correct subset of
|
/// Bump the version with conflicting extras, to ensure we're activating the correct subset of
|
||||||
/// extras during the resolve.
|
/// extras during the resolve.
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue