Add support for virtual projects (#6585)

## Summary

The basic idea here is: any project can either be a package, or not
("virtual").

If a project is virtual, we don't build or install it.

A project is virtual if either of the following are true:

- `tool.uv.virtual = true` is set.
- `[build-system]` is absent.

The concept of "virtual projects" only applies to workspace member right
now; it doesn't apply to `path` dependencies which are treated like
arbitrary Python source trees.

TODOs that should be resolved prior to merging:

- [ ] Documentation
- [ ] How do we reconcile this with "virtual workspace roots" which are
a little different -- they omit `[project]` entirely and don't even have
a name?
- [x] `uv init --virtual` should create a virtual project rather than a
virtual workspace.
- [x] Running `uv sync` in a virtual project after `uv init --virtual`
shows `Audited 0 packages in 0.01ms`, which is awkward. (See:
https://github.com/astral-sh/uv/pull/6588.)

Closes https://github.com/astral-sh/uv/issues/6511.
This commit is contained in:
Charlie Marsh 2024-08-27 13:42:46 -04:00 committed by GitHub
parent 6d38d42b41
commit eb14056e9c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 1710 additions and 147 deletions

View file

@ -2099,11 +2099,12 @@ pub struct InitArgs {
#[arg(long)] #[arg(long)]
pub name: Option<PackageName>, pub name: Option<PackageName>,
/// Create a virtual workspace instead of a project. /// Create a virtual project, rather than a package.
/// ///
/// A virtual workspace does not define project dependencies and cannot be /// A virtual project is a project that is not intended to be built as a Python package,
/// published. Instead, workspace members declare project dependencies. /// such as a project that only contains scripts or other application code.
/// Development dependencies may still be declared. ///
/// Virtual projects themselves are not installed into the Python environment.
#[arg(long)] #[arg(long)]
pub r#virtual: bool, pub r#virtual: bool,

View file

@ -70,6 +70,10 @@ pub struct Options {
#[serde(default, skip_serializing)] #[serde(default, skip_serializing)]
#[cfg_attr(feature = "schemars", schemars(skip))] #[cfg_attr(feature = "schemars", schemars(skip))]
managed: serde::de::IgnoredAny, managed: serde::de::IgnoredAny,
#[serde(default, skip_serializing)]
#[cfg_attr(feature = "schemars", schemars(skip))]
r#package: serde::de::IgnoredAny,
} }
impl Options { impl Options {

View file

@ -33,6 +33,10 @@ pub struct PyProjectToml {
/// The raw unserialized document. /// The raw unserialized document.
#[serde(skip)] #[serde(skip)]
pub raw: String, pub raw: String,
/// Used to determine whether a `build-system` is present.
#[serde(default, skip_serializing)]
build_system: Option<serde::de::IgnoredAny>,
} }
impl PyProjectToml { impl PyProjectToml {
@ -41,6 +45,23 @@ impl PyProjectToml {
let pyproject = toml::from_str(&raw)?; let pyproject = toml::from_str(&raw)?;
Ok(PyProjectToml { raw, ..pyproject }) Ok(PyProjectToml { raw, ..pyproject })
} }
/// Returns `true` if the project should be considered a Python package, as opposed to a
/// non-package ("virtual") project.
pub fn is_package(&self) -> bool {
// If `tool.uv.package` is set, defer to that explicit setting.
if let Some(is_package) = self
.tool
.as_ref()
.and_then(|tool| tool.uv.as_ref())
.and_then(|uv| uv.package)
{
return is_package;
}
// Otherwise, a project is assumed to be a package if `build-system` is present.
self.build_system.is_some()
}
} }
// Ignore raw document in comparison. // Ignore raw document in comparison.
@ -100,6 +121,24 @@ pub struct ToolUv {
"# "#
)] )]
pub managed: Option<bool>, pub managed: Option<bool>,
/// Whether the project should be considered a Python package, or a non-package ("virtual")
/// project.
///
/// Packages are built and installed into the virtual environment in editable mode and thus
/// require a build backend, while virtual projects are _not_ built or installed; instead, only
/// their dependencies are included in the virtual environment.
///
/// Creating a package requires that a `build-system` is present in the `pyproject.toml`, and
/// that the project adheres to a structure that adheres to the build backend's expectations
/// (e.g., a `src` layout).
#[option(
default = r#"true"#,
value_type = "bool",
example = r#"
package = false
"#
)]
pub package: Option<bool>,
/// The project's development dependencies. Development dependencies will be installed by /// The project's development dependencies. Development dependencies will be installed by
/// default in `uv run` and `uv sync`, but will not appear in the project's published metadata. /// default in `uv run` and `uv sync`, but will not appear in the project's published metadata.
#[cfg_attr( #[cfg_attr(

View file

@ -1339,7 +1339,7 @@ impl VirtualProject {
} }
} }
/// Return the [`PackageName`] of the project, if it's not a virtual workspace. /// Return the [`PackageName`] of the project, if it's not a virtual workspace root.
pub fn project_name(&self) -> Option<&PackageName> { pub fn project_name(&self) -> Option<&PackageName> {
match self { match self {
VirtualProject::Project(project) => Some(project.project_name()), VirtualProject::Project(project) => Some(project.project_name()),
@ -1347,7 +1347,7 @@ impl VirtualProject {
} }
} }
/// Returns `true` if the project is a virtual workspace. /// Returns `true` if the project is a virtual workspace root.
pub fn is_virtual(&self) -> bool { pub fn is_virtual(&self) -> bool {
matches!(self, VirtualProject::Virtual(_)) matches!(self, VirtualProject::Virtual(_))
} }
@ -1535,6 +1535,7 @@ mod tests {
"exclude": null "exclude": null
}, },
"managed": null, "managed": null,
"package": null,
"dev-dependencies": null, "dev-dependencies": null,
"environments": null, "environments": null,
"override-dependencies": null, "override-dependencies": null,
@ -1607,6 +1608,7 @@ mod tests {
"exclude": null "exclude": null
}, },
"managed": null, "managed": null,
"package": null,
"dev-dependencies": null, "dev-dependencies": null,
"environments": null, "environments": null,
"override-dependencies": null, "override-dependencies": null,

View file

@ -15,7 +15,7 @@ use uv_python::{
}; };
use uv_resolver::RequiresPython; use uv_resolver::RequiresPython;
use uv_workspace::pyproject_mut::{DependencyTarget, PyProjectTomlMut}; use uv_workspace::pyproject_mut::{DependencyTarget, PyProjectTomlMut};
use uv_workspace::{check_nested_workspaces, DiscoveryOptions, Workspace, WorkspaceError}; use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceError};
use crate::commands::project::find_requires_python; use crate::commands::project::find_requires_python;
use crate::commands::reporters::PythonDownloadReporter; use crate::commands::reporters::PythonDownloadReporter;
@ -69,24 +69,21 @@ pub(crate) async fn init(
} }
}; };
if r#virtual { init_project(
init_virtual_workspace(&path, no_workspace)?; &path,
} else { &name,
init_project( r#virtual,
&path, no_readme,
&name, python,
no_readme, no_workspace,
python, python_preference,
no_workspace, python_downloads,
python_preference, connectivity,
python_downloads, native_tls,
connectivity, cache,
native_tls, printer,
cache, )
printer, .await?;
)
.await?;
}
// Create the `README.md` if it does not already exist. // Create the `README.md` if it does not already exist.
if !no_readme { if !no_readme {
@ -126,29 +123,12 @@ pub(crate) async fn init(
Ok(ExitStatus::Success) Ok(ExitStatus::Success)
} }
/// Initialize a virtual workspace at the given path.
fn init_virtual_workspace(path: &Path, no_workspace: bool) -> Result<()> {
// Ensure that we aren't creating a nested workspace.
if !no_workspace {
check_nested_workspaces(path, &DiscoveryOptions::default());
}
// Create the `pyproject.toml`.
let pyproject = indoc::indoc! {r"
[tool.uv.workspace]
members = []
"};
fs_err::create_dir_all(path)?;
fs_err::write(path.join("pyproject.toml"), pyproject)?;
Ok(())
}
/// Initialize a project (and, implicitly, a workspace root) at the given path. /// Initialize a project (and, implicitly, a workspace root) at the given path.
#[allow(clippy::fn_params_excessive_bools)]
async fn init_project( async fn init_project(
path: &Path, path: &Path,
name: &PackageName, name: &PackageName,
r#virtual: bool,
no_readme: bool, no_readme: bool,
python: Option<String>, python: Option<String>,
no_workspace: bool, no_workspace: bool,
@ -265,38 +245,56 @@ async fn init_project(
RequiresPython::greater_than_equal_version(&interpreter.python_minor_version()) RequiresPython::greater_than_equal_version(&interpreter.python_minor_version())
}; };
// Create the `pyproject.toml`. if r#virtual {
let pyproject = indoc::formatdoc! {r#" // Create the `pyproject.toml`, but omit `[build-system]`.
[project] let pyproject = indoc::formatdoc! {r#"
name = "{name}" [project]
version = "0.1.0" name = "{name}"
description = "Add your description here"{readme} version = "0.1.0"
requires-python = "{requires_python}" description = "Add your description here"{readme}
dependencies = [] requires-python = "{requires_python}"
dependencies = []
"#,
readme = if no_readme { "" } else { "\nreadme = \"README.md\"" },
requires_python = requires_python.specifiers(),
};
[build-system] fs_err::create_dir_all(path)?;
requires = ["hatchling"] fs_err::write(path.join("pyproject.toml"), pyproject)?;
build-backend = "hatchling.build" } else {
"#, // Create the `pyproject.toml`.
readme = if no_readme { "" } else { "\nreadme = \"README.md\"" }, let pyproject = indoc::formatdoc! {r#"
requires_python = requires_python.specifiers(), [project]
}; name = "{name}"
version = "0.1.0"
description = "Add your description here"{readme}
requires-python = "{requires_python}"
dependencies = []
fs_err::create_dir_all(path)?; [build-system]
fs_err::write(path.join("pyproject.toml"), pyproject)?; requires = ["hatchling"]
build-backend = "hatchling.build"
"#,
readme = if no_readme { "" } else { "\nreadme = \"README.md\"" },
requires_python = requires_python.specifiers(),
};
// Create `src/{name}/__init__.py`, if it doesn't exist already. fs_err::create_dir_all(path)?;
let src_dir = path.join("src").join(&*name.as_dist_info_name()); fs_err::write(path.join("pyproject.toml"), pyproject)?;
let init_py = src_dir.join("__init__.py");
if !init_py.try_exists()? { // Create `src/{name}/__init__.py`, if it doesn't exist already.
fs_err::create_dir_all(&src_dir)?; let src_dir = path.join("src").join(&*name.as_dist_info_name());
fs_err::write( let init_py = src_dir.join("__init__.py");
init_py, if !init_py.try_exists()? {
indoc::formatdoc! {r#" fs_err::create_dir_all(&src_dir)?;
fs_err::write(
init_py,
indoc::formatdoc! {r#"
def hello() -> str: def hello() -> str:
return "Hello from {name}!" return "Hello from {name}!"
"#}, "#},
)?; )?;
}
} }
if let Some(workspace) = workspace { if let Some(workspace) = workspace {

View file

@ -1,5 +1,8 @@
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::FxHashSet;
use distribution_types::Name;
use pep508_rs::MarkerTree; use pep508_rs::MarkerTree;
use uv_auth::store_credentials_from_url; use uv_auth::store_credentials_from_url;
use uv_cache::Cache; use uv_cache::Cache;
@ -195,6 +198,9 @@ pub(super) async fn do_sync(
// Read the lockfile. // Read the lockfile.
let resolution = lock.to_resolution(project, &markers, tags, extras, &dev)?; let resolution = lock.to_resolution(project, &markers, tags, extras, &dev)?;
// Always skip virtual projects, which shouldn't be built or installed.
let resolution = apply_no_virtual_project(resolution, project);
// Filter resolution based on install-specific options. // Filter resolution based on install-specific options.
let resolution = install_options.filter_resolution(resolution, project); let resolution = install_options.filter_resolution(resolution, project);
@ -289,3 +295,32 @@ pub(super) async fn do_sync(
Ok(()) Ok(())
} }
/// Filter out any virtual workspace members.
fn apply_no_virtual_project(
resolution: distribution_types::Resolution,
project: &VirtualProject,
) -> distribution_types::Resolution {
let VirtualProject::Project(project) = project else {
// If the project is _only_ a virtual workspace root, we don't need to filter it out.
return resolution;
};
let virtual_members = project
.workspace()
.packages()
.iter()
.filter_map(|(name, package)| {
// A project is a package if it's explicitly marked as such, _or_ if a build system is
// present.
if package.pyproject_toml().is_package() {
None
} else {
Some(name)
}
})
.collect::<FxHashSet<_>>();
// Remove any virtual members from the resolution.
resolution.filter(|dist| !virtual_members.contains(dist.name()))
}

View file

@ -1110,8 +1110,9 @@ pub fn make_project(dir: &Path, name: &str, body: &str) -> anyhow::Result<()> {
{body} {body}
[build-system] [build-system]
requires = ["flit_core>=3.8,<4"] requires = ["setuptools>=42", "wheel"]
build-backend = "flit_core.buildapi" build-backend = "setuptools.build_meta"
"# "#
}; };
fs_err::create_dir_all(dir)?; fs_err::create_dir_all(dir)?;

View file

@ -20,9 +20,12 @@ fn add_registry() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]), @r###" uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]), @r###"
@ -50,11 +53,14 @@ fn add_registry() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"anyio==3.7.0", "anyio==3.7.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -143,6 +149,10 @@ fn add_git() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.lock(), @r###" uv_snapshot!(context.filters(), context.lock(), @r###"
@ -212,6 +222,10 @@ fn add_git() -> Result<()> {
"uv-public-pypackage", "uv-public-pypackage",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
uv-public-pypackage = { git = "https://github.com/astral-test/uv-public-pypackage", tag = "0.0.1" } uv-public-pypackage = { git = "https://github.com/astral-test/uv-public-pypackage", tag = "0.0.1" }
"### "###
@ -313,6 +327,10 @@ fn add_git_private_source() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&[&format!("uv-private-pypackage @ git+https://{token}@github.com/astral-test/uv-private-pypackage")]), @r###" uv_snapshot!(context.filters(), context.add(&[&format!("uv-private-pypackage @ git+https://{token}@github.com/astral-test/uv-private-pypackage")]), @r###"
@ -343,6 +361,10 @@ fn add_git_private_source() -> Result<()> {
"uv-private-pypackage", "uv-private-pypackage",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
uv-private-pypackage = { git = "https://github.com/astral-test/uv-private-pypackage" } uv-private-pypackage = { git = "https://github.com/astral-test/uv-private-pypackage" }
"### "###
@ -409,6 +431,10 @@ fn add_git_private_raw() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&[&format!("uv-private-pypackage @ git+https://{token}@github.com/astral-test/uv-private-pypackage")]).arg("--raw-sources"), @r###" uv_snapshot!(context.filters(), context.add(&[&format!("uv-private-pypackage @ git+https://{token}@github.com/astral-test/uv-private-pypackage")]).arg("--raw-sources"), @r###"
@ -443,6 +469,10 @@ fn add_git_private_raw() -> Result<()> {
dependencies = [ dependencies = [
"uv-private-pypackage @ git+https://***@github.com/astral-test/uv-private-pypackage", "uv-private-pypackage @ git+https://***@github.com/astral-test/uv-private-pypackage",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -504,6 +534,10 @@ fn add_git_error() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.lock(), @r###" uv_snapshot!(context.filters(), context.lock(), @r###"
@ -562,6 +596,10 @@ fn add_git_raw() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.lock(), @r###" uv_snapshot!(context.filters(), context.lock(), @r###"
@ -617,6 +655,10 @@ fn add_git_raw() -> Result<()> {
"anyio==3.7.0", "anyio==3.7.0",
"uv-public-pypackage @ git+https://github.com/astral-test/uv-public-pypackage@0.0.1", "uv-public-pypackage @ git+https://github.com/astral-test/uv-public-pypackage@0.0.1",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -714,6 +756,10 @@ fn add_git_implicit() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.lock(), @r###" uv_snapshot!(context.filters(), context.lock(), @r###"
@ -770,6 +816,10 @@ fn add_raw_error() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Provide a tag without a Git source. // Provide a tag without a Git source.
@ -800,9 +850,12 @@ fn add_unnamed() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["git+https://github.com/astral-test/uv-public-pypackage"]).arg("--tag=0.0.1"), @r###" uv_snapshot!(context.filters(), context.add(&["git+https://github.com/astral-test/uv-public-pypackage"]).arg("--tag=0.0.1"), @r###"
@ -828,12 +881,15 @@ fn add_unnamed() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"uv-public-pypackage", "uv-public-pypackage",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
uv-public-pypackage = { git = "https://github.com/astral-test/uv-public-pypackage", tag = "0.0.1" } uv-public-pypackage = { git = "https://github.com/astral-test/uv-public-pypackage", tag = "0.0.1" }
"### "###
@ -897,6 +953,10 @@ fn add_remove_dev() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--dev"), @r###" uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--dev"), @r###"
@ -927,6 +987,10 @@ fn add_remove_dev() -> Result<()> {
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv] [tool.uv]
dev-dependencies = [ dev-dependencies = [
"anyio==3.7.0", "anyio==3.7.0",
@ -1049,6 +1113,10 @@ fn add_remove_dev() -> Result<()> {
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv] [tool.uv]
dev-dependencies = [] dev-dependencies = []
"### "###
@ -1101,6 +1169,10 @@ fn add_remove_optional() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--optional=io"), @r###" uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--optional=io"), @r###"
@ -1135,6 +1207,10 @@ fn add_remove_optional() -> Result<()> {
io = [ io = [
"anyio==3.7.0", "anyio==3.7.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -1254,6 +1330,10 @@ fn add_remove_optional() -> Result<()> {
[project.optional-dependencies] [project.optional-dependencies]
io = [] io = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -1310,6 +1390,10 @@ fn add_remove_workspace() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
let pyproject_toml = context.temp_dir.child("child2/pyproject.toml"); let pyproject_toml = context.temp_dir.child("child2/pyproject.toml");
@ -1319,6 +1403,10 @@ fn add_remove_workspace() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Adding a workspace package with a mismatched source should error. // Adding a workspace package with a mismatched source should error.
@ -1374,6 +1462,10 @@ fn add_remove_workspace() -> Result<()> {
"child2", "child2",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
child2 = { workspace = true } child2 = { workspace = true }
"### "###
@ -1457,6 +1549,10 @@ fn add_remove_workspace() -> Result<()> {
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
"### "###
); );
@ -1525,6 +1621,10 @@ fn add_workspace_editable() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
let pyproject_toml = context.temp_dir.child("child2/pyproject.toml"); let pyproject_toml = context.temp_dir.child("child2/pyproject.toml");
@ -1534,6 +1634,10 @@ fn add_workspace_editable() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
let child1 = context.temp_dir.join("child1"); let child1 = context.temp_dir.join("child1");
@ -1568,6 +1672,10 @@ fn add_workspace_editable() -> Result<()> {
"child2", "child2",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
child2 = { workspace = true } child2 = { workspace = true }
"### "###
@ -1638,6 +1746,10 @@ fn add_path() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
let child = workspace.child("child"); let child = workspace.child("child");
@ -1647,6 +1759,10 @@ fn add_path() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["./child"]).current_dir(workspace.path()), @r###" uv_snapshot!(context.filters(), context.add(&["./child"]).current_dir(workspace.path()), @r###"
@ -1679,6 +1795,10 @@ fn add_path() -> Result<()> {
"child", "child",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
child = { path = "child" } child = { path = "child" }
"### "###
@ -1743,9 +1863,11 @@ fn update() -> Result<()> {
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = ["requests==2.31.0"]
"requests==2.31.0"
] [build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.lock(), @r###" uv_snapshot!(context.filters(), context.lock(), @r###"
@ -1801,6 +1923,10 @@ fn update() -> Result<()> {
dependencies = [ dependencies = [
"requests[security]==2.31.0", "requests[security]==2.31.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -1836,6 +1962,10 @@ fn update() -> Result<()> {
"requests[security]==2.31.0", "requests[security]==2.31.0",
"requests[socks,use-chardet-on-py3]>=2.31.0 ; python_full_version >= '3.8'", "requests[socks,use-chardet-on-py3]>=2.31.0 ; python_full_version >= '3.8'",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -1873,6 +2003,10 @@ fn update() -> Result<()> {
"requests[socks,use-chardet-on-py3]>=2.31.0 ; python_full_version >= '3.8'", "requests[socks,use-chardet-on-py3]>=2.31.0 ; python_full_version >= '3.8'",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
requests = { git = "https://github.com/psf/requests", tag = "v2.32.3" } requests = { git = "https://github.com/psf/requests", tag = "v2.32.3" }
"### "###
@ -2021,9 +2155,11 @@ fn add_update_marker() -> Result<()> {
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = [ dependencies = ["requests>=2.30; python_version >= '3.11'"]
"requests>=2.30; python_version >= '3.11'"
] [build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.lock(), @r###" uv_snapshot!(context.filters(), context.lock(), @r###"
success: true success: true
@ -2080,6 +2216,10 @@ fn add_update_marker() -> Result<()> {
"requests>=2.30; python_version >= '3.11'", "requests>=2.30; python_version >= '3.11'",
"requests>=2.0,<2.29 ; python_full_version < '3.11'", "requests>=2.0,<2.29 ; python_full_version < '3.11'",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2114,6 +2254,10 @@ fn add_update_marker() -> Result<()> {
"requests>=2.30; python_version >= '3.11'", "requests>=2.30; python_version >= '3.11'",
"requests>=2.0,<2.20 ; python_full_version < '3.11'", "requests>=2.0,<2.20 ; python_full_version < '3.11'",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2153,6 +2297,10 @@ fn add_update_marker() -> Result<()> {
"requests>=2.0,<2.20 ; python_full_version < '3.11'", "requests>=2.0,<2.20 ; python_full_version < '3.11'",
"requests>=2.31 ; python_full_version >= '3.12' and sys_platform == 'win32'", "requests>=2.31 ; python_full_version >= '3.12' and sys_platform == 'win32'",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2190,6 +2338,10 @@ fn add_update_marker() -> Result<()> {
"requests>=2.31 ; python_full_version >= '3.12' and sys_platform == 'win32'", "requests>=2.31 ; python_full_version >= '3.12' and sys_platform == 'win32'",
"requests>=2.10 ; sys_platform == 'win32'", "requests>=2.10 ; sys_platform == 'win32'",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2226,6 +2378,10 @@ fn add_update_marker() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2247,6 +2403,10 @@ fn update_source_replace_url() -> Result<()> {
dependencies = [ dependencies = [
"requests[security] @ https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl" "requests[security] @ https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl"
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Change the source. The existing URL should be removed. // Change the source. The existing URL should be removed.
@ -2282,6 +2442,10 @@ fn update_source_replace_url() -> Result<()> {
"requests[security]", "requests[security]",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
requests = { git = "https://github.com/psf/requests", tag = "v2.32.3" } requests = { git = "https://github.com/psf/requests", tag = "v2.32.3" }
"### "###
@ -2302,9 +2466,11 @@ fn add_inexact() -> Result<()> {
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = ["anyio == 3.7.0"]
"anyio == 3.7.0",
] [build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.lock(), @r###" uv_snapshot!(context.filters(), context.lock(), @r###"
@ -2337,6 +2503,10 @@ fn add_inexact() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["iniconfig==2.0.0"]), @r###" uv_snapshot!(context.filters(), context.add(&["iniconfig==2.0.0"]), @r###"
@ -2367,6 +2537,10 @@ fn add_inexact() -> Result<()> {
dependencies = [ dependencies = [
"iniconfig==2.0.0", "iniconfig==2.0.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2445,6 +2619,10 @@ fn remove_registry() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.lock(), @r###" uv_snapshot!(context.filters(), context.lock(), @r###"
@ -2498,6 +2676,10 @@ fn remove_registry() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2545,11 +2727,12 @@ fn add_preserves_indentation_in_pyproject_toml() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = ["anyio==3.7.0"]
"anyio==3.7.0"
] [build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["requests==2.31.0"]), @r###" uv_snapshot!(context.filters(), context.add(&["requests==2.31.0"]), @r###"
@ -2581,12 +2764,15 @@ fn add_preserves_indentation_in_pyproject_toml() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"anyio==3.7.0", "anyio==3.7.0",
"requests==2.31.0", "requests==2.31.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2602,9 +2788,12 @@ fn add_puts_default_indentation_in_pyproject_toml_if_not_observed() -> Result<()
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["requests==2.31.0"]), @r###" uv_snapshot!(context.filters(), context.add(&["requests==2.31.0"]), @r###"
@ -2636,12 +2825,15 @@ fn add_puts_default_indentation_in_pyproject_toml_if_not_observed() -> Result<()
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"anyio==3.7.0", "anyio==3.7.0",
"requests==2.31.0", "requests==2.31.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2658,9 +2850,12 @@ fn add_frozen() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--frozen"), @r###" uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--frozen"), @r###"
@ -2681,11 +2876,14 @@ fn add_frozen() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"anyio==3.7.0", "anyio==3.7.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2705,9 +2903,12 @@ fn add_no_sync() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--no-sync"), @r###" uv_snapshot!(context.filters(), context.add(&["anyio==3.7.0"]).arg("--no-sync"), @r###"
@ -2729,11 +2930,14 @@ fn add_no_sync() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"anyio==3.7.0", "anyio==3.7.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2821,9 +3025,12 @@ fn add_error() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["xyz"]), @r###" uv_snapshot!(context.filters(), context.add(&["xyz"]), @r###"
@ -2863,6 +3070,10 @@ fn add_lower_bound() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Adding `anyio` should include a lower-bound. // Adding `anyio` should include a lower-bound.
@ -2895,6 +3106,10 @@ fn add_lower_bound() -> Result<()> {
dependencies = [ dependencies = [
"anyio>=4.3.0", "anyio>=4.3.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2914,6 +3129,10 @@ fn add_lower_bound_existing() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio"] dependencies = ["anyio"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Adding `anyio` should _not_ set a lower-bound, since it's already present (even if // Adding `anyio` should _not_ set a lower-bound, since it's already present (even if
@ -2947,6 +3166,10 @@ fn add_lower_bound_existing() -> Result<()> {
dependencies = [ dependencies = [
"anyio", "anyio",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -2966,6 +3189,10 @@ fn add_lower_bound_raw() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio"] dependencies = ["anyio"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Adding `anyio` should _not_ set a lower-bound when using `--raw-sources`. // Adding `anyio` should _not_ set a lower-bound when using `--raw-sources`.
@ -2998,6 +3225,10 @@ fn add_lower_bound_raw() -> Result<()> {
dependencies = [ dependencies = [
"anyio", "anyio",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -3017,6 +3248,10 @@ fn add_lower_bound_dev() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Adding `anyio` should include a lower-bound. // Adding `anyio` should include a lower-bound.
@ -3048,6 +3283,10 @@ fn add_lower_bound_dev() -> Result<()> {
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv] [tool.uv]
dev-dependencies = [ dev-dependencies = [
"anyio>=4.3.0", "anyio>=4.3.0",
@ -3071,6 +3310,10 @@ fn add_lower_bound_optional() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Adding `anyio` should include a lower-bound. // Adding `anyio` should include a lower-bound.
@ -3106,6 +3349,10 @@ fn add_lower_bound_optional() -> Result<()> {
io = [ io = [
"anyio>=4.3.0", "anyio>=4.3.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -3185,6 +3432,10 @@ fn add_lower_bound_local() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Adding `torch` should include a lower-bound, but no local segment. // Adding `torch` should include a lower-bound, but no local segment.
@ -3215,6 +3466,10 @@ fn add_lower_bound_local() -> Result<()> {
dependencies = [ dependencies = [
"local-simple-a>=1.2.3", "local-simple-a>=1.2.3",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -3359,9 +3614,12 @@ fn add_repeat() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
uv_snapshot!(context.filters(), context.add(&["anyio"]), @r###" uv_snapshot!(context.filters(), context.add(&["anyio"]), @r###"
@ -3389,11 +3647,14 @@ fn add_repeat() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"anyio>=4.3.0", "anyio>=4.3.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -3418,11 +3679,14 @@ fn add_repeat() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"anyio>=4.3.0", "anyio>=4.3.0",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });
@ -3440,9 +3704,12 @@ fn add_requirements_file() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
let requirements_txt = context.temp_dir.child("requirements.txt"); let requirements_txt = context.temp_dir.child("requirements.txt");
@ -3481,13 +3748,16 @@ fn add_requirements_file() -> Result<()> {
[project] [project]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
# ...
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [ dependencies = [
"flask==2.3.2", "flask==2.3.2",
"anyio", "anyio",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
anyio = { git = "https://github.com/agronholm/anyio.git", rev = "4.4.0" } anyio = { git = "https://github.com/agronholm/anyio.git", rev = "4.4.0" }
"### "###
@ -3985,6 +4255,10 @@ fn fail_to_add_revert_project() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Adding `pytorch==1.0.2` should produce an error // Adding `pytorch==1.0.2` should produce an error
@ -4032,6 +4306,10 @@ fn fail_to_add_revert_project() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"### "###
); );
}); });

View file

@ -882,9 +882,9 @@ fn init_explicit_workspace() -> Result<()> {
Ok(()) Ok(())
} }
/// Run `uv init` from within a virtual workspace. /// Run `uv init --virtual` to create a virtual project.
#[test] #[test]
fn init_virtual_workspace() -> Result<()> { fn init_virtual_project() -> Result<()> {
let context = TestContext::new("3.12"); let context = TestContext::new("3.12");
let child = context.temp_dir.child("foo"); let child = context.temp_dir.child("foo");
@ -907,8 +907,13 @@ fn init_virtual_workspace() -> Result<()> {
}, { }, {
assert_snapshot!( assert_snapshot!(
pyproject, @r###" pyproject, @r###"
[tool.uv.workspace] [project]
members = [] name = "foo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
"### "###
); );
}); });
@ -923,6 +928,56 @@ fn init_virtual_workspace() -> Result<()> {
Initialized project `bar` at `[TEMP_DIR]/foo/bar` Initialized project `bar` at `[TEMP_DIR]/foo/bar`
"###); "###);
let pyproject = fs_err::read_to_string(pyproject_toml)?;
insta::with_settings!({
filters => context.filters(),
}, {
assert_snapshot!(
pyproject, @r###"
[project]
name = "foo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
[tool.uv.workspace]
members = ["bar"]
"###
);
});
Ok(())
}
/// Run `uv init` from within a virtual workspace.
#[test]
fn init_virtual_workspace() -> Result<()> {
let context = TestContext::new("3.12");
let child = context.temp_dir.child("foo");
child.create_dir_all()?;
// Create a virtual workspace.
let pyproject_toml = child.child("pyproject.toml");
pyproject_toml.write_str(indoc! {
r"
[tool.uv.workspace]
members = []
",
})?;
uv_snapshot!(context.filters(), context.init().current_dir(&child).arg("bar"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Adding `bar` as member of workspace `[TEMP_DIR]/foo`
Initialized project `bar` at `[TEMP_DIR]/foo/bar`
"###);
let pyproject = fs_err::read_to_string(pyproject_toml)?; let pyproject = fs_err::read_to_string(pyproject_toml)?;
insta::with_settings!({ insta::with_settings!({
filters => context.filters(), filters => context.filters(),
@ -957,7 +1012,7 @@ fn init_nested_virtual_workspace() -> Result<()> {
----- stdout ----- ----- stdout -----
----- stderr ----- ----- stderr -----
warning: Nested workspaces are not supported, but outer workspace (`[TEMP_DIR]/`) includes `[TEMP_DIR]/foo` Adding `foo` as member of workspace `[TEMP_DIR]/`
Initialized workspace `foo` at `[TEMP_DIR]/foo` Initialized workspace `foo` at `[TEMP_DIR]/foo`
"###); "###);
@ -967,8 +1022,13 @@ fn init_nested_virtual_workspace() -> Result<()> {
}, { }, {
assert_snapshot!( assert_snapshot!(
pyproject, @r###" pyproject, @r###"
[tool.uv.workspace] [project]
members = [] name = "foo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.12"
dependencies = []
"### "###
); );
}); });
@ -980,7 +1040,7 @@ fn init_nested_virtual_workspace() -> Result<()> {
assert_snapshot!( assert_snapshot!(
workspace, @r###" workspace, @r###"
[tool.uv.workspace] [tool.uv.workspace]
members = [] members = ["foo"]
"### "###
); );
}); });

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,10 @@ fn run_with_python_version() -> Result<()> {
"anyio==3.6.0 ; python_version == '3.11'", "anyio==3.6.0 ; python_version == '3.11'",
"anyio==3.7.0 ; python_version == '3.12'", "anyio==3.7.0 ; python_version == '3.12'",
] ]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
let test_script = context.temp_dir.child("main.py"); let test_script = context.temp_dir.child("main.py");
@ -146,6 +150,10 @@ fn run_args() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -201,6 +209,10 @@ fn run_pep723_script() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio"] dependencies = ["anyio"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -363,6 +375,10 @@ fn run_pythonw_script() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio"] dependencies = ["anyio"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -473,6 +489,10 @@ fn run_managed_false() -> Result<()> {
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio"] dependencies = ["anyio"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv] [tool.uv]
managed = false managed = false
"# "#
@ -501,6 +521,10 @@ fn run_with() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio", "sniffio==1.3.1"] dependencies = ["anyio", "sniffio==1.3.1"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -597,6 +621,10 @@ fn run_with_editable() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio", "sniffio==1.3.1"] dependencies = ["anyio", "sniffio==1.3.1"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -660,6 +688,10 @@ fn run_with_editable() -> Result<()> {
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio", "sniffio==1.3.1"] dependencies = ["anyio", "sniffio==1.3.1"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
anyio = { path = "./src/anyio_local", editable = true } anyio = { path = "./src/anyio_local", editable = true }
"# "#
@ -719,6 +751,10 @@ fn run_locked() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -745,6 +781,10 @@ fn run_locked() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["iniconfig"] dependencies = ["iniconfig"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -797,6 +837,10 @@ fn run_frozen() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -820,6 +864,10 @@ fn run_frozen() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["iniconfig"] dependencies = ["iniconfig"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -853,6 +901,10 @@ fn run_empty_requirements_txt() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio", "sniffio==1.3.1"] dependencies = ["anyio", "sniffio==1.3.1"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -909,6 +961,10 @@ fn run_requirements_txt() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio", "sniffio==1.3.1"] dependencies = ["anyio", "sniffio==1.3.1"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -1024,6 +1080,10 @@ fn run_requirements_txt_arguments() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["typing_extensions"] dependencies = ["typing_extensions"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -1069,15 +1129,15 @@ fn run_editable() -> Result<()> {
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#" pyproject_toml.write_str(indoc! { r#"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project] [project]
name = "foo" name = "foo"
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -1136,6 +1196,10 @@ fn run_from_directory() -> Result<()> {
[project.scripts] [project.scripts]
main = "main:main" main = "main:main"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
let main_script = project_dir.child("main.py"); let main_script = project_dir.child("main.py");
@ -1180,6 +1244,10 @@ fn run_without_output() -> Result<()> {
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio", "sniffio==1.3.1"] dependencies = ["anyio", "sniffio==1.3.1"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -1219,15 +1287,15 @@ fn run_isolated_python_version() -> Result<()> {
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#" pyproject_toml.write_str(indoc! { r#"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project] [project]
name = "foo" name = "foo"
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio"] dependencies = ["anyio"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;
@ -1318,15 +1386,15 @@ fn run_no_project() -> Result<()> {
let pyproject_toml = context.temp_dir.child("pyproject.toml"); let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! { r#" pyproject_toml.write_str(indoc! { r#"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project] [project]
name = "foo" name = "foo"
version = "1.0.0" version = "1.0.0"
requires-python = ">=3.8" requires-python = ">=3.8"
dependencies = ["anyio"] dependencies = ["anyio"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"# "#
})?; })?;

View file

@ -21,6 +21,10 @@ fn sync() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["iniconfig"] dependencies = ["iniconfig"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -55,6 +59,10 @@ fn locked() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -81,6 +89,10 @@ fn locked() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["iniconfig"] dependencies = ["iniconfig"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -115,6 +127,10 @@ fn frozen() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -138,6 +154,10 @@ fn frozen() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["iniconfig"] dependencies = ["iniconfig"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -212,6 +232,10 @@ fn package() -> Result<()> {
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["child", "anyio>3"] dependencies = ["child", "anyio>3"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
child = { workspace = true } child = { workspace = true }
@ -237,6 +261,10 @@ fn package() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["iniconfig>1"] dependencies = ["iniconfig>1"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -312,6 +340,10 @@ fn mixed_requires_python() -> Result<()> {
name = "bird-feeder" name = "bird-feeder"
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.8" requires-python = ">=3.8"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -381,6 +413,10 @@ fn virtual_workspace_dev_dependencies() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["iniconfig>1"] dependencies = ["iniconfig>1"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -441,6 +477,10 @@ fn sync_build_isolation() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz"] dependencies = ["source-distribution @ https://files.pythonhosted.org/packages/10/1f/57aa4cce1b1abf6b433106676e15f9fa2c92ed2bd4cf77c3b50a9e9ac773/source_distribution-0.0.1.tar.gz"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -877,6 +917,10 @@ fn sync_environment() -> Result<()> {
requires-python = ">=3.10" requires-python = ">=3.10"
dependencies = ["iniconfig"] dependencies = ["iniconfig"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv] [tool.uv]
environments = ["python_version < '3.11'"] environments = ["python_version < '3.11'"]
"#, "#,
@ -916,6 +960,10 @@ fn read_metadata_statically_over_the_cache() -> Result<()> {
requires-python = ">=3.12" requires-python = ">=3.12"
# Python string sorting is the other way round. # Python string sorting is the other way round.
dependencies = ["anyio>=4,<5"] dependencies = ["anyio>=4,<5"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -945,6 +993,10 @@ fn no_install_project() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
@ -996,6 +1048,10 @@ fn no_install_workspace() -> Result<()> {
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0", "child"] dependencies = ["anyio==3.7.0", "child"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.workspace] [tool.uv.workspace]
members = ["child"] members = ["child"]
@ -1015,8 +1071,8 @@ fn no_install_workspace() -> Result<()> {
dependencies = ["iniconfig>1"] dependencies = ["iniconfig>1"]
[build-system] [build-system]
requires = ["hatchling"] requires = ["setuptools>=42", "wheel"]
build-backend = "hatchling.build" build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;
child child
@ -1073,6 +1129,10 @@ fn no_install_package() -> Result<()> {
version = "0.1.0" version = "0.1.0"
requires-python = ">=3.12" requires-python = ">=3.12"
dependencies = ["anyio==3.7.0"] dependencies = ["anyio==3.7.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#, "#,
)?; )?;

View file

@ -965,6 +965,10 @@ fn workspace_inherit_sources() -> Result<()> {
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.workspace] [tool.uv.workspace]
members = ["packages/*"] members = ["packages/*"]
"#})?; "#})?;
@ -977,6 +981,10 @@ fn workspace_inherit_sources() -> Result<()> {
name = "leaf" name = "leaf"
version = "0.1.0" version = "0.1.0"
dependencies = ["library"] dependencies = ["library"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
leaf.child("src/__init__.py").touch()?; leaf.child("src/__init__.py").touch()?;
@ -987,6 +995,10 @@ fn workspace_inherit_sources() -> Result<()> {
name = "library" name = "library"
version = "0.1.0" version = "0.1.0"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
library.child("src/__init__.py").touch()?; library.child("src/__init__.py").touch()?;
@ -1013,6 +1025,10 @@ fn workspace_inherit_sources() -> Result<()> {
version = "0.1.0" version = "0.1.0"
dependencies = ["library"] dependencies = ["library"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
library = { path = "../../../library", editable = true } library = { path = "../../../library", editable = true }
"#})?; "#})?;
@ -1036,6 +1052,10 @@ fn workspace_inherit_sources() -> Result<()> {
name = "leaf" name = "leaf"
version = "0.1.0" version = "0.1.0"
dependencies = ["library"] dependencies = ["library"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
// Update the root to include the source. // Update the root to include the source.
@ -1046,6 +1066,10 @@ fn workspace_inherit_sources() -> Result<()> {
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
library = { path = "../library", editable = true } library = { path = "../library", editable = true }
@ -1117,6 +1141,10 @@ fn workspace_inherit_sources() -> Result<()> {
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
library = { path = "../library", editable = true } library = { path = "../library", editable = true }
@ -1131,9 +1159,12 @@ fn workspace_inherit_sources() -> Result<()> {
version = "0.1.0" version = "0.1.0"
dependencies = ["library"] dependencies = ["library"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.sources] [tool.uv.sources]
application = { path = "../application", editable = true } application = { path = "../application", editable = true }
"#})?; "#})?;
// Resolving should succeed; the member should still use the root's source, despite defining // Resolving should succeed; the member should still use the root's source, despite defining
@ -1167,6 +1198,10 @@ fn workspace_unsatisfiable_member_dependencies() -> Result<()> {
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.workspace] [tool.uv.workspace]
members = ["packages/*"] members = ["packages/*"]
"#})?; "#})?;
@ -1179,6 +1214,10 @@ fn workspace_unsatisfiable_member_dependencies() -> Result<()> {
name = "leaf" name = "leaf"
version = "0.1.0" version = "0.1.0"
dependencies = ["httpx>9999"] dependencies = ["httpx>9999"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
leaf.child("src/__init__.py").touch()?; leaf.child("src/__init__.py").touch()?;
@ -1215,6 +1254,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting() -> Result<()> {
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.workspace] [tool.uv.workspace]
members = ["packages/*"] members = ["packages/*"]
"#})?; "#})?;
@ -1227,6 +1270,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting() -> Result<()> {
name = "foo" name = "foo"
version = "0.1.0" version = "0.1.0"
dependencies = ["anyio==4.1.0"] dependencies = ["anyio==4.1.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
foo.child("src/__init__.py").touch()?; foo.child("src/__init__.py").touch()?;
let bar = workspace.child("packages").child("bar"); let bar = workspace.child("packages").child("bar");
@ -1235,6 +1282,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting() -> Result<()> {
name = "bar" name = "bar"
version = "0.1.0" version = "0.1.0"
dependencies = ["anyio==4.2.0"] dependencies = ["anyio==4.2.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
bar.child("src/__init__.py").touch()?; bar.child("src/__init__.py").touch()?;
@ -1271,6 +1322,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_threeway() -> Result<
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.workspace] [tool.uv.workspace]
members = ["packages/*"] members = ["packages/*"]
"#})?; "#})?;
@ -1283,6 +1338,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_threeway() -> Result<
name = "red" name = "red"
version = "0.1.0" version = "0.1.0"
dependencies = ["anyio==4.1.0"] dependencies = ["anyio==4.1.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
red.child("src/__init__.py").touch()?; red.child("src/__init__.py").touch()?;
let knot = workspace.child("packages").child("knot"); let knot = workspace.child("packages").child("knot");
@ -1291,6 +1350,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_threeway() -> Result<
name = "knot" name = "knot"
version = "0.1.0" version = "0.1.0"
dependencies = ["anyio==4.2.0"] dependencies = ["anyio==4.2.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
knot.child("src/__init__.py").touch()?; knot.child("src/__init__.py").touch()?;
@ -1302,6 +1365,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_threeway() -> Result<
name = "bird" name = "bird"
version = "0.1.0" version = "0.1.0"
dependencies = ["anyio==4.3.0"] dependencies = ["anyio==4.3.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
bird.child("src/__init__.py").touch()?; bird.child("src/__init__.py").touch()?;
@ -1338,6 +1405,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_extra() -> Result<()>
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.workspace] [tool.uv.workspace]
members = ["packages/*"] members = ["packages/*"]
"#})?; "#})?;
@ -1350,6 +1421,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_extra() -> Result<()>
name = "foo" name = "foo"
version = "0.1.0" version = "0.1.0"
dependencies = ["anyio==4.1.0"] dependencies = ["anyio==4.1.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
foo.child("src/__init__.py").touch()?; foo.child("src/__init__.py").touch()?;
let bar = workspace.child("packages").child("bar"); let bar = workspace.child("packages").child("bar");
@ -1360,6 +1435,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_extra() -> Result<()>
[project.optional-dependencies] [project.optional-dependencies]
some_extra = ["anyio==4.2.0"] some_extra = ["anyio==4.2.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
bar.child("src/__init__.py").touch()?; bar.child("src/__init__.py").touch()?;
@ -1396,6 +1475,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_dev() -> Result<()> {
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.workspace] [tool.uv.workspace]
members = ["packages/*"] members = ["packages/*"]
"#})?; "#})?;
@ -1408,6 +1491,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_dev() -> Result<()> {
name = "foo" name = "foo"
version = "0.1.0" version = "0.1.0"
dependencies = ["anyio==4.1.0"] dependencies = ["anyio==4.1.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
foo.child("src/__init__.py").touch()?; foo.child("src/__init__.py").touch()?;
let bar = workspace.child("packages").child("bar"); let bar = workspace.child("packages").child("bar");
@ -1416,6 +1503,10 @@ fn workspace_unsatisfiable_member_dependencies_conflicting_dev() -> Result<()> {
name = "bar" name = "bar"
version = "0.1.0" version = "0.1.0"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv] [tool.uv]
dev-dependencies = ["anyio==4.2.0"] dev-dependencies = ["anyio==4.2.0"]
"#})?; "#})?;
@ -1455,6 +1546,10 @@ fn workspace_member_name_shadows_dependencies() -> Result<()> {
dependencies = [] dependencies = []
requires-python = ">=3.12" requires-python = ">=3.12"
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
[tool.uv.workspace] [tool.uv.workspace]
members = ["packages/*"] members = ["packages/*"]
"#})?; "#})?;
@ -1467,6 +1562,10 @@ fn workspace_member_name_shadows_dependencies() -> Result<()> {
name = "foo" name = "foo"
version = "0.1.0" version = "0.1.0"
dependencies = ["anyio==4.1.0"] dependencies = ["anyio==4.1.0"]
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
foo.child("src/__init__.py").touch()?; foo.child("src/__init__.py").touch()?;
@ -1477,6 +1576,10 @@ fn workspace_member_name_shadows_dependencies() -> Result<()> {
name = "anyio" name = "anyio"
version = "0.1.0" version = "0.1.0"
dependencies = [] dependencies = []
[build-system]
requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"
"#})?; "#})?;
anyio.child("src/__init__.py").touch()?; anyio.child("src/__init__.py").touch()?;

View file

@ -455,9 +455,11 @@ uv init [OPTIONS] [PATH]
</dd><dt><code>--version</code>, <code>-V</code></dt><dd><p>Display the uv version</p> </dd><dt><code>--version</code>, <code>-V</code></dt><dd><p>Display the uv version</p>
</dd><dt><code>--virtual</code></dt><dd><p>Create a virtual workspace instead of a project.</p> </dd><dt><code>--virtual</code></dt><dd><p>Create a virtual project, rather than a package.</p>
<p>A virtual workspace does not define project dependencies and cannot be published. Instead, workspace members declare project dependencies. Development dependencies may still be declared.</p> <p>A virtual project is a project that is not intended to be built as a Python package, such as a project that only contains scripts or other application code.</p>
<p>Virtual projects themselves are not installed into the Python environment.</p>
</dd></dl> </dd></dl>

View file

@ -890,6 +890,40 @@ requirements of any constituent packages.
--- ---
#### [`package`](#package) {: #package }
Whether the project should be considered a Python package, or a non-package ("virtual")
project.
Packages are built and installed into the virtual environment in editable mode and thus
require a build backend, while virtual projects are _not_ built or installed; instead, only
their dependencies are included in the virtual environment.
Creating a package requires that a `build-system` is present in the `pyproject.toml`, and
that the project adheres to a structure that adheres to the build backend's expectations
(e.g., a `src` layout).
**Default value**: `true`
**Type**: `bool`
**Example usage**:
=== "pyproject.toml"
```toml
[tool.uv]
package = false
```
=== "uv.toml"
```toml
package = false
```
---
#### [`prerelease`](#prerelease) {: #prerelease } #### [`prerelease`](#prerelease) {: #prerelease }
The strategy to use when considering pre-release versions. The strategy to use when considering pre-release versions.

7
uv.schema.json generated
View file

@ -274,6 +274,13 @@
"type": "string" "type": "string"
} }
}, },
"package": {
"description": "Whether the project should be considered a Python package, or a non-package (\"virtual\") project.\n\nPackages are built and installed into the virtual environment in editable mode and thus require a build backend, while virtual projects are _not_ built or installed; instead, only their dependencies are included in the virtual environment.\n\nCreating a package requires that a `build-system` is present in the `pyproject.toml`, and that the project adheres to a structure that adheres to the build backend's expectations (e.g., a `src` layout).",
"type": [
"boolean",
"null"
]
},
"pip": { "pip": {
"anyOf": [ "anyOf": [
{ {