This commit is contained in:
हिमांशु 2025-07-04 16:53:00 -04:00 committed by GitHub
commit 7ed590cc3b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 201 additions and 1 deletions

View file

@ -379,6 +379,7 @@ pub struct ToolUv {
#[serde(flatten)]
pub top_level: ResolverInstallerOptions,
pub override_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub excluded_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub build_constraint_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
pub sources: Option<BTreeMap<PackageName, Sources>>,

View file

@ -111,6 +111,9 @@ pub struct Options {
#[cfg_attr(feature = "schemars", schemars(skip))]
pub override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
#[cfg_attr(feature = "schemars", schemars(skip))]
pub excluded_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
#[cfg_attr(feature = "schemars", schemars(skip))]
pub constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
@ -1860,6 +1863,7 @@ pub struct OptionsWire {
// `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
// They're respected in both `pyproject.toml` and `uv.toml` files.
override_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
excluded_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
build_constraint_dependencies: Option<Vec<Requirement<VerbatimParsedUrl>>>,
environments: Option<SupportedEnvironments>,
@ -1928,6 +1932,7 @@ impl From<OptionsWire> for Options {
pip,
cache_keys,
override_dependencies,
excluded_dependencies,
constraint_dependencies,
build_constraint_dependencies,
environments,
@ -1996,6 +2001,7 @@ impl From<OptionsWire> for Options {
cache_keys,
build_backend,
override_dependencies,
excluded_dependencies,
constraint_dependencies,
build_constraint_dependencies,
environments,

View file

@ -435,6 +435,37 @@ pub struct ToolUv {
)]
pub override_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
/// Excludes to apply when resolving the project's dependencies.
///
/// Excludes are used to prevent installation of a specific package, even if other dependencies
/// require it.
///
/// Including a package as an exclude will _not_ trigger installation of the package on its
/// own; instead, the package must be requested elsewhere in the project's first-party or
/// transitive dependencies.
///
/// !!! note
/// In `uv lock`, `uv sync`, and `uv run`, uv will only read `excluded-dependencies` from
/// the `pyproject.toml` at the workspace root, and will ignore any declarations in other
/// workspace members or `uv.toml` files.
#[cfg_attr(
feature = "schemars",
schemars(
with = "Option<Vec<String>>",
description = "PEP 508-style requirements, e.g., `ruff==0.5.0`, or `ruff @ https://...`."
)
)]
#[option(
default = "[]",
value_type = "list[str]",
example = r#"
# Always install Werkzeug 2.3.0, regardless of whether transitive dependencies request
# a different version.
excluded-dependencies = ["numpy"]
"#
)]
pub excluded_dependencies: Option<Vec<uv_pep508::Requirement<VerbatimParsedUrl>>>,
/// Constraints to apply when resolving the project's dependencies.
///
/// Constraints are used to restrict the versions of dependencies that are selected during

View file

@ -1870,6 +1870,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"excluded-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -1966,6 +1967,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"excluded-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2177,6 +2179,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"excluded-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2285,6 +2288,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"excluded-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2406,6 +2410,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"excluded-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,
@ -2501,6 +2506,7 @@ mod tests {
"dependency-groups": null,
"dev-dependencies": null,
"override-dependencies": null,
"excluded-dependencies": null,
"constraint-dependencies": null,
"build-constraint-dependencies": null,
"environments": null,

View file

@ -1711,6 +1711,66 @@ fn lock_project_with_overrides() -> Result<()> {
Ok(())
}
/// Lock a project with `uv.tool.excluded-dependencies`
#[test]
fn lock_project_with_excluded_dependencies() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["pytest==7.4.0", "requests==2.31.0"]
[tool.uv]
excluded-dependencies = ["pytest"]
"#,
)?;
uv_snapshot!(context.filters(), context.lock(), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 11 packages in [TIME]
");
// Re-run with `--locked`.
uv_snapshot!(context.filters(), context.lock().arg("--locked"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Resolved 11 packages in [TIME]
");
// Install the dependencies from the lockfile.
uv_snapshot!(context.filters(), context.sync().arg("--frozen"), @r"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Prepared 9 packages in [TIME]
Installed 9 packages in [TIME]
+ certifi==2024.2.2
+ charset-normalizer==3.3.2
+ idna==3.6
+ iniconfig==2.0.0
+ packaging==24.0
+ pluggy==1.4.0
+ pytest==7.4.0
+ requests==2.31.0
+ urllib3==2.2.1
");
Ok(())
}
/// Lock a project with `uv.tool.override-dependencies` that reference `tool.uv.sources`.
#[test]

View file

@ -3920,6 +3920,61 @@ fn override_dependency_from_pyproject() -> Result<()> {
Ok(())
}
/// Check the `tool.uv.excluded-dependencies` in `pyproject.toml` is respected
/// Check that `tool.uv.excluded-dependencies` in `pyproject.toml` excludes dependencies.
#[test]
fn exclude_dependency_from_pyproject() -> Result<()> {
let context = TestContext::new("3.12");
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(
r#"[project]
name = "example"
version = "0.0.0"
dependencies = [
"flask==3.0.0"
]
[tool.uv]
excluded-dependencies = [
"werkzeug"
]
"#,
)?;
uv_snapshot!(context.filters(), context.pip_compile()
.arg("pyproject.toml")
.current_dir(&context.temp_dir)
, @r"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] pyproject.toml
blinker==1.7.0
# via flask
click==8.1.7
# via flask
flask==3.0.0
# via example (pyproject.toml)
itsdangerous==2.1.2
# via flask
jinja2==3.1.3
# via flask
markupsafe==2.1.5
# via
# jinja2
# werkzeug
werkzeug==3.0.1
# via flask
----- stderr -----
Resolved 7 packages in [TIME]
"
);
Ok(())
}
/// Check that `tool.uv.constraint-dependencies` in `pyproject.toml` is respected.
#[test]
fn constraint_dependency_from_pyproject() -> Result<()> {

View file

@ -3987,7 +3987,7 @@ fn resolve_config_file() -> anyhow::Result<()> {
|
1 | [project]
| ^^^^^^^
unknown field `project`, expected one of `required-version`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend`
unknown field `project`, expected one of `required-version`, `native-tls`, `offline`, `no-cache`, `cache-dir`, `preview`, `python-preference`, `python-downloads`, `concurrent-downloads`, `concurrent-builds`, `concurrent-installs`, `index`, `index-url`, `extra-index-url`, `no-index`, `find-links`, `index-strategy`, `keyring-provider`, `allow-insecure-host`, `resolution`, `prerelease`, `fork-strategy`, `dependency-metadata`, `config-settings`, `no-build-isolation`, `no-build-isolation-package`, `exclude-newer`, `link-mode`, `compile-bytecode`, `no-sources`, `upgrade`, `upgrade-package`, `reinstall`, `reinstall-package`, `no-build`, `no-build-package`, `no-binary`, `no-binary-package`, `python-install-mirror`, `pypy-install-mirror`, `python-downloads-json-url`, `publish-url`, `trusted-publishing`, `check-url`, `add-bounds`, `pip`, `cache-keys`, `override-dependencies`, `excluded-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `dependency-groups`, `dev-dependencies`, `build-backend`
"
);

View file

@ -202,6 +202,37 @@ environments = ["sys_platform == 'darwin'"]
---
### [`excluded-dependencies`](#excluded-dependencies) {: #excluded-dependencies }
Excludes to apply when resolving the project's dependencies.
Excludes are used to prevent installation of a specific package, even if other dependencies
require it.
Including a package as an exclude will _not_ trigger installation of the package on its
own; instead, the package must be requested elsewhere in the project's first-party or
transitive dependencies.
!!! note
In `uv lock`, `uv sync`, and `uv run`, uv will only read `excluded-dependencies` from
the `pyproject.toml` at the workspace root, and will ignore any declarations in other
workspace members or `uv.toml` files.
**Default value**: `[]`
**Type**: `list[str]`
**Example usage**:
```toml title="pyproject.toml"
[tool.uv]
# Always install Werkzeug 2.3.0, regardless of whether transitive dependencies request
# a different version.
excluded-dependencies = ["numpy"]
```
---
### [`index`](#index) {: #index }
The indexes to use when resolving dependencies.

10
uv.schema.json generated
View file

@ -203,6 +203,16 @@
}
]
},
"excluded-dependencies": {
"description": "PEP 508-style requirements, e.g., `ruff==0.5.0`, or `ruff @ https://...`.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
},
"extra-index-url": {
"description": "Extra URLs of package indexes to use, in addition to `--index-url`.\n\nAccepts either a repository compliant with [PEP 503](https://peps.python.org/pep-0503/)\n(the simple repository API), or a local directory laid out in the same format.\n\nAll indexes provided via this flag take priority over the index specified by\n[`index_url`](#index-url) or [`index`](#index) with `default = true`. When multiple indexes\nare provided, earlier values take priority.\n\nTo control uv's resolution strategy when multiple indexes are present, see\n[`index_strategy`](#index-strategy).\n\n(Deprecated: use `index` instead.)",
"type": [