mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Workaround for panic due to missing global validation in clap (#14368)
Clap does not perform global validation, so flag that are declared as overriding can be set at the same time: https://github.com/clap-rs/clap/issues/6049. This would previously cause a panic. We work around this by choosing the yes-value always and writing a warning. An alternative would be erroring when both are set, but it's unclear to me if this may break things we want to support. (`UV_OFFLINE=1 cargo run -q pip --no-offline install tqdm --no-cache` is already banned). Fixes https://github.com/astral-sh/uv/pull/14299 **Test Plan** ``` $ cargo run -q pip --offline install --no-offline tqdm --no-cache warning: Boolean flags on different levels are not correctly supported (https://github.com/clap-rs/clap/issues/6049) × No solution found when resolving dependencies: ╰─▶ Because tqdm was not found in the cache and you require tqdm, we can conclude that your requirements are unsatisfiable. hint: Packages were unavailable because the network was disabled. When the network is disabled, registry packages may only be read from the cache. ```
This commit is contained in:
parent
29fcd6faee
commit
06df95adbf
3 changed files with 170 additions and 97 deletions
|
@ -1,7 +1,10 @@
|
|||
use anstream::eprintln;
|
||||
|
||||
use uv_cache::Refresh;
|
||||
use uv_configuration::ConfigSettings;
|
||||
use uv_resolver::PrereleaseMode;
|
||||
use uv_settings::{Combine, PipOptions, ResolverInstallerOptions, ResolverOptions};
|
||||
use uv_warnings::owo_colors::OwoColorize;
|
||||
|
||||
use crate::{
|
||||
BuildOptionsArgs, FetchArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs,
|
||||
|
@ -9,12 +12,27 @@ use crate::{
|
|||
};
|
||||
|
||||
/// Given a boolean flag pair (like `--upgrade` and `--no-upgrade`), resolve the value of the flag.
|
||||
pub fn flag(yes: bool, no: bool) -> Option<bool> {
|
||||
pub fn flag(yes: bool, no: bool, name: &str) -> Option<bool> {
|
||||
match (yes, no) {
|
||||
(true, false) => Some(true),
|
||||
(false, true) => Some(false),
|
||||
(false, false) => None,
|
||||
(..) => unreachable!("Clap should make this impossible"),
|
||||
(..) => {
|
||||
eprintln!(
|
||||
"{}{} `{}` and `{}` cannot be used together. \
|
||||
Boolean flags on different levels are currently not supported \
|
||||
(https://github.com/clap-rs/clap/issues/6049)",
|
||||
"error".bold().red(),
|
||||
":".bold(),
|
||||
format!("--{name}").green(),
|
||||
format!("--no-{name}").green(),
|
||||
);
|
||||
// No error forwarding since should eventually be solved on the clap side.
|
||||
#[allow(clippy::exit)]
|
||||
{
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +44,7 @@ impl From<RefreshArgs> for Refresh {
|
|||
refresh_package,
|
||||
} = value;
|
||||
|
||||
Self::from_args(flag(refresh, no_refresh), refresh_package)
|
||||
Self::from_args(flag(refresh, no_refresh, "no-refresh"), refresh_package)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -53,7 +71,7 @@ impl From<ResolverArgs> for PipOptions {
|
|||
} = args;
|
||||
|
||||
Self {
|
||||
upgrade: flag(upgrade, no_upgrade),
|
||||
upgrade: flag(upgrade, no_upgrade, "no-upgrade"),
|
||||
upgrade_package: Some(upgrade_package),
|
||||
index_strategy,
|
||||
keyring_provider,
|
||||
|
@ -66,7 +84,7 @@ impl From<ResolverArgs> for PipOptions {
|
|||
},
|
||||
config_settings: config_setting
|
||||
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
|
||||
no_build_isolation_package: Some(no_build_isolation_package),
|
||||
exclude_newer,
|
||||
link_mode,
|
||||
|
@ -96,16 +114,16 @@ impl From<InstallerArgs> for PipOptions {
|
|||
} = args;
|
||||
|
||||
Self {
|
||||
reinstall: flag(reinstall, no_reinstall),
|
||||
reinstall: flag(reinstall, no_reinstall, "reinstall"),
|
||||
reinstall_package: Some(reinstall_package),
|
||||
index_strategy,
|
||||
keyring_provider,
|
||||
config_settings: config_setting
|
||||
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
|
||||
exclude_newer,
|
||||
link_mode,
|
||||
compile_bytecode: flag(compile_bytecode, no_compile_bytecode),
|
||||
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
|
||||
no_sources: if no_sources { Some(true) } else { None },
|
||||
..PipOptions::from(index_args)
|
||||
}
|
||||
|
@ -140,9 +158,9 @@ impl From<ResolverInstallerArgs> for PipOptions {
|
|||
} = args;
|
||||
|
||||
Self {
|
||||
upgrade: flag(upgrade, no_upgrade),
|
||||
upgrade: flag(upgrade, no_upgrade, "upgrade"),
|
||||
upgrade_package: Some(upgrade_package),
|
||||
reinstall: flag(reinstall, no_reinstall),
|
||||
reinstall: flag(reinstall, no_reinstall, "reinstall"),
|
||||
reinstall_package: Some(reinstall_package),
|
||||
index_strategy,
|
||||
keyring_provider,
|
||||
|
@ -155,11 +173,11 @@ impl From<ResolverInstallerArgs> for PipOptions {
|
|||
fork_strategy,
|
||||
config_settings: config_setting
|
||||
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
|
||||
no_build_isolation_package: Some(no_build_isolation_package),
|
||||
exclude_newer,
|
||||
link_mode,
|
||||
compile_bytecode: flag(compile_bytecode, no_compile_bytecode),
|
||||
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
|
||||
no_sources: if no_sources { Some(true) } else { None },
|
||||
..PipOptions::from(index_args)
|
||||
}
|
||||
|
@ -289,7 +307,7 @@ pub fn resolver_options(
|
|||
.filter_map(Maybe::into_option)
|
||||
.collect()
|
||||
}),
|
||||
upgrade: flag(upgrade, no_upgrade),
|
||||
upgrade: flag(upgrade, no_upgrade, "no-upgrade"),
|
||||
upgrade_package: Some(upgrade_package),
|
||||
index_strategy,
|
||||
keyring_provider,
|
||||
|
@ -303,13 +321,13 @@ pub fn resolver_options(
|
|||
dependency_metadata: None,
|
||||
config_settings: config_setting
|
||||
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
|
||||
no_build_isolation_package: Some(no_build_isolation_package),
|
||||
exclude_newer,
|
||||
link_mode,
|
||||
no_build: flag(no_build, build),
|
||||
no_build: flag(no_build, build, "build"),
|
||||
no_build_package: Some(no_build_package),
|
||||
no_binary: flag(no_binary, binary),
|
||||
no_binary: flag(no_binary, binary, "binary"),
|
||||
no_binary_package: Some(no_binary_package),
|
||||
no_sources: if no_sources { Some(true) } else { None },
|
||||
}
|
||||
|
@ -386,13 +404,13 @@ pub fn resolver_installer_options(
|
|||
.filter_map(Maybe::into_option)
|
||||
.collect()
|
||||
}),
|
||||
upgrade: flag(upgrade, no_upgrade),
|
||||
upgrade: flag(upgrade, no_upgrade, "upgrade"),
|
||||
upgrade_package: if upgrade_package.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(upgrade_package)
|
||||
},
|
||||
reinstall: flag(reinstall, no_reinstall),
|
||||
reinstall: flag(reinstall, no_reinstall, "reinstall"),
|
||||
reinstall_package: if reinstall_package.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
@ -410,7 +428,7 @@ pub fn resolver_installer_options(
|
|||
dependency_metadata: None,
|
||||
config_settings: config_setting
|
||||
.map(|config_settings| config_settings.into_iter().collect::<ConfigSettings>()),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation),
|
||||
no_build_isolation: flag(no_build_isolation, build_isolation, "build-isolation"),
|
||||
no_build_isolation_package: if no_build_isolation_package.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
@ -418,14 +436,14 @@ pub fn resolver_installer_options(
|
|||
},
|
||||
exclude_newer,
|
||||
link_mode,
|
||||
compile_bytecode: flag(compile_bytecode, no_compile_bytecode),
|
||||
no_build: flag(no_build, build),
|
||||
compile_bytecode: flag(compile_bytecode, no_compile_bytecode, "compile-bytecode"),
|
||||
no_build: flag(no_build, build, "build"),
|
||||
no_build_package: if no_build_package.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(no_build_package)
|
||||
},
|
||||
no_binary: flag(no_binary, binary),
|
||||
no_binary: flag(no_binary, binary, "binary"),
|
||||
no_binary_package: if no_binary_package.is_empty() {
|
||||
None
|
||||
} else {
|
||||
|
|
|
@ -118,16 +118,20 @@ impl GlobalSettings {
|
|||
},
|
||||
show_settings: args.show_settings,
|
||||
preview: PreviewMode::from(
|
||||
flag(args.preview, args.no_preview)
|
||||
flag(args.preview, args.no_preview, "preview")
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.preview))
|
||||
.unwrap_or(false),
|
||||
),
|
||||
python_preference,
|
||||
python_downloads: flag(args.allow_python_downloads, args.no_python_downloads)
|
||||
.map(PythonDownloads::from)
|
||||
.combine(env(env::UV_PYTHON_DOWNLOADS))
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.python_downloads))
|
||||
.unwrap_or_default(),
|
||||
python_downloads: flag(
|
||||
args.allow_python_downloads,
|
||||
args.no_python_downloads,
|
||||
"python-downloads",
|
||||
)
|
||||
.map(PythonDownloads::from)
|
||||
.combine(env(env::UV_PYTHON_DOWNLOADS))
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.python_downloads))
|
||||
.unwrap_or_default(),
|
||||
// Disable the progress bar with `RUST_LOG` to avoid progress fragments interleaving
|
||||
// with log messages.
|
||||
no_progress: args.no_progress || std::env::var_os(EnvVars::RUST_LOG).is_some(),
|
||||
|
@ -161,7 +165,7 @@ pub(crate) struct NetworkSettings {
|
|||
|
||||
impl NetworkSettings {
|
||||
pub(crate) fn resolve(args: &GlobalArgs, workspace: Option<&FilesystemOptions>) -> Self {
|
||||
let connectivity = if flag(args.offline, args.no_offline)
|
||||
let connectivity = if flag(args.offline, args.no_offline, "offline")
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.offline))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
|
@ -169,7 +173,7 @@ impl NetworkSettings {
|
|||
} else {
|
||||
Connectivity::Online
|
||||
};
|
||||
let native_tls = flag(args.native_tls, args.no_native_tls)
|
||||
let native_tls = flag(args.native_tls, args.no_native_tls, "native-tls")
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.native_tls))
|
||||
.unwrap_or(false);
|
||||
let allow_insecure_host = args
|
||||
|
@ -274,8 +278,12 @@ impl InitSettings {
|
|||
(_, _, _) => unreachable!("`app`, `lib`, and `script` are mutually exclusive"),
|
||||
};
|
||||
|
||||
let package = flag(package || build_backend.is_some(), no_package || r#virtual)
|
||||
.unwrap_or(kind.packaged_by_default());
|
||||
let package = flag(
|
||||
package || build_backend.is_some(),
|
||||
no_package || r#virtual,
|
||||
"virtual",
|
||||
)
|
||||
.unwrap_or(kind.packaged_by_default());
|
||||
|
||||
let install_mirrors = filesystem
|
||||
.map(|fs| fs.install_mirrors.clone())
|
||||
|
@ -295,7 +303,7 @@ impl InitSettings {
|
|||
build_backend,
|
||||
no_readme: no_readme || bare,
|
||||
author_from,
|
||||
pin_python: flag(pin_python, no_pin_python).unwrap_or(!bare),
|
||||
pin_python: flag(pin_python, no_pin_python, "pin-python").unwrap_or(!bare),
|
||||
no_workspace,
|
||||
python: python.and_then(Maybe::into_option),
|
||||
install_mirrors,
|
||||
|
@ -398,7 +406,7 @@ impl RunSettings {
|
|||
false,
|
||||
// TODO(blueraft): support only_extra
|
||||
vec![],
|
||||
flag(all_extras, no_all_extras).unwrap_or_default(),
|
||||
flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
|
||||
),
|
||||
groups: DependencyGroups::from_args(
|
||||
dev,
|
||||
|
@ -411,7 +419,7 @@ impl RunSettings {
|
|||
all_groups,
|
||||
),
|
||||
editable: EditableMode::from_args(no_editable),
|
||||
modifications: if flag(exact, inexact).unwrap_or(false) {
|
||||
modifications: if flag(exact, inexact, "inexact").unwrap_or(false) {
|
||||
Modifications::Exact
|
||||
} else {
|
||||
Modifications::Sufficient
|
||||
|
@ -434,7 +442,7 @@ impl RunSettings {
|
|||
package,
|
||||
no_project,
|
||||
no_sync,
|
||||
active: flag(active, no_active),
|
||||
active: flag(active, no_active, "active"),
|
||||
python: python.and_then(Maybe::into_option),
|
||||
refresh: Refresh::from(refresh),
|
||||
settings: ResolverInstallerSettings::combine(
|
||||
|
@ -1081,7 +1089,7 @@ impl PythonFindSettings {
|
|||
request,
|
||||
show_version,
|
||||
no_project,
|
||||
system: flag(system, no_system).unwrap_or_default(),
|
||||
system: flag(system, no_system, "system").unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1116,7 +1124,7 @@ impl PythonPinSettings {
|
|||
|
||||
Self {
|
||||
request,
|
||||
resolved: flag(resolved, no_resolved).unwrap_or(false),
|
||||
resolved: flag(resolved, no_resolved, "resolved").unwrap_or(false),
|
||||
no_project,
|
||||
global,
|
||||
rm,
|
||||
|
@ -1195,7 +1203,7 @@ impl SyncSettings {
|
|||
filesystem,
|
||||
);
|
||||
|
||||
let check = flag(check, no_check).unwrap_or_default();
|
||||
let check = flag(check, no_check, "check").unwrap_or_default();
|
||||
let dry_run = if check {
|
||||
DryRun::Check
|
||||
} else {
|
||||
|
@ -1207,7 +1215,7 @@ impl SyncSettings {
|
|||
frozen,
|
||||
dry_run,
|
||||
script,
|
||||
active: flag(active, no_active),
|
||||
active: flag(active, no_active, "active"),
|
||||
extras: ExtrasSpecification::from_args(
|
||||
extra.unwrap_or_default(),
|
||||
no_extra,
|
||||
|
@ -1215,7 +1223,7 @@ impl SyncSettings {
|
|||
false,
|
||||
// TODO(blueraft): support only_extra
|
||||
vec![],
|
||||
flag(all_extras, no_all_extras).unwrap_or_default(),
|
||||
flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
|
||||
),
|
||||
groups: DependencyGroups::from_args(
|
||||
dev,
|
||||
|
@ -1233,7 +1241,7 @@ impl SyncSettings {
|
|||
no_install_workspace,
|
||||
no_install_package,
|
||||
),
|
||||
modifications: if flag(exact, inexact).unwrap_or(true) {
|
||||
modifications: if flag(exact, inexact, "inexact").unwrap_or(true) {
|
||||
Modifications::Exact
|
||||
} else {
|
||||
Modifications::Sufficient
|
||||
|
@ -1437,7 +1445,7 @@ impl AddSettings {
|
|||
Self {
|
||||
locked,
|
||||
frozen,
|
||||
active: flag(active, no_active),
|
||||
active: flag(active, no_active, "active"),
|
||||
no_sync,
|
||||
packages,
|
||||
requirements,
|
||||
|
@ -1455,7 +1463,7 @@ impl AddSettings {
|
|||
package,
|
||||
script,
|
||||
python: python.and_then(Maybe::into_option),
|
||||
editable: flag(editable, no_editable),
|
||||
editable: flag(editable, no_editable, "editable"),
|
||||
extras: extra.unwrap_or_default(),
|
||||
refresh: Refresh::from(refresh),
|
||||
indexes,
|
||||
|
@ -1531,7 +1539,7 @@ impl RemoveSettings {
|
|||
Self {
|
||||
locked,
|
||||
frozen,
|
||||
active: flag(active, no_active),
|
||||
active: flag(active, no_active, "active"),
|
||||
no_sync,
|
||||
packages,
|
||||
dependency_type,
|
||||
|
@ -1603,7 +1611,7 @@ impl VersionSettings {
|
|||
dry_run,
|
||||
locked,
|
||||
frozen,
|
||||
active: flag(active, no_active),
|
||||
active: flag(active, no_active, "active"),
|
||||
no_sync,
|
||||
package,
|
||||
python: python.and_then(Maybe::into_option),
|
||||
|
@ -1779,7 +1787,7 @@ impl ExportSettings {
|
|||
false,
|
||||
// TODO(blueraft): support only_extra
|
||||
vec![],
|
||||
flag(all_extras, no_all_extras).unwrap_or_default(),
|
||||
flag(all_extras, no_all_extras, "all-extras").unwrap_or_default(),
|
||||
),
|
||||
groups: DependencyGroups::from_args(
|
||||
dev,
|
||||
|
@ -1792,7 +1800,7 @@ impl ExportSettings {
|
|||
all_groups,
|
||||
),
|
||||
editable: EditableMode::from_args(no_editable),
|
||||
hashes: flag(hashes, no_hashes).unwrap_or(true),
|
||||
hashes: flag(hashes, no_hashes, "hashes").unwrap_or(true),
|
||||
install_options: InstallOptions::new(
|
||||
no_emit_project,
|
||||
no_emit_workspace,
|
||||
|
@ -1801,8 +1809,8 @@ impl ExportSettings {
|
|||
output_file,
|
||||
locked,
|
||||
frozen,
|
||||
include_annotations: flag(annotate, no_annotate).unwrap_or(true),
|
||||
include_header: flag(header, no_header).unwrap_or(true),
|
||||
include_annotations: flag(annotate, no_annotate, "annotate").unwrap_or(true),
|
||||
include_header: flag(header, no_header, "header").unwrap_or(true),
|
||||
script,
|
||||
python: python.and_then(Maybe::into_option),
|
||||
refresh: Refresh::from(refresh),
|
||||
|
@ -1955,30 +1963,42 @@ impl PipCompileSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
no_build: flag(no_build, build),
|
||||
system: flag(system, no_system, "system"),
|
||||
no_build: flag(no_build, build, "build"),
|
||||
no_binary,
|
||||
only_binary,
|
||||
extra,
|
||||
all_extras: flag(all_extras, no_all_extras),
|
||||
no_deps: flag(no_deps, deps),
|
||||
all_extras: flag(all_extras, no_all_extras, "all-extras"),
|
||||
no_deps: flag(no_deps, deps, "deps"),
|
||||
group: Some(group),
|
||||
output_file,
|
||||
no_strip_extras: flag(no_strip_extras, strip_extras),
|
||||
no_strip_markers: flag(no_strip_markers, strip_markers),
|
||||
no_annotate: flag(no_annotate, annotate),
|
||||
no_header: flag(no_header, header),
|
||||
no_strip_extras: flag(no_strip_extras, strip_extras, "strip-extras"),
|
||||
no_strip_markers: flag(no_strip_markers, strip_markers, "strip-markers"),
|
||||
no_annotate: flag(no_annotate, annotate, "annotate"),
|
||||
no_header: flag(no_header, header, "header"),
|
||||
custom_compile_command,
|
||||
generate_hashes: flag(generate_hashes, no_generate_hashes),
|
||||
generate_hashes: flag(generate_hashes, no_generate_hashes, "generate-hashes"),
|
||||
python_version,
|
||||
python_platform,
|
||||
universal: flag(universal, no_universal),
|
||||
universal: flag(universal, no_universal, "universal"),
|
||||
no_emit_package,
|
||||
emit_index_url: flag(emit_index_url, no_emit_index_url),
|
||||
emit_find_links: flag(emit_find_links, no_emit_find_links),
|
||||
emit_build_options: flag(emit_build_options, no_emit_build_options),
|
||||
emit_marker_expression: flag(emit_marker_expression, no_emit_marker_expression),
|
||||
emit_index_annotation: flag(emit_index_annotation, no_emit_index_annotation),
|
||||
emit_index_url: flag(emit_index_url, no_emit_index_url, "emit-index-url"),
|
||||
emit_find_links: flag(emit_find_links, no_emit_find_links, "emit-find-links"),
|
||||
emit_build_options: flag(
|
||||
emit_build_options,
|
||||
no_emit_build_options,
|
||||
"emit-build-options",
|
||||
),
|
||||
emit_marker_expression: flag(
|
||||
emit_marker_expression,
|
||||
no_emit_marker_expression,
|
||||
"emit-marker-expression",
|
||||
),
|
||||
emit_index_annotation: flag(
|
||||
emit_index_annotation,
|
||||
no_emit_index_annotation,
|
||||
"emit-index-annotation",
|
||||
),
|
||||
annotation_style,
|
||||
torch_backend,
|
||||
..PipOptions::from(resolver)
|
||||
|
@ -2050,22 +2070,27 @@ impl PipSyncSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
break_system_packages: flag(break_system_packages, no_break_system_packages),
|
||||
system: flag(system, no_system, "system"),
|
||||
break_system_packages: flag(
|
||||
break_system_packages,
|
||||
no_break_system_packages,
|
||||
"break-system-packages",
|
||||
),
|
||||
target,
|
||||
prefix,
|
||||
require_hashes: flag(require_hashes, no_require_hashes),
|
||||
verify_hashes: flag(verify_hashes, no_verify_hashes),
|
||||
no_build: flag(no_build, build),
|
||||
require_hashes: flag(require_hashes, no_require_hashes, "require-hashes"),
|
||||
verify_hashes: flag(verify_hashes, no_verify_hashes, "verify-hashes"),
|
||||
no_build: flag(no_build, build, "build"),
|
||||
no_binary,
|
||||
only_binary,
|
||||
allow_empty_requirements: flag(
|
||||
allow_empty_requirements,
|
||||
no_allow_empty_requirements,
|
||||
"allow-empty-requirements",
|
||||
),
|
||||
python_version,
|
||||
python_platform,
|
||||
strict: flag(strict, no_strict),
|
||||
strict: flag(strict, no_strict, "strict"),
|
||||
torch_backend,
|
||||
..PipOptions::from(installer)
|
||||
},
|
||||
|
@ -2199,7 +2224,7 @@ impl PipInstallSettings {
|
|||
constraints_from_workspace,
|
||||
overrides_from_workspace,
|
||||
build_constraints_from_workspace,
|
||||
modifications: if flag(exact, inexact).unwrap_or(false) {
|
||||
modifications: if flag(exact, inexact, "inexact").unwrap_or(false) {
|
||||
Modifications::Exact
|
||||
} else {
|
||||
Modifications::Sufficient
|
||||
|
@ -2208,22 +2233,26 @@ impl PipInstallSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
break_system_packages: flag(break_system_packages, no_break_system_packages),
|
||||
system: flag(system, no_system, "system"),
|
||||
break_system_packages: flag(
|
||||
break_system_packages,
|
||||
no_break_system_packages,
|
||||
"break-system-packages",
|
||||
),
|
||||
target,
|
||||
prefix,
|
||||
no_build: flag(no_build, build),
|
||||
no_build: flag(no_build, build, "build"),
|
||||
no_binary,
|
||||
only_binary,
|
||||
strict: flag(strict, no_strict),
|
||||
strict: flag(strict, no_strict, "strict"),
|
||||
extra,
|
||||
all_extras: flag(all_extras, no_all_extras),
|
||||
all_extras: flag(all_extras, no_all_extras, "all-extras"),
|
||||
group: Some(group),
|
||||
no_deps: flag(no_deps, deps),
|
||||
no_deps: flag(no_deps, deps, "deps"),
|
||||
python_version,
|
||||
python_platform,
|
||||
require_hashes: flag(require_hashes, no_require_hashes),
|
||||
verify_hashes: flag(verify_hashes, no_verify_hashes),
|
||||
require_hashes: flag(require_hashes, no_require_hashes, "require-hashes"),
|
||||
verify_hashes: flag(verify_hashes, no_verify_hashes, "verify-hashes"),
|
||||
torch_backend,
|
||||
..PipOptions::from(installer)
|
||||
},
|
||||
|
@ -2267,8 +2296,12 @@ impl PipUninstallSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
break_system_packages: flag(break_system_packages, no_break_system_packages),
|
||||
system: flag(system, no_system, "system"),
|
||||
break_system_packages: flag(
|
||||
break_system_packages,
|
||||
no_break_system_packages,
|
||||
"break-system-packages",
|
||||
),
|
||||
target,
|
||||
prefix,
|
||||
keyring_provider,
|
||||
|
@ -2308,8 +2341,8 @@ impl PipFreezeSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
strict: flag(strict, no_strict),
|
||||
system: flag(system, no_system, "system"),
|
||||
strict: flag(strict, no_strict, "strict"),
|
||||
..PipOptions::default()
|
||||
},
|
||||
filesystem,
|
||||
|
@ -2348,15 +2381,15 @@ impl PipListSettings {
|
|||
} = args;
|
||||
|
||||
Self {
|
||||
editable: flag(editable, exclude_editable),
|
||||
editable: flag(editable, exclude_editable, "exclude-editable"),
|
||||
exclude,
|
||||
format,
|
||||
outdated: flag(outdated, no_outdated).unwrap_or(false),
|
||||
outdated: flag(outdated, no_outdated, "outdated").unwrap_or(false),
|
||||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
strict: flag(strict, no_strict),
|
||||
system: flag(system, no_system, "system"),
|
||||
strict: flag(strict, no_strict, "strict"),
|
||||
..PipOptions::from(fetch)
|
||||
},
|
||||
filesystem,
|
||||
|
@ -2393,8 +2426,8 @@ impl PipShowSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
strict: flag(strict, no_strict),
|
||||
system: flag(system, no_system, "system"),
|
||||
strict: flag(strict, no_strict, "strict"),
|
||||
..PipOptions::default()
|
||||
},
|
||||
filesystem,
|
||||
|
@ -2442,8 +2475,8 @@ impl PipTreeSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
strict: flag(strict, no_strict),
|
||||
system: flag(system, no_system, "system"),
|
||||
strict: flag(strict, no_strict, "strict"),
|
||||
..PipOptions::from(fetch)
|
||||
},
|
||||
filesystem,
|
||||
|
@ -2471,7 +2504,7 @@ impl PipCheckSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
system: flag(system, no_system, "system"),
|
||||
..PipOptions::default()
|
||||
},
|
||||
filesystem,
|
||||
|
@ -2538,15 +2571,15 @@ impl BuildSettings {
|
|||
sdist,
|
||||
wheel,
|
||||
list,
|
||||
build_logs: flag(build_logs, no_build_logs).unwrap_or(true),
|
||||
build_logs: flag(build_logs, no_build_logs, "build-logs").unwrap_or(true),
|
||||
build_constraints: build_constraints
|
||||
.into_iter()
|
||||
.filter_map(Maybe::into_option)
|
||||
.collect(),
|
||||
force_pep517,
|
||||
hash_checking: HashCheckingMode::from_args(
|
||||
flag(require_hashes, no_require_hashes),
|
||||
flag(verify_hashes, no_verify_hashes),
|
||||
flag(require_hashes, no_require_hashes, "require-hashes"),
|
||||
flag(verify_hashes, no_verify_hashes, "verify-hashes"),
|
||||
),
|
||||
python: python.and_then(Maybe::into_option),
|
||||
refresh: Refresh::from(refresh),
|
||||
|
@ -2605,7 +2638,7 @@ impl VenvSettings {
|
|||
settings: PipSettings::combine(
|
||||
PipOptions {
|
||||
python: python.and_then(Maybe::into_option),
|
||||
system: flag(system, no_system),
|
||||
system: flag(system, no_system, "system"),
|
||||
index_strategy,
|
||||
keyring_provider,
|
||||
exclude_newer,
|
||||
|
|
|
@ -11486,3 +11486,25 @@ fn pep_751_dependency() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test that we show an error instead of panicking for conflicting arguments in different levels,
|
||||
/// which are not caught by clap.
|
||||
#[test]
|
||||
fn conflicting_flags_clap_bug() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
uv_snapshot!(context.filters(), context.command()
|
||||
.arg("pip")
|
||||
.arg("--offline")
|
||||
.arg("install")
|
||||
.arg("--no-offline")
|
||||
.arg("tqdm"), @r"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: `--offline` and `--no-offline` cannot be used together. Boolean flags on different levels are currently not supported (https://github.com/clap-rs/clap/issues/6049)
|
||||
"
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue