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

@ -33,6 +33,10 @@ pub struct PyProjectToml {
/// The raw unserialized document.
#[serde(skip)]
pub raw: String,
/// Used to determine whether a `build-system` is present.
#[serde(default, skip_serializing)]
build_system: Option<serde::de::IgnoredAny>,
}
impl PyProjectToml {
@ -41,6 +45,23 @@ impl PyProjectToml {
let pyproject = toml::from_str(&raw)?;
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.
@ -100,6 +121,24 @@ pub struct ToolUv {
"#
)]
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
/// default in `uv run` and `uv sync`, but will not appear in the project's published metadata.
#[cfg_attr(