1use std::env::VarError;
4use std::ffi::OsString;
5use std::fmt;
6use std::num::NonZeroUsize;
7use std::path::{Path, PathBuf};
8use std::process;
9use std::str::FromStr;
10use std::time::Duration;
11
12use anyhow::{Result, bail};
13use rustc_hash::FxHashSet;
14
15use uv_audit::{VulnerabilityID, VulnerabilityServiceFormat};
16use uv_auth::Service;
17use uv_cache::{CacheArgs, Refresh};
18use uv_client::{Certificates, Connectivity, MetadataRangeRequest};
19use uv_configuration::{
20 ActiveEnvironment, AddBoundsKind, AnnotationStyle, BuildIsolation, BuildOptions, Concurrency,
21 DependencyGroups, DependencyMode, DevMode, DryRun, EditableMode, EnvFile, ExcludeDependency,
22 ExcludeNewer, ExcludeNewerPackage, ExportFormat, ExtrasSpecification, ForkStrategy,
23 GitLfsSetting, HashCheckingMode, IndexStrategy, InitKind, InitProjectKind, InstallOptions,
24 KeyringProviderType, Modifications, NoBinary, NoBuild, NoSources, Override, PackageOverride,
25 PipCompileFormat, Prerelease, ProjectBuildBackend, ProxyUrl, PythonUpgrade,
26 PythonUpgradeSource, Reinstall, RequiredVersion, RequirementsInput, ResolutionMode,
27 TargetTriple, ToolRunCommand, TrustedHost, TrustedPublishing, Upgrade, VersionControlSystem,
28};
29use uv_distribution_types::{
30 ConfigSettings, DependencyMetadata, ExcludeNewerOverride, ExtraBuildVariables, Index,
31 IndexLocations, IndexUrl, MinimumLibcVersion, NameRequirementSpecification,
32 PackageConfigSettings, Requirement,
33};
34use uv_install_wheel::LinkMode;
35use uv_normalize::{ExtraName, PackageName, PipGroupName};
36use uv_pep440::Version;
37use uv_pep508::{MarkerTree, RequirementOrigin};
38use uv_preview::Preview;
39use uv_pypi_types::SupportedEnvironments;
40use uv_python_types::{
41 Prefix, PythonArchitecture, PythonDownloads, PythonPreference, PythonVersion, Target,
42};
43use uv_redacted::SafeUrl;
44use uv_settings::{
45 Combine, EnvFlag, EnvironmentOptions, FilesystemOptions, FrozenFlag, FrozenSource,
46 IndexOptions, LockCheck, LockedFlag, LockedSource, MalwareCheckSettings, Options, PipOptions,
47 PreviewFeaturesOption, PreviewOption, PublishOptions, PythonInstallMirrors, PythonListKinds,
48 ResolverInstallerOptions, ResolverInstallerSchema, ResolverInstallerSettings, ResolverOptions,
49 ResolverSettings, resolve_build_hash_checking, resolve_prerelease,
50};
51use uv_static::EnvVars;
52use uv_torch::{AmdGpuArchitecture, TorchMode};
53use uv_warnings::warn_user_once;
54use uv_workspace::pyproject::{DependencyType, ExtraBuildDependencies, OverrideDependency};
55
56use crate::comma::CommaSeparatedRequirements;
57use crate::{
58 AddArgs, AuditArgs, AuditCommonArgs, AuditOutputFormat, AuthLoginArgs, AuthLogoutArgs,
59 AuthTokenArgs, ColorChoice, DependencyConstraintsArgs, ExternalCommand, GlobalArgs, InitArgs,
60 ListFormat, LockArgs, Maybe, MetadataArgs, PipCheckArgs, PipCompileArgs, PipFreezeArgs,
61 PipInstallArgs, PipInstallFormat, PipListArgs, PipShowArgs, PipSyncArgs, PipTreeArgs,
62 PipUninstallArgs, ProjectDependencyGroupsArgs, PythonFindArgs, PythonInstallArgs,
63 PythonListArgs, PythonListFormat, PythonPinArgs, PythonUninstallArgs, PythonUpgradeArgs,
64 RemoveArgs, RunArgs, SyncArgs, SyncFormat, ToolAuditArgs, ToolDirArgs, ToolInstallArgs,
65 ToolListArgs, ToolRunArgs, ToolUninstallArgs, TreeArgs, TreeFormat, UpgradeArgs, VenvArgs,
66 VersionArgs, VersionBumpSpec, VersionFormat,
67};
68use crate::{
69 AuthorFrom, BuildArgs, BuildOptionsArgs, CheckArgs, ExcludeNewerArgs, ExportArgs, FormatArgs,
70 HashCheckingArgs, PackageExcludeNewerArgs, PublishArgs, PythonDirArgs, RegistryClientArgs,
71 ResolverArgs, ResolverInstallerArgs, ToolUpgradeArgs,
72 options::{
73 Flag, FlagSource, IntoPipOptions, check_conflicts, flag, resolve_flag, resolve_flag_pair,
74 resolver_installer_options, resolver_options, upgrade_options,
75 },
76};
77
78const PYPI_PUBLISH_URL: &str = "https://upload.pypi.org/legacy/";
80
81#[derive(Debug, Clone)]
83pub struct GlobalSettings {
84 pub required_version: Option<RequiredVersion>,
85 pub quiet: u8,
86 pub verbose: u8,
87 pub color: ColorChoice,
88 pub network_settings: NetworkSettings,
89 pub concurrency: Concurrency,
90 pub show_settings: bool,
91 pub preview: Preview,
92 pub python_preference: PythonPreference,
93 pub python_arch: Option<PythonArchitecture>,
94 pub python_downloads: PythonDownloads,
95 pub no_progress: bool,
96 pub installer_metadata: bool,
97}
98
99impl GlobalSettings {
100 pub fn resolve(
102 args: &GlobalArgs,
103 workspace: Option<&FilesystemOptions>,
104 environment: &EnvironmentOptions,
105 custom_certificate_file: Option<&Path>,
106 ) -> anyhow::Result<Self> {
107 let network_settings =
108 NetworkSettings::resolve(args, workspace, environment, custom_certificate_file)?;
109 let python_preference = resolve_python_preference(args, workspace, environment)?;
110 let color = resolve_color(args);
111 Ok(Self {
112 required_version: workspace
113 .and_then(|workspace| workspace.globals.required_version.clone()),
114 quiet: args.quiet,
115 verbose: args.verbose,
116 color,
117 network_settings,
118 concurrency: Concurrency::new(
119 environment
120 .concurrency
121 .downloads
122 .combine(workspace.and_then(|workspace| workspace.globals.concurrent_downloads))
123 .map(NonZeroUsize::get)
124 .unwrap_or(Concurrency::DEFAULT_DOWNLOADS),
125 environment
126 .concurrency
127 .builds
128 .combine(workspace.and_then(|workspace| workspace.globals.concurrent_builds))
129 .map(NonZeroUsize::get)
130 .unwrap_or_else(Concurrency::threads),
131 environment
132 .concurrency
133 .installs
134 .combine(workspace.and_then(|workspace| workspace.globals.concurrent_installs))
135 .map(NonZeroUsize::get)
136 .unwrap_or_else(Concurrency::threads),
137 environment
138 .concurrency
139 .cache_reads
140 .map(NonZeroUsize::get)
141 .unwrap_or(Concurrency::DEFAULT_CACHE_READS),
142 ),
143 show_settings: args.show_settings,
144 preview: resolve_preview(args, workspace, environment)?,
145 python_preference,
146 python_arch: environment.python_arch,
147 python_downloads: flag(
148 args.allow_python_downloads,
149 args.no_python_downloads,
150 "python-downloads",
151 )?
152 .map(PythonDownloads::from)
153 .combine(env(env::UV_PYTHON_DOWNLOADS))
154 .combine(workspace.and_then(|workspace| workspace.globals.python_downloads))
155 .unwrap_or_default(),
156 no_progress: resolve_flag(args.no_progress, "no-progress", environment.no_progress)
159 .is_enabled()
160 || std::env::var_os(EnvVars::RUST_LOG).is_some(),
161 installer_metadata: !resolve_flag(
162 args.no_installer_metadata,
163 "no-installer-metadata",
164 environment.no_installer_metadata,
165 )
166 .is_enabled(),
167 })
168 }
169}
170
171pub fn resolve_color(args: &GlobalArgs) -> ColorChoice {
173 if let Some(color_choice) = args.color {
174 color_choice
176 } else if args.no_color {
177 ColorChoice::Never
179 } else if std::env::var_os(EnvVars::NO_COLOR)
180 .as_ref()
181 .is_some_and(|v| !v.is_empty())
182 {
183 ColorChoice::Never
185 } else if std::env::var_os(EnvVars::FORCE_COLOR)
186 .as_ref()
187 .is_some_and(|v| !v.is_empty())
188 || std::env::var_os(EnvVars::CLICOLOR_FORCE)
189 .as_ref()
190 .is_some_and(|v| !v.is_empty())
191 {
192 ColorChoice::Always
194 } else {
195 ColorChoice::Auto
196 }
197}
198
199fn resolve_python_preference(
200 args: &GlobalArgs,
201 workspace: Option<&FilesystemOptions>,
202 environment: &EnvironmentOptions,
203) -> anyhow::Result<PythonPreference> {
204 let (managed_python, no_managed_python) = resolve_flag_pair(
206 args.managed_python,
207 args.no_managed_python,
208 "managed-python",
209 "no-managed-python",
210 Some(environment.managed_python),
211 Some(environment.no_managed_python),
212 );
213
214 if managed_python.is_enabled() && args.python_preference.is_some() {
216 check_conflicts(managed_python, Flag::from_cli("python-preference"))?;
217 }
218
219 if no_managed_python.is_enabled() && args.python_preference.is_some() {
221 check_conflicts(no_managed_python, Flag::from_cli("python-preference"))?;
222 }
223
224 Ok(if managed_python.is_enabled() {
225 PythonPreference::OnlyManaged
226 } else if no_managed_python.is_enabled() {
227 PythonPreference::OnlySystem
228 } else {
229 args.python_preference
230 .combine(workspace.and_then(|workspace| workspace.globals.python_preference))
231 .unwrap_or_default()
232 })
233}
234
235pub fn resolve_preview(
237 args: &GlobalArgs,
238 workspace: Option<&FilesystemOptions>,
239 environment: &EnvironmentOptions,
240) -> anyhow::Result<Preview> {
241 if let Some(enabled) = flag(args.preview, args.no_preview, "preview")? {
243 return Ok(if enabled {
244 Preview::all()
245 } else {
246 Preview::default()
247 });
248 }
249
250 if environment.preview.value == Some(true) {
252 return Ok(Preview::all());
253 }
254
255 let configured = workspace.and_then(|workspace| workspace.globals.preview.as_ref());
256
257 if matches!(
259 configured,
260 Some(
261 PreviewOption::Preview(true)
262 | PreviewOption::PreviewFeatures(PreviewFeaturesOption::Toggle(true))
263 )
264 ) {
265 return Ok(Preview::all());
266 }
267
268 if !args.preview_features.is_empty() {
270 return Ok(Preview::from_feature_names(&args.preview_features));
271 }
272
273 Ok(configured.map(PreviewOption::resolve).unwrap_or_default())
275}
276
277#[derive(Debug, Clone)]
279pub struct NetworkSettings {
280 pub connectivity: Connectivity,
281 offline: Flag,
282 pub system_certs: bool,
283 pub custom_certificates: Option<Certificates>,
284 pub http_proxy: Option<ProxyUrl>,
285 pub https_proxy: Option<ProxyUrl>,
286 pub no_proxy: Option<Vec<String>>,
287 pub allow_insecure_host: Vec<TrustedHost>,
288 pub read_timeout: Duration,
289 pub connect_timeout: Duration,
290 pub retries: u32,
291 pub metadata_range_request: MetadataRangeRequest,
292}
293
294impl NetworkSettings {
295 #[allow(deprecated)]
296 fn resolve(
297 args: &GlobalArgs,
298 workspace: Option<&FilesystemOptions>,
299 environment: &EnvironmentOptions,
300 custom_certificate_file: Option<&Path>,
301 ) -> anyhow::Result<Self> {
302 let offline = match flag(args.offline, args.no_offline, "offline")? {
305 Some(true) => Flag::from_cli("offline"),
306 Some(false) => Flag::disabled(),
307 None => {
308 let env_flag = resolve_flag(false, "offline", environment.offline);
310 if environment.offline.value.is_some() {
311 env_flag
312 } else if workspace
313 .and_then(|workspace| workspace.globals.offline)
314 .unwrap_or(false)
315 {
316 Flag::from_config("offline")
318 } else {
319 Flag::disabled()
320 }
321 }
322 };
323
324 let connectivity = if offline.is_enabled() {
325 Connectivity::Offline
326 } else {
327 Connectivity::Online
328 };
329
330 if args.native_tls {
331 warn_user_once!(
332 "The `--native-tls` flag is deprecated and will be removed in a future release. Use `--system-certs` instead."
333 );
334 }
335 if args.no_native_tls {
336 warn_user_once!(
337 "The `--no-native-tls` flag is deprecated and will be removed in a future release. Use `--no-system-certs` instead."
338 );
339 }
340 if environment.native_tls.value.is_some() && environment.system_certs.value.is_none() {
341 warn_user_once!(
342 "The `UV_NATIVE_TLS` environment variable is deprecated and will be removed in a future release. Use `UV_SYSTEM_CERTS` instead."
343 );
344 }
345 if let Some(workspace) = workspace
346 && workspace.globals.native_tls.is_some()
347 && workspace.globals.system_certs.is_none()
348 {
349 warn_user_once!(
350 "The `native-tls` setting is deprecated and will be removed in a future release. Use `system-certs` instead."
351 );
352 }
353
354 let system_certs =
361 if let Some(value) = flag(args.system_certs, args.no_system_certs, "system-certs")? {
362 value
363 } else if let Some(value) = flag(args.native_tls, args.no_native_tls, "native-tls")? {
364 value
365 } else if let Some(value) = environment.system_certs.value {
366 value
367 } else if let Some(value) = environment.native_tls.value {
368 value
369 } else {
370 workspace
371 .and_then(|workspace| {
372 workspace
373 .globals
374 .system_certs
375 .or(workspace.globals.native_tls)
376 })
377 .unwrap_or(false)
378 };
379
380 let allow_insecure_host = args
381 .allow_insecure_host
382 .as_ref()
383 .map(|allow_insecure_host| {
384 allow_insecure_host
385 .iter()
386 .filter_map(|value| value.clone().into_option())
387 })
388 .into_iter()
389 .flatten()
390 .chain(
391 workspace
392 .and_then(|workspace| workspace.globals.allow_insecure_host.clone())
393 .into_iter()
394 .flatten(),
395 )
396 .collect();
397 let http_proxy = workspace.and_then(|workspace| workspace.globals.http_proxy.clone());
398 let https_proxy = workspace.and_then(|workspace| workspace.globals.https_proxy.clone());
399 let no_proxy = workspace.and_then(|workspace| workspace.globals.no_proxy.clone());
400
401 let custom_certificates = custom_certificate_file
402 .map(Certificates::from_file)
403 .transpose()?
404 .or_else(Certificates::from_env);
405
406 Ok(Self {
407 connectivity,
408 offline,
409 system_certs,
410 custom_certificates,
411 http_proxy,
412 https_proxy,
413 no_proxy,
414 allow_insecure_host,
415 read_timeout: environment.http_read_timeout,
416 connect_timeout: environment.http_connect_timeout,
417 retries: environment.http_retries,
418 metadata_range_request: environment
419 .require_metadata_range_requests
420 .unwrap_or_default()
421 .into(),
422 })
423 }
424
425 pub fn check_refresh_conflict(&self, refresh: &Refresh) -> anyhow::Result<()> {
430 if !matches!(refresh, Refresh::None(_)) {
431 check_conflicts(self.offline, Flag::from_cli("refresh"))?;
435 }
436 Ok(())
437 }
438}
439
440#[derive(Debug, Clone)]
442pub struct CacheSettings {
443 pub no_cache: bool,
444 pub cache_dir: Option<PathBuf>,
445}
446
447impl CacheSettings {
448 pub fn resolve(
450 args: CacheArgs,
451 workspace: Option<&FilesystemOptions>,
452 environment: &EnvironmentOptions,
453 ) -> Self {
454 Self {
455 no_cache: args
456 .no_cache
457 .then_some(true)
458 .combine(environment.no_cache.value)
459 .combine(workspace.and_then(|workspace| workspace.globals.no_cache))
460 .unwrap_or(false),
461 cache_dir: args
462 .cache_dir
463 .or_else(|| workspace.and_then(|workspace| workspace.globals.cache_dir.clone())),
464 }
465 }
466}
467
468#[derive(Debug, Clone)]
470pub struct InitSettings {
471 pub path: Option<PathBuf>,
472 pub name: Option<PackageName>,
473 pub kind: InitKind,
474 pub bare: bool,
475 pub description: Option<String>,
476 pub no_description: bool,
477 pub vcs: Option<VersionControlSystem>,
478 pub build_backend: Option<ProjectBuildBackend>,
479 pub no_readme: bool,
480 pub author_from: Option<AuthorFrom>,
481 pub pin_python: bool,
482 pub no_workspace: bool,
483 pub python: Option<String>,
484 pub install_mirrors: PythonInstallMirrors,
485}
486
487impl InitSettings {
488 pub fn resolve(
490 args: InitArgs,
491 filesystem: Option<FilesystemOptions>,
492 environment: EnvironmentOptions,
493 ) -> Result<Self> {
494 let InitArgs {
495 path,
496 name,
497 r#virtual,
498 package,
499 no_package,
500 bare,
501 app,
502 lib,
503 script,
504 description,
505 no_description,
506 vcs,
507 build_backend,
508 no_readme,
509 author_from,
510 no_pin_python,
511 pin_python,
512 no_workspace,
513 python,
514 ..
515 } = args;
516
517 let bare = resolve_flag(bare, "bare", environment.init_bare).is_enabled();
518
519 let filesystem_install_mirrors = filesystem
520 .map(|fs| fs.install_mirrors.clone())
521 .unwrap_or_default();
522
523 let no_description = no_description || (bare && description.is_none());
524
525 let build_backend = match build_backend {
528 Some(build_backend) => Some(build_backend),
529 None if script || no_package || r#virtual => None,
530 None => environment.init_build_backend,
531 };
532
533 if r#virtual && lib {
534 bail!("`--virtual` and `--lib` are mutually exclusive");
535 }
536 if r#virtual && build_backend.is_some() {
537 bail!("`--virtual` and `--build-backend` are mutually exclusive");
538 }
539
540 let package = flag(
541 package || build_backend.is_some(),
542 no_package || r#virtual,
543 "virtual",
544 )?;
545
546 let kind = if script {
547 InitKind::Script
548 } else if bare {
549 if package == Some(true) || lib {
550 InitKind::Project(InitProjectKind::BareWithBuildSystem)
551 } else {
552 InitKind::Project(InitProjectKind::Bare)
553 }
554 } else {
555 let app_lib_kind = match (app, lib) {
557 (false, false) => InitProjectKind::ApplicationWithLibrary,
558 (true, false) => InitProjectKind::Application,
559 (false, true) => InitProjectKind::Library,
560 (true, true) => bail!("`app` and `lib` are mutually exclusive"),
561 };
562
563 let app_lib_kind = match (app_lib_kind, package) {
565 (InitProjectKind::ApplicationWithLibrary, None | Some(true)) => {
566 InitProjectKind::ApplicationWithLibrary
567 }
568 (InitProjectKind::ApplicationWithLibrary, Some(false)) => {
569 InitProjectKind::Application
570 }
571 (InitProjectKind::Application, None | Some(true)) => {
572 InitProjectKind::ApplicationWithLibrary
573 }
574 (InitProjectKind::Application, Some(false)) => InitProjectKind::Application,
575 (InitProjectKind::Library, None | Some(true)) => InitProjectKind::Library,
576 (InitProjectKind::Library, Some(false)) => {
577 bail!("`lib` and `no_package` are mutually exclusive");
578 }
579 (InitProjectKind::Bare | InitProjectKind::BareWithBuildSystem, _) => {
580 unreachable!()
581 }
582 };
583 InitKind::Project(app_lib_kind)
584 };
585
586 if script && package == Some(true) {
587 warn_user_once!("`--package` is a no-op for Python scripts, which are standalone");
588 }
589
590 Ok(Self {
591 path,
592 name,
593 kind,
594 bare,
595 description,
596 no_description,
597 vcs: vcs.or(bare.then_some(VersionControlSystem::None)),
598 build_backend,
599 no_readme,
600 author_from,
601 pin_python: flag(pin_python, no_pin_python, "pin-python")?.unwrap_or(!bare),
602 no_workspace,
603 python: python.and_then(Maybe::into_option),
604 install_mirrors: environment
605 .install_mirrors
606 .combine(filesystem_install_mirrors),
607 })
608 }
609}
610
611impl From<LockCheck> for Flag {
612 fn from(lock_check: LockCheck) -> Self {
613 match lock_check {
614 LockCheck::Enabled(LockedSource::Cli(flag)) => Self::from_cli(flag.name()),
615 LockCheck::Enabled(LockedSource::Env) => Self::Enabled {
616 source: FlagSource::Env(EnvVars::UV_LOCKED),
617 name: "locked",
618 },
619 LockCheck::Disabled => Self::disabled(),
620 }
621 }
622}
623
624impl From<FrozenSource> for Flag {
625 fn from(source: FrozenSource) -> Self {
626 match source {
627 FrozenSource::Cli(flag) => Self::from_cli(flag.name()),
628 FrozenSource::Env => Self::Enabled {
629 source: FlagSource::Env(EnvVars::UV_FROZEN),
630 name: "frozen",
631 },
632 }
633 }
634}
635
636fn resolve_lock_flags(
638 locked: LockCheck,
639 frozen: Option<FrozenSource>,
640) -> anyhow::Result<(LockCheck, Option<FrozenSource>)> {
641 match (locked, frozen) {
642 (LockCheck::Enabled(LockedSource::Cli(flag)), Some(FrozenSource::Env)) => {
643 warn_user_once!("Ignoring `UV_FROZEN` because `{flag}` was provided");
644 Ok((locked, None))
645 }
646 (LockCheck::Enabled(LockedSource::Env), Some(FrozenSource::Cli(flag))) => {
647 warn_user_once!("Ignoring `UV_LOCKED` because `{flag}` was provided");
648 Ok((LockCheck::Disabled, frozen))
649 }
650 _ => {
651 check_conflicts(
652 Flag::from(locked),
653 frozen.map_or(Flag::Disabled, Flag::from),
654 )?;
655 Ok((locked, frozen))
656 }
657 }
658}
659
660fn resolve_frozen(
662 enabled: bool,
663 disabled: bool,
664 cli_flag: FrozenFlag,
665 environment: EnvFlag,
666) -> Option<FrozenSource> {
667 if enabled {
668 Some(FrozenSource::Cli(cli_flag))
669 } else if !disabled && environment.value == Some(true) {
670 Some(FrozenSource::Env)
671 } else {
672 None
673 }
674}
675
676fn resolve_lock_check(
678 enabled: bool,
679 disabled: bool,
680 cli_flag: LockedFlag,
681 environment: EnvFlag,
682) -> LockCheck {
683 if enabled {
684 LockCheck::Enabled(LockedSource::Cli(cli_flag))
685 } else if !disabled && environment.value == Some(true) {
686 LockCheck::Enabled(LockedSource::Env)
687 } else {
688 LockCheck::Disabled
689 }
690}
691
692#[derive(Debug, Clone)]
694pub struct RunSettings {
695 pub lock_check: LockCheck,
696 pub frozen: Option<FrozenSource>,
697 pub extras: ExtrasSpecification,
698 pub groups: DependencyGroups,
699 pub editable: Option<EditableMode>,
700 pub modifications: Modifications,
701 pub with: Vec<String>,
702 pub with_editable: Vec<String>,
703 pub with_requirements: Vec<RequirementsInput>,
704 pub isolated: bool,
705 pub show_resolution: bool,
706 pub all_packages: bool,
707 pub package: Option<PackageName>,
708 pub no_project: bool,
709 pub active: ActiveEnvironment,
710 pub no_sync: bool,
711 pub python: Option<String>,
712 pub python_platform: Option<TargetTriple>,
713 pub install_mirrors: PythonInstallMirrors,
714 pub refresh: Refresh,
715 pub settings: ResolverInstallerSettings,
716 pub env_file: EnvFile,
717 pub recursion_depth: u32,
718 pub max_recursion_depth: u32,
719 pub malware_settings: MalwareCheckSettings,
720 #[cfg(unix)]
721 pub run_rlimit_nofile: Option<u32>,
722}
723
724impl RunSettings {
725 const DEFAULT_MAX_RECURSION_DEPTH: u32 = 100;
730
731 pub fn resolve(
733 args: RunArgs,
734 filesystem: Option<FilesystemOptions>,
735 environment: EnvironmentOptions,
736 ) -> anyhow::Result<Self> {
737 let RunArgs {
738 extra,
739 all_extras,
740 no_extra,
741 no_all_extras,
742 dependency_groups:
743 ProjectDependencyGroupsArgs {
744 dev,
745 no_dev,
746 only_dev,
747 group,
748 no_group,
749 no_default_groups,
750 only_group,
751 all_groups,
752 },
753 module: _,
754 editable,
755 no_editable,
756 no_editable_package,
757 inexact,
758 exact,
759 script: _,
760 gui_script: _,
761 command: _,
762 with,
763 with_editable,
764 with_requirements,
765 isolated,
766 active,
767 no_active,
768 no_sync,
769 locked,
770 no_locked,
771 frozen,
772 no_frozen,
773 installer,
774 build,
775 refresh,
776 all_packages,
777 package,
778 no_project,
779 python,
780 python_platform,
781 show_resolution,
782 env_file,
783 no_env_file,
784 max_recursion_depth,
785 } = args;
786
787 let filesystem_install_mirrors = filesystem
788 .as_ref()
789 .map(|fs| fs.install_mirrors.clone())
790 .unwrap_or_default();
791
792 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
794 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
795 let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);
796
797 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
798
799 let (dev, no_dev) = resolve_flag_pair(
800 dev,
801 no_dev,
802 "dev",
803 "no-dev",
804 Some(environment.dev),
805 Some(environment.no_dev),
806 );
807
808 let (editable, no_editable) = resolve_flag_pair(
809 editable,
810 no_editable,
811 "editable",
812 "no-editable",
813 None,
814 Some(environment.no_editable),
815 );
816 let isolated = isolated || environment.isolated.value == Some(true);
817 let show_resolution = show_resolution || environment.show_resolution.value == Some(true);
818 let no_env_file = no_env_file || environment.no_env_file.value == Some(true);
819
820 let malware_settings = MalwareCheckSettings::resolve(filesystem.as_ref(), &environment);
821
822 Ok(Self {
823 lock_check: locked,
824 frozen,
825 extras: ExtrasSpecification::from_args(
826 extra.unwrap_or_default(),
827 no_extra,
828 false,
830 vec![],
832 flag(all_extras, no_all_extras, "all-extras")?.unwrap_or_default(),
833 ),
834 groups: DependencyGroups::from_args(
835 DevMode::from_args(dev.into(), no_dev.into(), only_dev),
836 group,
837 if no_group.is_empty() {
838 environment.no_group.clone().unwrap_or_default()
839 } else {
840 no_group
841 },
842 no_default_groups,
843 only_group,
844 all_groups,
845 ),
846 editable: EditableMode::from_args(
847 flag(editable.into(), no_editable.into(), "editable")?,
848 no_editable_package,
849 ),
850 modifications: if flag(exact, inexact, "inexact")?.unwrap_or(false) {
851 Modifications::Exact
852 } else {
853 Modifications::Sufficient
854 },
855 with: with
856 .into_iter()
857 .flat_map(CommaSeparatedRequirements::into_iter)
858 .collect(),
859 with_editable: with_editable
860 .into_iter()
861 .flat_map(CommaSeparatedRequirements::into_iter)
862 .collect(),
863 with_requirements: with_requirements
864 .into_iter()
865 .filter_map(Maybe::into_option)
866 .collect(),
867 isolated,
868 show_resolution,
869 all_packages,
870 package,
871 no_project,
872 no_sync: no_sync.is_enabled(),
873 active: flag(active, no_active, "active")?.into(),
874 python: python.and_then(Maybe::into_option),
875 python_platform,
876 refresh: Refresh::try_from(refresh)?,
877 settings: resolve_resolver_installer_settings(
878 installer,
879 build,
880 filesystem,
881 &environment,
882 )?,
883 env_file: EnvFile::from_args(env_file, no_env_file),
884 install_mirrors: environment
885 .install_mirrors
886 .combine(filesystem_install_mirrors),
887 recursion_depth: environment.run_recursion_depth.unwrap_or_default(),
888 max_recursion_depth: max_recursion_depth.unwrap_or(Self::DEFAULT_MAX_RECURSION_DEPTH),
889 malware_settings,
890 #[cfg(unix)]
891 run_rlimit_nofile: environment.run_rlimit_nofile,
892 })
893 }
894}
895
896#[derive(Debug, Clone)]
898pub struct ToolRunSettings {
899 pub command: Option<Vec<OsString>>,
900 pub from: Option<String>,
901 pub with: Vec<String>,
902 pub with_requirements: Vec<RequirementsInput>,
903 pub with_editable: Vec<String>,
904 pub constraints: Vec<RequirementsInput>,
905 pub overrides: Vec<RequirementsInput>,
906 pub build_constraints: Vec<RequirementsInput>,
907 pub isolated: bool,
908 pub show_resolution: bool,
909 pub lfs: GitLfsSetting,
910 pub python: Option<String>,
911 pub python_platform: Option<TargetTriple>,
912 pub install_mirrors: PythonInstallMirrors,
913 pub refresh: Refresh,
914 pub options: ResolverInstallerOptions,
915 pub settings: ResolverInstallerSettings,
916 pub env_file: Vec<PathBuf>,
917 pub no_env_file: bool,
918}
919
920impl ToolRunSettings {
921 pub fn resolve(
923 args: ToolRunArgs,
924 filesystem: Option<FilesystemOptions>,
925 invocation_source: ToolRunCommand,
926 environment: EnvironmentOptions,
927 ) -> anyhow::Result<Self> {
928 let ToolRunArgs {
929 command,
930 from,
931 with,
932 with_editable,
933 with_requirements,
934 constraints,
935 overrides,
936 build_constraints,
937 isolated,
938 env_file,
939 no_env_file,
940 show_resolution,
941 installer,
942 build,
943 refresh,
944 lfs,
945 python,
946 python_platform,
947 torch_backend,
948 generate_shell_completion: _,
949 } = args;
950
951 if installer.upgrade || !installer.upgrade_package.is_empty() {
953 if with.is_empty() && with_requirements.is_empty() {
954 warn_user_once!(
955 "Tools cannot be upgraded via `{invocation_source}`; use `uv tool upgrade --all` to upgrade all installed tools, or `{invocation_source} package@latest` to run the latest version of a tool."
956 );
957 } else {
958 warn_user_once!(
959 "Tools cannot be upgraded via `{invocation_source}`; use `uv tool upgrade --all` to upgrade all installed tools, `{invocation_source} package@latest` to run the latest version of a tool, or `{invocation_source} --refresh package` to upgrade any `--with` dependencies."
960 );
961 }
962 }
963
964 if installer.reinstall.reinstall || !installer.reinstall.reinstall_package.is_empty() {
966 if with.is_empty() && with_requirements.is_empty() {
967 warn_user_once!(
968 "Tools cannot be reinstalled via `{invocation_source}`; use `uv tool upgrade --all --reinstall` to reinstall all installed tools, `{invocation_source} package@latest` to run the latest version of a tool, or `uv cache prune` to clear any cached tool environments."
969 );
970 } else {
971 warn_user_once!(
972 "Tools cannot be reinstalled via `{invocation_source}`; use `uv tool upgrade --all --reinstall` to reinstall all installed tools, `{invocation_source} package@latest` to run the latest version of a tool, `{invocation_source} --refresh package` to reinstall any `--with` dependencies, or `uv cache prune` to clear any cached tool environments."
973 );
974 }
975 }
976
977 let filesystem_options = filesystem.map(FilesystemOptions::into_options);
978
979 let options = resolver_installer_options_with_environment(
980 resolver_installer_options(
981 installer,
982 build,
983 filesystem_options
984 .as_ref()
985 .and_then(|options| options.top_level.index.as_deref())
986 .unwrap_or_default(),
987 )?,
988 &environment,
989 )
990 .combine(ResolverInstallerOptions::from(
991 filesystem_options
992 .as_ref()
993 .map(|options| options.top_level.clone())
994 .unwrap_or_default(),
995 ));
996
997 let filesystem_install_mirrors = filesystem_options
998 .map(|options| options.install_mirrors.clone())
999 .unwrap_or_default();
1000
1001 let mut settings = ResolverInstallerSettings::from(options.clone());
1002 if torch_backend.is_some() {
1003 settings.resolver.torch_backend = torch_backend;
1004 }
1005 let lfs = GitLfsSetting::new(lfs.then_some(true), environment.lfs);
1006
1007 let isolated = isolated || environment.isolated.value == Some(true);
1009 let show_resolution = show_resolution || environment.show_resolution.value == Some(true);
1010 let no_env_file = no_env_file || environment.no_env_file.value == Some(true);
1011
1012 Ok(Self {
1013 command: command.map(|ExternalCommand::Cmd(command)| command),
1014 from,
1015 with: with
1016 .into_iter()
1017 .flat_map(CommaSeparatedRequirements::into_iter)
1018 .collect(),
1019 with_editable: with_editable
1020 .into_iter()
1021 .flat_map(CommaSeparatedRequirements::into_iter)
1022 .collect(),
1023 with_requirements: with_requirements
1024 .into_iter()
1025 .filter_map(Maybe::into_option)
1026 .collect(),
1027 constraints: requirement_files(constraints, environment.constraints.clone()),
1028 overrides: requirement_files(overrides, environment.overrides.clone()),
1029 build_constraints: requirement_files(
1030 build_constraints,
1031 environment.build_constraints.clone(),
1032 ),
1033 isolated,
1034 show_resolution,
1035 lfs,
1036 python: python.and_then(Maybe::into_option),
1037 python_platform,
1038 refresh: Refresh::try_from(refresh)?,
1039 settings,
1040 options,
1041 install_mirrors: environment
1042 .install_mirrors
1043 .combine(filesystem_install_mirrors),
1044 env_file,
1045 no_env_file,
1046 })
1047 }
1048}
1049
1050#[derive(Debug, Clone)]
1052pub struct ToolInstallSettings {
1053 pub package: String,
1054 pub from: Option<String>,
1055 pub with: Vec<String>,
1056 pub with_requirements: Vec<RequirementsInput>,
1057 pub with_executables_from: Vec<String>,
1058 pub with_editable: Vec<String>,
1059 pub constraints: Vec<RequirementsInput>,
1060 pub overrides: Vec<RequirementsInput>,
1061 pub excludes: Vec<RequirementsInput>,
1062 pub build_constraints: Vec<RequirementsInput>,
1063 pub lfs: GitLfsSetting,
1064 pub python: Option<String>,
1065 pub python_platform: Option<TargetTriple>,
1066 pub refresh: Refresh,
1067 pub options: ResolverInstallerOptions,
1068 pub settings: ResolverInstallerSettings,
1069 pub force: bool,
1070 pub editable: bool,
1071 pub install_mirrors: PythonInstallMirrors,
1072}
1073
1074impl ToolInstallSettings {
1075 pub fn resolve(
1077 args: ToolInstallArgs,
1078 filesystem: Option<FilesystemOptions>,
1079 environment: EnvironmentOptions,
1080 ) -> anyhow::Result<Self> {
1081 let ToolInstallArgs {
1082 package,
1083 editable,
1084 from,
1085 with,
1086 with_editable,
1087 with_requirements,
1088 with_executables_from,
1089 constraints:
1090 DependencyConstraintsArgs {
1091 constraints,
1092 overrides,
1093 excludes,
1094 build_constraints,
1095 },
1096 lfs,
1097 installer,
1098 force,
1099 build,
1100 refresh,
1101 python,
1102 python_platform,
1103 torch_backend,
1104 } = args;
1105
1106 let filesystem_options = filesystem.map(FilesystemOptions::into_options);
1107
1108 let options = resolver_installer_options_with_environment(
1109 resolver_installer_options(
1110 installer,
1111 build,
1112 filesystem_options
1113 .as_ref()
1114 .and_then(|options| options.top_level.index.as_deref())
1115 .unwrap_or_default(),
1116 )?,
1117 &environment,
1118 )
1119 .combine(ResolverInstallerOptions::from(
1120 filesystem_options
1121 .as_ref()
1122 .map(|options| options.top_level.clone())
1123 .unwrap_or_default(),
1124 ));
1125
1126 let filesystem_install_mirrors = filesystem_options
1127 .map(|options| options.install_mirrors.clone())
1128 .unwrap_or_default();
1129
1130 let mut settings = ResolverInstallerSettings::from(options.clone());
1131 if torch_backend.is_some() {
1132 settings.resolver.torch_backend = torch_backend;
1133 }
1134 let lfs = GitLfsSetting::new(lfs.then_some(true), environment.lfs);
1135
1136 Ok(Self {
1137 package,
1138 from,
1139 with: with
1140 .into_iter()
1141 .flat_map(CommaSeparatedRequirements::into_iter)
1142 .collect(),
1143 with_editable: with_editable
1144 .into_iter()
1145 .flat_map(CommaSeparatedRequirements::into_iter)
1146 .collect(),
1147 with_requirements: with_requirements
1148 .into_iter()
1149 .filter_map(Maybe::into_option)
1150 .collect(),
1151 with_executables_from: with_executables_from
1152 .into_iter()
1153 .flat_map(CommaSeparatedRequirements::into_iter)
1154 .collect(),
1155 constraints: requirement_files(constraints, environment.constraints.clone()),
1156 overrides: requirement_files(overrides, environment.overrides.clone()),
1157 excludes: requirement_files(excludes, environment.excludes.clone()),
1158 build_constraints: requirement_files(
1159 build_constraints,
1160 environment.build_constraints.clone(),
1161 ),
1162 lfs,
1163 python: python.and_then(Maybe::into_option),
1164 python_platform,
1165 force,
1166 editable,
1167 refresh: Refresh::try_from(refresh)?,
1168 options,
1169 settings,
1170 install_mirrors: environment
1171 .install_mirrors
1172 .combine(filesystem_install_mirrors),
1173 })
1174 }
1175}
1176
1177#[derive(Debug, Clone)]
1179pub struct ToolUpgradeSettings {
1180 pub names: Vec<String>,
1181 pub python: Option<String>,
1182 pub python_platform: Option<TargetTriple>,
1183 pub install_mirrors: PythonInstallMirrors,
1184 pub args: ResolverInstallerOptions,
1185 pub filesystem: ResolverInstallerOptions,
1186}
1187impl ToolUpgradeSettings {
1188 pub fn resolve(
1190 args: ToolUpgradeArgs,
1191 filesystem: Option<FilesystemOptions>,
1192 environment: &EnvironmentOptions,
1193 ) -> anyhow::Result<Self> {
1194 let ToolUpgradeArgs {
1195 name,
1196 python,
1197 python_platform,
1198 upgrade,
1199 upgrade_package,
1200 upgrade_group,
1201 index_args,
1202 all,
1203 reinstall,
1204 registry_client,
1205 version_selection,
1206 config_setting,
1207 config_setting_package: config_settings_package,
1208 build_isolation,
1209 exclude_newer,
1210 link_mode,
1211 compile_bytecode,
1212 sources,
1213 build,
1214 } = args;
1215
1216 if upgrade {
1217 warn_user_once!("`--upgrade` is enabled by default on `uv tool upgrade`");
1218 }
1219 if !upgrade_package.is_empty() {
1220 warn_user_once!("`--upgrade-package` is enabled by default on `uv tool upgrade`");
1221 }
1222
1223 let installer = ResolverInstallerArgs {
1225 index_args,
1226 upgrade: upgrade_package.is_empty(),
1227 no_upgrade: false,
1228 upgrade_package,
1229 upgrade_group,
1230 reinstall,
1231 registry_client,
1232 version_selection,
1233 config_setting,
1234 config_settings_package,
1235 build_isolation,
1236 exclude_newer,
1237 link_mode,
1238 compile_bytecode,
1239 sources,
1240 };
1241
1242 let args = resolver_installer_options_with_environment(
1243 resolver_installer_options(installer, build, configured_indexes(filesystem.as_ref()))?,
1244 environment,
1245 );
1246 let filesystem = filesystem.map(FilesystemOptions::into_options);
1247 let filesystem_install_mirrors = filesystem
1248 .as_ref()
1249 .map(|options| options.install_mirrors.clone())
1250 .unwrap_or_default();
1251 let top_level = ResolverInstallerOptions::from(
1252 filesystem
1253 .map(|options| options.top_level)
1254 .unwrap_or_default(),
1255 );
1256
1257 Ok(Self {
1258 names: if all { vec![] } else { name },
1259 python: python.and_then(Maybe::into_option),
1260 python_platform,
1261 args,
1262 filesystem: top_level,
1263 install_mirrors: environment
1264 .install_mirrors
1265 .clone()
1266 .combine(filesystem_install_mirrors),
1267 })
1268 }
1269}
1270
1271#[derive(Debug, Clone)]
1273pub struct ToolListSettings {
1274 pub show_paths: bool,
1275 pub show_version_specifiers: bool,
1276 pub show_with: bool,
1277 pub show_extras: bool,
1278 pub show_python: bool,
1279 pub outdated: bool,
1280 pub args: ResolverInstallerOptions,
1281 pub filesystem: ResolverInstallerOptions,
1282}
1283
1284impl ToolListSettings {
1285 pub fn resolve(
1287 args: ToolListArgs,
1288 filesystem: Option<FilesystemOptions>,
1289 ) -> anyhow::Result<Self> {
1290 let ToolListArgs {
1291 show_paths,
1292 show_version_specifiers,
1293 show_with,
1294 show_extras,
1295 show_python,
1296 outdated,
1297 no_outdated,
1298 exclude_newer:
1299 PackageExcludeNewerArgs {
1300 exclude_newer: ExcludeNewerArgs { exclude_newer },
1301 exclude_newer_package,
1302 },
1303 python_preference: _,
1304 no_python_downloads: _,
1305 } = args;
1306
1307 let top_level = filesystem
1308 .map(FilesystemOptions::into_options)
1309 .map(|options| options.top_level)
1310 .unwrap_or_default();
1311 let filesystem = ResolverInstallerOptions {
1312 indexes: IndexOptions {
1313 index: top_level.index,
1314 ..IndexOptions::default()
1315 },
1316 exclude_newer: top_level.exclude_newer,
1317 exclude_newer_package: top_level.exclude_newer_package,
1318 ..ResolverInstallerOptions::default()
1319 };
1320
1321 Ok(Self {
1322 show_paths,
1323 show_version_specifiers,
1324 show_with,
1325 show_extras,
1326 show_python,
1327 outdated: flag(outdated, no_outdated, "outdated")?.unwrap_or(false),
1328 args: ResolverInstallerOptions {
1329 exclude_newer,
1330 exclude_newer_package: exclude_newer_package.map(ExcludeNewerPackage::from_iter),
1331 ..ResolverInstallerOptions::default()
1332 },
1333 filesystem,
1334 })
1335 }
1336}
1337
1338#[derive(Debug, Clone)]
1340pub struct ToolAuditSettings {
1341 pub names: Vec<PackageName>,
1342 pub output_format: AuditOutputFormat,
1343 pub service_format: VulnerabilityServiceFormat,
1344 pub service_url: Option<SafeUrl>,
1345 pub ignore: Vec<VulnerabilityID>,
1346 pub ignore_until_fixed: Vec<VulnerabilityID>,
1347 pub filesystem: ResolverInstallerOptions,
1348}
1349
1350impl ToolAuditSettings {
1351 pub fn resolve(args: ToolAuditArgs, filesystem: Option<FilesystemOptions>) -> Self {
1353 let ToolAuditArgs {
1354 name,
1355 all,
1356 audit:
1357 AuditCommonArgs {
1358 offline: _,
1359 output_format,
1360 ignore,
1361 ignore_until_fixed,
1362 service_format,
1363 service_url,
1364 },
1365 } = args;
1366
1367 let audit = filesystem
1368 .as_ref()
1369 .and_then(|options| options.audit.clone())
1370 .unwrap_or_default();
1371 let filesystem = filesystem
1372 .map(FilesystemOptions::into_options)
1373 .map(|options| ResolverInstallerOptions::from(options.top_level))
1374 .unwrap_or_default();
1375
1376 let ignore = ignore
1377 .into_iter()
1378 .chain(audit.ignore.unwrap_or_default())
1379 .map(VulnerabilityID::new)
1380 .collect();
1381 let ignore_until_fixed = ignore_until_fixed
1382 .into_iter()
1383 .chain(audit.ignore_until_fixed.unwrap_or_default())
1384 .map(VulnerabilityID::new)
1385 .collect();
1386
1387 Self {
1388 names: if all { vec![] } else { name },
1389 output_format,
1390 service_format,
1391 service_url,
1392 ignore,
1393 ignore_until_fixed,
1394 filesystem,
1395 }
1396 }
1397}
1398
1399#[derive(Debug, Clone)]
1401pub struct ToolUninstallSettings {
1402 pub name: Vec<PackageName>,
1403}
1404
1405impl ToolUninstallSettings {
1406 pub fn resolve(args: ToolUninstallArgs, _filesystem: Option<FilesystemOptions>) -> Self {
1408 let ToolUninstallArgs { name, all } = args;
1409
1410 Self {
1411 name: if all { vec![] } else { name },
1412 }
1413 }
1414}
1415
1416#[derive(Debug, Clone)]
1418pub struct ToolDirSettings {
1419 pub bin: bool,
1420}
1421
1422impl ToolDirSettings {
1423 #[expect(clippy::needless_pass_by_value)]
1425 pub fn resolve(args: ToolDirArgs, _filesystem: Option<FilesystemOptions>) -> Self {
1426 let ToolDirArgs { bin } = args;
1427
1428 Self { bin }
1429 }
1430}
1431
1432#[derive(Debug, Clone)]
1434pub struct PythonListSettings {
1435 pub request: Option<String>,
1436 pub kinds: PythonListKinds,
1437 pub all_platforms: bool,
1438 pub all_arches: bool,
1439 pub all_versions: bool,
1440 pub show_urls: bool,
1441 pub output_format: PythonListFormat,
1442 pub install_mirrors: PythonInstallMirrors,
1443}
1444
1445impl PythonListSettings {
1446 pub fn resolve(
1448 args: PythonListArgs,
1449 filesystem: Option<FilesystemOptions>,
1450 environment: EnvironmentOptions,
1451 ) -> Self {
1452 let PythonListArgs {
1453 request,
1454 all_versions,
1455 all_platforms,
1456 all_arches,
1457 only_installed,
1458 only_downloads,
1459 show_urls,
1460 output_format,
1461 python_downloads_json_url: python_downloads_json_url_arg,
1462 } = args;
1463
1464 let filesystem_install_mirrors = filesystem
1465 .map(|fs| fs.install_mirrors.clone())
1466 .unwrap_or_default();
1467
1468 let install_mirrors = PythonInstallMirrors {
1469 python_downloads_json_url: python_downloads_json_url_arg,
1470 ..Default::default()
1471 }
1472 .combine(environment.install_mirrors)
1473 .combine(filesystem_install_mirrors);
1474
1475 let kinds = if only_installed {
1476 PythonListKinds::Installed
1477 } else if only_downloads {
1478 PythonListKinds::Downloads
1479 } else {
1480 PythonListKinds::default()
1481 };
1482
1483 Self {
1484 request,
1485 kinds,
1486 all_platforms,
1487 all_arches,
1488 all_versions,
1489 show_urls,
1490 output_format,
1491 install_mirrors,
1492 }
1493 }
1494}
1495
1496#[derive(Debug, Clone)]
1498pub struct PythonDirSettings {
1499 pub bin: bool,
1500}
1501
1502impl PythonDirSettings {
1503 #[expect(clippy::needless_pass_by_value)]
1505 pub fn resolve(args: PythonDirArgs, _filesystem: Option<FilesystemOptions>) -> Self {
1506 let PythonDirArgs { bin } = args;
1507
1508 Self { bin }
1509 }
1510}
1511
1512#[derive(Debug, Clone)]
1514pub struct PythonInstallSettings {
1515 pub install_dir: Option<PathBuf>,
1516 pub targets: Vec<String>,
1517 pub reinstall: bool,
1518 pub force: bool,
1519 pub upgrade: PythonUpgrade,
1520 pub bin: Option<bool>,
1521 pub registry: Option<bool>,
1522 pub install_mirrors: PythonInstallMirrors,
1523 pub default: bool,
1524 pub compile_bytecode: bool,
1525}
1526
1527impl PythonInstallSettings {
1528 pub fn resolve(
1530 args: PythonInstallArgs,
1531 filesystem: Option<FilesystemOptions>,
1532 environment: EnvironmentOptions,
1533 ) -> anyhow::Result<Self> {
1534 let filesystem_install_mirrors = filesystem
1535 .map(|fs| fs.install_mirrors.clone())
1536 .unwrap_or_default();
1537
1538 let install_mirrors = args
1539 .install_mirrors()
1540 .combine(environment.install_mirrors)
1541 .combine(filesystem_install_mirrors);
1542
1543 let PythonInstallArgs {
1544 install_dir,
1545 targets,
1546 reinstall,
1547 bin,
1548 no_bin,
1549 registry,
1550 no_registry,
1551 force,
1552 upgrade,
1553 mirror: _,
1554 pypy_mirror: _,
1555 graalpy_mirror: _,
1556 pyodide_mirror: _,
1557 python_downloads_json_url: _,
1558 default,
1559 compile_bytecode,
1560 } = args;
1561
1562 Ok(Self {
1563 install_dir,
1564 targets,
1565 reinstall,
1566 force,
1567 upgrade: if upgrade {
1568 PythonUpgrade::Enabled(PythonUpgradeSource::Install)
1569 } else {
1570 PythonUpgrade::Disabled
1571 },
1572 bin: flag(bin, no_bin, "bin")?.or(environment.python_install_bin),
1573 registry: match flag(registry, no_registry, "registry")? {
1574 Some(registry) => Some(registry),
1575 None => environment.python_install_registry.or(
1576 if environment.python_no_registry.value == Some(true) {
1577 Some(false)
1578 } else {
1579 None
1580 },
1581 ),
1582 },
1583 install_mirrors,
1584 default,
1585 compile_bytecode: flag(
1586 compile_bytecode.compile_bytecode,
1587 compile_bytecode.no_compile_bytecode,
1588 "compile-bytecode",
1589 )?
1590 .unwrap_or_default(),
1591 })
1592 }
1593}
1594
1595#[expect(clippy::struct_excessive_bools)]
1597#[derive(Debug, Clone)]
1598pub struct PythonUpgradeSettings {
1599 pub install_dir: Option<PathBuf>,
1600 pub targets: Vec<String>,
1601 pub force: bool,
1602 pub registry: Option<bool>,
1603 pub install_mirrors: PythonInstallMirrors,
1604 pub reinstall: bool,
1605 pub default: bool,
1606 pub bin: Option<bool>,
1607 pub compile_bytecode: bool,
1608}
1609
1610impl PythonUpgradeSettings {
1611 pub fn resolve(
1613 args: PythonUpgradeArgs,
1614 filesystem: Option<FilesystemOptions>,
1615 environment: EnvironmentOptions,
1616 ) -> anyhow::Result<Self> {
1617 let filesystem_install_mirrors = filesystem
1618 .map(|fs| fs.install_mirrors.clone())
1619 .unwrap_or_default();
1620
1621 let install_mirrors = args
1622 .install_mirrors()
1623 .combine(environment.install_mirrors)
1624 .combine(filesystem_install_mirrors);
1625
1626 let force = false;
1627 let default = false;
1628 let bin = None;
1629 let registry = environment.python_install_registry.or(
1630 if environment.python_no_registry.value == Some(true) {
1631 Some(false)
1632 } else {
1633 None
1634 },
1635 );
1636
1637 let PythonUpgradeArgs {
1638 install_dir,
1639 targets,
1640 mirror: _,
1641 pypy_mirror: _,
1642 graalpy_mirror: _,
1643 pyodide_mirror: _,
1644 reinstall,
1645 python_downloads_json_url: _,
1646 compile_bytecode,
1647 } = args;
1648
1649 Ok(Self {
1650 install_dir,
1651 targets,
1652 force,
1653 registry,
1654 install_mirrors,
1655 reinstall,
1656 default,
1657 bin,
1658 compile_bytecode: flag(
1659 compile_bytecode.compile_bytecode,
1660 compile_bytecode.no_compile_bytecode,
1661 "compile-bytecode",
1662 )?
1663 .unwrap_or_default(),
1664 })
1665 }
1666}
1667
1668#[derive(Debug, Clone)]
1670pub struct PythonUninstallSettings {
1671 pub install_dir: Option<PathBuf>,
1672 pub targets: Vec<String>,
1673 pub all: bool,
1674}
1675
1676impl PythonUninstallSettings {
1677 pub fn resolve(args: PythonUninstallArgs, _filesystem: Option<FilesystemOptions>) -> Self {
1679 let PythonUninstallArgs {
1680 install_dir,
1681 targets,
1682 all,
1683 } = args;
1684
1685 Self {
1686 install_dir,
1687 targets,
1688 all,
1689 }
1690 }
1691}
1692
1693#[derive(Debug, Clone)]
1695pub struct PythonFindSettings {
1696 pub request: Option<String>,
1697 pub show_version: bool,
1698 pub resolve_links: bool,
1699 pub no_project: bool,
1700 pub system: bool,
1701 pub python_downloads_json_url: Option<String>,
1702}
1703
1704impl PythonFindSettings {
1705 pub fn resolve(
1707 args: PythonFindArgs,
1708 filesystem: Option<FilesystemOptions>,
1709 environment: EnvironmentOptions,
1710 ) -> anyhow::Result<Self> {
1711 let PythonFindArgs {
1712 request,
1713 show_version,
1714 resolve_links,
1715 no_project,
1716 system,
1717 no_system,
1718 script: _,
1719 python_downloads_json_url,
1720 } = args;
1721
1722 let filesystem_install_mirrors = filesystem
1723 .map(|fs| fs.install_mirrors.clone())
1724 .unwrap_or_default();
1725
1726 let install_mirrors = PythonInstallMirrors {
1727 python_downloads_json_url,
1728 ..Default::default()
1729 }
1730 .combine(environment.install_mirrors)
1731 .combine(filesystem_install_mirrors);
1732
1733 let PythonInstallMirrors {
1734 python_install_mirror: _,
1735 pypy_install_mirror: _,
1736 graalpy_install_mirror: _,
1737 pyodide_install_mirror: _,
1738 python_downloads_json_url,
1739 } = install_mirrors;
1740
1741 Ok(Self {
1742 request,
1743 show_version,
1744 resolve_links,
1745 no_project,
1746 system: flag(system, no_system, "system")?.unwrap_or_default(),
1747 python_downloads_json_url,
1748 })
1749 }
1750}
1751
1752#[derive(Debug, Clone)]
1754pub struct PythonPinSettings {
1755 pub request: Option<String>,
1756 pub resolved: bool,
1757 pub no_project: bool,
1758 pub global: bool,
1759 pub rm: bool,
1760 pub install_mirrors: PythonInstallMirrors,
1761}
1762
1763impl PythonPinSettings {
1764 pub fn resolve(
1766 args: PythonPinArgs,
1767 filesystem: Option<FilesystemOptions>,
1768 environment: EnvironmentOptions,
1769 ) -> anyhow::Result<Self> {
1770 let PythonPinArgs {
1771 request,
1772 no_resolved,
1773 resolved,
1774 no_project,
1775 global,
1776 rm,
1777 python_downloads_json_url,
1778 } = args;
1779
1780 let filesystem_install_mirrors = filesystem
1781 .map(|fs| fs.install_mirrors.clone())
1782 .unwrap_or_default();
1783
1784 let install_mirrors = PythonInstallMirrors {
1785 python_downloads_json_url,
1786 ..Default::default()
1787 }
1788 .combine(environment.install_mirrors)
1789 .combine(filesystem_install_mirrors);
1790
1791 Ok(Self {
1792 request,
1793 resolved: flag(resolved, no_resolved, "resolved")?.unwrap_or(false),
1794 no_project,
1795 global,
1796 rm,
1797 install_mirrors,
1798 })
1799 }
1800}
1801
1802#[derive(Debug, Clone)]
1804pub struct SyncSettings {
1805 pub lock_check: LockCheck,
1806 pub frozen: Option<FrozenSource>,
1807 pub dry_run: DryRun,
1808 pub script: Option<PathBuf>,
1809 pub active: ActiveEnvironment,
1810 pub extras: ExtrasSpecification,
1811 pub groups: DependencyGroups,
1812 pub editable: Option<EditableMode>,
1813 pub install_options: InstallOptions,
1814 pub modifications: Modifications,
1815 pub all_packages: bool,
1816 pub package: Vec<PackageName>,
1817 pub python: Option<String>,
1818 pub python_platform: Option<TargetTriple>,
1819 pub install_mirrors: PythonInstallMirrors,
1820 pub refresh: Refresh,
1821 pub settings: ResolverInstallerSettings,
1822 pub output_format: SyncFormat,
1823 pub malware_settings: MalwareCheckSettings,
1824}
1825
1826impl SyncSettings {
1827 pub fn resolve(
1829 args: SyncArgs,
1830 filesystem: Option<FilesystemOptions>,
1831 environment: EnvironmentOptions,
1832 ) -> anyhow::Result<Self> {
1833 let SyncArgs {
1834 extra,
1835 all_extras,
1836 no_extra,
1837 no_all_extras,
1838 dependency_groups:
1839 ProjectDependencyGroupsArgs {
1840 dev,
1841 no_dev,
1842 only_dev,
1843 group,
1844 no_group,
1845 no_default_groups,
1846 only_group,
1847 all_groups,
1848 },
1849 editable,
1850 no_editable,
1851 no_editable_package,
1852 inexact,
1853 exact,
1854 no_install_project,
1855 only_install_project,
1856 no_install_workspace,
1857 only_install_workspace,
1858 no_install_local,
1859 only_install_local,
1860 no_install_package,
1861 only_install_package,
1862 locked,
1863 no_locked,
1864 frozen,
1865 no_frozen,
1866 active,
1867 no_active,
1868 dry_run,
1869 installer,
1870 build,
1871 refresh,
1872 all_packages,
1873 package,
1874 script,
1875 python,
1876 python_platform,
1877 check,
1878 no_check,
1879 output_format,
1880 } = args;
1881 let filesystem_install_mirrors = filesystem
1882 .as_ref()
1883 .map(|fs| fs.install_mirrors.clone())
1884 .unwrap_or_default();
1885
1886 let malware_settings = MalwareCheckSettings::resolve(filesystem.as_ref(), &environment);
1887 let settings =
1888 resolve_resolver_installer_settings(installer, build, filesystem, &environment)?;
1889
1890 let check = flag(check, no_check, "check")?.unwrap_or_default();
1891 let dry_run = if check {
1892 DryRun::Check
1893 } else {
1894 DryRun::from_args(dry_run)
1895 };
1896
1897 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
1899 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
1900
1901 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
1902
1903 let (dev, no_dev) = resolve_flag_pair(
1904 dev,
1905 no_dev,
1906 "dev",
1907 "no-dev",
1908 Some(environment.dev),
1909 Some(environment.no_dev),
1910 );
1911 let (editable, no_editable) = resolve_flag_pair(
1912 editable,
1913 no_editable,
1914 "editable",
1915 "no-editable",
1916 None,
1917 Some(environment.no_editable),
1918 );
1919
1920 let (no_install_project, only_install_project) = resolve_flag_pair(
1921 no_install_project,
1922 only_install_project,
1923 "no-install-project",
1924 "only-install-project",
1925 Some(environment.no_install_project),
1926 Some(environment.only_install_project),
1927 );
1928 let (no_install_workspace, only_install_workspace) = resolve_flag_pair(
1929 no_install_workspace,
1930 only_install_workspace,
1931 "no-install-workspace",
1932 "only-install-workspace",
1933 Some(environment.no_install_workspace),
1934 Some(environment.only_install_workspace),
1935 );
1936 let (no_install_local, only_install_local) = resolve_flag_pair(
1937 no_install_local,
1938 only_install_local,
1939 "no-install-local",
1940 "only-install-local",
1941 Some(environment.no_install_local),
1942 Some(environment.only_install_local),
1943 );
1944 check_conflicts(no_install_project, only_install_project)?;
1945 check_conflicts(no_install_workspace, only_install_workspace)?;
1946 check_conflicts(no_install_local, only_install_local)?;
1947 if script.is_some() {
1948 let script = Flag::from_cli("script");
1949 check_conflicts(no_install_project, script)?;
1950 check_conflicts(no_install_workspace, script)?;
1951 check_conflicts(no_install_local, script)?;
1952 }
1953 let no_install_project = no_install_project.is_enabled();
1954 let only_install_project = only_install_project.is_enabled();
1955 let no_install_workspace = no_install_workspace.is_enabled();
1956 let only_install_workspace = only_install_workspace.is_enabled();
1957 let no_install_local = no_install_local.is_enabled();
1958 let only_install_local = only_install_local.is_enabled();
1959
1960 Ok(Self {
1961 output_format,
1962 lock_check: locked,
1963 frozen,
1964 dry_run,
1965 script,
1966 active: flag(active, no_active, "active")?.into(),
1967 extras: ExtrasSpecification::from_args(
1968 extra.unwrap_or_default(),
1969 no_extra,
1970 false,
1972 vec![],
1974 flag(all_extras, no_all_extras, "all-extras")?.unwrap_or_default(),
1975 ),
1976 groups: DependencyGroups::from_args(
1977 DevMode::from_args(dev.into(), no_dev.into(), only_dev),
1978 group,
1979 if no_group.is_empty() {
1980 environment.no_group.clone().unwrap_or_default()
1981 } else {
1982 no_group
1983 },
1984 no_default_groups,
1985 only_group,
1986 all_groups,
1987 ),
1988 editable: EditableMode::from_args(
1989 flag(editable.into(), no_editable.into(), "editable")?,
1990 no_editable_package,
1991 ),
1992 install_options: InstallOptions::new(
1993 no_install_project,
1994 only_install_project,
1995 no_install_workspace,
1996 only_install_workspace,
1997 no_install_local,
1998 only_install_local,
1999 no_install_package,
2000 only_install_package,
2001 ),
2002 modifications: if flag(exact, inexact, "inexact")?.unwrap_or(true) {
2003 Modifications::Exact
2004 } else {
2005 Modifications::Sufficient
2006 },
2007 all_packages,
2008 package,
2009 python: python.and_then(Maybe::into_option),
2010 python_platform,
2011 refresh: Refresh::try_from(refresh)?,
2012 settings,
2013 install_mirrors: environment
2014 .install_mirrors
2015 .combine(filesystem_install_mirrors),
2016 malware_settings,
2017 })
2018 }
2019}
2020
2021#[derive(Debug, Clone)]
2023pub struct LockSettings {
2024 pub lock_check: LockCheck,
2025 pub frozen: Option<FrozenSource>,
2026 pub dry_run: DryRun,
2027 pub script: Option<PathBuf>,
2028 pub python: Option<String>,
2029 pub install_mirrors: PythonInstallMirrors,
2030 pub refresh: Refresh,
2031 pub settings: ResolverSettings,
2032}
2033
2034impl LockSettings {
2035 pub fn resolve(
2037 args: LockArgs,
2038 filesystem: Option<FilesystemOptions>,
2039 environment: EnvironmentOptions,
2040 ) -> anyhow::Result<Self> {
2041 let LockArgs {
2042 check,
2043 locked,
2044 no_locked,
2045 check_exists,
2046 frozen,
2047 no_frozen,
2048 dry_run,
2049 script,
2050 resolver,
2051 build,
2052 refresh,
2053 python,
2054 } = args;
2055
2056 let filesystem_install_mirrors = filesystem
2057 .as_ref()
2058 .map(|fs| fs.install_mirrors.clone())
2059 .unwrap_or_default();
2060
2061 let locked = resolve_lock_check(
2063 locked || check,
2064 no_locked,
2065 if check {
2066 LockedFlag::Check
2067 } else {
2068 LockedFlag::Locked
2069 },
2070 environment.locked,
2071 );
2072 let frozen = resolve_frozen(
2073 frozen || check_exists,
2074 no_frozen,
2075 if check_exists {
2076 FrozenFlag::CheckExists
2077 } else {
2078 FrozenFlag::Frozen
2079 },
2080 environment.frozen,
2081 );
2082
2083 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
2084
2085 Ok(Self {
2086 lock_check: locked,
2087 frozen,
2088 dry_run: DryRun::from_args(dry_run),
2089 script,
2090 python: python.and_then(Maybe::into_option),
2091 refresh: Refresh::try_from(refresh)?,
2092 settings: resolve_resolver_settings(resolver, build, filesystem, &environment)?,
2093 install_mirrors: environment
2094 .install_mirrors
2095 .combine(filesystem_install_mirrors),
2096 })
2097 }
2098}
2099
2100#[derive(Debug, Clone)]
2102pub struct UpgradeSettings {
2103 pub packages: Vec<PackageName>,
2104 pub exclude: Vec<PackageName>,
2105 pub install_mirrors: PythonInstallMirrors,
2106 pub settings: ResolverSettings,
2107}
2108
2109impl UpgradeSettings {
2110 pub fn resolve(
2112 args: UpgradeArgs,
2113 filesystem: Option<FilesystemOptions>,
2114 environment: EnvironmentOptions,
2115 ) -> anyhow::Result<Self> {
2116 let filesystem_install_mirrors = filesystem
2117 .as_ref()
2118 .map(|fs| fs.install_mirrors.clone())
2119 .unwrap_or_default();
2120 let (packages, exclude, options) =
2121 upgrade_options(args, configured_indexes(filesystem.as_ref()))?;
2122 let mut settings = combine_resolver_settings(options, filesystem, &environment);
2123 settings.upgrade = if packages.is_empty() {
2124 Upgrade::default()
2125 } else {
2126 Upgrade::from_packages(packages.clone())
2127 };
2128
2129 Ok(Self {
2130 packages,
2131 exclude,
2132 install_mirrors: environment
2133 .install_mirrors
2134 .combine(filesystem_install_mirrors),
2135 settings,
2136 })
2137 }
2138}
2139
2140#[derive(Debug, Clone)]
2142pub struct MetadataSettings {
2143 #[expect(dead_code)]
2144 script: Option<PathBuf>,
2145 pub lock_check: LockCheck,
2146 pub frozen: Option<FrozenSource>,
2147 pub sync: Option<Modifications>,
2148 pub active: ActiveEnvironment,
2149 pub python: Option<String>,
2150 pub install_mirrors: PythonInstallMirrors,
2151 pub refresh: Refresh,
2152 pub settings: ResolverSettings,
2153 pub malware_settings: MalwareCheckSettings,
2154}
2155
2156impl MetadataSettings {
2157 pub fn resolve(
2159 args: Box<MetadataArgs>,
2160 filesystem: Option<FilesystemOptions>,
2161 environment: EnvironmentOptions,
2162 ) -> anyhow::Result<Self> {
2163 let MetadataArgs {
2164 script,
2165 locked,
2166 no_locked,
2167 frozen,
2168 no_frozen,
2169 resolver,
2170 build,
2171 refresh,
2172 sync,
2173 exact,
2174 active,
2175 python,
2176 } = *args;
2177
2178 let filesystem_install_mirrors = filesystem
2179 .as_ref()
2180 .map(|fs| fs.install_mirrors.clone())
2181 .unwrap_or_default();
2182
2183 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
2185 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
2186
2187 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
2188
2189 let malware_settings = MalwareCheckSettings::resolve(filesystem.as_ref(), &environment);
2190
2191 Ok(Self {
2192 script,
2193 lock_check: locked,
2194 frozen,
2195 sync: sync.then_some(if exact {
2196 Modifications::Exact
2197 } else {
2198 Modifications::Sufficient
2199 }),
2200 active: Some(active).into(),
2201 python: python.and_then(Maybe::into_option),
2202 refresh: Refresh::try_from(refresh)?,
2203 settings: resolve_resolver_settings(resolver, build, filesystem, &environment)?,
2204 install_mirrors: environment
2205 .install_mirrors
2206 .combine(filesystem_install_mirrors),
2207 malware_settings,
2208 })
2209 }
2210}
2211
2212#[expect(clippy::struct_excessive_bools)]
2214#[derive(Debug, Clone)]
2215pub struct AddSettings {
2216 pub lock_check: LockCheck,
2217 pub frozen: Option<FrozenSource>,
2218 pub active: ActiveEnvironment,
2219 pub no_sync: bool,
2220 pub packages: Vec<String>,
2221 pub requirements: Vec<RequirementsInput>,
2222 pub constraints: Vec<RequirementsInput>,
2223 pub marker: Option<MarkerTree>,
2224 pub dependency_type: DependencyType,
2225 pub editable: Option<EditableMode>,
2226 pub extras: Vec<ExtraName>,
2227 pub raw: bool,
2228 pub bounds: Option<AddBoundsKind>,
2229 pub rev: Option<String>,
2230 pub tag: Option<String>,
2231 pub branch: Option<String>,
2232 pub lfs: GitLfsSetting,
2233 pub package: Option<PackageName>,
2234 pub script: Option<PathBuf>,
2235 pub python: Option<String>,
2236 pub workspace: Option<bool>,
2237 pub no_install_project: bool,
2238 pub only_install_project: bool,
2239 pub no_install_workspace: bool,
2240 pub only_install_workspace: bool,
2241 pub no_install_local: bool,
2242 pub only_install_local: bool,
2243 pub no_install_package: Vec<PackageName>,
2244 pub only_install_package: Vec<PackageName>,
2245 pub install_mirrors: PythonInstallMirrors,
2246 pub refresh: Refresh,
2247 pub indexes: Vec<Index>,
2248 pub settings: ResolverInstallerSettings,
2249 pub malware_settings: MalwareCheckSettings,
2250}
2251
2252impl AddSettings {
2253 pub fn resolve(
2255 args: AddArgs,
2256 filesystem: Option<FilesystemOptions>,
2257 environment: EnvironmentOptions,
2258 ) -> anyhow::Result<Self> {
2259 let AddArgs {
2260 packages,
2261 requirements,
2262 constraints,
2263 marker,
2264 dev,
2265 optional,
2266 group,
2267 editable,
2268 no_editable,
2269 no_editable_package,
2270 extra,
2271 raw,
2272 bounds,
2273 rev,
2274 tag,
2275 branch,
2276 lfs,
2277 no_sync,
2278 locked,
2279 no_locked,
2280 frozen,
2281 no_frozen,
2282 active,
2283 no_active,
2284 installer,
2285 build,
2286 refresh,
2287 package,
2288 script,
2289 python,
2290 workspace,
2291 no_workspace,
2292 no_install_project,
2293 only_install_project,
2294 no_install_workspace,
2295 only_install_workspace,
2296 no_install_local,
2297 only_install_local,
2298 no_install_package,
2299 only_install_package,
2300 } = args;
2301
2302 let dev = dev || environment.dev.value == Some(true);
2304 let (editable, no_editable) = resolve_flag_pair(
2305 editable,
2306 no_editable,
2307 "editable",
2308 "no-editable",
2309 None,
2310 Some(environment.no_editable),
2311 );
2312
2313 let (no_install_project, only_install_project) = resolve_flag_pair(
2314 no_install_project,
2315 only_install_project,
2316 "no-install-project",
2317 "only-install-project",
2318 Some(environment.no_install_project),
2319 Some(environment.only_install_project),
2320 );
2321 let (no_install_workspace, only_install_workspace) = resolve_flag_pair(
2322 no_install_workspace,
2323 only_install_workspace,
2324 "no-install-workspace",
2325 "only-install-workspace",
2326 Some(environment.no_install_workspace),
2327 Some(environment.only_install_workspace),
2328 );
2329 let (no_install_local, only_install_local) = resolve_flag_pair(
2330 no_install_local,
2331 only_install_local,
2332 "no-install-local",
2333 "only-install-local",
2334 Some(environment.no_install_local),
2335 Some(environment.only_install_local),
2336 );
2337 check_conflicts(no_install_project, only_install_project)?;
2338 check_conflicts(no_install_workspace, only_install_workspace)?;
2339 check_conflicts(no_install_local, only_install_local)?;
2340
2341 let dependency_type = if let Some(extra) = optional {
2342 DependencyType::Optional(extra)
2343 } else if let Some(group) = group {
2344 DependencyType::Group(group)
2345 } else if dev {
2346 DependencyType::Dev
2347 } else {
2348 DependencyType::Production
2349 };
2350
2351 if installer
2353 .index_args
2354 .index_url
2355 .as_ref()
2356 .is_some_and(Maybe::is_some)
2357 {
2358 if script.is_some() {
2359 warn_user_once!(
2360 "Indexes specified via `--index-url` will not be persisted to the script; use `--default-index` instead."
2361 );
2362 } else {
2363 warn_user_once!(
2364 "Indexes specified via `--index-url` will not be persisted to the `pyproject.toml` file; use `--default-index` instead."
2365 );
2366 }
2367 }
2368
2369 if installer
2370 .index_args
2371 .extra_index_url
2372 .as_ref()
2373 .is_some_and(|extra_index_url| extra_index_url.iter().any(Maybe::is_some))
2374 {
2375 if script.is_some() {
2376 warn_user_once!(
2377 "Indexes specified via `--extra-index-url` will not be persisted to the script; use `--index` instead."
2378 );
2379 } else {
2380 warn_user_once!(
2381 "Indexes specified via `--extra-index-url` will not be persisted to the `pyproject.toml` file; use `--index` instead."
2382 );
2383 }
2384 }
2385
2386 let filesystem_install_mirrors = filesystem
2387 .as_ref()
2388 .map(|fs| fs.install_mirrors.clone())
2389 .unwrap_or_default();
2390
2391 let bounds = bounds.or(filesystem.as_ref().and_then(|fs| fs.add.add_bounds));
2392 let lfs = GitLfsSetting::new(lfs.then_some(true), environment.lfs);
2393
2394 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
2396 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
2397 let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);
2398
2399 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
2400
2401 check_conflicts(no_sync, frozen.map_or(Flag::Disabled, Flag::from))?;
2403
2404 let no_install_package_flag = if no_install_package.is_empty() {
2405 Flag::disabled()
2406 } else {
2407 Flag::from_cli("no-install-package")
2408 };
2409 let only_install_package_flag = if only_install_package.is_empty() {
2410 Flag::disabled()
2411 } else {
2412 Flag::from_cli("only-install-package")
2413 };
2414
2415 for install_flag in [
2416 no_install_project,
2417 no_install_workspace,
2418 no_install_local,
2419 only_install_project,
2420 only_install_workspace,
2421 only_install_local,
2422 no_install_package_flag,
2423 only_install_package_flag,
2424 ] {
2425 check_conflicts(install_flag, frozen.map_or(Flag::Disabled, Flag::from))?;
2426 check_conflicts(install_flag, no_sync)?;
2427 }
2428
2429 let no_install_project = no_install_project.is_enabled();
2430 let only_install_project = only_install_project.is_enabled();
2431 let no_install_workspace = no_install_workspace.is_enabled();
2432 let only_install_workspace = only_install_workspace.is_enabled();
2433 let no_install_local = no_install_local.is_enabled();
2434 let only_install_local = only_install_local.is_enabled();
2435
2436 let malware_settings = MalwareCheckSettings::resolve(filesystem.as_ref(), &environment);
2437 let active = flag(active, no_active, "active")?.into();
2438 let workspace = flag(workspace, no_workspace, "workspace")?;
2439 let editable = EditableMode::from_args(
2440 flag(editable.into(), no_editable.into(), "editable")?,
2441 no_editable_package,
2442 );
2443 let refresh = Refresh::try_from(refresh)?;
2444 let options =
2445 resolver_installer_options(installer, build, configured_indexes(filesystem.as_ref()))?;
2446 let indexes = options.indexes.index.clone().unwrap_or_default();
2447
2448 Ok(Self {
2449 lock_check: locked,
2450 frozen,
2451 active,
2452 no_sync: no_sync.is_enabled(),
2453 packages,
2454 requirements,
2455 constraints: requirement_files(constraints, environment.constraints.clone()),
2456 marker,
2457 dependency_type,
2458 raw,
2459 bounds,
2460 rev,
2461 tag,
2462 branch,
2463 lfs,
2464 package,
2465 script,
2466 python: python.and_then(Maybe::into_option),
2467 workspace,
2468 no_install_project,
2469 only_install_project,
2470 no_install_workspace,
2471 only_install_workspace,
2472 no_install_local,
2473 only_install_local,
2474 no_install_package,
2475 only_install_package,
2476 editable,
2477 extras: extra.unwrap_or_default(),
2478 refresh,
2479 indexes,
2480 settings: combine_resolver_installer_settings(options, filesystem, &environment),
2481 install_mirrors: environment
2482 .install_mirrors
2483 .combine(filesystem_install_mirrors),
2484 malware_settings,
2485 })
2486 }
2487}
2488
2489#[derive(Debug, Clone)]
2491pub struct RemoveSettings {
2492 pub lock_check: LockCheck,
2493 pub frozen: Option<FrozenSource>,
2494 pub active: ActiveEnvironment,
2495 pub no_sync: bool,
2496 pub packages: Vec<PackageName>,
2497 pub dependency_type: DependencyType,
2498 pub package: Option<PackageName>,
2499 pub script: Option<PathBuf>,
2500 pub python: Option<String>,
2501 pub install_mirrors: PythonInstallMirrors,
2502 pub refresh: Refresh,
2503 pub settings: ResolverInstallerSettings,
2504 pub malware_settings: MalwareCheckSettings,
2505}
2506
2507impl RemoveSettings {
2508 pub fn resolve(
2510 args: RemoveArgs,
2511 filesystem: Option<FilesystemOptions>,
2512 environment: EnvironmentOptions,
2513 ) -> anyhow::Result<Self> {
2514 let RemoveArgs {
2515 dev,
2516 optional,
2517 packages,
2518 group,
2519 no_sync,
2520 locked,
2521 no_locked,
2522 frozen,
2523 no_frozen,
2524 active,
2525 no_active,
2526 installer,
2527 build,
2528 refresh,
2529 package,
2530 script,
2531 python,
2532 } = args;
2533
2534 let dev = dev || environment.dev.value == Some(true);
2536
2537 let dependency_type = if let Some(extra) = optional {
2538 DependencyType::Optional(extra)
2539 } else if let Some(group) = group {
2540 DependencyType::Group(group)
2541 } else if dev {
2542 DependencyType::Dev
2543 } else {
2544 DependencyType::Production
2545 };
2546
2547 let filesystem_install_mirrors = filesystem
2548 .as_ref()
2549 .map(|fs| fs.install_mirrors.clone())
2550 .unwrap_or_default();
2551
2552 let packages = packages
2553 .into_iter()
2554 .map(|requirement| requirement.name)
2555 .collect();
2556
2557 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
2559 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
2560 let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);
2561
2562 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
2563
2564 check_conflicts(no_sync, frozen.map_or(Flag::Disabled, Flag::from))?;
2566
2567 let malware_settings = MalwareCheckSettings::resolve(filesystem.as_ref(), &environment);
2568
2569 Ok(Self {
2570 lock_check: locked,
2571 frozen,
2572 active: flag(active, no_active, "active")?.into(),
2573 no_sync: no_sync.is_enabled(),
2574 packages,
2575 dependency_type,
2576 package,
2577 script,
2578 python: python.and_then(Maybe::into_option),
2579 refresh: Refresh::try_from(refresh)?,
2580 settings: resolve_resolver_installer_settings(
2581 installer,
2582 build,
2583 filesystem,
2584 &environment,
2585 )?,
2586 install_mirrors: environment
2587 .install_mirrors
2588 .combine(filesystem_install_mirrors),
2589 malware_settings,
2590 })
2591 }
2592}
2593
2594#[derive(Debug, Clone)]
2596pub struct VersionSettings {
2597 pub value: Option<String>,
2598 pub bump: Vec<VersionBumpSpec>,
2599 pub short: bool,
2600 pub output_format: VersionFormat,
2601 pub dry_run: bool,
2602 pub lock_check: LockCheck,
2603 pub frozen: Option<FrozenSource>,
2604 pub active: ActiveEnvironment,
2605 pub no_sync: bool,
2606 pub package: Option<PackageName>,
2607 pub python: Option<String>,
2608 pub install_mirrors: PythonInstallMirrors,
2609 pub refresh: Refresh,
2610 pub settings: ResolverInstallerSettings,
2611 pub malware_settings: MalwareCheckSettings,
2612}
2613
2614impl VersionSettings {
2615 pub fn resolve(
2617 args: VersionArgs,
2618 filesystem: Option<FilesystemOptions>,
2619 environment: EnvironmentOptions,
2620 ) -> anyhow::Result<Self> {
2621 let VersionArgs {
2622 value,
2623 bump,
2624 short,
2625 output_format,
2626 dry_run,
2627 no_sync,
2628 locked,
2629 no_locked,
2630 frozen,
2631 no_frozen,
2632 active,
2633 no_active,
2634 installer,
2635 build,
2636 refresh,
2637 package,
2638 python,
2639 } = args;
2640
2641 let filesystem_install_mirrors = filesystem
2642 .as_ref()
2643 .map(|fs| fs.install_mirrors.clone())
2644 .unwrap_or_default();
2645
2646 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
2648 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
2649 let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);
2650
2651 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
2652
2653 check_conflicts(no_sync, frozen.map_or(Flag::Disabled, Flag::from))?;
2655
2656 let malware_settings = MalwareCheckSettings::resolve(filesystem.as_ref(), &environment);
2657
2658 Ok(Self {
2659 value,
2660 bump,
2661 short,
2662 output_format,
2663 dry_run,
2664 lock_check: locked,
2665 frozen,
2666 active: flag(active, no_active, "active")?.into(),
2667 no_sync: no_sync.is_enabled(),
2668 package,
2669 python: python.and_then(Maybe::into_option),
2670 refresh: Refresh::try_from(refresh)?,
2671 settings: resolve_resolver_installer_settings(
2672 installer,
2673 build,
2674 filesystem,
2675 &environment,
2676 )?,
2677 install_mirrors: environment
2678 .install_mirrors
2679 .combine(filesystem_install_mirrors),
2680 malware_settings,
2681 })
2682 }
2683}
2684
2685#[derive(Debug, Clone)]
2687pub struct TreeSettings {
2688 pub groups: DependencyGroups,
2689 pub lock_check: LockCheck,
2690 pub frozen: Option<FrozenSource>,
2691 pub universal: bool,
2692 pub format: TreeFormat,
2693 pub depth: u8,
2694 pub prune: Vec<PackageName>,
2695 pub package: Vec<PackageName>,
2696 pub no_dedupe: bool,
2697 pub invert: bool,
2698 pub outdated: bool,
2699 pub show_sizes: bool,
2700 pub script: Option<PathBuf>,
2701 pub python_version: Option<PythonVersion>,
2702 pub python_platform: Option<TargetTriple>,
2703 pub python: Option<String>,
2704 pub install_mirrors: PythonInstallMirrors,
2705 pub resolver: ResolverSettings,
2706}
2707
2708impl TreeSettings {
2709 pub fn resolve(
2711 args: TreeArgs,
2712 filesystem: Option<FilesystemOptions>,
2713 environment: EnvironmentOptions,
2714 ) -> anyhow::Result<Self> {
2715 let TreeArgs {
2716 tree,
2717 universal,
2718 format,
2719 dependency_groups:
2720 ProjectDependencyGroupsArgs {
2721 dev,
2722 no_dev,
2723 only_dev,
2724 group,
2725 no_group,
2726 no_default_groups,
2727 only_group,
2728 all_groups,
2729 },
2730 locked,
2731 no_locked,
2732 frozen,
2733 no_frozen,
2734 build,
2735 resolver,
2736 script,
2737 python_version,
2738 python_platform,
2739 python,
2740 } = args;
2741
2742 let filesystem_install_mirrors = filesystem
2743 .as_ref()
2744 .map(|fs| fs.install_mirrors.clone())
2745 .unwrap_or_default();
2746
2747 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
2749 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
2750
2751 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
2752
2753 let (dev, no_dev) = resolve_flag_pair(
2754 dev,
2755 no_dev,
2756 "dev",
2757 "no-dev",
2758 Some(environment.dev),
2759 Some(environment.no_dev),
2760 );
2761
2762 Ok(Self {
2763 groups: DependencyGroups::from_args(
2764 DevMode::from_args(dev.into(), no_dev.into(), only_dev),
2765 group,
2766 if no_group.is_empty() {
2767 environment.no_group.clone().unwrap_or_default()
2768 } else {
2769 no_group
2770 },
2771 no_default_groups,
2772 only_group,
2773 all_groups,
2774 ),
2775 lock_check: locked,
2776 frozen,
2777 universal,
2778 format,
2779 depth: tree.depth,
2780 prune: tree.prune,
2781 package: tree.package,
2782 no_dedupe: tree.no_dedupe,
2783 invert: tree.invert,
2784 outdated: tree.outdated,
2785 show_sizes: tree.show_sizes,
2786 script,
2787 python_version,
2788 python_platform,
2789 python: python.and_then(Maybe::into_option),
2790 resolver: resolve_resolver_settings(resolver, build, filesystem, &environment)?,
2791 install_mirrors: environment
2792 .install_mirrors
2793 .combine(filesystem_install_mirrors),
2794 })
2795 }
2796}
2797
2798#[expect(clippy::struct_excessive_bools)]
2800#[derive(Debug, Clone)]
2801pub struct ExportSettings {
2802 pub format: Option<ExportFormat>,
2803 pub all_packages: bool,
2804 pub package: Vec<PackageName>,
2805 pub prune: Vec<PackageName>,
2806 pub extras: ExtrasSpecification,
2807 pub groups: DependencyGroups,
2808 pub editable: Option<EditableMode>,
2809 pub hashes: bool,
2810 pub install_options: InstallOptions,
2811 pub batch: Option<PathBuf>,
2812 pub output_file: Option<PathBuf>,
2813 pub lock_check: LockCheck,
2814 pub frozen: Option<FrozenSource>,
2815 pub include_annotations: bool,
2816 pub include_header: bool,
2817 pub include_index_url: bool,
2818 pub include_find_links: bool,
2819 pub script: Option<PathBuf>,
2820 pub python: Option<String>,
2821 pub install_mirrors: PythonInstallMirrors,
2822 pub refresh: Refresh,
2823 pub settings: ResolverSettings,
2824}
2825
2826impl ExportSettings {
2827 pub fn resolve(
2829 args: ExportArgs,
2830 filesystem: Option<FilesystemOptions>,
2831 environment: EnvironmentOptions,
2832 ) -> anyhow::Result<Self> {
2833 let ExportArgs {
2834 format,
2835 all_packages,
2836 package,
2837 prune,
2838 extra,
2839 all_extras,
2840 no_extra,
2841 no_all_extras,
2842 dependency_groups:
2843 ProjectDependencyGroupsArgs {
2844 dev,
2845 no_dev,
2846 only_dev,
2847 group,
2848 no_group,
2849 no_default_groups,
2850 only_group,
2851 all_groups,
2852 },
2853 annotate,
2854 no_annotate,
2855 header,
2856 no_header,
2857 emit_index_url,
2858 no_emit_index_url,
2859 emit_find_links,
2860 no_emit_find_links,
2861 editable,
2862 no_editable,
2863 no_editable_package,
2864 hashes,
2865 no_hashes,
2866 batch,
2867 output_file,
2868 no_emit_project,
2869 only_emit_project,
2870 no_emit_workspace,
2871 only_emit_workspace,
2872 no_emit_local,
2873 only_emit_local,
2874 no_emit_package,
2875 only_emit_package,
2876 locked,
2877 no_locked,
2878 frozen: frozen_cli,
2879 no_frozen,
2880 resolver,
2881 build,
2882 refresh,
2883 script,
2884 python,
2885 } = args;
2886 let filesystem_install_mirrors = filesystem
2887 .as_ref()
2888 .map(|fs| fs.install_mirrors.clone())
2889 .unwrap_or_default();
2890
2891 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
2893 let frozen = resolve_frozen(
2894 frozen_cli,
2895 no_frozen,
2896 FrozenFlag::Frozen,
2897 environment.frozen,
2898 );
2899
2900 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
2901
2902 let (dev, no_dev) = resolve_flag_pair(
2903 dev,
2904 no_dev,
2905 "dev",
2906 "no-dev",
2907 Some(environment.dev),
2908 Some(environment.no_dev),
2909 );
2910 let (editable, no_editable) = resolve_flag_pair(
2911 editable,
2912 no_editable,
2913 "editable",
2914 "no-editable",
2915 None,
2916 Some(environment.no_editable),
2917 );
2918
2919 Ok(Self {
2920 format,
2921 all_packages,
2922 package,
2923 prune,
2924 extras: ExtrasSpecification::from_args(
2925 extra.unwrap_or_default(),
2926 no_extra,
2927 false,
2929 vec![],
2931 flag(all_extras, no_all_extras, "all-extras")?.unwrap_or_default(),
2932 ),
2933 groups: DependencyGroups::from_args(
2934 DevMode::from_args(dev.into(), no_dev.into(), only_dev),
2935 group,
2936 if no_group.is_empty() {
2937 environment.no_group.clone().unwrap_or_default()
2938 } else {
2939 no_group
2940 },
2941 no_default_groups,
2942 only_group,
2943 all_groups,
2944 ),
2945 editable: EditableMode::from_args(
2946 flag(editable.into(), no_editable.into(), "editable")?,
2947 no_editable_package,
2948 ),
2949 hashes: flag(hashes, no_hashes, "hashes")?.unwrap_or(true),
2950 install_options: InstallOptions::new(
2951 no_emit_project,
2952 only_emit_project,
2953 no_emit_workspace,
2954 only_emit_workspace,
2955 no_emit_local,
2956 only_emit_local,
2957 no_emit_package,
2958 only_emit_package,
2959 ),
2960 batch,
2961 output_file,
2962 lock_check: locked,
2963 frozen,
2964 include_annotations: flag(annotate, no_annotate, "annotate")?.unwrap_or(true),
2965 include_header: flag(header, no_header, "header")?.unwrap_or(true),
2966 include_index_url: flag(emit_index_url, no_emit_index_url, "emit-index-url")?
2967 .unwrap_or(false),
2968 include_find_links: flag(emit_find_links, no_emit_find_links, "emit-find-links")?
2969 .unwrap_or(false),
2970 script,
2971 python: python.and_then(Maybe::into_option),
2972 refresh: Refresh::try_from(refresh)?,
2973 settings: resolve_resolver_settings(resolver, build, filesystem, &environment)?,
2974 install_mirrors: environment
2975 .install_mirrors
2976 .combine(filesystem_install_mirrors),
2977 })
2978 }
2979}
2980
2981#[derive(Debug, Clone)]
2983pub struct FormatSettings {
2984 pub ruff_path: Option<PathBuf>,
2985 pub check: bool,
2986 pub diff: bool,
2987 pub extra_args: Vec<String>,
2988 pub version: Option<String>,
2989 pub exclude_newer: Option<jiff::Timestamp>,
2990 pub no_project: bool,
2991 pub show_version: bool,
2992}
2993
2994impl FormatSettings {
2995 pub fn resolve(
2997 args: FormatArgs,
2998 _filesystem: Option<FilesystemOptions>,
2999 environment: EnvironmentOptions,
3000 ) -> Self {
3001 let FormatArgs {
3002 check,
3003 diff,
3004 extra_args,
3005 version,
3006 exclude_newer,
3007 no_project,
3008 show_version,
3009 } = args;
3010
3011 Self {
3012 ruff_path: environment.ruff_path,
3013 check,
3014 diff,
3015 extra_args,
3016 version,
3017 exclude_newer: exclude_newer
3018 .and_then(ExcludeNewerOverride::into_value)
3019 .map(|value| value.timestamp()),
3020 no_project,
3021 show_version,
3022 }
3023 }
3024}
3025
3026#[derive(Debug, Clone)]
3028pub struct CheckSettings {
3029 pub ty_path: Option<PathBuf>,
3030 #[expect(dead_code)]
3031 script: Option<PathBuf>,
3032 pub fix: bool,
3033 pub all_packages: bool,
3034 pub package: Vec<PackageName>,
3035 pub extras: ExtrasSpecification,
3036 pub groups: DependencyGroups,
3037 pub lock_check: LockCheck,
3038 pub frozen: Option<FrozenSource>,
3039 pub no_sync: bool,
3040 pub no_install_project: bool,
3041 pub isolated: bool,
3042 pub python: Option<String>,
3043 pub install_mirrors: PythonInstallMirrors,
3044 pub refresh: Refresh,
3045 pub settings: ResolverInstallerSettings,
3046 pub ty_version: Option<String>,
3047 pub show_version: bool,
3048 pub show_command: bool,
3049 pub no_project: bool,
3050 pub malware_settings: MalwareCheckSettings,
3051}
3052
3053impl CheckSettings {
3054 pub fn resolve(
3056 args: CheckArgs,
3057 filesystem: Option<FilesystemOptions>,
3058 environment: EnvironmentOptions,
3059 ) -> anyhow::Result<Self> {
3060 let CheckArgs {
3061 fix,
3062 all_packages,
3063 package,
3064 script,
3065 extra,
3066 all_extras,
3067 no_extra,
3068 no_all_extras,
3069 dependency_groups:
3070 ProjectDependencyGroupsArgs {
3071 dev,
3072 no_dev,
3073 only_dev,
3074 group,
3075 no_group,
3076 no_default_groups,
3077 only_group,
3078 all_groups,
3079 },
3080 locked,
3081 no_locked,
3082 frozen,
3083 no_frozen,
3084 no_sync,
3085 no_install_project,
3086 isolated,
3087 python,
3088 ty_version,
3089 show_version,
3090 show_command,
3091 no_project,
3092 installer,
3093 build,
3094 refresh,
3095 } = args;
3096
3097 let filesystem_install_mirrors = filesystem
3098 .as_ref()
3099 .map(|fs| fs.install_mirrors.clone())
3100 .unwrap_or_default();
3101
3102 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
3103 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
3104 let no_sync = resolve_flag(no_sync, "no-sync", environment.no_sync);
3105 let no_install_project = resolve_flag(
3106 no_install_project,
3107 "no-install-project",
3108 environment.no_install_project,
3109 );
3110 let isolated = resolve_flag(isolated, "isolated", environment.isolated).is_enabled();
3111 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
3112 check_conflicts(no_install_project, no_sync)?;
3113 if script.is_some() {
3114 check_conflicts(no_install_project, Flag::from_cli("script"))?;
3115 }
3116 if no_project {
3117 check_conflicts(no_install_project, Flag::from_cli("no-project"))?;
3118 }
3119
3120 let (dev, no_dev) = resolve_flag_pair(
3121 dev,
3122 no_dev,
3123 "dev",
3124 "no-dev",
3125 Some(environment.dev),
3126 Some(environment.no_dev),
3127 );
3128 let malware_settings = MalwareCheckSettings::resolve(filesystem.as_ref(), &environment);
3129 let settings =
3130 resolve_resolver_installer_settings(installer, build, filesystem, &environment)?;
3131 Ok(Self {
3132 ty_path: environment.ty_path,
3133 script,
3134 fix,
3135 all_packages,
3136 package,
3137 extras: ExtrasSpecification::from_args(
3138 extra.unwrap_or_default(),
3139 no_extra,
3140 false,
3141 vec![],
3142 flag(all_extras, no_all_extras, "all-extras")?.unwrap_or_default(),
3143 ),
3144 groups: DependencyGroups::from_args(
3145 DevMode::from_args(dev.into(), no_dev.into(), only_dev),
3146 group,
3147 if no_group.is_empty() {
3148 environment.no_group.clone().unwrap_or_default()
3149 } else {
3150 no_group
3151 },
3152 no_default_groups,
3153 only_group,
3154 all_groups,
3155 ),
3156 lock_check: locked,
3157 frozen,
3158 no_sync: no_sync.is_enabled(),
3159 no_install_project: no_install_project.is_enabled(),
3160 isolated,
3161 python: python.and_then(Maybe::into_option),
3162 install_mirrors: environment
3163 .install_mirrors
3164 .combine(filesystem_install_mirrors),
3165 refresh: Refresh::try_from(refresh)?,
3166 settings,
3167 ty_version,
3168 show_version,
3169 show_command,
3170 no_project,
3171 malware_settings,
3172 })
3173 }
3174}
3175
3176#[derive(Debug, Clone)]
3178pub struct AuditSettings {
3179 pub extras: ExtrasSpecification,
3180 pub groups: DependencyGroups,
3181 pub lock_check: LockCheck,
3182 pub frozen: Option<FrozenSource>,
3183 pub python_version: Option<PythonVersion>,
3184 pub python_platform: Option<TargetTriple>,
3185 pub install_mirrors: PythonInstallMirrors,
3186 pub settings: ResolverSettings,
3187 pub output_format: AuditOutputFormat,
3188 pub service_format: VulnerabilityServiceFormat,
3189 pub service_url: Option<SafeUrl>,
3190 pub ignore: Vec<VulnerabilityID>,
3191 pub ignore_until_fixed: Vec<VulnerabilityID>,
3192}
3193
3194impl AuditSettings {
3195 pub fn resolve(
3197 args: AuditArgs,
3198 filesystem: Option<FilesystemOptions>,
3199 environment: EnvironmentOptions,
3200 ) -> anyhow::Result<Self> {
3201 let AuditArgs {
3202 no_extra,
3203 no_dev,
3204 no_group,
3205 no_default_groups,
3206 only_group,
3207 only_dev,
3208 script: _,
3209 python_version,
3210 python_platform,
3211 locked,
3212 no_locked,
3213 frozen,
3214 no_frozen,
3215 audit:
3216 AuditCommonArgs {
3217 offline: _,
3218 output_format,
3219 ignore,
3220 ignore_until_fixed,
3221 service_format,
3222 service_url,
3223 },
3224 build,
3225 resolver,
3226 } = args;
3227
3228 let filesystem_install_mirrors = filesystem
3229 .as_ref()
3230 .map(|fs| fs.install_mirrors.clone())
3231 .unwrap_or_default();
3232
3233 let filesystem_audit = filesystem
3234 .as_ref()
3235 .and_then(|fs| fs.audit.clone())
3236 .unwrap_or_default();
3237
3238 let no_dev = no_dev || environment.no_dev.value == Some(true);
3239
3240 let locked = resolve_lock_check(locked, no_locked, LockedFlag::Locked, environment.locked);
3242 let frozen = resolve_frozen(frozen, no_frozen, FrozenFlag::Frozen, environment.frozen);
3243
3244 let (locked, frozen) = resolve_lock_flags(locked, frozen)?;
3245
3246 let all_groups = only_group.is_empty() && !only_dev && !no_default_groups;
3249
3250 Ok(Self {
3251 extras: ExtrasSpecification::from_args(
3252 vec![],
3253 no_extra,
3254 false,
3256 vec![],
3258 true,
3259 ),
3260 groups: DependencyGroups::from_args(
3261 DevMode::from_args(all_groups, no_dev, only_dev),
3262 vec![],
3263 if no_group.is_empty() {
3264 environment.no_group.clone().unwrap_or_default()
3265 } else {
3266 no_group
3267 },
3268 no_default_groups,
3269 only_group,
3270 all_groups,
3271 ),
3272 lock_check: locked,
3273 frozen,
3274 python_version,
3275 python_platform,
3276 settings: resolve_resolver_settings(resolver, build, filesystem, &environment)?,
3277 install_mirrors: environment
3278 .install_mirrors
3279 .combine(filesystem_install_mirrors),
3280 output_format,
3281 service_format,
3282 service_url,
3283 ignore: {
3284 let config_ignore = filesystem_audit.ignore.unwrap_or_default();
3285 let mut merged = ignore;
3286 merged.extend(config_ignore);
3287 merged.into_iter().map(VulnerabilityID::new).collect()
3288 },
3289 ignore_until_fixed: {
3290 let config_ignore_until_fixed =
3291 filesystem_audit.ignore_until_fixed.unwrap_or_default();
3292 let mut merged = ignore_until_fixed;
3293 merged.extend(config_ignore_until_fixed);
3294 merged.into_iter().map(VulnerabilityID::new).collect()
3295 },
3296 })
3297 }
3298}
3299
3300fn workspace_overrides(filesystem: Option<&FilesystemOptions>) -> Vec<Override<Requirement>> {
3301 let mut overrides = Vec::new();
3302 for dependency in filesystem
3303 .and_then(|configuration| configuration.override_dependencies.as_ref())
3304 .into_iter()
3305 .flatten()
3306 {
3307 match dependency {
3308 OverrideDependency::Requirement(requirement) => {
3309 overrides.push(Override::Requirement(Requirement::from(
3310 requirement
3311 .clone()
3312 .with_origin(RequirementOrigin::Workspace),
3313 )));
3314 }
3315 OverrideDependency::Package(package) => {
3316 overrides.push(Override::Package(PackageOverride {
3317 package: package.package.clone(),
3318 dependencies: package
3319 .dependencies
3320 .iter()
3321 .cloned()
3322 .map(|requirement| {
3323 Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
3324 })
3325 .collect(),
3326 }));
3327 }
3328 }
3329 }
3330 overrides
3331}
3332
3333fn requirement_files(
3336 arguments: Vec<Maybe<RequirementsInput>>,
3337 environment: Option<Vec<RequirementsInput>>,
3338) -> Vec<RequirementsInput> {
3339 if arguments.is_empty() {
3340 environment.unwrap_or_default()
3341 } else {
3342 arguments
3343 .into_iter()
3344 .filter_map(Maybe::into_option)
3345 .collect()
3346 }
3347}
3348
3349#[derive(Debug, Clone)]
3351pub struct PipCompileSettings {
3352 pub format: Option<PipCompileFormat>,
3353 pub src_file: Vec<RequirementsInput>,
3354 pub constraints: Vec<RequirementsInput>,
3355 pub overrides: Vec<RequirementsInput>,
3356 pub excludes: Vec<RequirementsInput>,
3357 pub build_constraints: Vec<RequirementsInput>,
3358 pub build_hash_checking: HashCheckingMode,
3359 pub constraints_from_workspace: Vec<Requirement>,
3360 pub overrides_from_workspace: Vec<Override<Requirement>>,
3361 pub excludes_from_workspace: Vec<ExcludeDependency>,
3362 pub build_constraints_from_workspace: Vec<NameRequirementSpecification>,
3363 pub environments: SupportedEnvironments,
3364 pub required_environments: SupportedEnvironments,
3365 pub minimum_libc_version: Option<MinimumLibcVersion>,
3366 pub refresh: Refresh,
3367 pub settings: PipSettings,
3368}
3369
3370impl PipCompileSettings {
3371 pub fn resolve(
3373 args: PipCompileArgs,
3374 filesystem: Option<FilesystemOptions>,
3375 environment: EnvironmentOptions,
3376 ) -> anyhow::Result<Self> {
3377 let PipCompileArgs {
3378 src_file,
3379 constraints:
3380 DependencyConstraintsArgs {
3381 constraints,
3382 overrides,
3383 excludes,
3384 build_constraints,
3385 },
3386 extra,
3387 all_extras,
3388 no_all_extras,
3389 require_build_hashes,
3390 no_require_build_hashes,
3391 refresh,
3392 no_deps,
3393 deps,
3394 group,
3395 output_file,
3396 format,
3397 no_strip_extras,
3398 strip_extras,
3399 no_strip_markers,
3400 strip_markers,
3401 no_annotate,
3402 annotate,
3403 no_header,
3404 header,
3405 annotation_style,
3406 custom_compile_command,
3407 resolver,
3408 python,
3409 system,
3410 no_system,
3411 generate_hashes,
3412 no_generate_hashes,
3413 no_build,
3414 build,
3415 no_binary,
3416 only_binary,
3417 python_version,
3418 python_platform,
3419 universal,
3420 no_universal,
3421 no_emit_package,
3422 emit_index_url,
3423 no_emit_index_url,
3424 emit_find_links,
3425 no_emit_find_links,
3426 emit_build_options,
3427 no_emit_build_options,
3428 emit_marker_expression,
3429 no_emit_marker_expression,
3430 emit_index_annotation,
3431 no_emit_index_annotation,
3432 torch_backend,
3433 compat_args: _,
3434 } = args;
3435
3436 let constraints_from_workspace = if let Some(configuration) = &filesystem {
3437 configuration
3438 .constraint_dependencies
3439 .clone()
3440 .unwrap_or_default()
3441 .into_iter()
3442 .map(|requirement| {
3443 Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
3444 })
3445 .collect()
3446 } else {
3447 Vec::new()
3448 };
3449
3450 let overrides_from_workspace = workspace_overrides(filesystem.as_ref());
3451
3452 let excludes_from_workspace = if let Some(configuration) = &filesystem {
3453 configuration
3454 .exclude_dependencies
3455 .clone()
3456 .unwrap_or_default()
3457 } else {
3458 Vec::new()
3459 };
3460
3461 let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
3462 configuration
3463 .build_constraint_dependencies
3464 .clone()
3465 .unwrap_or_default()
3466 .into_iter()
3467 .map(|requirement| {
3468 let (requirement, hashes) = requirement.into_parts();
3469 NameRequirementSpecification {
3470 requirement: Requirement::from(
3471 requirement.with_origin(RequirementOrigin::Workspace),
3472 ),
3473 hashes,
3474 }
3475 })
3476 .collect()
3477 } else {
3478 Vec::new()
3479 };
3480
3481 let environments = if let Some(configuration) = &filesystem {
3482 configuration.environments.clone().unwrap_or_default()
3483 } else {
3484 SupportedEnvironments::default()
3485 };
3486
3487 let required_environments = if let Some(configuration) = &filesystem {
3488 configuration
3489 .required_environments
3490 .clone()
3491 .unwrap_or_default()
3492 } else {
3493 SupportedEnvironments::default()
3494 };
3495
3496 let minimum_libc_version = filesystem
3497 .as_ref()
3498 .and_then(|configuration| configuration.minimum_libc_version);
3499
3500 Ok(Self {
3501 format,
3502 src_file,
3503 constraints: requirement_files(constraints, environment.constraints.clone()),
3504 build_constraints: requirement_files(
3505 build_constraints,
3506 environment.build_constraints.clone(),
3507 ),
3508 build_hash_checking: resolve_pip_build_hash_checking(
3509 flag(
3510 require_build_hashes,
3511 no_require_build_hashes,
3512 "require-build-hashes",
3513 )?,
3514 filesystem.as_ref(),
3515 &environment,
3516 ),
3517 overrides: requirement_files(overrides, environment.overrides.clone()),
3518 excludes: requirement_files(excludes, environment.excludes.clone()),
3519 constraints_from_workspace,
3520 overrides_from_workspace,
3521 excludes_from_workspace,
3522 build_constraints_from_workspace,
3523 environments,
3524 required_environments,
3525 minimum_libc_version,
3526 refresh: Refresh::try_from(refresh)?,
3527 settings: PipSettings::combine(
3528 PipOptions {
3529 python: python.and_then(Maybe::into_option),
3530 system: flag(system, no_system, "system")?,
3531 no_build: flag(no_build, build, "build")?,
3532 no_binary,
3533 only_binary,
3534 extra,
3535 all_extras: flag(all_extras, no_all_extras, "all-extras")?,
3536 no_deps: flag(no_deps, deps, "deps")?,
3537 group: Some(group),
3538 output_file,
3539 no_strip_extras: flag(no_strip_extras, strip_extras, "strip-extras")?,
3540 no_strip_markers: flag(no_strip_markers, strip_markers, "strip-markers")?,
3541 no_annotate: flag(no_annotate, annotate, "annotate")?,
3542 no_header: flag(no_header, header, "header")?,
3543 custom_compile_command,
3544 generate_hashes: flag(generate_hashes, no_generate_hashes, "generate-hashes")?,
3545 python_version,
3546 python_platform,
3547 universal: flag(universal, no_universal, "universal")?,
3548 no_emit_package,
3549 emit_index_url: flag(emit_index_url, no_emit_index_url, "emit-index-url")?,
3550 emit_find_links: flag(emit_find_links, no_emit_find_links, "emit-find-links")?,
3551 emit_build_options: flag(
3552 emit_build_options,
3553 no_emit_build_options,
3554 "emit-build-options",
3555 )?,
3556 emit_marker_expression: flag(
3557 emit_marker_expression,
3558 no_emit_marker_expression,
3559 "emit-marker-expression",
3560 )?,
3561 emit_index_annotation: flag(
3562 emit_index_annotation,
3563 no_emit_index_annotation,
3564 "emit-index-annotation",
3565 )?,
3566 annotation_style,
3567 torch_backend,
3568 ..resolver.into_pip_options(configured_indexes(filesystem.as_ref()))?
3569 },
3570 filesystem,
3571 environment,
3572 ),
3573 })
3574 }
3575}
3576
3577#[derive(Debug, Clone)]
3579pub struct PipSyncSettings {
3580 pub src_file: Vec<RequirementsInput>,
3581 pub constraints: Vec<RequirementsInput>,
3582 pub build_constraints: Vec<RequirementsInput>,
3583 pub build_hash_checking: HashCheckingMode,
3584 pub dry_run: DryRun,
3585 pub output_format: PipInstallFormat,
3586 pub refresh: Refresh,
3587 pub settings: PipSettings,
3588}
3589
3590impl PipSyncSettings {
3591 pub fn resolve(
3593 args: Box<PipSyncArgs>,
3594 filesystem: Option<FilesystemOptions>,
3595 environment: EnvironmentOptions,
3596 ) -> anyhow::Result<Self> {
3597 let PipSyncArgs {
3598 src_file,
3599 constraints,
3600 build_constraints,
3601 require_build_hashes,
3602 no_require_build_hashes,
3603 extra,
3604 all_extras,
3605 no_all_extras,
3606 group,
3607 installer,
3608 refresh,
3609 hash_checking:
3610 HashCheckingArgs {
3611 require_hashes,
3612 no_require_hashes,
3613 verify_hashes,
3614 no_verify_hashes,
3615 },
3616 python,
3617 system,
3618 no_system,
3619 break_system_packages,
3620 no_break_system_packages,
3621 target,
3622 prefix,
3623 allow_empty_requirements,
3624 no_allow_empty_requirements,
3625 no_build,
3626 build,
3627 no_binary,
3628 only_binary,
3629 python_version,
3630 python_platform,
3631 strict,
3632 no_strict,
3633 dry_run,
3634 output_format,
3635 torch_backend,
3636 compat_args: _,
3637 check,
3638 } = *args;
3639
3640 Ok(Self {
3641 src_file,
3642 constraints: requirement_files(constraints, environment.constraints.clone()),
3643 build_constraints: requirement_files(
3644 build_constraints,
3645 environment.build_constraints.clone(),
3646 ),
3647 build_hash_checking: resolve_pip_build_hash_checking(
3648 flag(
3649 require_build_hashes,
3650 no_require_build_hashes,
3651 "require-build-hashes",
3652 )?,
3653 filesystem.as_ref(),
3654 &environment,
3655 ),
3656 dry_run: if check {
3657 DryRun::Check
3658 } else {
3659 DryRun::from_args(dry_run)
3660 },
3661 output_format,
3662 refresh: Refresh::try_from(refresh)?,
3663 settings: PipSettings::combine(
3664 PipOptions {
3665 python: python.and_then(Maybe::into_option),
3666 system: flag(system, no_system, "system")?,
3667 break_system_packages: flag(
3668 break_system_packages,
3669 no_break_system_packages,
3670 "break-system-packages",
3671 )?,
3672 target,
3673 prefix,
3674 require_hashes: flag(require_hashes, no_require_hashes, "require-hashes")?,
3675 verify_hashes: flag(verify_hashes, no_verify_hashes, "verify-hashes")?,
3676 no_build: flag(no_build, build, "build")?,
3677 no_binary,
3678 only_binary,
3679 allow_empty_requirements: flag(
3680 allow_empty_requirements,
3681 no_allow_empty_requirements,
3682 "allow-empty-requirements",
3683 )?,
3684 python_version,
3685 python_platform,
3686 strict: flag(strict, no_strict, "strict")?,
3687 extra,
3688 all_extras: flag(all_extras, no_all_extras, "all-extras")?,
3689 group: Some(group),
3690 torch_backend,
3691 ..installer.into_pip_options(configured_indexes(filesystem.as_ref()))?
3692 },
3693 filesystem,
3694 environment,
3695 ),
3696 })
3697 }
3698}
3699
3700#[derive(Debug, Clone)]
3702pub struct PipInstallSettings {
3703 pub package: Vec<String>,
3704 pub requirements: Vec<RequirementsInput>,
3705 pub editables: Vec<String>,
3706 pub editable: Option<EditableMode>,
3707 pub constraints: Vec<RequirementsInput>,
3708 pub overrides: Vec<RequirementsInput>,
3709 pub excludes: Vec<RequirementsInput>,
3710 pub build_constraints: Vec<RequirementsInput>,
3711 pub build_hash_checking: HashCheckingMode,
3712 pub dry_run: DryRun,
3713 pub output_format: PipInstallFormat,
3714 pub constraints_from_workspace: Vec<Requirement>,
3715 pub overrides_from_workspace: Vec<Override<Requirement>>,
3716 pub excludes_from_workspace: Vec<ExcludeDependency>,
3717 pub build_constraints_from_workspace: Vec<NameRequirementSpecification>,
3718 pub modifications: Modifications,
3719 pub refresh: Refresh,
3720 pub settings: PipSettings,
3721}
3722
3723impl PipInstallSettings {
3724 pub fn resolve(
3726 args: PipInstallArgs,
3727 filesystem: Option<FilesystemOptions>,
3728 environment: EnvironmentOptions,
3729 ) -> anyhow::Result<Self> {
3730 let PipInstallArgs {
3731 package,
3732 requirements,
3733 editable,
3734 no_editable,
3735 no_editable_package,
3736 constraints:
3737 DependencyConstraintsArgs {
3738 constraints,
3739 overrides,
3740 excludes,
3741 build_constraints,
3742 },
3743 require_build_hashes,
3744 no_require_build_hashes,
3745 extra,
3746 all_extras,
3747 no_all_extras,
3748 installer,
3749 refresh,
3750 no_deps,
3751 deps,
3752 group,
3753 hash_checking:
3754 HashCheckingArgs {
3755 require_hashes,
3756 no_require_hashes,
3757 verify_hashes,
3758 no_verify_hashes,
3759 },
3760 python,
3761 system,
3762 no_system,
3763 break_system_packages,
3764 no_break_system_packages,
3765 target,
3766 prefix,
3767 no_build,
3768 build,
3769 no_binary,
3770 only_binary,
3771 python_version,
3772 python_platform,
3773 inexact,
3774 exact,
3775 strict,
3776 no_strict,
3777 dry_run,
3778 output_format,
3779 torch_backend,
3780 compat_args: _,
3781 check,
3782 } = args;
3783
3784 let constraints_from_workspace = if let Some(configuration) = &filesystem {
3785 configuration
3786 .constraint_dependencies
3787 .clone()
3788 .unwrap_or_default()
3789 .into_iter()
3790 .map(|requirement| {
3791 Requirement::from(requirement.with_origin(RequirementOrigin::Workspace))
3792 })
3793 .collect()
3794 } else {
3795 Vec::new()
3796 };
3797
3798 let overrides_from_workspace = workspace_overrides(filesystem.as_ref());
3799
3800 let excludes_from_workspace = if let Some(configuration) = &filesystem {
3801 configuration
3802 .exclude_dependencies
3803 .clone()
3804 .unwrap_or_default()
3805 } else {
3806 Vec::new()
3807 };
3808
3809 let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
3810 configuration
3811 .build_constraint_dependencies
3812 .clone()
3813 .unwrap_or_default()
3814 .into_iter()
3815 .map(|requirement| {
3816 let (requirement, hashes) = requirement.into_parts();
3817 NameRequirementSpecification {
3818 requirement: Requirement::from(
3819 requirement.with_origin(RequirementOrigin::Workspace),
3820 ),
3821 hashes,
3822 }
3823 })
3824 .collect()
3825 } else {
3826 Vec::new()
3827 };
3828
3829 Ok(Self {
3830 package,
3831 requirements,
3832 editables: editable,
3833 constraints: requirement_files(constraints, environment.constraints.clone()),
3834 overrides: requirement_files(overrides, environment.overrides.clone()),
3835 excludes: requirement_files(excludes, environment.excludes.clone()),
3836 build_constraints: requirement_files(
3837 build_constraints,
3838 environment.build_constraints.clone(),
3839 ),
3840 build_hash_checking: resolve_pip_build_hash_checking(
3841 flag(
3842 require_build_hashes,
3843 no_require_build_hashes,
3844 "require-build-hashes",
3845 )?,
3846 filesystem.as_ref(),
3847 &environment,
3848 ),
3849 dry_run: if check {
3850 DryRun::Check
3851 } else {
3852 DryRun::from_args(dry_run)
3853 },
3854 output_format,
3855 constraints_from_workspace,
3856 overrides_from_workspace,
3857 excludes_from_workspace,
3858 build_constraints_from_workspace,
3859 modifications: if flag(exact, inexact, "inexact")?.unwrap_or(false) {
3860 Modifications::Exact
3861 } else {
3862 Modifications::Sufficient
3863 },
3864 editable: EditableMode::from_args(
3865 if no_editable || environment.no_editable.value == Some(true) {
3866 Some(false)
3867 } else {
3868 None
3869 },
3870 no_editable_package,
3871 ),
3872 refresh: Refresh::try_from(refresh)?,
3873 settings: PipSettings::combine(
3874 PipOptions {
3875 python: python.and_then(Maybe::into_option),
3876 system: flag(system, no_system, "system")?,
3877 break_system_packages: flag(
3878 break_system_packages,
3879 no_break_system_packages,
3880 "break-system-packages",
3881 )?,
3882 target,
3883 prefix,
3884 no_build: flag(no_build, build, "build")?,
3885 no_binary,
3886 only_binary,
3887 strict: flag(strict, no_strict, "strict")?,
3888 extra,
3889 all_extras: flag(all_extras, no_all_extras, "all-extras")?,
3890 group: Some(group),
3891 no_deps: flag(no_deps, deps, "deps")?,
3892 python_version,
3893 python_platform,
3894 require_hashes: flag(require_hashes, no_require_hashes, "require-hashes")?,
3895 verify_hashes: flag(verify_hashes, no_verify_hashes, "verify-hashes")?,
3896 torch_backend,
3897 ..installer.into_pip_options(configured_indexes(filesystem.as_ref()))?
3898 },
3899 filesystem,
3900 environment,
3901 ),
3902 })
3903 }
3904}
3905
3906#[derive(Debug, Clone)]
3908pub struct PipUninstallSettings {
3909 pub package: Vec<String>,
3910 pub requirements: Vec<RequirementsInput>,
3911 pub dry_run: DryRun,
3912 pub settings: PipSettings,
3913}
3914
3915impl PipUninstallSettings {
3916 pub fn resolve(
3918 args: PipUninstallArgs,
3919 filesystem: Option<FilesystemOptions>,
3920 environment: EnvironmentOptions,
3921 ) -> anyhow::Result<Self> {
3922 let PipUninstallArgs {
3923 package,
3924 requirements,
3925 python,
3926 keyring_provider,
3927 system,
3928 no_system,
3929 break_system_packages,
3930 no_break_system_packages,
3931 target,
3932 prefix,
3933 dry_run,
3934 compat_args: _,
3935 } = args;
3936
3937 Ok(Self {
3938 package,
3939 requirements,
3940 dry_run: DryRun::from_args(dry_run),
3941 settings: PipSettings::combine(
3942 PipOptions {
3943 python: python.and_then(Maybe::into_option),
3944 system: flag(system, no_system, "system")?,
3945 break_system_packages: flag(
3946 break_system_packages,
3947 no_break_system_packages,
3948 "break-system-packages",
3949 )?,
3950 target,
3951 prefix,
3952 keyring_provider,
3953 ..PipOptions::default()
3954 },
3955 filesystem,
3956 environment,
3957 ),
3958 })
3959 }
3960}
3961
3962#[derive(Debug, Clone)]
3964pub struct PipFreezeSettings {
3965 pub exclude_editable: bool,
3966 pub exclude: FxHashSet<PackageName>,
3967 pub paths: Option<Vec<PathBuf>>,
3968 pub settings: PipSettings,
3969}
3970
3971impl PipFreezeSettings {
3972 pub fn resolve(
3974 args: PipFreezeArgs,
3975 filesystem: Option<FilesystemOptions>,
3976 environment: EnvironmentOptions,
3977 ) -> anyhow::Result<Self> {
3978 let PipFreezeArgs {
3979 exclude_editable,
3980 exclude,
3981 strict,
3982 no_strict,
3983 python,
3984 paths,
3985 system,
3986 no_system,
3987 target,
3988 prefix,
3989 compat_args: _,
3990 } = args;
3991
3992 Ok(Self {
3993 exclude_editable,
3994 exclude: exclude.into_iter().collect(),
3995 paths,
3996 settings: PipSettings::combine(
3997 PipOptions {
3998 python: python.and_then(Maybe::into_option),
3999 system: flag(system, no_system, "system")?,
4000 strict: flag(strict, no_strict, "strict")?,
4001 target,
4002 prefix,
4003 ..PipOptions::default()
4004 },
4005 filesystem,
4006 environment,
4007 ),
4008 })
4009 }
4010}
4011
4012#[derive(Debug, Clone)]
4014pub struct PipListSettings {
4015 pub editable: Option<bool>,
4016 pub exclude: FxHashSet<PackageName>,
4017 pub format: ListFormat,
4018 pub outdated: bool,
4019 pub settings: PipSettings,
4020}
4021
4022impl PipListSettings {
4023 pub fn resolve(
4025 args: PipListArgs,
4026 filesystem: Option<FilesystemOptions>,
4027 environment: EnvironmentOptions,
4028 ) -> anyhow::Result<Self> {
4029 let PipListArgs {
4030 editable,
4031 exclude_editable,
4032 exclude,
4033 format,
4034 outdated,
4035 no_outdated,
4036 strict,
4037 no_strict,
4038 fetch,
4039 python,
4040 system,
4041 no_system,
4042 target,
4043 prefix,
4044 compat_args: _,
4045 } = args;
4046
4047 Ok(Self {
4048 editable: flag(editable, exclude_editable, "exclude-editable")?,
4049 exclude: exclude.into_iter().collect(),
4050 format,
4051 outdated: flag(outdated, no_outdated, "outdated")?.unwrap_or(false),
4052 settings: PipSettings::combine(
4053 PipOptions {
4054 python: python.and_then(Maybe::into_option),
4055 system: flag(system, no_system, "system")?,
4056 strict: flag(strict, no_strict, "strict")?,
4057 target,
4058 prefix,
4059 ..fetch.into_pip_options(configured_indexes(filesystem.as_ref()))?
4060 },
4061 filesystem,
4062 environment,
4063 ),
4064 })
4065 }
4066}
4067
4068#[derive(Debug, Clone)]
4070pub struct PipShowSettings {
4071 pub package: Vec<PackageName>,
4072 pub files: bool,
4073 pub settings: PipSettings,
4074}
4075
4076impl PipShowSettings {
4077 pub fn resolve(
4079 args: PipShowArgs,
4080 filesystem: Option<FilesystemOptions>,
4081 environment: EnvironmentOptions,
4082 ) -> anyhow::Result<Self> {
4083 let PipShowArgs {
4084 package,
4085 strict,
4086 no_strict,
4087 files,
4088 python,
4089 system,
4090 no_system,
4091 target,
4092 prefix,
4093 compat_args: _,
4094 } = args;
4095
4096 Ok(Self {
4097 package,
4098 files,
4099 settings: PipSettings::combine(
4100 PipOptions {
4101 python: python.and_then(Maybe::into_option),
4102 system: flag(system, no_system, "system")?,
4103 strict: flag(strict, no_strict, "strict")?,
4104 target,
4105 prefix,
4106 ..PipOptions::default()
4107 },
4108 filesystem,
4109 environment,
4110 ),
4111 })
4112 }
4113}
4114
4115#[derive(Debug, Clone)]
4117pub struct PipTreeSettings {
4118 pub show_version_specifiers: bool,
4119 pub depth: u8,
4120 pub prune: Vec<PackageName>,
4121 pub package: Vec<PackageName>,
4122 pub no_dedupe: bool,
4123 pub invert: bool,
4124 pub outdated: bool,
4125 pub settings: PipSettings,
4126}
4127
4128impl PipTreeSettings {
4129 pub fn resolve(
4131 args: PipTreeArgs,
4132 filesystem: Option<FilesystemOptions>,
4133 environment: EnvironmentOptions,
4134 ) -> anyhow::Result<Self> {
4135 let PipTreeArgs {
4136 show_version_specifiers,
4137 tree,
4138 strict,
4139 no_strict,
4140 fetch,
4141 python,
4142 system,
4143 no_system,
4144 compat_args: _,
4145 } = args;
4146
4147 Ok(Self {
4148 show_version_specifiers,
4149 depth: tree.depth,
4150 prune: tree.prune,
4151 no_dedupe: tree.no_dedupe,
4152 invert: tree.invert,
4153 package: tree.package,
4154 outdated: tree.outdated,
4155 settings: PipSettings::combine(
4156 PipOptions {
4157 python: python.and_then(Maybe::into_option),
4158 system: flag(system, no_system, "system")?,
4159 strict: flag(strict, no_strict, "strict")?,
4160 ..fetch.into_pip_options(configured_indexes(filesystem.as_ref()))?
4161 },
4162 filesystem,
4163 environment,
4164 ),
4165 })
4166 }
4167}
4168
4169#[derive(Debug, Clone)]
4171pub struct PipCheckSettings {
4172 pub settings: PipSettings,
4173}
4174
4175impl PipCheckSettings {
4176 pub fn resolve(
4178 args: PipCheckArgs,
4179 filesystem: Option<FilesystemOptions>,
4180 environment: EnvironmentOptions,
4181 ) -> anyhow::Result<Self> {
4182 let PipCheckArgs {
4183 python,
4184 system,
4185 no_system,
4186 python_version,
4187 python_platform,
4188 } = args;
4189
4190 Ok(Self {
4191 settings: PipSettings::combine(
4192 PipOptions {
4193 python: python.and_then(Maybe::into_option),
4194 system: flag(system, no_system, "system")?,
4195 python_version,
4196 python_platform,
4197 ..PipOptions::default()
4198 },
4199 filesystem,
4200 environment,
4201 ),
4202 })
4203 }
4204}
4205
4206#[derive(Debug, Clone)]
4208pub struct BuildSettings {
4209 pub skip_dependency_check: bool,
4210 pub src: Option<PathBuf>,
4211 pub package: Option<PackageName>,
4212 pub all_packages: bool,
4213 pub out_dir: Option<PathBuf>,
4214 pub sdist: bool,
4215 pub wheel: bool,
4216 pub list: bool,
4217 pub build_logs: bool,
4218 pub gitignore: bool,
4219 pub force_pep517: bool,
4220 pub clear: bool,
4221 pub build_constraints: Vec<RequirementsInput>,
4222 pub build_constraints_from_workspace: Vec<NameRequirementSpecification>,
4223 pub hash_checking: Option<HashCheckingMode>,
4224 pub python: Option<String>,
4225 pub install_mirrors: PythonInstallMirrors,
4226 pub refresh: Refresh,
4227 pub settings: ResolverSettings,
4228}
4229
4230impl BuildSettings {
4231 pub fn resolve(
4233 args: BuildArgs,
4234 filesystem: Option<FilesystemOptions>,
4235 environment: EnvironmentOptions,
4236 ) -> anyhow::Result<Self> {
4237 let BuildArgs {
4238 skip_dependency_check,
4239 src,
4240 out_dir,
4241 package,
4242 all_packages,
4243 sdist,
4244 wheel,
4245 list,
4246 force_pep517,
4247 clear,
4248 build_constraints,
4249 hash_checking:
4250 HashCheckingArgs {
4251 require_hashes,
4252 no_require_hashes,
4253 verify_hashes,
4254 no_verify_hashes,
4255 },
4256 build_logs,
4257 no_build_logs,
4258 create_gitignore,
4259 no_create_gitignore,
4260 python,
4261 build,
4262 refresh,
4263 resolver,
4264 } = args;
4265 let filesystem_install_mirrors = match &filesystem {
4266 Some(fs) => fs.install_mirrors.clone(),
4267 None => PythonInstallMirrors::default(),
4268 };
4269 let build_constraints_from_workspace = if let Some(configuration) = &filesystem {
4270 configuration
4271 .build_constraint_dependencies
4272 .clone()
4273 .unwrap_or_default()
4274 .into_iter()
4275 .map(|requirement| {
4276 let (requirement, hashes) = requirement.into_parts();
4277 NameRequirementSpecification {
4278 requirement: Requirement::from(
4279 requirement.with_origin(RequirementOrigin::Workspace),
4280 ),
4281 hashes,
4282 }
4283 })
4284 .collect()
4285 } else {
4286 Vec::new()
4287 };
4288
4289 Ok(Self {
4290 skip_dependency_check,
4291 src,
4292 package,
4293 all_packages,
4294 out_dir,
4295 sdist,
4296 wheel,
4297 list,
4298 build_logs: flag(build_logs, no_build_logs, "build-logs")?.unwrap_or(true),
4299 force_pep517,
4300 clear,
4301 gitignore: flag(create_gitignore, no_create_gitignore, "create-gitignore")?
4302 .unwrap_or(true),
4303 build_constraints: requirement_files(
4304 build_constraints,
4305 environment.build_constraints.clone(),
4306 ),
4307 build_constraints_from_workspace,
4308 hash_checking: HashCheckingMode::from_args(
4309 flag(require_hashes, no_require_hashes, "require-hashes")?,
4310 flag(verify_hashes, no_verify_hashes, "verify-hashes")?,
4311 ),
4312 python: python.and_then(Maybe::into_option),
4313 refresh: Refresh::try_from(refresh)?,
4314 settings: resolve_resolver_settings(resolver, build, filesystem, &environment)?,
4315 install_mirrors: environment
4316 .install_mirrors
4317 .combine(filesystem_install_mirrors),
4318 })
4319 }
4320}
4321
4322#[derive(Debug, Clone)]
4324pub struct VenvSettings {
4325 pub seed: bool,
4326 pub allow_existing: bool,
4327 pub clear: bool,
4328 pub force: bool,
4329 pub no_clear: bool,
4330 pub path: Option<PathBuf>,
4331 pub prompt: Option<String>,
4332 pub system_site_packages: bool,
4333 pub relocatable: bool,
4334 pub no_relocatable: bool,
4335 pub no_project: bool,
4336 pub refresh: Refresh,
4337 pub settings: PipSettings,
4338}
4339
4340impl VenvSettings {
4341 pub fn resolve(
4343 args: VenvArgs,
4344 filesystem: Option<FilesystemOptions>,
4345 environment: EnvironmentOptions,
4346 ) -> anyhow::Result<Self> {
4347 let VenvArgs {
4348 python,
4349 system,
4350 no_system,
4351 seed,
4352 allow_existing,
4353 clear,
4354 force,
4355 no_clear,
4356 path,
4357 prompt,
4358 system_site_packages,
4359 relocatable,
4360 no_relocatable,
4361 index_args,
4362 registry_client:
4363 RegistryClientArgs {
4364 index_strategy,
4365 keyring_provider,
4366 },
4367 exclude_newer:
4368 PackageExcludeNewerArgs {
4369 exclude_newer: ExcludeNewerArgs { exclude_newer },
4370 exclude_newer_package,
4371 },
4372 no_project,
4373 link_mode,
4374 refresh,
4375 compat_args: _,
4376 } = args;
4377
4378 let seed = seed || environment.venv_seed.value == Some(true);
4380 let (clear, no_clear) = resolve_flag_pair(
4381 clear,
4382 no_clear,
4383 "clear",
4384 "no-clear",
4385 Some(environment.venv_clear),
4386 None,
4387 );
4388 let (relocatable, no_relocatable) = resolve_flag_pair(
4389 relocatable,
4390 no_relocatable,
4391 "relocatable",
4392 "no-relocatable",
4393 Some(environment.venv_relocatable),
4394 None,
4395 );
4396
4397 Ok(Self {
4398 seed,
4399 allow_existing,
4400 clear: clear.into(),
4401 force,
4402 no_clear: no_clear.into(),
4403 path,
4404 prompt,
4405 system_site_packages,
4406 no_project,
4407 relocatable: relocatable.into(),
4408 no_relocatable: no_relocatable.into(),
4409 refresh: Refresh::try_from(refresh)?,
4410 settings: PipSettings::combine(
4411 PipOptions {
4412 python: python.and_then(Maybe::into_option),
4413 system: flag(system, no_system, "system")?,
4414 index_strategy,
4415 keyring_provider,
4416 exclude_newer,
4417 exclude_newer_package: exclude_newer_package
4418 .map(ExcludeNewerPackage::from_iter),
4419 link_mode,
4420 ..index_args.into_pip_options(configured_indexes(filesystem.as_ref()))?
4421 },
4422 filesystem,
4423 environment,
4424 ),
4425 })
4426 }
4427}
4428
4429fn resolve_pip_build_hash_checking(
4431 require_build_hashes: Option<bool>,
4432 filesystem: Option<&FilesystemOptions>,
4433 environment: &EnvironmentOptions,
4434) -> HashCheckingMode {
4435 let configured = filesystem.and_then(|filesystem| {
4436 filesystem
4437 .pip
4438 .as_ref()
4439 .and_then(|pip| pip.require_build_hashes)
4440 .or(filesystem.top_level.require_build_hashes)
4441 });
4442 resolve_build_hash_checking(
4443 require_build_hashes
4444 .or(environment.require_build_hashes)
4445 .or(configured),
4446 )
4447}
4448
4449fn configured_indexes(filesystem: Option<&FilesystemOptions>) -> &[Index] {
4451 filesystem
4452 .and_then(|options| options.top_level.index.as_deref())
4453 .unwrap_or_default()
4454}
4455
4456fn resolve_resolver_settings(
4458 args: ResolverArgs,
4459 build: BuildOptionsArgs,
4460 filesystem: Option<FilesystemOptions>,
4461 environment: &EnvironmentOptions,
4462) -> Result<ResolverSettings> {
4463 let args = resolver_options(args, build, configured_indexes(filesystem.as_ref()))?;
4464
4465 Ok(combine_resolver_settings(args, filesystem, environment))
4466}
4467
4468fn combine_resolver_settings(
4470 mut args: ResolverOptions,
4471 filesystem: Option<FilesystemOptions>,
4472 environment: &EnvironmentOptions,
4473) -> ResolverSettings {
4474 args.require_build_hashes = args
4475 .require_build_hashes
4476 .or(environment.require_build_hashes);
4477 args.no_binary_package = args
4478 .no_binary_package
4479 .or(environment.no_binary_package.clone());
4480 args.no_build_package = args
4481 .no_build_package
4482 .or(environment.no_build_package.clone());
4483 args.no_sources_package = args
4484 .no_sources_package
4485 .or(environment.no_sources_package.clone());
4486
4487 let options = args.combine(ResolverOptions::from(
4490 filesystem
4491 .map(FilesystemOptions::into_options)
4492 .map(|options| options.top_level)
4493 .unwrap_or_default(),
4494 ));
4495
4496 ResolverSettings {
4497 cuda_driver_version: environment.cuda_driver_version.clone(),
4498 amd_gpu_architecture: environment.amd_gpu_architecture,
4499 ..ResolverSettings::from(options)
4500 }
4501}
4502
4503fn resolve_resolver_installer_settings(
4505 args: ResolverInstallerArgs,
4506 build: BuildOptionsArgs,
4507 filesystem: Option<FilesystemOptions>,
4508 environment: &EnvironmentOptions,
4509) -> Result<ResolverInstallerSettings> {
4510 let args = resolver_installer_options(args, build, configured_indexes(filesystem.as_ref()))?;
4511
4512 Ok(combine_resolver_installer_settings(
4513 args,
4514 filesystem,
4515 environment,
4516 ))
4517}
4518
4519fn combine_resolver_installer_settings(
4521 args: ResolverInstallerOptions,
4522 filesystem: Option<FilesystemOptions>,
4523 environment: &EnvironmentOptions,
4524) -> ResolverInstallerSettings {
4525 let options = resolver_installer_options_with_environment(args, environment).combine(
4526 ResolverInstallerOptions::from(
4527 filesystem
4528 .map(FilesystemOptions::into_options)
4529 .map(|options| options.top_level)
4530 .unwrap_or_default(),
4531 ),
4532 );
4533
4534 let base = ResolverInstallerSettings::from(options);
4535 ResolverInstallerSettings {
4536 resolver: ResolverSettings {
4537 cuda_driver_version: environment.cuda_driver_version.clone(),
4538 amd_gpu_architecture: environment.amd_gpu_architecture,
4539 ..base.resolver
4540 },
4541 ..base
4542 }
4543}
4544
4545fn resolver_installer_options_with_environment(
4546 mut options: ResolverInstallerOptions,
4547 environment: &EnvironmentOptions,
4548) -> ResolverInstallerOptions {
4549 options.require_build_hashes = options
4550 .require_build_hashes
4551 .or(environment.require_build_hashes);
4552 options.no_binary_package = options
4553 .no_binary_package
4554 .or(environment.no_binary_package.clone());
4555 options.no_build_package = options
4556 .no_build_package
4557 .or(environment.no_build_package.clone());
4558 options.no_sources_package = options
4559 .no_sources_package
4560 .or(environment.no_sources_package.clone());
4561 options
4562}
4563
4564#[derive(Debug, Clone)]
4569pub struct PipSettings {
4570 pub index_locations: IndexLocations,
4571 pub python: Option<String>,
4572 pub install_mirrors: PythonInstallMirrors,
4573 pub system: bool,
4574 pub extras: ExtrasSpecification,
4575 pub groups: Vec<PipGroupName>,
4576 pub break_system_packages: bool,
4577 pub target: Option<Target>,
4578 pub prefix: Option<Prefix>,
4579 pub index_strategy: IndexStrategy,
4580 pub keyring_provider: KeyringProviderType,
4581 pub torch_backend: Option<TorchMode>,
4582 pub cuda_driver_version: Option<Version>,
4583 pub amd_gpu_architecture: Option<AmdGpuArchitecture>,
4584 pub build_isolation: BuildIsolation,
4585 pub extra_build_dependencies: ExtraBuildDependencies,
4586 pub extra_build_variables: ExtraBuildVariables,
4587 pub build_options: BuildOptions,
4588 pub allow_empty_requirements: bool,
4589 pub strict: bool,
4590 pub dependency_mode: DependencyMode,
4591 pub resolution: ResolutionMode,
4592 pub prerelease: Prerelease,
4593 pub fork_strategy: ForkStrategy,
4594 pub dependency_metadata: DependencyMetadata,
4595 pub output_file: Option<PathBuf>,
4596 pub no_strip_extras: bool,
4597 pub no_strip_markers: bool,
4598 pub no_annotate: bool,
4599 pub no_header: bool,
4600 pub custom_compile_command: Option<String>,
4601 pub generate_hashes: bool,
4602 pub config_setting: ConfigSettings,
4603 pub config_settings_package: PackageConfigSettings,
4604 pub python_version: Option<PythonVersion>,
4605 pub python_platform: Option<TargetTriple>,
4606 pub universal: bool,
4607 pub exclude_newer: ExcludeNewer,
4608 pub no_emit_package: Vec<PackageName>,
4609 pub emit_index_url: bool,
4610 pub emit_find_links: bool,
4611 pub emit_build_options: bool,
4612 pub emit_marker_expression: bool,
4613 pub emit_index_annotation: bool,
4614 pub annotation_style: AnnotationStyle,
4615 pub link_mode: LinkMode,
4616 pub compile_bytecode: bool,
4617 pub sources: NoSources,
4618 pub hash_checking: Option<HashCheckingMode>,
4619 pub upgrade: Upgrade,
4620 pub reinstall: Reinstall,
4621}
4622
4623impl PipSettings {
4624 fn combine(
4626 args: PipOptions,
4627 filesystem: Option<FilesystemOptions>,
4628 environment: EnvironmentOptions,
4629 ) -> Self {
4630 let Options {
4631 top_level,
4632 pip,
4633 install_mirrors: filesystem_install_mirrors,
4634 ..
4635 } = filesystem
4636 .map(FilesystemOptions::into_options)
4637 .unwrap_or_default();
4638
4639 let PipOptions {
4640 python,
4641 system,
4642 break_system_packages,
4643 target,
4644 prefix,
4645 index,
4646 index_url,
4647 extra_index_url,
4648 no_index,
4649 find_links,
4650 index_strategy,
4651 torch_backend,
4652 keyring_provider,
4653 no_build,
4654 no_binary,
4655 only_binary,
4656 no_build_isolation,
4657 require_build_hashes: _,
4658 no_build_isolation_package,
4659 extra_build_dependencies,
4660 extra_build_variables,
4661 strict,
4662 extra,
4663 all_extras,
4664 no_extra,
4665 group,
4666 no_deps,
4667 allow_empty_requirements,
4668 resolution,
4669 prerelease,
4670 prerelease_package: _,
4671 fork_strategy,
4672 dependency_metadata,
4673 output_file,
4674 no_strip_extras,
4675 no_strip_markers,
4676 no_annotate,
4677 no_header,
4678 custom_compile_command,
4679 generate_hashes,
4680 config_settings,
4681 config_settings_package,
4682 python_version,
4683 python_platform,
4684 universal,
4685 exclude_newer,
4686 no_emit_package,
4687 emit_index_url,
4688 emit_find_links,
4689 emit_build_options,
4690 emit_marker_expression,
4691 emit_index_annotation,
4692 annotation_style,
4693 link_mode,
4694 compile_bytecode,
4695 require_hashes,
4696 verify_hashes,
4697 no_sources,
4698 no_sources_package,
4699 upgrade,
4700 upgrade_package,
4701 reinstall,
4702 reinstall_package,
4703 exclude_newer_package,
4704 } = pip.unwrap_or_default();
4705
4706 let ResolverInstallerSchema {
4707 index: top_level_index,
4708 index_url: top_level_index_url,
4709 extra_index_url: top_level_extra_index_url,
4710 no_index: top_level_no_index,
4711 find_links: top_level_find_links,
4712 index_strategy: top_level_index_strategy,
4713 keyring_provider: top_level_keyring_provider,
4714 resolution: top_level_resolution,
4715 prerelease: top_level_prerelease,
4716 prerelease_package: top_level_prerelease_package,
4717 fork_strategy: top_level_fork_strategy,
4718 dependency_metadata: top_level_dependency_metadata,
4719 config_settings: top_level_config_settings,
4720 config_settings_package: top_level_config_settings_package,
4721 no_build_isolation: top_level_no_build_isolation,
4722 no_build_isolation_package: top_level_no_build_isolation_package,
4723 extra_build_dependencies: top_level_extra_build_dependencies,
4724 extra_build_variables: top_level_extra_build_variables,
4725 exclude_newer: top_level_exclude_newer,
4726 link_mode: top_level_link_mode,
4727 compile_bytecode: top_level_compile_bytecode,
4728 no_sources: top_level_no_sources,
4729 no_sources_package: top_level_no_sources_package,
4730 upgrade: top_level_upgrade,
4731 upgrade_package: top_level_upgrade_package,
4732 reinstall: top_level_reinstall,
4733 reinstall_package: top_level_reinstall_package,
4734 no_build: top_level_no_build,
4735 no_build_package: top_level_no_build_package,
4736 no_binary: top_level_no_binary,
4737 no_binary_package: top_level_no_binary_package,
4738 exclude_newer_package: top_level_exclude_newer_package,
4739 torch_backend: top_level_torch_backend,
4740 require_build_hashes: _,
4741 } = top_level;
4742
4743 let index = index.combine(top_level_index);
4748 let no_index = no_index.combine(top_level_no_index);
4749 let index_url = index_url.combine(top_level_index_url);
4750 let extra_index_url = extra_index_url.combine(top_level_extra_index_url);
4751 let find_links = find_links.combine(top_level_find_links);
4752 let index_strategy = index_strategy.combine(top_level_index_strategy);
4753 let keyring_provider = keyring_provider.combine(top_level_keyring_provider);
4754 let resolution = resolution.combine(top_level_resolution);
4755 let prerelease = prerelease.combine(top_level_prerelease);
4756 let prerelease_package = args
4757 .prerelease_package
4758 .combine(top_level_prerelease_package)
4759 .unwrap_or_default();
4760 let fork_strategy = fork_strategy.combine(top_level_fork_strategy);
4761 let dependency_metadata = dependency_metadata.combine(top_level_dependency_metadata);
4762 let config_settings = config_settings.combine(top_level_config_settings);
4763 let config_settings_package =
4764 config_settings_package.combine(top_level_config_settings_package);
4765 let no_build_isolation = no_build_isolation.combine(top_level_no_build_isolation);
4766 let no_build_isolation_package =
4767 no_build_isolation_package.combine(top_level_no_build_isolation_package);
4768 let extra_build_dependencies =
4769 extra_build_dependencies.combine(top_level_extra_build_dependencies);
4770 let extra_build_variables = extra_build_variables.combine(top_level_extra_build_variables);
4771 let exclude_newer = args
4772 .exclude_newer
4773 .combine(exclude_newer)
4774 .combine(top_level_exclude_newer);
4775 let exclude_newer_package = args
4776 .exclude_newer_package
4777 .combine(exclude_newer_package)
4778 .combine(top_level_exclude_newer_package)
4779 .unwrap_or_default();
4780 let link_mode = link_mode.combine(top_level_link_mode);
4781 let compile_bytecode = compile_bytecode.combine(top_level_compile_bytecode);
4782 let no_sources = no_sources.combine(top_level_no_sources);
4783 let no_sources_package = no_sources_package.combine(top_level_no_sources_package);
4784 let upgrade = upgrade.combine(top_level_upgrade);
4785 let upgrade_package = upgrade_package.combine(top_level_upgrade_package);
4786 let reinstall = reinstall.combine(top_level_reinstall);
4787 let reinstall_package = reinstall_package.combine(top_level_reinstall_package);
4788 let torch_backend = torch_backend.combine(top_level_torch_backend);
4789 let args_no_sources_package = args
4790 .no_sources_package
4791 .or(environment.no_sources_package.clone());
4792
4793 Self {
4794 index_locations: IndexLocations::new(
4795 args.index
4796 .into_iter()
4797 .flatten()
4798 .chain(args.extra_index_url.into_iter().flatten().map(Index::from))
4799 .chain(args.index_url.into_iter().map(Index::from))
4800 .chain(index.into_iter().flatten())
4801 .chain(extra_index_url.into_iter().flatten().map(Index::from))
4802 .chain(index_url.into_iter().map(Index::from))
4803 .collect(),
4804 args.find_links
4805 .combine(find_links)
4806 .into_iter()
4807 .flatten()
4808 .map(Index::from)
4809 .collect(),
4810 args.no_index.combine(no_index).unwrap_or_default(),
4811 ),
4812 extras: ExtrasSpecification::from_args(
4813 args.extra.combine(extra).unwrap_or_default(),
4814 args.no_extra.combine(no_extra).unwrap_or_default(),
4815 false,
4817 vec![],
4819 args.all_extras.combine(all_extras).unwrap_or_default(),
4820 ),
4821
4822 groups: args.group.combine(group).unwrap_or_default(),
4823 dependency_mode: if args.no_deps.combine(no_deps).unwrap_or_default() {
4824 DependencyMode::Direct
4825 } else {
4826 DependencyMode::Transitive
4827 },
4828 resolution: args.resolution.combine(resolution).unwrap_or_default(),
4829 prerelease: resolve_prerelease(
4830 args.prerelease.combine(prerelease).unwrap_or_default(),
4831 prerelease_package,
4832 ),
4833 fork_strategy: args
4834 .fork_strategy
4835 .combine(fork_strategy)
4836 .unwrap_or_default(),
4837 dependency_metadata: DependencyMetadata::from_entries(
4838 args.dependency_metadata
4839 .combine(dependency_metadata)
4840 .unwrap_or_default(),
4841 ),
4842 output_file: args.output_file.combine(output_file),
4843 no_strip_extras: args
4844 .no_strip_extras
4845 .combine(no_strip_extras)
4846 .unwrap_or_default(),
4847 no_strip_markers: args
4848 .no_strip_markers
4849 .combine(no_strip_markers)
4850 .unwrap_or_default(),
4851 no_annotate: args.no_annotate.combine(no_annotate).unwrap_or_default(),
4852 no_header: args.no_header.combine(no_header).unwrap_or_default(),
4853 custom_compile_command: args.custom_compile_command.combine(custom_compile_command),
4854 annotation_style: args
4855 .annotation_style
4856 .combine(annotation_style)
4857 .unwrap_or_default(),
4858 index_strategy: args
4859 .index_strategy
4860 .combine(index_strategy)
4861 .unwrap_or_default(),
4862 keyring_provider: args
4863 .keyring_provider
4864 .combine(keyring_provider)
4865 .unwrap_or_default(),
4866 generate_hashes: args
4867 .generate_hashes
4868 .combine(generate_hashes)
4869 .unwrap_or_default(),
4870 allow_empty_requirements: args
4871 .allow_empty_requirements
4872 .combine(allow_empty_requirements)
4873 .unwrap_or_default(),
4874 build_isolation: BuildIsolation::from_args(
4875 args.no_build_isolation,
4876 args.no_build_isolation_package.unwrap_or_default(),
4877 )
4878 .combine(BuildIsolation::from_args(
4879 no_build_isolation,
4880 no_build_isolation_package.unwrap_or_default(),
4881 ))
4882 .unwrap_or_default(),
4883 extra_build_dependencies: args
4884 .extra_build_dependencies
4885 .combine(extra_build_dependencies)
4886 .unwrap_or_default(),
4887 extra_build_variables: args
4888 .extra_build_variables
4889 .combine(extra_build_variables)
4890 .unwrap_or_default(),
4891 config_setting: args
4892 .config_settings
4893 .combine(config_settings)
4894 .unwrap_or_default(),
4895 config_settings_package: args
4896 .config_settings_package
4897 .combine(config_settings_package)
4898 .unwrap_or_default(),
4899 torch_backend: args.torch_backend.combine(torch_backend),
4900 cuda_driver_version: environment.cuda_driver_version.clone(),
4901 amd_gpu_architecture: environment.amd_gpu_architecture,
4902 python_version: args.python_version.combine(python_version),
4903 python_platform: args.python_platform.combine(python_platform),
4904 universal: args.universal.combine(universal).unwrap_or_default(),
4905 exclude_newer: ExcludeNewer::from_args(
4906 exclude_newer,
4907 exclude_newer_package.into_iter().map(Into::into).collect(),
4908 ),
4909 no_emit_package: args
4910 .no_emit_package
4911 .combine(no_emit_package)
4912 .unwrap_or_default(),
4913 emit_index_url: args
4914 .emit_index_url
4915 .combine(emit_index_url)
4916 .unwrap_or_default(),
4917 emit_find_links: args
4918 .emit_find_links
4919 .combine(emit_find_links)
4920 .unwrap_or_default(),
4921 emit_build_options: args
4922 .emit_build_options
4923 .combine(emit_build_options)
4924 .unwrap_or_default(),
4925 emit_marker_expression: args
4926 .emit_marker_expression
4927 .combine(emit_marker_expression)
4928 .unwrap_or_default(),
4929 emit_index_annotation: args
4930 .emit_index_annotation
4931 .combine(emit_index_annotation)
4932 .unwrap_or_default(),
4933 link_mode: args.link_mode.combine(link_mode).unwrap_or_default(),
4934 hash_checking: HashCheckingMode::from_args(
4935 args.require_hashes.combine(require_hashes),
4936 args.verify_hashes.combine(verify_hashes),
4937 ),
4938 python: args.python.combine(python),
4939 system: args.system.combine(system).unwrap_or_default(),
4940 break_system_packages: args
4941 .break_system_packages
4942 .combine(break_system_packages)
4943 .unwrap_or_default(),
4944 target: args.target.combine(target).map(Target::from),
4945 prefix: args.prefix.combine(prefix).map(Prefix::from),
4946 compile_bytecode: args
4947 .compile_bytecode
4948 .combine(compile_bytecode)
4949 .unwrap_or_default(),
4950 sources: NoSources::from_args(
4951 args.no_sources.combine(no_sources),
4952 args_no_sources_package
4953 .combine(no_sources_package)
4954 .unwrap_or_default(),
4955 ),
4956 strict: args.strict.combine(strict).unwrap_or_default(),
4957 upgrade: Upgrade::from_args(
4958 args.upgrade,
4959 args.upgrade_package
4960 .into_iter()
4961 .flatten()
4962 .map(Requirement::from)
4963 .collect(),
4964 Vec::new(),
4965 )
4966 .combine(Upgrade::from_args(
4967 upgrade,
4968 upgrade_package
4969 .into_iter()
4970 .flatten()
4971 .map(Requirement::from)
4972 .collect(),
4973 Vec::new(),
4974 ))
4975 .unwrap_or_default(),
4976 reinstall: Reinstall::from_args(
4977 args.reinstall,
4978 args.reinstall_package.unwrap_or_default(),
4979 )
4980 .combine(Reinstall::from_args(
4981 reinstall,
4982 reinstall_package.unwrap_or_default(),
4983 ))
4984 .unwrap_or_default(),
4985 build_options: BuildOptions::new(
4986 NoBinary::from_pip_args(args.no_binary.combine(no_binary).unwrap_or_default())
4987 .combine(NoBinary::from_args(
4988 top_level_no_binary,
4989 top_level_no_binary_package.unwrap_or_default(),
4990 )),
4991 NoBuild::from_pip_args(
4992 args.only_binary.combine(only_binary).unwrap_or_default(),
4993 args.no_build.combine(no_build).unwrap_or_default(),
4994 )
4995 .combine(NoBuild::from_args(
4996 top_level_no_build,
4997 top_level_no_build_package.unwrap_or_default(),
4998 )),
4999 ),
5000 install_mirrors: environment
5001 .install_mirrors
5002 .combine(filesystem_install_mirrors),
5003 }
5004 }
5005}
5006
5007#[derive(Clone)]
5009pub struct PublishSettings {
5010 pub files: Vec<String>,
5012 pub username: Option<String>,
5013 pub password: Option<String>,
5014 pub index: Option<String>,
5015 pub dry_run: bool,
5016 pub no_attestations: bool,
5017
5018 pub publish_url: SafeUrl,
5020 pub trusted_publishing: TrustedPublishing,
5021 pub keyring_provider: KeyringProviderType,
5022 pub check_url: Option<IndexUrl>,
5023
5024 pub index_locations: IndexLocations,
5026}
5027
5028impl fmt::Debug for PublishSettings {
5029 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5030 f.debug_struct("PublishSettings")
5031 .field("files", &self.files)
5032 .field("username", &self.username)
5033 .field("password", &self.password.as_ref().map(|_| "****"))
5034 .field("index", &self.index)
5035 .field("dry_run", &self.dry_run)
5036 .field("no_attestations", &self.no_attestations)
5037 .field("publish_url", &self.publish_url)
5038 .field("trusted_publishing", &self.trusted_publishing)
5039 .field("keyring_provider", &self.keyring_provider)
5040 .field("check_url", &self.check_url)
5041 .field("index_locations", &self.index_locations)
5042 .finish()
5043 }
5044}
5045
5046impl PublishSettings {
5047 pub fn resolve(args: PublishArgs, filesystem: Option<FilesystemOptions>) -> Self {
5049 let Options {
5050 publish, top_level, ..
5051 } = filesystem
5052 .map(FilesystemOptions::into_options)
5053 .unwrap_or_default();
5054
5055 let PublishOptions {
5056 publish_url,
5057 trusted_publishing,
5058 check_url,
5059 } = publish;
5060 let ResolverInstallerSchema {
5061 keyring_provider,
5062 index,
5063 extra_index_url,
5064 index_url,
5065 ..
5066 } = top_level;
5067
5068 let (username, password) = if let Some(token) = args.token {
5070 (Some("__token__".to_string()), Some(token))
5071 } else {
5072 (args.username, args.password)
5073 };
5074
5075 Self {
5076 files: args.files,
5077 username,
5078 password,
5079 dry_run: args.dry_run,
5080 no_attestations: args.no_attestations,
5081 publish_url: args
5082 .publish_url
5083 .combine(publish_url)
5084 .unwrap_or_else(|| SafeUrl::parse(PYPI_PUBLISH_URL).unwrap()),
5085 trusted_publishing: args
5086 .trusted_publishing
5087 .combine(trusted_publishing)
5088 .unwrap_or_default(),
5089 keyring_provider: args
5090 .keyring_provider
5091 .combine(keyring_provider)
5092 .unwrap_or_default(),
5093 check_url: args.check_url.combine(check_url),
5094 index: args.index,
5095 index_locations: IndexLocations::new(
5096 index
5097 .into_iter()
5098 .flatten()
5099 .chain(extra_index_url.into_iter().flatten().map(Index::from))
5100 .chain(index_url.into_iter().map(Index::from))
5101 .collect(),
5102 Vec::new(),
5103 false,
5104 ),
5105 }
5106 }
5107}
5108
5109#[derive(Debug, Clone)]
5111pub struct AuthLogoutSettings {
5112 pub service: Service,
5113 pub username: Option<String>,
5114}
5115
5116impl AuthLogoutSettings {
5117 pub fn resolve(args: AuthLogoutArgs) -> Self {
5119 Self {
5120 service: args.service,
5121 username: args.username,
5122 }
5123 }
5124}
5125
5126#[derive(Debug, Clone)]
5128pub struct AuthTokenSettings {
5129 pub service: Service,
5130 pub username: Option<String>,
5131}
5132
5133impl AuthTokenSettings {
5134 pub fn resolve(args: AuthTokenArgs) -> Self {
5136 Self {
5137 service: args.service,
5138 username: args.username,
5139 }
5140 }
5141}
5142
5143#[derive(Debug, Clone)]
5145pub struct AuthLoginSettings {
5146 pub service: Service,
5147 pub username: Option<String>,
5148 pub password: Option<String>,
5149 pub token: Option<String>,
5150}
5151
5152impl AuthLoginSettings {
5153 pub fn resolve(args: AuthLoginArgs) -> Self {
5155 Self {
5156 service: args.service,
5157 username: args.username,
5158 password: args.password,
5159 token: args.token,
5160 }
5161 }
5162}
5163
5164mod env {
5166 use uv_static::EnvVars;
5167 pub(super) const UV_PYTHON_DOWNLOADS: (&str, &str) = (
5168 EnvVars::UV_PYTHON_DOWNLOADS,
5169 "one of 'auto', 'true', 'manual', 'never', or 'false'",
5170 );
5171}
5172
5173fn env<T>((name, expected): (&str, &str)) -> Option<T>
5178where
5179 T: FromStr,
5180{
5181 let val = match std::env::var(name) {
5182 Ok(val) => val,
5183 Err(VarError::NotPresent) => return None,
5184 Err(VarError::NotUnicode(_)) => parse_failure(name, expected),
5185 };
5186 Some(
5187 val.parse()
5188 .unwrap_or_else(|_| parse_failure(name, expected)),
5189 )
5190}
5191
5192#[expect(clippy::exit, clippy::print_stderr)]
5194fn parse_failure(name: &str, expected: &str) -> ! {
5195 eprintln!("error: invalid value for {name}, expected {expected}");
5196 process::exit(1)
5197}
5198
5199#[cfg(test)]
5200mod tests {
5201 use crate::{IndexArgs, RegistryClientArgs};
5202
5203 use super::*;
5204
5205 #[test]
5206 fn upgrade_settings_target_only_requested_package() -> anyhow::Result<()> {
5207 let package = PackageName::from_str("anyio")?;
5208 let settings = UpgradeSettings::resolve(
5209 UpgradeArgs {
5210 packages: vec![package.clone()],
5211 exclude: Vec::new(),
5212 index_args: IndexArgs {
5213 index: None,
5214 default_index: None,
5215 index_url: None,
5216 extra_index_url: None,
5217 find_links: None,
5218 no_index: false,
5219 },
5220 registry_client: RegistryClientArgs {
5221 index_strategy: None,
5222 keyring_provider: None,
5223 },
5224 require_build_hashes: false,
5225 no_require_build_hashes: false,
5226 },
5227 None,
5228 EnvironmentOptions::new()?,
5229 )?;
5230 let expected = FxHashSet::from_iter([package]);
5231
5232 assert!(!settings.settings.upgrade.is_all());
5233 assert_eq!(settings.settings.upgrade.packages(), Some(&expected));
5234 Ok(())
5235 }
5236}