From b94a7581a9a4727e4f8e5b38a153113b0bbdc693 Mon Sep 17 00:00:00 2001 From: Ahmed Ilyas Date: Thu, 17 Apr 2025 20:43:43 +0200 Subject: [PATCH 1/6] Add `default-extras` field in `[tool.uv]` pyproject.toml config --- crates/uv-settings/src/lib.rs | 6 ++++ crates/uv-settings/src/settings.rs | 6 ++++ crates/uv-workspace/src/pyproject.rs | 14 ++++++++- crates/uv-workspace/src/workspace.rs | 6 ++++ crates/uv/src/commands/project/add.rs | 8 ++--- crates/uv/src/commands/project/export.rs | 5 ++- crates/uv/src/commands/project/mod.rs | 39 ++++++++++++++++++++++-- crates/uv/src/commands/project/remove.rs | 8 ++--- crates/uv/src/commands/project/run.rs | 4 +-- crates/uv/src/commands/project/sync.rs | 7 +++-- crates/uv/tests/it/show_settings.rs | 6 ++-- docs/reference/settings.md | 19 ++++++++++++ uv.schema.json | 39 ++++++++++++++++++++++++ 13 files changed, 144 insertions(+), 23 deletions(-) diff --git a/crates/uv-settings/src/lib.rs b/crates/uv-settings/src/lib.rs index 54ae4e261b651..225a6753cc612 100644 --- a/crates/uv-settings/src/lib.rs +++ b/crates/uv-settings/src/lib.rs @@ -216,6 +216,12 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> { "default-groups", )); } + if options.default_extras.is_some() { + return Err(Error::PyprojectOnlyField( + path.to_path_buf(), + "default-extras", + )); + } if options.managed.is_some() { return Err(Error::PyprojectOnlyField(path.to_path_buf(), "managed")); } diff --git a/crates/uv-settings/src/settings.rs b/crates/uv-settings/src/settings.rs index 7696992878445..fcda4b0f61922 100644 --- a/crates/uv-settings/src/settings.rs +++ b/crates/uv-settings/src/settings.rs @@ -136,6 +136,9 @@ pub struct Options { #[cfg_attr(feature = "schemars", schemars(skip))] pub default_groups: Option, + #[cfg_attr(feature = "schemars", schemars(skip))] + pub default_extras: Option, + #[cfg_attr(feature = "schemars", schemars(skip))] pub managed: Option, @@ -1842,6 +1845,7 @@ pub struct OptionsWire { managed: Option, r#package: Option, default_groups: Option, + default_extras: Option, dev_dependencies: Option, // Build backend @@ -1905,6 +1909,7 @@ impl From for Options { workspace, sources, default_groups, + default_extras, dev_dependencies, managed, package, @@ -1978,6 +1983,7 @@ impl From for Options { sources, dev_dependencies, default_groups, + default_extras, managed, package, } diff --git a/crates/uv-workspace/src/pyproject.rs b/crates/uv-workspace/src/pyproject.rs index fab891cd78560..63f03787f3b94 100644 --- a/crates/uv-workspace/src/pyproject.rs +++ b/crates/uv-workspace/src/pyproject.rs @@ -23,7 +23,7 @@ use uv_distribution_types::{Index, IndexName, RequirementSource}; use uv_fs::{relative_to, PortablePathBuf}; use uv_git_types::GitReference; use uv_macros::OptionsMetadata; -use uv_normalize::{DefaultGroups, ExtraName, GroupName, PackageName}; +use uv_normalize::{DefaultExtras, DefaultGroups, ExtraName, GroupName, PackageName}; use uv_pep440::{Version, VersionSpecifiers}; use uv_pep508::MarkerTree; use uv_pypi_types::{ @@ -341,6 +341,18 @@ pub struct ToolUv { )] pub package: Option, + /// The list of `optional-dependencies` extras to install by default. + /// + /// Can also be the literal "all" to default enable all extras. + #[option( + default = r#"[""]"#, + value_type = r#"str | list[str]"#, + example = r#" + default-extras = ["docs", "dev"] + "# + )] + pub default_extras: Option, + /// The list of `dependency-groups` to install by default. /// /// Can also be the literal `"all"` to default enable all groups. diff --git a/crates/uv-workspace/src/workspace.rs b/crates/uv-workspace/src/workspace.rs index 374b9e76204ae..b4928378b2b7c 100644 --- a/crates/uv-workspace/src/workspace.rs +++ b/crates/uv-workspace/src/workspace.rs @@ -1817,6 +1817,7 @@ mod tests { }, "managed": null, "package": null, + "default-extras": null, "default-groups": null, "dev-dependencies": null, "override-dependencies": null, @@ -1912,6 +1913,7 @@ mod tests { }, "managed": null, "package": null, + "default-extras": null, "default-groups": null, "dev-dependencies": null, "override-dependencies": null, @@ -2122,6 +2124,7 @@ mod tests { }, "managed": null, "package": null, + "default-extras": null, "default-groups": null, "dev-dependencies": null, "override-dependencies": null, @@ -2229,6 +2232,7 @@ mod tests { }, "managed": null, "package": null, + "default-extras": null, "default-groups": null, "dev-dependencies": null, "override-dependencies": null, @@ -2349,6 +2353,7 @@ mod tests { }, "managed": null, "package": null, + "default-extras": null, "default-groups": null, "dev-dependencies": null, "override-dependencies": null, @@ -2443,6 +2448,7 @@ mod tests { }, "managed": null, "package": null, + "default-extras": null, "default-groups": null, "dev-dependencies": null, "override-dependencies": null, diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 7c7b9304a5d38..681dcbd7c780f 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -30,7 +30,7 @@ use uv_distribution_types::{ use uv_fs::Simplified; use uv_git::GIT_STORE; use uv_git_types::GitReference; -use uv_normalize::{DefaultExtras, PackageName, DEV_DEPENDENCIES}; +use uv_normalize::{PackageName, DEV_DEPENDENCIES}; use uv_pep508::{ExtraName, MarkerTree, UnnamedRequirement, VersionOrUrl}; use uv_pypi_types::{ParsedUrl, VerbatimParsedUrl}; use uv_python::{Interpreter, PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest}; @@ -52,8 +52,8 @@ use crate::commands::project::install_target::InstallTarget; use crate::commands::project::lock::LockMode; use crate::commands::project::lock_target::LockTarget; use crate::commands::project::{ - default_dependency_groups, init_script_python_requirement, PlatformState, ProjectEnvironment, - ProjectError, ProjectInterpreter, ScriptInterpreter, UniversalState, + default_dependency_groups, default_extras, init_script_python_requirement, PlatformState, + ProjectEnvironment, ProjectError, ProjectInterpreter, ScriptInterpreter, UniversalState, }; use crate::commands::reporters::{PythonDownloadReporter, ResolverReporter}; use crate::commands::{diagnostics, project, ExitStatus, ScriptPath}; @@ -917,7 +917,7 @@ async fn lock_and_sync( let default_groups = default_dependency_groups(project.pyproject_toml())?; // Determine the default extras to include. - let default_extras = DefaultExtras::default(); + let default_extras = default_extras(project.pyproject_toml())?; // Identify the installation target. let target = match &project { diff --git a/crates/uv/src/commands/project/export.rs b/crates/uv/src/commands/project/export.rs index 577f19d7ad582..18d99c9133003 100644 --- a/crates/uv/src/commands/project/export.rs +++ b/crates/uv/src/commands/project/export.rs @@ -24,7 +24,7 @@ use crate::commands::project::install_target::InstallTarget; use crate::commands::project::lock::{LockMode, LockOperation}; use crate::commands::project::lock_target::LockTarget; use crate::commands::project::{ - default_dependency_groups, detect_conflicts, ProjectError, ProjectInterpreter, + default_dependency_groups, default_extras, detect_conflicts, ProjectError, ProjectInterpreter, ScriptInterpreter, UniversalState, }; use crate::commands::{diagnostics, ExitStatus, OutputWriter}; @@ -118,10 +118,9 @@ pub(crate) async fn export( // Determine the default extras to include. let default_extras = match &target { - ExportTarget::Project(_project) => DefaultExtras::default(), + ExportTarget::Project(project) => default_extras(project.pyproject_toml())?, ExportTarget::Script(_) => DefaultExtras::default(), }; - let dev = dev.with_defaults(default_groups); let extras = extras.with_defaults(default_extras); diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 1677754a2db02..29d754faa9654 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -14,7 +14,7 @@ use uv_cache_key::cache_digest; use uv_client::{BaseClientBuilder, FlatIndexClient, RegistryClientBuilder}; use uv_configuration::{ Concurrency, Constraints, DependencyGroupsWithDefaults, DryRun, ExtrasSpecification, - PreviewMode, Reinstall, SourceStrategy, Upgrade, + ExtrasSpecificationWithDefaults, PreviewMode, Reinstall, SourceStrategy, Upgrade, }; use uv_dispatch::{BuildDispatch, SharedState}; use uv_distribution::{DistributionDatabase, LoweredRequirement}; @@ -24,7 +24,9 @@ use uv_distribution_types::{ use uv_fs::{LockedFile, Simplified, CWD}; use uv_git::ResolvedRepositoryReference; use uv_installer::{SatisfiesResult, SitePackages}; -use uv_normalize::{DefaultGroups, ExtraName, GroupName, PackageName, DEV_DEPENDENCIES}; +use uv_normalize::{ + DefaultExtras, DefaultGroups, ExtraName, GroupName, PackageName, DEV_DEPENDENCIES, +}; use uv_pep440::{Version, VersionSpecifiers}; use uv_pep508::MarkerTreeContents; use uv_pypi_types::{ConflictPackage, ConflictSet, Conflicts}; @@ -152,6 +154,9 @@ pub(crate) enum ProjectError { #[error("PEP 723 scripts do not support dependency groups, but group `{0}` was specified")] MissingGroupScript(GroupName), + #[error("Default extra `{0}` (from `tool.uv.default-extras`) is not defined in the project's `optional-dependencies` table")] + MissingDefaultExtra(ExtraName), + #[error("Default group `{0}` (from `tool.uv.default-groups`) is not defined in the project's `dependency-groups` table")] MissingDefaultGroup(GroupName), @@ -2321,12 +2326,40 @@ pub(crate) fn default_dependency_groups( } } +/// Returns the default extras from the [`PyProjectToml`]. +#[allow(clippy::result_large_err)] +pub(crate) fn default_extras( + pyproject_toml: &PyProjectToml, +) -> Result { + if let Some(defaults) = pyproject_toml + .tool + .as_ref() + .and_then(|tool| tool.uv.as_ref().and_then(|uv| uv.default_extras.as_ref())) + { + if let DefaultExtras::List(defaults) = defaults { + for extra in defaults { + if pyproject_toml + .project + .as_ref() + .and_then(|project| project.optional_dependencies.as_ref()) + .is_none_or(|extras| !extras.contains_key(extra)) + { + return Err(ProjectError::MissingDefaultExtra(extra.clone())); + } + } + } + Ok(defaults.clone()) + } else { + Ok(DefaultExtras::default()) + } +} + /// Validate that we aren't trying to install extras or groups that /// are declared as conflicting. #[allow(clippy::result_large_err)] pub(crate) fn detect_conflicts( lock: &Lock, - extras: &ExtrasSpecification, + extras: &ExtrasSpecificationWithDefaults, dev: &DependencyGroupsWithDefaults, ) -> Result<(), ProjectError> { // Note that we need to collect all extras and groups that match in diff --git a/crates/uv/src/commands/project/remove.rs b/crates/uv/src/commands/project/remove.rs index f77f162201fdf..6d2872c71ed0a 100644 --- a/crates/uv/src/commands/project/remove.rs +++ b/crates/uv/src/commands/project/remove.rs @@ -13,7 +13,7 @@ use uv_configuration::{ PreviewMode, }; use uv_fs::Simplified; -use uv_normalize::{DefaultExtras, DEV_DEPENDENCIES}; +use uv_normalize::DEV_DEPENDENCIES; use uv_pep508::PackageName; use uv_python::{PythonDownloads, PythonPreference, PythonRequest}; use uv_scripts::{Pep723ItemRef, Pep723Metadata, Pep723Script}; @@ -30,8 +30,8 @@ use crate::commands::project::install_target::InstallTarget; use crate::commands::project::lock::LockMode; use crate::commands::project::lock_target::LockTarget; use crate::commands::project::{ - default_dependency_groups, ProjectEnvironment, ProjectError, ProjectInterpreter, - ScriptInterpreter, UniversalState, + default_dependency_groups, default_extras, ProjectEnvironment, ProjectError, + ProjectInterpreter, ScriptInterpreter, UniversalState, }; use crate::commands::{diagnostics, project, ExitStatus}; use crate::printer::Printer; @@ -318,7 +318,7 @@ pub(crate) async fn remove( let default_groups = default_dependency_groups(project.pyproject_toml())?; // Determine the default extras to include. - let default_extras = DefaultExtras::default(); + let default_extras = default_extras(project.pyproject_toml())?; // Identify the installation target. let target = match &project { diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 0d7694e6370d1..d6d182126fb55 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -48,7 +48,7 @@ use crate::commands::project::install_target::InstallTarget; use crate::commands::project::lock::LockMode; use crate::commands::project::lock_target::LockTarget; use crate::commands::project::{ - default_dependency_groups, script_specification, update_environment, + default_dependency_groups, default_extras, script_specification, update_environment, validate_project_requires_python, EnvironmentSpecification, ProjectEnvironment, ProjectError, ScriptEnvironment, ScriptInterpreter, UniversalState, WorkspacePython, }; @@ -672,7 +672,7 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl let default_groups = default_dependency_groups(project.pyproject_toml())?; // Determine the default extras to include. - let default_extras = DefaultExtras::default(); + let default_extras = default_extras(project.pyproject_toml())?; // Determine the lock mode. let mode = if frozen { diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index d95dfc016611e..7ee8d698a5741 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -40,8 +40,9 @@ use crate::commands::project::install_target::InstallTarget; use crate::commands::project::lock::{LockMode, LockOperation, LockResult}; use crate::commands::project::lock_target::LockTarget; use crate::commands::project::{ - default_dependency_groups, detect_conflicts, script_specification, update_environment, - PlatformState, ProjectEnvironment, ProjectError, ScriptEnvironment, UniversalState, + default_dependency_groups, default_extras, detect_conflicts, script_specification, + update_environment, PlatformState, ProjectEnvironment, ProjectError, ScriptEnvironment, + UniversalState, }; use crate::commands::{diagnostics, ExitStatus}; use crate::printer::Printer; @@ -123,7 +124,7 @@ pub(crate) async fn sync( // Determine the default extras to include. let default_extras = match &target { - SyncTarget::Project(_project) => DefaultExtras::default(), + SyncTarget::Project(project) => default_extras(project.pyproject_toml())?, SyncTarget::Script(..) => DefaultExtras::default(), }; diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index 2d5ac86094168..281b6f2a4f273 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -3939,7 +3939,7 @@ fn resolve_config_file() -> anyhow::Result<()> { .arg("--show-settings") .arg("--config-file") .arg(config.path()) - .arg("requirements.in"), @r###" + .arg("requirements.in"), @r" success: false exit_code: 2 ----- stdout ----- @@ -3950,8 +3950,8 @@ 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`, `publish-url`, `trusted-publishing`, `check-url`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-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`, `publish-url`, `trusted-publishing`, `check-url`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `default-extras`, `dev-dependencies`, `build-backend` + " ); // Write an _actual_ `pyproject.toml`. diff --git a/docs/reference/settings.md b/docs/reference/settings.md index 2d83a96d690bc..3dcdc3ead838d 100644 --- a/docs/reference/settings.md +++ b/docs/reference/settings.md @@ -108,6 +108,25 @@ constraint-dependencies = ["grpcio<1.65"] --- +### [`default-extras`](#default-extras) {: #default-extras } + +The list of `optional-dependencies` extras to install by default. + +Can also be the literal "all" to default enable all extras. + +**Default value**: `[""]` + +**Type**: `str | list[str]` + +**Example usage**: + +```toml title="pyproject.toml" +[tool.uv] +default-extras = ["docs", "dev"] +``` + +--- + ### [`default-groups`](#default-groups) {: #default-groups } The list of `dependency-groups` to install by default. diff --git a/uv.schema.json b/uv.schema.json index aab9c9e455022..dfdd3e3f5dadb 100644 --- a/uv.schema.json +++ b/uv.schema.json @@ -129,6 +129,17 @@ "type": "string" } }, + "default-extras": { + "description": "The list of `optional-dependencies` extras to install by default.\n\nCan also be the literal \"all\" to default enable all extras.", + "anyOf": [ + { + "$ref": "#/definitions/DefaultExtras" + }, + { + "type": "null" + } + ] + }, "default-groups": { "description": "The list of `dependency-groups` to install by default.\n\nCan also be the literal `\"all\"` to default enable all groups.", "anyOf": [ @@ -744,6 +755,34 @@ "$ref": "#/definitions/ConfigSettingValue" } }, + "DefaultExtras": { + "description": "Either the literal \"all\" or a list of extras", + "oneOf": [ + { + "description": "All extras are defaulted", + "type": "string", + "enum": [ + "All" + ] + }, + { + "description": "A list of extras", + "type": "object", + "required": [ + "List" + ], + "properties": { + "List": { + "type": "array", + "items": { + "$ref": "#/definitions/ExtraName" + } + } + }, + "additionalProperties": false + } + ] + }, "DefaultGroups": { "description": "Either the literal \"all\" or a list of groups", "oneOf": [ From 3da712a92f9bf7a20d38371c79a0034ad75788e6 Mon Sep 17 00:00:00 2001 From: Ahmed Ilyas Date: Fri, 18 Apr 2025 11:02:01 +0200 Subject: [PATCH 2/6] Support default extras in `uv sync` --- crates/uv-cli/src/lib.rs | 18 +- crates/uv/src/settings.rs | 8 +- crates/uv/tests/it/sync.rs | 376 +++++++++++++++++++++++++++++++++++++ docs/reference/cli.md | 12 +- 4 files changed, 408 insertions(+), 6 deletions(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 31f66388a6a59..2d9b58b1917e4 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -3092,12 +3092,28 @@ pub struct SyncArgs { #[arg(long, conflicts_with = "extra")] pub all_extras: bool, - /// Exclude the specified optional dependencies, if `--all-extras` is supplied. + /// Disable the specified extra. + /// + /// This options always takes precedence over default extras, + /// `--all-extras`, and `--extra`. /// /// May be provided multiple times. #[arg(long)] pub no_extra: Vec, + /// Ignore the default extras. + /// + /// uv includes the extras defined in `tool.uv.default-extras` by default. + /// This disables that option, however, specific extras can still be included with `--extra`. + #[arg(long)] + pub no_default_extras: bool, + + /// Only include dependencies from the specified extra. + /// + /// May be provided multiple times. Implies `--no-default-extras`. + #[arg(long, conflicts_with_all = ["extra", "all_extras"])] + pub only_extra: Vec, + #[arg(long, overrides_with("all_extras"), hide = true)] pub no_all_extras: bool, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 698bac2ded7fe..ec8c0e11278ec 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -1075,6 +1075,8 @@ impl SyncSettings { let SyncArgs { extra, all_extras, + no_default_extras, + only_extra, no_extra, no_all_extras, dev, @@ -1132,10 +1134,8 @@ impl SyncSettings { extras: ExtrasSpecification::from_args( extra.unwrap_or_default(), no_extra, - // TODO(blueraft): support no_default_extras - false, - // TODO(blueraft): support only_extra - vec![], + no_default_extras, + only_extra, flag(all_extras, no_all_extras).unwrap_or_default(), ), dev: DependencyGroups::from_args( diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index 6ab00bfc636d1..a8f8d6c5f308b 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -2144,6 +2144,382 @@ fn sync_non_existent_default_group() -> Result<()> { Ok(()) } +#[test] +fn sync_default_extras() -> 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 = ["typing-extensions"] + + [project.optional-dependencies] + dev = ["iniconfig"] + foo = ["anyio"] + bar = ["requests"] + "#, + )?; + + context.lock().assert().success(); + + // Only the main dependencies should be installed. + uv_snapshot!(context.filters(), context.sync(), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + typing-extensions==4.10.0 + "); + + // If we set a different default extras, it should be synced instead. + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["typing-extensions"] + + [project.optional-dependencies] + dev = ["iniconfig"] + foo = ["anyio"] + bar = ["requests"] + + [tool.uv] + default-extras = ["foo"] + "#, + )?; + + uv_snapshot!(context.filters(), context.sync(), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 3 packages in [TIME] + Installed 3 packages in [TIME] + + anyio==4.3.0 + + idna==3.6 + + sniffio==1.3.1 + "###); + + // `--no-extra` should remove from the defaults. + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["typing-extensions"] + + [project.optional-dependencies] + dev = ["iniconfig"] + foo = ["anyio"] + bar = ["requests"] + + [tool.uv] + default-extras = ["foo"] + "#, + )?; + + uv_snapshot!(context.filters(), context.sync().arg("--no-extra").arg("foo"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 3 packages in [TIME] + - anyio==4.3.0 + - idna==3.6 + - sniffio==1.3.1 + "###); + + // Using `--extra` should include the defaults + uv_snapshot!(context.filters(), context.sync().arg("--extra").arg("dev"), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 1 package in [TIME] + Installed 4 packages in [TIME] + + anyio==4.3.0 + + idna==3.6 + + iniconfig==2.0.0 + + sniffio==1.3.1 + "); + + // Using `--all-extras` should include the defaults + uv_snapshot!(context.filters(), context.sync().arg("--all-extras"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 4 packages in [TIME] + Installed 4 packages in [TIME] + + certifi==2024.2.2 + + charset-normalizer==3.3.2 + + requests==2.31.0 + + urllib3==2.2.1 + "###); + + // Using `--only-extra` should exclude the defaults + uv_snapshot!(context.filters(), context.sync().arg("--only-extra").arg("dev"), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 7 packages in [TIME] + - anyio==4.3.0 + - certifi==2024.2.2 + - charset-normalizer==3.3.2 + - idna==3.6 + - requests==2.31.0 + - sniffio==1.3.1 + - urllib3==2.2.1 + "); + + uv_snapshot!(context.filters(), context.sync().arg("--all-extras"), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Installed 7 packages in [TIME] + + anyio==4.3.0 + + certifi==2024.2.2 + + charset-normalizer==3.3.2 + + idna==3.6 + + requests==2.31.0 + + sniffio==1.3.1 + + urllib3==2.2.1 + "); + + // Using `--no-default-extras` should exclude all extras. + uv_snapshot!(context.filters(), context.sync().arg("--no-default-extras"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 8 packages in [TIME] + - anyio==4.3.0 + - certifi==2024.2.2 + - charset-normalizer==3.3.2 + - idna==3.6 + - iniconfig==2.0.0 + - requests==2.31.0 + - sniffio==1.3.1 + - urllib3==2.2.1 + "###); + + uv_snapshot!(context.filters(), context.sync().arg("--all-extras"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Installed 8 packages in [TIME] + + anyio==4.3.0 + + certifi==2024.2.2 + + charset-normalizer==3.3.2 + + idna==3.6 + + iniconfig==2.0.0 + + requests==2.31.0 + + sniffio==1.3.1 + + urllib3==2.2.1 + "###); + + // Using `--no-default-extras` with `--extra foo` and `--extras bar` should include those + // extras. + uv_snapshot!(context.filters(), context.sync().arg("--no-default-extras").arg("--extra").arg("foo").arg("--extra").arg("bar"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 1 package in [TIME] + - iniconfig==2.0.0 + "###); + + Ok(()) +} + +/// default-extras = "all" sugar works +#[test] +fn sync_default_extras_all() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "myproject" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["typing-extensions"] + + [project.optional-dependencies] + dev = ["iniconfig"] + foo = ["anyio"] + bar = ["requests"] + + [tool.uv] + default-extras = "all" + "#, + )?; + + context.lock().assert().success(); + + // groups = "all" should behave like --all-extras in contexts where defaults exist + uv_snapshot!(context.filters(), context.sync(), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 9 packages in [TIME] + Installed 9 packages in [TIME] + + anyio==4.3.0 + + certifi==2024.2.2 + + charset-normalizer==3.3.2 + + idna==3.6 + + iniconfig==2.0.0 + + requests==2.31.0 + + sniffio==1.3.1 + + typing-extensions==4.10.0 + + urllib3==2.2.1 + "); + + // Using `--no-default-extras` should still work + uv_snapshot!(context.filters(), context.sync().arg("--no-default-extras"), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 8 packages in [TIME] + - anyio==4.3.0 + - certifi==2024.2.2 + - charset-normalizer==3.3.2 + - idna==3.6 + - iniconfig==2.0.0 + - requests==2.31.0 + - sniffio==1.3.1 + - urllib3==2.2.1 + "); + + // Using `--all-extras` should be redundant and work fine + uv_snapshot!(context.filters(), context.sync().arg("--all-extras"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Installed 8 packages in [TIME] + + anyio==4.3.0 + + certifi==2024.2.2 + + charset-normalizer==3.3.2 + + idna==3.6 + + iniconfig==2.0.0 + + requests==2.31.0 + + sniffio==1.3.1 + + urllib3==2.2.1 + "###); + + // Using `--extra` should be redundant and still work fine + uv_snapshot!(context.filters(), context.sync().arg("--extra").arg("foo"), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Audited 9 packages in [TIME] + "); + + // Using `--only-extra` should still disable defaults + uv_snapshot!(context.filters(), context.sync().arg("--only-extra").arg("foo"), @r" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 5 packages in [TIME] + - certifi==2024.2.2 + - charset-normalizer==3.3.2 + - iniconfig==2.0.0 + - requests==2.31.0 + - urllib3==2.2.1 + "); + + Ok(()) +} + +/// default-extras = "gibberish" error +#[test] +fn sync_default_extras_gibberish() -> 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 = ["typing-extensions"] + + [project.optional-dependencies] + dev = ["iniconfig"] + foo = ["anyio"] + bar = ["requests"] + + [tool.uv] + default-extras = "gibberish" + "#, + )?; + + uv_snapshot!(context.filters(), context.sync(), @r#" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Failed to parse: `pyproject.toml` + Caused by: TOML parse error at line 14, column 26 + | + 14 | default-extras = "gibberish" + | ^^^^^^^^^^^ + default-extras must be "all" or a ["list", "of", "extras"] + "#); + + Ok(()) +} + #[test] fn sync_default_groups() -> Result<()> { let context = TestContext::new("3.12"); diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 049ef7670b4d3..cadf3c18f351c 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -1740,6 +1740,10 @@ uv sync [OPTIONS]

Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.

May also be set with the UV_NO_CONFIG environment variable.

+
--no-default-extras

Ignore the default extras.

+ +

uv includes the extras defined in tool.uv.default-extras by default. This disables that option, however, specific extras can still be included with --extra.

+
--no-default-groups

Ignore the default dependency groups.

uv includes the groups defined in tool.uv.default-groups by default. This disables that option, however, specific groups can still be included with --group.

@@ -1751,7 +1755,9 @@ uv sync [OPTIONS]
--no-editable

Install any editable dependencies, including the project and any workspace members, as non-editable

May also be set with the UV_NO_EDITABLE environment variable.

-
--no-extra no-extra

Exclude the specified optional dependencies, if --all-extras is supplied.

+
--no-extra no-extra

Disable the specified extra.

+ +

This options always takes precedence over default extras, --all-extras, and --extra.

May be provided multiple times.

@@ -1800,6 +1806,10 @@ uv sync [OPTIONS]

This option is an alias for --only-group dev. Implies --no-default-groups.

+
--only-extra only-extra

Only include dependencies from the specified extra.

+ +

May be provided multiple times. Implies --no-default-extras.

+
--only-group only-group

Only include dependencies from the specified dependency group.

The project and its dependencies will be omitted.

From bcac36db67ec91f0f94add63b8c16b79d766b848 Mon Sep 17 00:00:00 2001 From: Ahmed Ilyas Date: Fri, 18 Apr 2025 12:28:33 +0200 Subject: [PATCH 3/6] Support default extras in `uv run` and `uv export` --- crates/uv-cli/src/lib.rs | 36 ++++++++++++++++++++++++++++++++++-- crates/uv/src/settings.rs | 16 ++++++++-------- docs/reference/cli.md | 24 ++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 2d9b58b1917e4..0b5a406ce318c 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -2801,12 +2801,28 @@ pub struct RunArgs { #[arg(long, conflicts_with = "extra")] pub all_extras: bool, - /// Exclude the specified optional dependencies, if `--all-extras` is supplied. + /// Disable the specified extra. + /// + /// This options always takes precedence over default extras, + /// `--all-extras`, and `--extra`. /// /// May be provided multiple times. #[arg(long)] pub no_extra: Vec, + /// Ignore the default extras. + /// + /// uv includes the extras defined in `tool.uv.default-extras` by default. + /// This disables that option, however, specific extras can still be included with `--extra`. + #[arg(long)] + pub no_default_extras: bool, + + /// Only include dependencies from the specified extra. + /// + /// May be provided multiple times. Implies `--no-default-extras`. + #[arg(long, conflicts_with_all = ["extra", "all_extras"])] + pub only_extra: Vec, + #[arg(long, overrides_with("all_extras"), hide = true)] pub no_all_extras: bool, @@ -3829,12 +3845,28 @@ pub struct ExportArgs { #[arg(long, conflicts_with = "extra")] pub all_extras: bool, - /// Exclude the specified optional dependencies, if `--all-extras` is supplied. + /// Disable the specified extra. + /// + /// This options always takes precedence over default extras, + /// `--all-extras`, and `--extra`. /// /// May be provided multiple times. #[arg(long)] pub no_extra: Vec, + /// Ignore the default extras. + /// + /// uv includes the extras defined in `tool.uv.default-extras` by default. + /// This disables that option, however, specific extras can still be included with `--extra`. + #[arg(long)] + pub no_default_extras: bool, + + /// Only include dependencies from the specified extra. + /// + /// May be provided multiple times. Implies `--no-default-extras`. + #[arg(long, conflicts_with_all = ["extra", "all_extras"])] + pub only_extra: Vec, + #[arg(long, overrides_with("all_extras"), hide = true)] pub no_all_extras: bool, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index ec8c0e11278ec..4fb991390b39c 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -348,6 +348,8 @@ impl RunSettings { extra, all_extras, no_extra, + no_default_extras, + only_extra, no_all_extras, dev, no_dev, @@ -397,10 +399,8 @@ impl RunSettings { extras: ExtrasSpecification::from_args( extra.unwrap_or_default(), no_extra, - // TODO(blueraft): support no_default_extras - false, - // TODO(blueraft): support only_extra - vec![], + no_default_extras, + only_extra, flag(all_extras, no_all_extras).unwrap_or_default(), ), dev: DependencyGroups::from_args( @@ -1567,6 +1567,8 @@ impl ExportSettings { prune, extra, all_extras, + no_default_extras, + only_extra, no_extra, no_all_extras, dev, @@ -1609,10 +1611,8 @@ impl ExportSettings { extras: ExtrasSpecification::from_args( extra.unwrap_or_default(), no_extra, - // TODO(blueraft): support no_default_extras - false, - // TODO(blueraft): support only_extra - vec![], + no_default_extras, + only_extra, flag(all_extras, no_all_extras).unwrap_or_default(), ), dev: DependencyGroups::from_args( diff --git a/docs/reference/cli.md b/docs/reference/cli.md index cadf3c18f351c..742344be0adbd 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -333,6 +333,10 @@ uv run [OPTIONS] [COMMAND]

Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.

May also be set with the UV_NO_CONFIG environment variable.

+
--no-default-extras

Ignore the default extras.

+ +

uv includes the extras defined in tool.uv.default-extras by default. This disables that option, however, specific extras can still be included with --extra.

+
--no-default-groups

Ignore the default dependency groups.

uv includes the groups defined in tool.uv.default-groups by default. This disables that option, however, specific groups can still be included with --group.

@@ -349,7 +353,9 @@ uv run [OPTIONS] [COMMAND]
--no-env-file

Avoid reading environment variables from a .env file

May also be set with the UV_NO_ENV_FILE environment variable.

-
--no-extra no-extra

Exclude the specified optional dependencies, if --all-extras is supplied.

+
--no-extra no-extra

Disable the specified extra.

+ +

This options always takes precedence over default extras, --all-extras, and --extra.

May be provided multiple times.

@@ -397,6 +403,10 @@ uv run [OPTIONS] [COMMAND]

This option is an alias for --only-group dev. Implies --no-default-groups.

+
--only-extra only-extra

Only include dependencies from the specified extra.

+ +

May be provided multiple times. Implies --no-default-extras.

+
--only-group only-group

Only include dependencies from the specified dependency group.

The project and its dependencies will be omitted.

@@ -2474,6 +2484,10 @@ uv export [OPTIONS]

Normally, configuration files are discovered in the current directory, parent directories, or user configuration directories.

May also be set with the UV_NO_CONFIG environment variable.

+
--no-default-extras

Ignore the default extras.

+ +

uv includes the extras defined in tool.uv.default-extras by default. This disables that option, however, specific extras can still be included with --extra.

+
--no-default-groups

Ignore the default dependency groups.

uv includes the groups defined in tool.uv.default-groups by default. This disables that option, however, specific groups can still be included with --group.

@@ -2496,7 +2510,9 @@ uv export [OPTIONS]

By default, all workspace members and their dependencies are included in the exported requirements file, with all of their dependencies. The --no-emit-workspace option allows exclusion of all the workspace members while retaining their dependencies.

-
--no-extra no-extra

Exclude the specified optional dependencies, if --all-extras is supplied.

+
--no-extra no-extra

Disable the specified extra.

+ +

This options always takes precedence over default extras, --all-extras, and --extra.

May be provided multiple times.

@@ -2537,6 +2553,10 @@ uv export [OPTIONS]

This option is an alias for --only-group dev. Implies --no-default-groups.

+
--only-extra only-extra

Only include dependencies from the specified extra.

+ +

May be provided multiple times. Implies --no-default-extras.

+
--only-group only-group

Only include dependencies from the specified dependency group.

The project and its dependencies will be omitted.

From 4bdd5c524aa2cb553bb68a074844e3f73225e33d Mon Sep 17 00:00:00 2001 From: Ahmed Ilyas Date: Mon, 28 Apr 2025 15:51:45 +0200 Subject: [PATCH 4/6] tests: run default extras --- crates/uv/tests/it/run.rs | 263 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs index 8327910f04f78..eb2c3a7169548 100644 --- a/crates/uv/tests/it/run.rs +++ b/crates/uv/tests/it/run.rs @@ -1045,6 +1045,269 @@ fn run_pep723_script_lock() -> Result<()> { Ok(()) } +#[test] +fn run_default_extras() -> 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 = ["typing-extensions"] + + [project.optional-dependencies] + dev = ["iniconfig"] + foo = ["anyio"] + bar = ["requests"] + "#, + )?; + + context.lock().assert().success(); + + // Only the main dependencies should be installed. + uv_snapshot!(context.filters(), context.run().arg("python").arg("-c").arg("import typing_extensions"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 1 package in [TIME] + Installed 1 package in [TIME] + + typing-extensions==4.10.0 + "###); + + // If we set a different default extras, it should be synced instead. + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["typing-extensions"] + + [project.optional-dependencies] + dev = ["iniconfig"] + foo = ["anyio"] + bar = ["requests"] + + [tool.uv] + default-extras = ["foo"] + "#, + )?; + + uv_snapshot!(context.filters(), context.run().arg("python").arg("-c").arg("import typing_extensions"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 3 packages in [TIME] + Installed 3 packages in [TIME] + + anyio==4.3.0 + + idna==3.6 + + sniffio==1.3.1 + "###); + + // `--no-extra` should remove from the defaults. + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = ["typing-extensions"] + + [project.optional-dependencies] + dev = ["iniconfig"] + foo = ["anyio"] + bar = ["requests"] + + [tool.uv] + default-extras = ["foo"] + "#, + )?; + + uv_snapshot!(context.filters(), context.run() + .arg("--no-extra") + .arg("foo") + .arg("--exact") + .arg("python") + .arg("-c") + .arg("import typing_extensions"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 3 packages in [TIME] + - anyio==4.3.0 + - idna==3.6 + - sniffio==1.3.1 + "###); + + // Using `--extra` should include the defaults + uv_snapshot!(context.filters(), context.run() + .arg("--extra") + .arg("dev") + .arg("python") + .arg("-c") + .arg("import iniconfig"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 1 package in [TIME] + Installed 4 packages in [TIME] + + anyio==4.3.0 + + idna==3.6 + + iniconfig==2.0.0 + + sniffio==1.3.1 + "###); + + // Using `--all-extras` should include the defaults + uv_snapshot!(context.filters(), context.run() + .arg("--all-extras") + .arg("python") + .arg("-c") + .arg("import iniconfig"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Prepared 4 packages in [TIME] + Installed 4 packages in [TIME] + + certifi==2024.2.2 + + charset-normalizer==3.3.2 + + requests==2.31.0 + + urllib3==2.2.1 + "###); + + // Using `--only-extra` should exclude the defaults + uv_snapshot!(context.filters(), context.run() + .arg("--exact") + .arg("--only-extra") + .arg("dev") + .arg("python") + .arg("-c") + .arg("import typing_extensions"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 7 packages in [TIME] + - anyio==4.3.0 + - certifi==2024.2.2 + - charset-normalizer==3.3.2 + - idna==3.6 + - requests==2.31.0 + - sniffio==1.3.1 + - urllib3==2.2.1 + "###); + + uv_snapshot!(context.filters(), context.run() + .arg("--all-extras") + .arg("python") + .arg("-c") + .arg("import iniconfig"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Installed 7 packages in [TIME] + + anyio==4.3.0 + + certifi==2024.2.2 + + charset-normalizer==3.3.2 + + idna==3.6 + + requests==2.31.0 + + sniffio==1.3.1 + + urllib3==2.2.1 + "###); + + // Using `--no-default-extras` should exclude all extras. + uv_snapshot!(context.filters(), context.run() + .arg("--no-default-extras") + .arg("--exact") + .arg("python") + .arg("-c") + .arg("import typing_extensions"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 8 packages in [TIME] + - anyio==4.3.0 + - certifi==2024.2.2 + - charset-normalizer==3.3.2 + - idna==3.6 + - iniconfig==2.0.0 + - requests==2.31.0 + - sniffio==1.3.1 + - urllib3==2.2.1 + "###); + + uv_snapshot!(context.filters(), context.run() + .arg("--all-extras") + .arg("python") + .arg("-c") + .arg("import iniconfig"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Installed 8 packages in [TIME] + + anyio==4.3.0 + + certifi==2024.2.2 + + charset-normalizer==3.3.2 + + idna==3.6 + + iniconfig==2.0.0 + + requests==2.31.0 + + sniffio==1.3.1 + + urllib3==2.2.1 + "###); + + // Using `--no-default-extras` with `--extra foo` and `--extras bar` should include those + // extras. + uv_snapshot!(context.filters(), context.run() + .arg("--exact") + .arg("--no-default-extras") + .arg("--extra") + .arg("foo") + .arg("--extra") + .arg("bar") + .arg("python") + .arg("-c") + .arg("import typing_extensions"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 10 packages in [TIME] + Uninstalled 1 package in [TIME] + - iniconfig==2.0.0 + "###); + + Ok(()) +} + /// With `managed = false`, we should avoid installing the project itself. #[test] fn run_managed_false() -> Result<()> { From 7edb8b5fe0bde2cbb0afc3210f169c96d13d699a Mon Sep 17 00:00:00 2001 From: Ahmed Ilyas Date: Mon, 28 Apr 2025 16:24:17 +0200 Subject: [PATCH 5/6] tests: export default extras --- crates/uv/tests/it/export.rs | 215 +++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/crates/uv/tests/it/export.rs b/crates/uv/tests/it/export.rs index 3836711e26f5b..36393cbaa67fc 100644 --- a/crates/uv/tests/it/export.rs +++ b/crates/uv/tests/it/export.rs @@ -337,6 +337,221 @@ fn requirements_txt_project_extra() -> Result<()> { Ok(()) } +#[test] +fn requirements_txt_project_default_extras() -> 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 = ["typing-extensions"] + + [project.optional-dependencies] + async = ["anyio==3.7.0"] + pytest = ["iniconfig"] + + [tool.uv] + default-extras = ["async"] + + [build-system] + requires = ["setuptools>=42"] + build-backend = "setuptools.build_meta" + "#, + )?; + + context.lock().assert().success(); + + uv_snapshot!(context.filters(), context.export(), @r" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] + -e . + anyio==3.7.0 \ + --hash=sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce \ + --hash=sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0 + # via project + idna==3.6 \ + --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ + --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f + # via anyio + sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via anyio + typing-extensions==4.10.0 \ + --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \ + --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb + # via project + + ----- stderr ----- + Resolved 6 packages in [TIME] + "); + + uv_snapshot!(context.filters(), context.export().arg("--extra").arg("pytest").arg("--extra").arg("async").arg("--no-extra").arg("pytest"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] --extra pytest --extra async --no-extra pytest + -e . + anyio==3.7.0 \ + --hash=sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce \ + --hash=sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0 + # via project + idna==3.6 \ + --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ + --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f + # via anyio + sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via anyio + typing-extensions==4.10.0 \ + --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \ + --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb + # via project + + ----- stderr ----- + Resolved 6 packages in [TIME] + "###); + + uv_snapshot!(context.filters(), context.export().arg("--extra").arg("pytest"), @r" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] --extra pytest + -e . + anyio==3.7.0 \ + --hash=sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce \ + --hash=sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0 + # via project + idna==3.6 \ + --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ + --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f + # via anyio + iniconfig==2.0.0 \ + --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ + --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 + # via project + sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via anyio + typing-extensions==4.10.0 \ + --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \ + --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb + # via project + + ----- stderr ----- + Resolved 6 packages in [TIME] + "); + + uv_snapshot!(context.filters(), context.export().arg("--all-extras"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] --all-extras + -e . + anyio==3.7.0 \ + --hash=sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce \ + --hash=sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0 + # via project + idna==3.6 \ + --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ + --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f + # via anyio + iniconfig==2.0.0 \ + --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ + --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 + # via project + sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via anyio + typing-extensions==4.10.0 \ + --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \ + --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb + # via project + + ----- stderr ----- + Resolved 6 packages in [TIME] + "###); + + uv_snapshot!(context.filters(), context.export().arg("--all-extras").arg("--no-extra").arg("pytest"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] --all-extras --no-extra pytest + -e . + anyio==3.7.0 \ + --hash=sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce \ + --hash=sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0 + # via project + idna==3.6 \ + --hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \ + --hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f + # via anyio + sniffio==1.3.1 \ + --hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \ + --hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc + # via anyio + typing-extensions==4.10.0 \ + --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \ + --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb + # via project + + ----- stderr ----- + Resolved 6 packages in [TIME] + "###); + + uv_snapshot!(context.filters(), context.export().arg("--no-default-extras"), @r" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] --no-default-extras + -e . + typing-extensions==4.10.0 \ + --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \ + --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb + # via project + + ----- stderr ----- + Resolved 6 packages in [TIME] + "); + + uv_snapshot!(context.filters(), context.export().arg("--no-default-extras").arg("--only-extra").arg("pytest"), @r" + success: true + exit_code: 0 + ----- stdout ----- + # This file was autogenerated by uv via the following command: + # uv export --cache-dir [CACHE_DIR] --no-default-extras --only-extra pytest + -e . + iniconfig==2.0.0 \ + --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ + --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 + # via project + typing-extensions==4.10.0 \ + --hash=sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475 \ + --hash=sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb + # via project + + ----- stderr ----- + Resolved 6 packages in [TIME] + "); + + Ok(()) +} + #[test] fn requirements_txt_prune() -> Result<()> { let context = TestContext::new("3.12"); From cd5df25e478d420002a58ed92ae42e7408521f92 Mon Sep 17 00:00:00 2001 From: Ahmed Ilyas Date: Wed, 11 Jun 2025 15:51:31 +0200 Subject: [PATCH 6/6] Conflicts --- crates/uv/src/commands/project/add.rs | 2 +- crates/uv/src/commands/project/export.rs | 4 +- crates/uv/src/commands/project/mod.rs | 6 +- crates/uv/src/commands/project/sync.rs | 4 +- crates/uv/tests/it/show_settings.rs | 126 +++++++++++++++++------ 5 files changed, 104 insertions(+), 38 deletions(-) diff --git a/crates/uv/src/commands/project/add.rs b/crates/uv/src/commands/project/add.rs index 5949f045872a1..3ae3944d4dbf7 100644 --- a/crates/uv/src/commands/project/add.rs +++ b/crates/uv/src/commands/project/add.rs @@ -29,7 +29,7 @@ use uv_distribution_types::{ use uv_fs::{LockedFile, Simplified}; use uv_git::GIT_STORE; use uv_git_types::GitReference; -use uv_normalize::{DEV_DEPENDENCIES, DefaultExtras, PackageName}; +use uv_normalize::{DEV_DEPENDENCIES, PackageName}; use uv_pep508::{ExtraName, MarkerTree, UnnamedRequirement, VersionOrUrl}; use uv_pypi_types::{ParsedUrl, VerbatimParsedUrl}; use uv_python::{Interpreter, PythonDownloads, PythonEnvironment, PythonPreference, PythonRequest}; diff --git a/crates/uv/src/commands/project/export.rs b/crates/uv/src/commands/project/export.rs index 95a577c913894..f25888705002a 100644 --- a/crates/uv/src/commands/project/export.rs +++ b/crates/uv/src/commands/project/export.rs @@ -24,8 +24,8 @@ use crate::commands::project::install_target::InstallTarget; use crate::commands::project::lock::{LockMode, LockOperation}; use crate::commands::project::lock_target::LockTarget; use crate::commands::project::{ - default_dependency_groups, default_extras, detect_conflicts, ProjectError, ProjectInterpreter, - ScriptInterpreter, UniversalState, + ProjectError, ProjectInterpreter, ScriptInterpreter, UniversalState, default_dependency_groups, + default_extras, detect_conflicts, }; use crate::commands::{ExitStatus, OutputWriter, diagnostics}; use crate::printer::Printer; diff --git a/crates/uv/src/commands/project/mod.rs b/crates/uv/src/commands/project/mod.rs index 0a92b1a440215..6054c257a1a79 100644 --- a/crates/uv/src/commands/project/mod.rs +++ b/crates/uv/src/commands/project/mod.rs @@ -24,7 +24,7 @@ use uv_fs::{CWD, LockedFile, Simplified}; use uv_git::ResolvedRepositoryReference; use uv_installer::{SatisfiesResult, SitePackages}; use uv_normalize::{ - DefaultExtras, DefaultGroups, ExtraName, GroupName, PackageName, DEV_DEPENDENCIES, + DEV_DEPENDENCIES, DefaultExtras, DefaultGroups, ExtraName, GroupName, PackageName, }; use uv_pep440::{Version, VersionSpecifiers}; use uv_pep508::MarkerTreeContents; @@ -176,7 +176,9 @@ pub(crate) enum ProjectError { #[error("PEP 723 scripts do not support dependency groups, but group `{0}` was specified")] MissingGroupScript(GroupName), - #[error("Default extra `{0}` (from `tool.uv.default-extras`) is not defined in the project's `optional-dependencies` table")] + #[error( + "Default extra `{0}` (from `tool.uv.default-extras`) is not defined in the project's `optional-dependencies` table" + )] MissingDefaultExtra(ExtraName), #[error( diff --git a/crates/uv/src/commands/project/sync.rs b/crates/uv/src/commands/project/sync.rs index 958f1bfba2820..7f3341fa2a6ef 100644 --- a/crates/uv/src/commands/project/sync.rs +++ b/crates/uv/src/commands/project/sync.rs @@ -39,9 +39,9 @@ use crate::commands::project::install_target::InstallTarget; use crate::commands::project::lock::{LockMode, LockOperation, LockResult}; use crate::commands::project::lock_target::LockTarget; use crate::commands::project::{ + PlatformState, ProjectEnvironment, ProjectError, ScriptEnvironment, UniversalState, default_dependency_groups, default_extras, detect_conflicts, script_specification, - update_environment, PlatformState, ProjectEnvironment, ProjectError, ScriptEnvironment, - UniversalState, + update_environment, }; use crate::commands::{ExitStatus, diagnostics}; use crate::printer::Printer; diff --git a/crates/uv/tests/it/show_settings.rs b/crates/uv/tests/it/show_settings.rs index 281b6f2a4f273..8958750b7c2d2 100644 --- a/crates/uv/tests/it/show_settings.rs +++ b/crates/uv/tests/it/show_settings.rs @@ -4,7 +4,7 @@ use std::process::Command; use assert_fs::prelude::*; use uv_static::EnvVars; -use crate::common::{uv_snapshot, TestContext}; +use crate::common::{TestContext, uv_snapshot}; /// Add shared arguments to a command. /// @@ -112,7 +112,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -138,6 +138,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -147,6 +148,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -291,7 +293,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -317,6 +319,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -326,6 +329,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -471,7 +475,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -497,6 +501,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -506,6 +511,7 @@ fn resolve_uv_toml() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -683,7 +689,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -709,6 +715,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -718,6 +725,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -867,6 +875,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -1023,7 +1032,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -1049,6 +1058,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -1058,6 +1068,7 @@ fn resolve_pyproject_toml() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -1229,7 +1240,7 @@ fn resolve_index_url() -> anyhow::Result<()> { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -1255,12 +1266,13 @@ fn resolve_index_url() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -1286,6 +1298,7 @@ fn resolve_index_url() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -1295,6 +1308,7 @@ fn resolve_index_url() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -1441,7 +1455,7 @@ fn resolve_index_url() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -1469,12 +1483,13 @@ fn resolve_index_url() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -1500,12 +1515,13 @@ fn resolve_index_url() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -1531,6 +1547,7 @@ fn resolve_index_url() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -1540,6 +1557,7 @@ fn resolve_index_url() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -1710,7 +1728,7 @@ fn resolve_find_links() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -1736,6 +1754,7 @@ fn resolve_find_links() -> anyhow::Result<()> { format: Flat, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], no_index: true, @@ -1744,6 +1763,7 @@ fn resolve_find_links() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -1915,6 +1935,7 @@ fn resolve_top_level() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -2076,7 +2097,7 @@ fn resolve_top_level() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -2102,12 +2123,13 @@ fn resolve_top_level() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -2133,6 +2155,7 @@ fn resolve_top_level() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -2142,6 +2165,7 @@ fn resolve_top_level() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -2286,7 +2310,7 @@ fn resolve_top_level() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -2312,12 +2336,13 @@ fn resolve_top_level() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -2343,6 +2368,7 @@ fn resolve_top_level() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -2352,6 +2378,7 @@ fn resolve_top_level() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -2522,6 +2549,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -2676,6 +2704,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -2830,6 +2859,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -2986,6 +3016,7 @@ fn resolve_user_configuration() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -3216,6 +3247,7 @@ fn resolve_tool() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, } @@ -3325,6 +3357,7 @@ fn resolve_poetry_toml() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -3504,7 +3537,7 @@ fn resolve_both() -> anyhow::Result<()> { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -3530,6 +3563,7 @@ fn resolve_both() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -3539,6 +3573,7 @@ fn resolve_both() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -3808,7 +3843,7 @@ fn resolve_config_file() -> anyhow::Result<()> { name: None, url: Pypi( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -3834,6 +3869,7 @@ fn resolve_config_file() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -3843,6 +3879,7 @@ fn resolve_config_file() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -3950,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`, `publish-url`, `trusted-publishing`, `check-url`, `pip`, `cache-keys`, `override-dependencies`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `default-extras`, `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`, `constraint-dependencies`, `build-constraint-dependencies`, `environments`, `required-environments`, `conflicts`, `workspace`, `sources`, `managed`, `package`, `default-groups`, `default-extras`, `dev-dependencies`, `build-backend` " ); @@ -4091,6 +4128,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -4248,6 +4286,7 @@ fn resolve_skip_empty() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -4424,6 +4463,7 @@ fn allow_insecure_host() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -4589,7 +4629,7 @@ fn index_priority() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -4617,12 +4657,13 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -4648,6 +4689,7 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -4657,6 +4699,7 @@ fn index_priority() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -4801,7 +4844,7 @@ fn index_priority() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -4829,12 +4872,13 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -4860,6 +4904,7 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -4869,6 +4914,7 @@ fn index_priority() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -5019,7 +5065,7 @@ fn index_priority() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -5047,12 +5093,13 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -5078,6 +5125,7 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -5087,6 +5135,7 @@ fn index_priority() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -5232,7 +5281,7 @@ fn index_priority() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -5260,12 +5309,13 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -5291,6 +5341,7 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -5300,6 +5351,7 @@ fn index_priority() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -5452,7 +5504,7 @@ fn index_priority() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -5480,12 +5532,13 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -5511,6 +5564,7 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -5520,6 +5574,7 @@ fn index_priority() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -5665,7 +5720,7 @@ fn index_priority() -> anyhow::Result<()> { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -5693,12 +5748,13 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, Index { name: None, url: Url( VerbatimUrl { - url: Url { + url: DisplaySafeUrl { scheme: "https", cannot_be_a_base: false, username: "", @@ -5724,6 +5780,7 @@ fn index_priority() -> anyhow::Result<()> { format: Simple, publish_url: None, authenticate: Auto, + ignore_error_codes: None, }, ], flat_index: [], @@ -5733,6 +5790,7 @@ fn index_priority() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -5894,6 +5952,7 @@ fn verify_hashes() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -6041,6 +6100,7 @@ fn verify_hashes() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -6186,6 +6246,7 @@ fn verify_hashes() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -6333,6 +6394,7 @@ fn verify_hashes() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -6478,6 +6540,7 @@ fn verify_hashes() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification( @@ -6624,6 +6687,7 @@ fn verify_hashes() -> anyhow::Result<()> { install_mirrors: PythonInstallMirrors { python_install_mirror: None, pypy_install_mirror: None, + python_downloads_json_url: None, }, system: false, extras: ExtrasSpecification(