diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 005ab499680e1..53ae970ae41ea 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -73,7 +73,7 @@ pub struct Options { #[serde(default, skip_serializing)] #[cfg_attr(feature = "schemars", schemars(skip))] - r#virtual: serde::de::IgnoredAny, + r#package: serde::de::IgnoredAny, } impl Options { diff --git a/crates/uv-workspace/src/pyproject.rs b/crates/uv-workspace/src/pyproject.rs index fb093be226cbe..89c4af68200ea 100644 --- a/crates/uv-workspace/src/pyproject.rs +++ b/crates/uv-workspace/src/pyproject.rs @@ -46,25 +46,21 @@ impl PyProjectToml { Ok(PyProjectToml { raw, ..pyproject }) } - /// Returns `true` if the project should be considered "virtual". - pub fn is_virtual(&self) -> bool { - // A project is virtual if `virtual = true` is set... - if self + /// 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.r#virtual) - == Some(true) + .and_then(|uv| uv.package) { - return true; + return is_package; } - // Or if `build-system` is not present. - if self.build_system.is_none() { - return true; - } - - false + // Otherwise, a project is assumed to be a package if `build-system` is present. + self.build_system.is_some() } } @@ -125,15 +121,24 @@ pub struct ToolUv { "# )] pub managed: Option, - /// Whether the project should be considered "virtual". + /// 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#" - virtual = false + package = false "# )] - pub r#virtual: Option, + pub package: Option, /// 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( diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index c42954ec55257..645d128879e9c 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -311,12 +311,12 @@ fn apply_no_virtual_project( .packages() .iter() .filter_map(|(name, package)| { - // A project is virtual if it's explicitly marked as virtual, _or_ if it's missing a - // build system. - if package.pyproject_toml().is_virtual() { - Some(name) - } else { + // 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::>(); diff --git a/crates/uv/tests/lock.rs b/crates/uv/tests/lock.rs index 5018cdcce9595..a1102a4119a56 100644 --- a/crates/uv/tests/lock.rs +++ b/crates/uv/tests/lock.rs @@ -11190,7 +11190,7 @@ fn lock_trailing_slash() -> Result<()> { Ok(()) } -/// Lock a project with `virtual = true`. +/// Lock a project with `package = false`, making it a virtual project. #[test] fn lock_explicit_virtual_project() -> Result<()> { let context = TestContext::new("3.12"); @@ -11203,11 +11203,13 @@ fn lock_explicit_virtual_project() -> Result<()> { version = "0.1.0" requires-python = ">=3.12" dependencies = ["black"] + [build-system] requires = ["setuptools>=42", "wheel"] build-backend = "setuptools.build_meta" + [tool.uv] - virtual = true + package = false dev-dependencies = [ "anyio" ] @@ -11423,6 +11425,7 @@ fn lock_implicit_virtual_project() -> Result<()> { version = "0.1.0" requires-python = ">=3.12" dependencies = ["black"] + [tool.uv] dev-dependencies = [ "anyio" diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 9b8e599d9149b..1f6dd3f668beb 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -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 } The strategy to use when considering pre-release versions. @@ -1010,31 +1044,6 @@ those that are downloaded and installed by uv. --- -#### [`r#virtual`](#r#virtual) {: #r#virtual } - -Whether the project should be considered "virtual". - -**Default value**: `true` - -**Type**: `bool` - -**Example usage**: - -=== "pyproject.toml" - - ```toml - [tool.uv] - virtual = false - ``` -=== "uv.toml" - - ```toml - - virtual = false - ``` - ---- - #### [`reinstall`](#reinstall) {: #reinstall } Reinstall all packages, regardless of whether they're already installed. Implies `refresh`. diff --git a/uv.schema.json b/uv.schema.json index 6518136176dc1..da3e127c8c38c 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -274,6 +274,13 @@ "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": { "anyOf": [ { @@ -379,13 +386,6 @@ "$ref": "#/definitions/Requirement" } }, - "virtual": { - "description": "Whether the project should be considered \"virtual\".", - "type": [ - "boolean", - "null" - ] - }, "workspace": { "description": "The workspace definition for the project, if any.", "anyOf": [