uv/
lib.rs

1#![deny(clippy::print_stdout, clippy::print_stderr)]
2
3use std::borrow::Cow;
4use std::ffi::OsString;
5use std::fmt::Write;
6use std::io::stdout;
7#[cfg(feature = "self-update")]
8use std::ops::Bound;
9use std::path::Path;
10use std::process::ExitCode;
11use std::str::FromStr;
12use std::sync::atomic::Ordering;
13
14use anstream::eprintln;
15use anyhow::{Result, anyhow, bail};
16use clap::error::{ContextKind, ContextValue};
17use clap::{CommandFactory, Parser};
18use futures::FutureExt;
19use owo_colors::OwoColorize;
20use settings::PipTreeSettings;
21use tokio::task::spawn_blocking;
22use tracing::{debug, instrument, trace};
23
24#[cfg(not(feature = "self-update"))]
25use crate::install_source::InstallSource;
26use uv_cache::{Cache, Refresh};
27use uv_cache_info::Timestamp;
28#[cfg(feature = "self-update")]
29use uv_cli::SelfUpdateArgs;
30use uv_cli::{
31    AuthCommand, AuthHelperCommand, AuthNamespace, BuildBackendCommand, CacheCommand,
32    CacheNamespace, Cli, Commands, PipCommand, PipNamespace, ProjectCommand, PythonCommand,
33    PythonNamespace, SelfCommand, SelfNamespace, ToolCommand, ToolNamespace, TopLevelArgs,
34    WorkspaceCommand, WorkspaceNamespace, compat::CompatArgs,
35};
36use uv_client::BaseClientBuilder;
37use uv_configuration::min_stack_size;
38use uv_flags::EnvironmentFlags;
39use uv_fs::{CWD, Simplified};
40#[cfg(feature = "self-update")]
41use uv_pep440::release_specifiers_to_ranges;
42use uv_pep508::VersionOrUrl;
43use uv_preview::PreviewFeatures;
44use uv_pypi_types::{ParsedDirectoryUrl, ParsedUrl};
45use uv_python::PythonRequest;
46use uv_requirements::{GroupsSpecification, RequirementsSource};
47use uv_requirements_txt::RequirementsTxtRequirement;
48use uv_scripts::{Pep723Error, Pep723Item, Pep723Metadata, Pep723Script};
49use uv_settings::{Combine, EnvironmentOptions, FilesystemOptions, Options};
50use uv_static::EnvVars;
51use uv_warnings::{warn_user, warn_user_once};
52use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceCache};
53
54use crate::commands::{ExitStatus, RunCommand, ScriptPath, ToolRunCommand};
55use crate::printer::Printer;
56use crate::settings::{
57    CacheSettings, GlobalSettings, PipCheckSettings, PipCompileSettings, PipFreezeSettings,
58    PipInstallSettings, PipListSettings, PipShowSettings, PipSyncSettings, PipUninstallSettings,
59    PublishSettings,
60};
61
62pub(crate) mod child;
63pub(crate) mod commands;
64#[cfg(not(feature = "self-update"))]
65mod install_source;
66pub(crate) mod logging;
67pub(crate) mod printer;
68pub(crate) mod settings;
69#[cfg(windows)]
70mod windows_exception;
71
72#[instrument(skip_all)]
73async fn run(mut cli: Cli) -> Result<ExitStatus> {
74    // Enable flag to pick up warnings generated by workspace loading.
75    if cli.top_level.global_args.quiet == 0 {
76        uv_warnings::enable();
77    }
78
79    // Respect `UV_WORKING_DIRECTORY` for backwards compatibility.
80    let directory =
81        cli.top_level.global_args.directory.clone().or_else(|| {
82            std::env::var_os(EnvVars::UV_WORKING_DIRECTORY).map(std::path::PathBuf::from)
83        });
84
85    // Switch directories as early as possible.
86    if let Some(directory) = directory.as_ref() {
87        std::env::set_current_dir(directory)?;
88    }
89
90    // Determine the project directory.
91    let project_dir = cli
92        .top_level
93        .global_args
94        .project
95        .as_deref()
96        .map(std::path::absolute)
97        .transpose()?
98        .map(uv_fs::normalize_path_buf)
99        .map(Cow::Owned)
100        .unwrap_or_else(|| Cow::Borrowed(&*CWD));
101
102    // Load environment variables not handled by Clap
103    let environment = EnvironmentOptions::new()?;
104
105    // The `--isolated` argument is deprecated on preview APIs, and warns on non-preview APIs.
106    let deprecated_isolated = if cli.top_level.global_args.isolated {
107        match &*cli.command {
108            // Supports `--isolated` as its own argument, so we can't warn either way.
109            Commands::Tool(ToolNamespace {
110                command: ToolCommand::Uvx(_) | ToolCommand::Run(_),
111            }) => false,
112
113            // Supports `--isolated` as its own argument, so we can't warn either way.
114            Commands::Project(command) if matches!(**command, ProjectCommand::Run(_)) => false,
115
116            // `--isolated` moved to `--no-workspace`.
117            Commands::Project(command) if matches!(**command, ProjectCommand::Init(_)) => {
118                warn_user!(
119                    "The `--isolated` flag is deprecated and has no effect. Instead, use `--no-config` to prevent uv from discovering configuration files or `--no-workspace` to prevent uv from adding the initialized project to the containing workspace."
120                );
121                false
122            }
123
124            // Preview APIs. Ignore `--isolated` and warn.
125            Commands::Project(_) | Commands::Tool(_) | Commands::Python(_) => {
126                warn_user!(
127                    "The `--isolated` flag is deprecated and has no effect. Instead, use `--no-config` to prevent uv from discovering configuration files."
128                );
129                false
130            }
131
132            // Non-preview APIs. Continue to support `--isolated`, but warn.
133            _ => {
134                warn_user!(
135                    "The `--isolated` flag is deprecated. Instead, use `--no-config` to prevent uv from discovering configuration files."
136                );
137                true
138            }
139        }
140    } else {
141        false
142    };
143
144    // Load configuration from the filesystem, prioritizing (in order):
145    // 1. The configuration file specified on the command-line.
146    // 2. The nearest configuration file (`uv.toml` or `pyproject.toml`) above the workspace root.
147    //    If found, this file is combined with the user configuration file.
148    // 3. The nearest configuration file (`uv.toml` or `pyproject.toml`) in the directory tree,
149    //    starting from the current directory.
150    let workspace_cache = WorkspaceCache::default();
151    let filesystem = if let Some(config_file) = cli.top_level.config_file.as_ref() {
152        if config_file
153            .file_name()
154            .is_some_and(|file_name| file_name == "pyproject.toml")
155        {
156            warn_user!(
157                "The `--config-file` argument expects to receive a `uv.toml` file, not a `pyproject.toml`. If you're trying to run a command from another project, use the `--project` argument instead."
158            );
159        }
160        Some(FilesystemOptions::from_file(config_file)?)
161    } else if deprecated_isolated || cli.top_level.no_config {
162        None
163    } else if matches!(&*cli.command, Commands::Tool(_) | Commands::Self_(_)) {
164        // For commands that operate at the user-level, ignore local configuration.
165        FilesystemOptions::user()?.combine(FilesystemOptions::system()?)
166    } else if let Ok(workspace) =
167        Workspace::discover(&project_dir, &DiscoveryOptions::default(), &workspace_cache).await
168    {
169        let project = FilesystemOptions::find(workspace.install_path())?;
170        let system = FilesystemOptions::system()?;
171        let user = FilesystemOptions::user()?;
172        project.combine(user).combine(system)
173    } else {
174        let project = FilesystemOptions::find(&project_dir)?;
175        let system = FilesystemOptions::system()?;
176        let user = FilesystemOptions::user()?;
177        project.combine(user).combine(system)
178    };
179
180    // Parse the external command, if necessary.
181    let run_command = if let Commands::Project(command) = &mut *cli.command {
182        if let ProjectCommand::Run(uv_cli::RunArgs {
183            command: Some(command),
184            module,
185            script,
186            gui_script,
187            ..
188        }) = &mut **command
189        {
190            let settings = GlobalSettings::resolve(
191                &cli.top_level.global_args,
192                filesystem.as_ref(),
193                &environment,
194            );
195            let client_builder = BaseClientBuilder::new(
196                settings.network_settings.connectivity,
197                settings.network_settings.native_tls,
198                settings.network_settings.allow_insecure_host,
199                settings.preview,
200                settings.network_settings.timeout,
201                settings.network_settings.retries,
202            )
203            .http_proxy(settings.network_settings.http_proxy)
204            .https_proxy(settings.network_settings.https_proxy)
205            .no_proxy(settings.network_settings.no_proxy);
206            Some(
207                RunCommand::from_args(command, client_builder, *module, *script, *gui_script)
208                    .await?,
209            )
210        } else {
211            None
212        }
213    } else {
214        None
215    };
216
217    // If the target is a PEP 723 script, parse it.
218    let script = if let Commands::Project(command) = &*cli.command {
219        match &**command {
220            ProjectCommand::Run(uv_cli::RunArgs { .. }) => match run_command.as_ref() {
221                Some(
222                    RunCommand::PythonScript(script, _) | RunCommand::PythonGuiScript(script, _),
223                ) => match Pep723Script::read(&script).await {
224                    Ok(Some(script)) => Some(Pep723Item::Script(script)),
225                    Ok(None) => None,
226                    Err(Pep723Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => None,
227                    Err(err) => return Err(err.into()),
228                },
229                Some(RunCommand::PythonRemote(url, script, _)) => {
230                    match Pep723Metadata::read(&script).await {
231                        Ok(Some(metadata)) => Some(Pep723Item::Remote(metadata, url.clone())),
232                        Ok(None) => None,
233                        Err(Pep723Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
234                            None
235                        }
236                        Err(err) => return Err(err.into()),
237                    }
238                }
239                Some(
240                    RunCommand::PythonStdin(contents, _) | RunCommand::PythonGuiStdin(contents, _),
241                ) => Pep723Metadata::parse(contents)?.map(Pep723Item::Stdin),
242                _ => None,
243            },
244            // For `uv add --script` and `uv lock --script`, we'll create a PEP 723 tag if it
245            // doesn't already exist.
246            ProjectCommand::Add(uv_cli::AddArgs {
247                script: Some(script),
248                ..
249            })
250            | ProjectCommand::Lock(uv_cli::LockArgs {
251                script: Some(script),
252                ..
253            }) => match Pep723Script::read(&script).await {
254                Ok(Some(script)) => Some(Pep723Item::Script(script)),
255                Ok(None) => None,
256                Err(err) => return Err(err.into()),
257            },
258            // For the remaining commands, the PEP 723 tag must exist already.
259            ProjectCommand::Remove(uv_cli::RemoveArgs {
260                script: Some(script),
261                ..
262            })
263            | ProjectCommand::Sync(uv_cli::SyncArgs {
264                script: Some(script),
265                ..
266            })
267            | ProjectCommand::Tree(uv_cli::TreeArgs {
268                script: Some(script),
269                ..
270            })
271            | ProjectCommand::Export(uv_cli::ExportArgs {
272                script: Some(script),
273                ..
274            }) => match Pep723Script::read(&script).await {
275                Ok(Some(script)) => Some(Pep723Item::Script(script)),
276                Ok(None) => {
277                    bail!(
278                        "`{}` does not contain a PEP 723 metadata tag; run `{}` to initialize the script",
279                        script.user_display().cyan(),
280                        format!("uv init --script {}", script.user_display()).green()
281                    )
282                }
283                Err(Pep723Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
284                    bail!(
285                        "Failed to read `{}` (not found); run `{}` to create a PEP 723 script",
286                        script.user_display().cyan(),
287                        format!("uv init --script {}", script.user_display()).green()
288                    )
289                }
290                Err(err) => return Err(err.into()),
291            },
292            _ => None,
293        }
294    } else if let Commands::Python(uv_cli::PythonNamespace {
295        command:
296            PythonCommand::Find(uv_cli::PythonFindArgs {
297                script: Some(script),
298                ..
299            }),
300    }) = &*cli.command
301    {
302        match Pep723Script::read(&script).await {
303            Ok(Some(script)) => Some(Pep723Item::Script(script)),
304            Ok(None) => {
305                bail!(
306                    "`{}` does not contain a PEP 723 metadata tag; run `{}` to initialize the script",
307                    script.user_display().cyan(),
308                    format!("uv init --script {}", script.user_display()).green()
309                )
310            }
311            Err(Pep723Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => {
312                bail!(
313                    "Failed to read `{}` (not found); run `{}` to create a PEP 723 script",
314                    script.user_display().cyan(),
315                    format!("uv init --script {}", script.user_display()).green()
316                )
317            }
318            Err(err) => return Err(err.into()),
319        }
320    } else {
321        None
322    };
323
324    // If the target is a PEP 723 script, merge the metadata into the filesystem metadata.
325    let filesystem = script
326        .as_ref()
327        .map(Pep723Item::metadata)
328        .and_then(|metadata| metadata.tool.as_ref())
329        .and_then(|tool| tool.uv.as_ref())
330        .map(|uv| Options::simple(uv.globals.clone(), uv.top_level.clone()))
331        .map(FilesystemOptions::from)
332        .combine(filesystem);
333
334    // Resolve the global settings.
335    let globals = GlobalSettings::resolve(
336        &cli.top_level.global_args,
337        filesystem.as_ref(),
338        &environment,
339    );
340
341    // Resolve the cache settings.
342    let cache_settings = CacheSettings::resolve(*cli.top_level.cache_args, filesystem.as_ref());
343
344    // Set the global flags.
345    uv_flags::init(EnvironmentFlags::from(&environment))
346        .map_err(|()| anyhow::anyhow!("Flags are already initialized"))?;
347
348    // Enforce the required version.
349    if let Some(required_version) = globals.required_version.as_ref() {
350        let package_version = uv_pep440::Version::from_str(uv_version::version())?;
351        if !required_version.contains(&package_version) {
352            #[cfg(feature = "self-update")]
353            let hint = {
354                // If the required version range includes a lower bound that's higher than
355                // the current version, suggest `uv self update`.
356                let ranges = release_specifiers_to_ranges(required_version.specifiers().clone());
357
358                if let Some(singleton) = ranges.as_singleton() {
359                    // E.g., `==1.0.0`
360                    format!(
361                        ". Update `uv` by running `{}`.",
362                        format!("uv self update {singleton}").green()
363                    )
364                } else if ranges
365                    .bounding_range()
366                    .iter()
367                    .any(|(lowest, _highest)| match lowest {
368                        Bound::Included(version) => **version > package_version,
369                        Bound::Excluded(version) => **version > package_version,
370                        Bound::Unbounded => false,
371                    })
372                {
373                    // E.g., `>=1.0.0`
374                    format!(". Update `uv` by running `{}`.", "uv self update".cyan())
375                } else {
376                    String::new()
377                }
378            };
379            #[cfg(not(feature = "self-update"))]
380            let hint = "";
381            return Err(anyhow::anyhow!(
382                "Required uv version `{required_version}` does not match the running version `{package_version}`{hint}",
383            ));
384        }
385    }
386
387    // Configure the `tracing` crate, which controls internal logging.
388    #[cfg(feature = "tracing-durations-export")]
389    let (durations_layer, _duration_guard) =
390        logging::setup_durations(environment.tracing_durations_file.as_ref())?;
391    #[cfg(not(feature = "tracing-durations-export"))]
392    let durations_layer = None::<tracing_subscriber::layer::Identity>;
393    logging::setup_logging(
394        match globals.verbose {
395            0 => logging::Level::Off,
396            1 => logging::Level::DebugUv,
397            2 => logging::Level::TraceUv,
398            3.. => logging::Level::TraceAll,
399        },
400        durations_layer,
401        globals.color,
402        environment.log_context.unwrap_or_default(),
403    )?;
404
405    // Configure the `Printer`, which controls user-facing output in the CLI.
406    let printer = if globals.quiet == 1 {
407        Printer::Quiet
408    } else if globals.quiet > 1 {
409        Printer::Silent
410    } else if globals.verbose > 0 {
411        Printer::Verbose
412    } else if globals.no_progress {
413        Printer::NoProgress
414    } else {
415        Printer::Default
416    };
417
418    // Configure the `warn!` macros, which control user-facing warnings in the CLI.
419    if globals.quiet > 0 {
420        uv_warnings::disable();
421    } else {
422        uv_warnings::enable();
423    }
424
425    anstream::ColorChoice::write_global(globals.color.into());
426
427    miette::set_hook(Box::new(|_| {
428        Box::new(
429            miette::MietteHandlerOpts::new()
430                .break_words(false)
431                .word_separator(textwrap::WordSeparator::AsciiSpace)
432                .word_splitter(textwrap::WordSplitter::NoHyphenation)
433                .wrap_lines(
434                    std::env::var(EnvVars::UV_NO_WRAP)
435                        .map(|_| false)
436                        .unwrap_or(true),
437                )
438                .build(),
439        )
440    }))?;
441
442    // Don't initialize the rayon threadpool yet, this is too costly when we're doing a noop sync.
443    uv_configuration::RAYON_PARALLELISM.store(globals.concurrency.installs, Ordering::Relaxed);
444
445    debug!("uv {}", uv_cli::version::uv_self_version());
446
447    // Write out any resolved settings.
448    macro_rules! show_settings {
449        ($arg:expr) => {
450            if globals.show_settings {
451                writeln!(printer.stdout(), "{:#?}", $arg)?;
452                return Ok(ExitStatus::Success);
453            }
454        };
455        ($arg:expr, false) => {
456            if globals.show_settings {
457                writeln!(printer.stdout(), "{:#?}", $arg)?;
458            }
459        };
460    }
461    show_settings!(globals, false);
462    show_settings!(cache_settings, false);
463
464    // Configure the cache.
465    if cache_settings.no_cache {
466        debug!("Disabling the uv cache due to `--no-cache`");
467    }
468    let cache = Cache::from_settings(cache_settings.no_cache, cache_settings.cache_dir)?;
469
470    // Configure the global network settings.
471    let client_builder = BaseClientBuilder::new(
472        globals.network_settings.connectivity,
473        globals.network_settings.native_tls,
474        globals.network_settings.allow_insecure_host.clone(),
475        globals.preview,
476        globals.network_settings.timeout,
477        globals.network_settings.retries,
478    )
479    .http_proxy(globals.network_settings.http_proxy.clone())
480    .https_proxy(globals.network_settings.https_proxy.clone())
481    .no_proxy(globals.network_settings.no_proxy.clone());
482
483    match *cli.command {
484        Commands::Auth(AuthNamespace {
485            command: AuthCommand::Login(args),
486        }) => {
487            // Resolve the settings from the command-line arguments and workspace configuration.
488            let args = settings::AuthLoginSettings::resolve(args);
489            show_settings!(args);
490
491            commands::auth_login(
492                args.service,
493                args.username,
494                args.password,
495                args.token,
496                client_builder,
497                printer,
498                globals.preview,
499            )
500            .await
501        }
502        Commands::Auth(AuthNamespace {
503            command: AuthCommand::Logout(args),
504        }) => {
505            // Resolve the settings from the command-line arguments and workspace configuration.
506            let args = settings::AuthLogoutSettings::resolve(args);
507            show_settings!(args);
508
509            commands::auth_logout(
510                args.service,
511                args.username,
512                client_builder,
513                printer,
514                globals.preview,
515            )
516            .await
517        }
518        Commands::Auth(AuthNamespace {
519            command: AuthCommand::Token(args),
520        }) => {
521            // Resolve the settings from the command-line arguments and workspace configuration.
522            let args = settings::AuthTokenSettings::resolve(args);
523            show_settings!(args);
524
525            commands::auth_token(
526                args.service,
527                args.username,
528                client_builder,
529                printer,
530                globals.preview,
531            )
532            .await
533        }
534        Commands::Auth(AuthNamespace {
535            command: AuthCommand::Dir(args),
536        }) => {
537            commands::auth_dir(args.service.as_ref(), printer)?;
538            Ok(ExitStatus::Success)
539        }
540        Commands::Auth(AuthNamespace {
541            command: AuthCommand::Helper(args),
542        }) => {
543            use uv_cli::AuthHelperProtocol;
544
545            // Validate protocol (currently only Bazel is supported)
546            match args.protocol {
547                AuthHelperProtocol::Bazel => {}
548            }
549
550            match args.command {
551                AuthHelperCommand::Get => {
552                    commands::auth_helper(client_builder, globals.preview, printer).await
553                }
554            }
555        }
556        Commands::Help(args) => commands::help(
557            args.command.unwrap_or_default().as_slice(),
558            printer,
559            args.no_pager,
560        ),
561        Commands::Pip(PipNamespace {
562            command: PipCommand::Compile(args),
563        }) => {
564            args.compat_args.validate()?;
565
566            // Resolve the settings from the command-line arguments and workspace configuration.
567            let args = PipCompileSettings::resolve(args, filesystem, environment);
568            show_settings!(args);
569
570            // Check for conflicts between offline and refresh.
571            globals
572                .network_settings
573                .check_refresh_conflict(&args.refresh);
574
575            // Initialize the cache.
576            let cache = cache.init().await?.with_refresh(
577                args.refresh
578                    .combine(Refresh::from(args.settings.reinstall.clone()))
579                    .combine(Refresh::from(args.settings.upgrade.clone())),
580            );
581
582            let requirements = args
583                .src_file
584                .into_iter()
585                .map(RequirementsSource::from_requirements_file)
586                .collect::<Result<Vec<_>, _>>()?;
587            let constraints = args
588                .constraints
589                .into_iter()
590                .map(RequirementsSource::from_constraints_txt)
591                .collect::<Result<Vec<_>, _>>()?;
592            let overrides = args
593                .overrides
594                .into_iter()
595                .map(RequirementsSource::from_overrides_txt)
596                .collect::<Result<Vec<_>, _>>()?;
597            let excludes = args
598                .excludes
599                .into_iter()
600                .map(RequirementsSource::from_requirements_txt)
601                .collect::<Result<Vec<_>, _>>()?;
602            let build_constraints = args
603                .build_constraints
604                .into_iter()
605                .map(RequirementsSource::from_constraints_txt)
606                .collect::<Result<Vec<_>, _>>()?;
607            let groups = GroupsSpecification {
608                root: project_dir.to_path_buf(),
609                groups: args.settings.groups,
610            };
611
612            commands::pip_compile(
613                &requirements,
614                &constraints,
615                &overrides,
616                &excludes,
617                &build_constraints,
618                args.constraints_from_workspace,
619                args.overrides_from_workspace,
620                args.excludes_from_workspace,
621                args.build_constraints_from_workspace,
622                args.environments,
623                args.settings.extras,
624                groups,
625                args.settings.output_file.as_deref(),
626                args.format,
627                args.settings.resolution,
628                args.settings.prerelease,
629                args.settings.fork_strategy,
630                args.settings.dependency_mode,
631                args.settings.upgrade,
632                args.settings.generate_hashes,
633                args.settings.no_emit_package,
634                args.settings.no_strip_extras,
635                args.settings.no_strip_markers,
636                !args.settings.no_annotate,
637                !args.settings.no_header,
638                args.settings.custom_compile_command,
639                args.settings.emit_index_url,
640                args.settings.emit_find_links,
641                args.settings.emit_build_options,
642                args.settings.emit_marker_expression,
643                args.settings.emit_index_annotation,
644                args.settings.index_locations,
645                args.settings.index_strategy,
646                args.settings.torch_backend,
647                args.settings.dependency_metadata,
648                args.settings.keyring_provider,
649                &client_builder.subcommand(vec!["pip".to_owned(), "compile".to_owned()]),
650                args.settings.config_setting,
651                args.settings.config_settings_package,
652                args.settings.build_isolation.clone(),
653                &args.settings.extra_build_dependencies,
654                &args.settings.extra_build_variables,
655                args.settings.build_options,
656                args.settings.install_mirrors,
657                args.settings.python_version,
658                args.settings.python_platform,
659                globals.python_downloads,
660                args.settings.universal,
661                args.settings.exclude_newer,
662                args.settings.sources,
663                args.settings.annotation_style,
664                args.settings.link_mode,
665                args.settings.python,
666                args.settings.system,
667                globals.python_preference,
668                globals.concurrency,
669                globals.quiet > 0,
670                cache,
671                printer,
672                globals.preview,
673            )
674            .await
675        }
676        Commands::Pip(PipNamespace {
677            command: PipCommand::Sync(args),
678        }) => {
679            args.compat_args.validate()?;
680
681            // Resolve the settings from the command-line arguments and workspace configuration.
682            let args = PipSyncSettings::resolve(args, filesystem, environment);
683            show_settings!(args);
684
685            // Check for conflicts between offline and refresh.
686            globals
687                .network_settings
688                .check_refresh_conflict(&args.refresh);
689
690            // Initialize the cache.
691            let cache = cache.init().await?.with_refresh(
692                args.refresh
693                    .combine(Refresh::from(args.settings.reinstall.clone()))
694                    .combine(Refresh::from(args.settings.upgrade.clone())),
695            );
696
697            let requirements = args
698                .src_file
699                .into_iter()
700                .map(RequirementsSource::from_requirements_file)
701                .collect::<Result<Vec<_>, _>>()?;
702            let constraints = args
703                .constraints
704                .into_iter()
705                .map(RequirementsSource::from_constraints_txt)
706                .collect::<Result<Vec<_>, _>>()?;
707            let build_constraints = args
708                .build_constraints
709                .into_iter()
710                .map(RequirementsSource::from_constraints_txt)
711                .collect::<Result<Vec<_>, _>>()?;
712            let groups = GroupsSpecification {
713                root: project_dir.to_path_buf(),
714                groups: args.settings.groups,
715            };
716
717            commands::pip_sync(
718                &requirements,
719                &constraints,
720                &build_constraints,
721                &args.settings.extras,
722                &groups,
723                args.settings.reinstall,
724                args.settings.link_mode,
725                args.settings.compile_bytecode,
726                args.settings.hash_checking,
727                args.settings.index_locations,
728                args.settings.index_strategy,
729                args.settings.torch_backend,
730                args.settings.dependency_metadata,
731                args.settings.keyring_provider,
732                &client_builder.subcommand(vec!["pip".to_owned(), "sync".to_owned()]),
733                args.settings.allow_empty_requirements,
734                globals.installer_metadata,
735                &args.settings.config_setting,
736                &args.settings.config_settings_package,
737                args.settings.build_isolation.clone(),
738                &args.settings.extra_build_dependencies,
739                &args.settings.extra_build_variables,
740                args.settings.build_options,
741                args.settings.python_version,
742                args.settings.python_platform,
743                globals.python_downloads,
744                args.settings.install_mirrors,
745                args.settings.strict,
746                args.settings.exclude_newer,
747                args.settings.python,
748                args.settings.system,
749                args.settings.break_system_packages,
750                args.settings.target,
751                args.settings.prefix,
752                args.settings.sources,
753                globals.python_preference,
754                globals.concurrency,
755                cache,
756                args.dry_run,
757                printer,
758                globals.preview,
759            )
760            .await
761        }
762        Commands::Pip(PipNamespace {
763            command: PipCommand::Install(args),
764        }) => {
765            args.compat_args.validate()?;
766
767            // Resolve the settings from the command-line arguments and workspace configuration.
768            let mut args = PipInstallSettings::resolve(args, filesystem, environment);
769            show_settings!(args);
770
771            let mut requirements = Vec::with_capacity(
772                args.package.len() + args.editables.len() + args.requirements.len(),
773            );
774            for package in args.package {
775                requirements.push(RequirementsSource::from_package_argument(&package)?);
776            }
777            for package in args.editables {
778                requirements.push(RequirementsSource::from_editable(&package)?);
779            }
780            requirements.extend(
781                args.requirements
782                    .into_iter()
783                    .map(RequirementsSource::from_requirements_file)
784                    .collect::<Result<Vec<_>, _>>()?,
785            );
786            let constraints = args
787                .constraints
788                .into_iter()
789                .map(RequirementsSource::from_constraints_txt)
790                .collect::<Result<Vec<_>, _>>()?;
791            let overrides = args
792                .overrides
793                .into_iter()
794                .map(RequirementsSource::from_overrides_txt)
795                .collect::<Result<Vec<_>, _>>()?;
796            let excludes = args
797                .excludes
798                .into_iter()
799                .map(RequirementsSource::from_requirements_txt)
800                .collect::<Result<Vec<_>, _>>()?;
801            let build_constraints = args
802                .build_constraints
803                .into_iter()
804                .map(RequirementsSource::from_overrides_txt)
805                .collect::<Result<Vec<_>, _>>()?;
806            let groups = GroupsSpecification {
807                root: project_dir.to_path_buf(),
808                groups: args.settings.groups,
809            };
810
811            // Special-case: any source trees specified on the command-line are automatically
812            // reinstalled. This matches user expectations: `uv pip install .` should always
813            // re-build and re-install the package in the current working directory.
814            for requirement in &requirements {
815                let requirement = match requirement {
816                    RequirementsSource::Package(requirement) => requirement,
817                    RequirementsSource::Editable(requirement) => requirement,
818                    _ => continue,
819                };
820                match requirement {
821                    RequirementsTxtRequirement::Named(requirement) => {
822                        if let Some(VersionOrUrl::Url(url)) = requirement.version_or_url.as_ref() {
823                            if let ParsedUrl::Directory(ParsedDirectoryUrl {
824                                install_path, ..
825                            }) = &url.parsed_url
826                            {
827                                debug!(
828                                    "Marking explicit source tree for reinstall: `{}`",
829                                    install_path.display()
830                                );
831                                args.settings.reinstall = args
832                                    .settings
833                                    .reinstall
834                                    .with_package(requirement.name.clone());
835                            }
836                        }
837                    }
838                    RequirementsTxtRequirement::Unnamed(requirement) => {
839                        if let ParsedUrl::Directory(ParsedDirectoryUrl { install_path, .. }) =
840                            &requirement.url.parsed_url
841                        {
842                            debug!(
843                                "Marking explicit source tree for reinstall: `{}`",
844                                install_path.display()
845                            );
846                            args.settings.reinstall =
847                                args.settings.reinstall.with_path(install_path.clone());
848                        }
849                    }
850                }
851            }
852
853            // Check for conflicts between offline and refresh.
854            globals
855                .network_settings
856                .check_refresh_conflict(&args.refresh);
857
858            // Initialize the cache.
859            let cache = cache.init().await?.with_refresh(
860                args.refresh
861                    .combine(Refresh::from(args.settings.reinstall.clone()))
862                    .combine(Refresh::from(args.settings.upgrade.clone())),
863            );
864
865            Box::pin(commands::pip_install(
866                &requirements,
867                &constraints,
868                &overrides,
869                &excludes,
870                &build_constraints,
871                args.constraints_from_workspace,
872                args.overrides_from_workspace,
873                args.excludes_from_workspace,
874                args.build_constraints_from_workspace,
875                &args.settings.extras,
876                &groups,
877                args.settings.resolution,
878                args.settings.prerelease,
879                args.settings.dependency_mode,
880                args.settings.upgrade,
881                args.settings.index_locations,
882                args.settings.index_strategy,
883                args.settings.torch_backend,
884                args.settings.dependency_metadata,
885                args.settings.keyring_provider,
886                &client_builder.subcommand(vec!["pip".to_owned(), "install".to_owned()]),
887                args.settings.reinstall,
888                args.settings.link_mode,
889                args.settings.compile_bytecode,
890                args.settings.hash_checking,
891                globals.installer_metadata,
892                &args.settings.config_setting,
893                &args.settings.config_settings_package,
894                args.settings.build_isolation.clone(),
895                &args.settings.extra_build_dependencies,
896                &args.settings.extra_build_variables,
897                args.settings.build_options,
898                args.modifications,
899                args.settings.python_version,
900                args.settings.python_platform,
901                globals.python_downloads,
902                args.settings.install_mirrors,
903                args.settings.strict,
904                args.settings.exclude_newer,
905                args.settings.sources,
906                args.settings.python,
907                args.settings.system,
908                args.settings.break_system_packages,
909                args.settings.target,
910                args.settings.prefix,
911                globals.python_preference,
912                globals.concurrency,
913                cache,
914                args.dry_run,
915                printer,
916                globals.preview,
917            ))
918            .await
919        }
920        Commands::Pip(PipNamespace {
921            command: PipCommand::Uninstall(args),
922        }) => {
923            // Resolve the settings from the command-line arguments and workspace configuration.
924            let args = PipUninstallSettings::resolve(args, filesystem, environment);
925            show_settings!(args);
926
927            // Initialize the cache.
928            let cache = cache.init().await?;
929
930            let mut sources = Vec::with_capacity(args.package.len() + args.requirements.len());
931            for package in args.package {
932                sources.push(RequirementsSource::from_package_argument(&package)?);
933            }
934            sources.extend(
935                args.requirements
936                    .into_iter()
937                    .map(RequirementsSource::from_requirements_file)
938                    .collect::<Result<Vec<_>, _>>()?,
939            );
940            commands::pip_uninstall(
941                &sources,
942                args.settings.python,
943                args.settings.system,
944                args.settings.break_system_packages,
945                args.settings.target,
946                args.settings.prefix,
947                cache,
948                args.settings.keyring_provider,
949                &client_builder.subcommand(vec!["pip".to_owned(), "uninstall".to_owned()]),
950                args.dry_run,
951                printer,
952                globals.preview,
953            )
954            .await
955        }
956        Commands::Pip(PipNamespace {
957            command: PipCommand::Freeze(args),
958        }) => {
959            // Resolve the settings from the command-line arguments and workspace configuration.
960            let args = PipFreezeSettings::resolve(args, filesystem, environment);
961            show_settings!(args);
962
963            // Initialize the cache.
964            let cache = cache.init().await?;
965
966            commands::pip_freeze(
967                args.exclude_editable,
968                args.settings.strict,
969                args.settings.python.as_deref(),
970                args.settings.system,
971                args.settings.target,
972                args.settings.prefix,
973                args.paths,
974                &cache,
975                printer,
976                globals.preview,
977            )
978        }
979        Commands::Pip(PipNamespace {
980            command: PipCommand::List(args),
981        }) => {
982            args.compat_args.validate()?;
983
984            // Resolve the settings from the command-line arguments and workspace configuration.
985            let args = PipListSettings::resolve(args, filesystem, environment);
986            show_settings!(args);
987
988            // Initialize the cache.
989            let cache = cache.init().await?;
990
991            commands::pip_list(
992                args.editable,
993                &args.exclude,
994                &args.format,
995                args.outdated,
996                args.settings.prerelease,
997                args.settings.index_locations,
998                args.settings.index_strategy,
999                args.settings.keyring_provider,
1000                &client_builder.subcommand(vec!["pip".to_owned(), "list".to_owned()]),
1001                globals.concurrency,
1002                args.settings.strict,
1003                args.settings.exclude_newer,
1004                args.settings.python.as_deref(),
1005                args.settings.system,
1006                args.settings.target,
1007                args.settings.prefix,
1008                &cache,
1009                printer,
1010                globals.preview,
1011            )
1012            .await
1013        }
1014        Commands::Pip(PipNamespace {
1015            command: PipCommand::Show(args),
1016        }) => {
1017            // Resolve the settings from the command-line arguments and workspace configuration.
1018            let args = PipShowSettings::resolve(args, filesystem, environment);
1019            show_settings!(args);
1020
1021            // Initialize the cache.
1022            let cache = cache.init().await?;
1023
1024            commands::pip_show(
1025                args.package,
1026                args.settings.strict,
1027                args.settings.python.as_deref(),
1028                args.settings.system,
1029                args.settings.target,
1030                args.settings.prefix,
1031                args.files,
1032                &cache,
1033                printer,
1034                globals.preview,
1035            )
1036        }
1037        Commands::Pip(PipNamespace {
1038            command: PipCommand::Tree(args),
1039        }) => {
1040            // Resolve the settings from the command-line arguments and workspace configuration.
1041            let args = PipTreeSettings::resolve(args, filesystem, environment);
1042
1043            // Initialize the cache.
1044            let cache = cache.init().await?;
1045
1046            commands::pip_tree(
1047                args.show_version_specifiers,
1048                args.depth,
1049                &args.prune,
1050                &args.package,
1051                args.no_dedupe,
1052                args.invert,
1053                args.outdated,
1054                args.settings.prerelease,
1055                args.settings.index_locations,
1056                args.settings.index_strategy,
1057                args.settings.keyring_provider,
1058                client_builder.subcommand(vec!["pip".to_owned(), "tree".to_owned()]),
1059                globals.concurrency,
1060                args.settings.strict,
1061                args.settings.exclude_newer,
1062                args.settings.python.as_deref(),
1063                args.settings.system,
1064                &cache,
1065                printer,
1066                globals.preview,
1067            )
1068            .await
1069        }
1070        Commands::Pip(PipNamespace {
1071            command: PipCommand::Check(args),
1072        }) => {
1073            // Resolve the settings from the command-line arguments and workspace configuration.
1074            let args = PipCheckSettings::resolve(args, filesystem, environment);
1075            show_settings!(args);
1076
1077            // Initialize the cache.
1078            let cache = cache.init().await?;
1079
1080            commands::pip_check(
1081                args.settings.python.as_deref(),
1082                args.settings.system,
1083                args.settings.python_version.as_ref(),
1084                args.settings.python_platform.as_ref(),
1085                &cache,
1086                printer,
1087                globals.preview,
1088            )
1089        }
1090        Commands::Pip(PipNamespace {
1091            command: PipCommand::Debug(_),
1092        }) => Err(anyhow!(
1093            "pip's `debug` is unsupported (consider using `uvx pip debug` instead)"
1094        )),
1095        Commands::Cache(CacheNamespace {
1096            command: CacheCommand::Clean(args),
1097        })
1098        | Commands::Clean(args) => {
1099            show_settings!(args);
1100            commands::cache_clean(&args.package, args.force, cache, printer).await
1101        }
1102        Commands::Cache(CacheNamespace {
1103            command: CacheCommand::Prune(args),
1104        }) => {
1105            show_settings!(args);
1106            commands::cache_prune(args.ci, args.force, cache, printer).await
1107        }
1108        Commands::Cache(CacheNamespace {
1109            command: CacheCommand::Dir,
1110        }) => commands::cache_dir(&cache, printer),
1111        Commands::Cache(CacheNamespace {
1112            command: CacheCommand::Size(args),
1113        }) => commands::cache_size(&cache, args.human, printer, globals.preview),
1114        Commands::Build(args) => {
1115            // Resolve the settings from the command-line arguments and workspace configuration.
1116            let args = settings::BuildSettings::resolve(args, filesystem, environment);
1117            show_settings!(args);
1118
1119            // Check for conflicts between offline and refresh.
1120            globals
1121                .network_settings
1122                .check_refresh_conflict(&args.refresh);
1123
1124            // Initialize the cache.
1125            let cache = cache.init().await?.with_refresh(
1126                args.refresh
1127                    .combine(Refresh::from(args.settings.upgrade.clone())),
1128            );
1129
1130            // Resolve the build constraints.
1131            let build_constraints = args
1132                .build_constraints
1133                .into_iter()
1134                .map(RequirementsSource::from_constraints_txt)
1135                .collect::<Result<Vec<_>, _>>()?;
1136
1137            commands::build_frontend(
1138                &project_dir,
1139                args.src,
1140                args.package,
1141                args.all_packages,
1142                args.out_dir,
1143                args.sdist,
1144                args.wheel,
1145                args.list,
1146                args.build_logs,
1147                args.gitignore,
1148                args.force_pep517,
1149                args.clear,
1150                build_constraints,
1151                args.hash_checking,
1152                args.python,
1153                args.install_mirrors,
1154                &args.settings,
1155                &client_builder.subcommand(vec!["build".to_owned()]),
1156                cli.top_level.no_config,
1157                globals.python_preference,
1158                globals.python_downloads,
1159                globals.concurrency,
1160                &cache,
1161                printer,
1162                globals.preview,
1163            )
1164            .await
1165        }
1166        Commands::Venv(args) => {
1167            args.compat_args.validate()?;
1168
1169            if args.no_system {
1170                warn_user_once!(
1171                    "The `--no-system` flag has no effect, a system Python interpreter is always used in `uv venv`"
1172                );
1173            }
1174
1175            if args.system {
1176                warn_user_once!(
1177                    "The `--system` flag has no effect, a system Python interpreter is always used in `uv venv`"
1178                );
1179            }
1180
1181            // Resolve the settings from the command-line arguments and workspace configuration.
1182            let args = settings::VenvSettings::resolve(args, filesystem, environment);
1183            show_settings!(args);
1184
1185            // Check for conflicts between offline and refresh.
1186            globals
1187                .network_settings
1188                .check_refresh_conflict(&args.refresh);
1189
1190            // Initialize the cache.
1191            let cache = cache.init().await?.with_refresh(
1192                args.refresh
1193                    .combine(Refresh::from(args.settings.reinstall.clone()))
1194                    .combine(Refresh::from(args.settings.upgrade.clone())),
1195            );
1196
1197            // Since we use ".venv" as the default name, we use "." as the default prompt.
1198            let prompt = args.prompt.or_else(|| {
1199                if args.path.is_none() {
1200                    Some(".".to_string())
1201                } else {
1202                    None
1203                }
1204            });
1205
1206            let python_request: Option<PythonRequest> =
1207                args.settings.python.as_deref().map(PythonRequest::parse);
1208
1209            let on_existing = uv_virtualenv::OnExisting::from_args(
1210                args.allow_existing,
1211                args.clear,
1212                args.no_clear,
1213            );
1214
1215            commands::venv(
1216                &project_dir,
1217                args.path,
1218                python_request,
1219                args.settings.install_mirrors,
1220                globals.python_preference,
1221                globals.python_downloads,
1222                args.settings.link_mode,
1223                &args.settings.index_locations,
1224                args.settings.index_strategy,
1225                args.settings.dependency_metadata,
1226                args.settings.keyring_provider,
1227                &client_builder.subcommand(vec!["venv".to_owned()]),
1228                uv_virtualenv::Prompt::from_args(prompt),
1229                args.system_site_packages,
1230                args.seed,
1231                on_existing,
1232                args.settings.exclude_newer,
1233                globals.concurrency,
1234                cli.top_level.no_config,
1235                args.no_project,
1236                &cache,
1237                printer,
1238                args.relocatable,
1239                globals.preview,
1240            )
1241            .await
1242        }
1243        Commands::Project(project) => {
1244            Box::pin(run_project(
1245                project,
1246                &project_dir,
1247                run_command,
1248                script,
1249                globals,
1250                cli.top_level.no_config,
1251                cli.top_level.global_args.project.is_some(),
1252                client_builder,
1253                filesystem,
1254                cache,
1255                printer,
1256            ))
1257            .await
1258        }
1259        #[cfg(feature = "self-update")]
1260        Commands::Self_(SelfNamespace {
1261            command:
1262                SelfCommand::Update(SelfUpdateArgs {
1263                    target_version,
1264                    token,
1265                    dry_run,
1266                }),
1267        }) => {
1268            commands::self_update(
1269                target_version,
1270                token,
1271                dry_run,
1272                printer,
1273                client_builder.subcommand(vec!["self".to_owned(), "update".to_owned()]),
1274            )
1275            .await
1276        }
1277        Commands::Self_(SelfNamespace {
1278            command:
1279                SelfCommand::Version {
1280                    short,
1281                    output_format,
1282                },
1283        }) => {
1284            commands::self_version(short, output_format, printer)?;
1285            Ok(ExitStatus::Success)
1286        }
1287        #[cfg(not(feature = "self-update"))]
1288        Commands::Self_(_) => {
1289            const BASE_MESSAGE: &str =
1290                "uv was installed through an external package manager and cannot update itself.";
1291
1292            let message = match InstallSource::detect() {
1293                Some(source) => format!(
1294                    "{base}\n\n{hint}{colon} You installed uv using {}. To update uv, run `{}`",
1295                    source.description(),
1296                    source.update_instructions().green(),
1297                    hint = "hint".bold().cyan(),
1298                    colon = ":".bold(),
1299                    base = BASE_MESSAGE
1300                ),
1301                None => format!("{BASE_MESSAGE} Please use your package manager to update uv."),
1302            };
1303
1304            anyhow::bail!(message);
1305        }
1306        Commands::GenerateShellCompletion(args) => {
1307            args.shell.generate(&mut Cli::command(), &mut stdout());
1308            Ok(ExitStatus::Success)
1309        }
1310        Commands::Tool(ToolNamespace {
1311            command: run_variant @ (ToolCommand::Uvx(_) | ToolCommand::Run(_)),
1312        }) => {
1313            let (args, invocation_source) = match run_variant {
1314                ToolCommand::Uvx(args) => (args.tool_run, ToolRunCommand::Uvx),
1315                ToolCommand::Run(args) => (args, ToolRunCommand::ToolRun),
1316                // OK guarded by the outer match statement
1317                _ => unreachable!(),
1318            };
1319
1320            if let Some(shell) = args.generate_shell_completion {
1321                // uvx: combine `uv tool uvx` with the top-level arguments
1322                let mut uvx = Cli::command()
1323                    .find_subcommand("tool")
1324                    .unwrap()
1325                    .find_subcommand("uvx")
1326                    .unwrap()
1327                    .clone()
1328                    // Avoid duplicating the `--help` and `--version` flags from the top-level
1329                    // arguments.
1330                    .disable_help_flag(true)
1331                    .disable_version_flag(true);
1332
1333                // Copy the top-level arguments into the `uvx` command, as in `Args::augment_args`,
1334                // but expanded to skip collisions.
1335                for arg in TopLevelArgs::command().get_arguments() {
1336                    if arg.get_id() != "isolated" && arg.get_id() != "version" {
1337                        uvx = uvx.arg(arg);
1338                    }
1339                }
1340                shell.generate(&mut uvx, &mut stdout());
1341                return Ok(ExitStatus::Success);
1342            }
1343
1344            // Resolve the settings from the command-line arguments and workspace configuration.
1345            let args = settings::ToolRunSettings::resolve(
1346                args,
1347                filesystem,
1348                invocation_source,
1349                environment,
1350            );
1351            show_settings!(args);
1352
1353            // Check for conflicts between offline and refresh.
1354            globals
1355                .network_settings
1356                .check_refresh_conflict(&args.refresh);
1357
1358            // Initialize the cache.
1359            let cache = cache.init().await?.with_refresh(
1360                args.refresh
1361                    .combine(Refresh::from(args.settings.reinstall.clone()))
1362                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
1363            );
1364
1365            let requirements = {
1366                let mut requirements = Vec::with_capacity(
1367                    args.with.len() + args.with_editable.len() + args.with_requirements.len(),
1368                );
1369                for package in args.with {
1370                    requirements.push(RequirementsSource::from_with_package_argument(&package)?);
1371                }
1372                for package in args.with_editable {
1373                    requirements.push(RequirementsSource::from_editable(&package)?);
1374                }
1375                requirements.extend(
1376                    args.with_requirements
1377                        .into_iter()
1378                        .map(RequirementsSource::from_requirements_file)
1379                        .collect::<Result<Vec<_>, _>>()?,
1380                );
1381                requirements
1382            };
1383            let constraints = args
1384                .constraints
1385                .into_iter()
1386                .map(RequirementsSource::from_constraints_txt)
1387                .collect::<Result<Vec<_>, _>>()?;
1388            let overrides = args
1389                .overrides
1390                .into_iter()
1391                .map(RequirementsSource::from_overrides_txt)
1392                .collect::<Result<Vec<_>, _>>()?;
1393
1394            let build_constraints = args
1395                .build_constraints
1396                .into_iter()
1397                .map(RequirementsSource::from_constraints_txt)
1398                .collect::<Result<Vec<_>, _>>()?;
1399
1400            let client_builder = match invocation_source {
1401                ToolRunCommand::Uvx => client_builder.subcommand(vec!["uvx".to_owned()]),
1402                ToolRunCommand::ToolRun => {
1403                    client_builder.subcommand(vec!["tool".to_owned(), "run".to_owned()])
1404                }
1405            };
1406
1407            Box::pin(commands::tool_run(
1408                args.command,
1409                args.from,
1410                &requirements,
1411                &constraints,
1412                &overrides,
1413                &build_constraints,
1414                args.show_resolution || globals.verbose > 0,
1415                args.lfs,
1416                args.python,
1417                args.python_platform,
1418                args.install_mirrors,
1419                args.options,
1420                args.settings,
1421                client_builder,
1422                invocation_source,
1423                args.isolated,
1424                globals.python_preference,
1425                globals.python_downloads,
1426                globals.installer_metadata,
1427                globals.concurrency,
1428                cache,
1429                printer,
1430                args.env_file,
1431                args.no_env_file,
1432                globals.preview,
1433            ))
1434            .await
1435        }
1436        Commands::Tool(ToolNamespace {
1437            command: ToolCommand::Install(args),
1438        }) => {
1439            // Resolve the settings from the command-line arguments and workspace configuration.
1440            let args = settings::ToolInstallSettings::resolve(args, filesystem, environment);
1441            show_settings!(args);
1442
1443            // Check for conflicts between offline and refresh.
1444            globals
1445                .network_settings
1446                .check_refresh_conflict(&args.refresh);
1447
1448            // Initialize the cache.
1449            let cache = cache.init().await?.with_refresh(
1450                args.refresh
1451                    .combine(Refresh::from(args.settings.reinstall.clone()))
1452                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
1453            );
1454
1455            let mut entrypoints = Vec::with_capacity(args.with_executables_from.len());
1456            let mut requirements = Vec::with_capacity(
1457                args.with.len()
1458                    + args.with_editable.len()
1459                    + args.with_requirements.len()
1460                    + args.with_executables_from.len(),
1461            );
1462            for pkg in args.with {
1463                requirements.push(RequirementsSource::from_with_package_argument(&pkg)?);
1464            }
1465            for pkg in args.with_editable {
1466                requirements.push(RequirementsSource::from_editable(&pkg)?);
1467            }
1468            for path in args.with_requirements {
1469                requirements.push(RequirementsSource::from_requirements_file(path)?);
1470            }
1471            for pkg in &args.with_executables_from {
1472                let source = RequirementsSource::from_with_package_argument(pkg)?;
1473                let RequirementsSource::Package(RequirementsTxtRequirement::Named(requirement)) =
1474                    &source
1475                else {
1476                    bail!(
1477                        "Expected a named package for `--with-executables-from`, but got: {}",
1478                        source.to_string().cyan()
1479                    )
1480                };
1481                entrypoints.push(requirement.name.clone());
1482                requirements.push(source);
1483            }
1484
1485            let constraints = args
1486                .constraints
1487                .into_iter()
1488                .map(RequirementsSource::from_constraints_txt)
1489                .collect::<Result<Vec<_>, _>>()?;
1490            let overrides = args
1491                .overrides
1492                .into_iter()
1493                .map(RequirementsSource::from_overrides_txt)
1494                .collect::<Result<Vec<_>, _>>()?;
1495            let excludes = args
1496                .excludes
1497                .into_iter()
1498                .map(RequirementsSource::from_requirements_txt)
1499                .collect::<Result<Vec<_>, _>>()?;
1500            let build_constraints = args
1501                .build_constraints
1502                .into_iter()
1503                .map(RequirementsSource::from_constraints_txt)
1504                .collect::<Result<Vec<_>, _>>()?;
1505
1506            Box::pin(commands::tool_install(
1507                args.package,
1508                args.editable,
1509                args.from,
1510                &requirements,
1511                &constraints,
1512                &overrides,
1513                &excludes,
1514                &build_constraints,
1515                &entrypoints,
1516                args.lfs,
1517                args.python,
1518                args.python_platform,
1519                args.install_mirrors,
1520                args.force,
1521                args.options,
1522                args.settings,
1523                client_builder.subcommand(vec!["tool".to_owned(), "install".to_owned()]),
1524                globals.python_preference,
1525                globals.python_downloads,
1526                globals.installer_metadata,
1527                globals.concurrency,
1528                cache,
1529                printer,
1530                globals.preview,
1531            ))
1532            .await
1533        }
1534        Commands::Tool(ToolNamespace {
1535            command: ToolCommand::List(args),
1536        }) => {
1537            // Resolve the settings from the command-line arguments and workspace configuration.
1538            let args = settings::ToolListSettings::resolve(args, filesystem);
1539            show_settings!(args);
1540
1541            // Initialize the cache.
1542            let cache = cache.init().await?;
1543
1544            commands::tool_list(
1545                args.show_paths,
1546                args.show_version_specifiers,
1547                args.show_with,
1548                args.show_extras,
1549                args.show_python,
1550                &cache,
1551                printer,
1552            )
1553            .await
1554        }
1555        Commands::Tool(ToolNamespace {
1556            command: ToolCommand::Upgrade(args),
1557        }) => {
1558            // Resolve the settings from the command-line arguments and workspace configuration.
1559            let args = settings::ToolUpgradeSettings::resolve(args, filesystem, &environment);
1560            show_settings!(args);
1561
1562            // Initialize the cache.
1563            let cache = cache
1564                .init()
1565                .await?
1566                .with_refresh(Refresh::All(Timestamp::now()));
1567
1568            Box::pin(commands::tool_upgrade(
1569                args.names,
1570                args.python,
1571                args.python_platform,
1572                args.install_mirrors,
1573                args.args,
1574                args.filesystem,
1575                client_builder.subcommand(vec!["tool".to_owned(), "upgrade".to_owned()]),
1576                globals.python_preference,
1577                globals.python_downloads,
1578                globals.installer_metadata,
1579                globals.concurrency,
1580                &cache,
1581                printer,
1582                globals.preview,
1583            ))
1584            .await
1585        }
1586        Commands::Tool(ToolNamespace {
1587            command: ToolCommand::Uninstall(args),
1588        }) => {
1589            // Resolve the settings from the command-line arguments and workspace configuration.
1590            let args = settings::ToolUninstallSettings::resolve(args, filesystem);
1591            show_settings!(args);
1592
1593            commands::tool_uninstall(args.name, printer).await
1594        }
1595        Commands::Tool(ToolNamespace {
1596            command: ToolCommand::UpdateShell,
1597        }) => {
1598            commands::tool_update_shell(printer).await?;
1599            Ok(ExitStatus::Success)
1600        }
1601        Commands::Tool(ToolNamespace {
1602            command: ToolCommand::Dir(args),
1603        }) => {
1604            // Resolve the settings from the command-line arguments and workspace configuration.
1605            let args = settings::ToolDirSettings::resolve(args, filesystem);
1606            show_settings!(args);
1607
1608            commands::tool_dir(args.bin, globals.preview, printer)?;
1609            Ok(ExitStatus::Success)
1610        }
1611        Commands::Python(PythonNamespace {
1612            command: PythonCommand::List(args),
1613        }) => {
1614            // Resolve the settings from the command-line arguments and workspace configuration.
1615            let args = settings::PythonListSettings::resolve(args, filesystem, environment);
1616            show_settings!(args);
1617
1618            // Initialize the cache.
1619            let cache = cache.init().await?;
1620
1621            commands::python_list(
1622                args.request,
1623                args.kinds,
1624                args.all_versions,
1625                args.all_platforms,
1626                args.all_arches,
1627                args.show_urls,
1628                args.output_format,
1629                args.python_downloads_json_url,
1630                args.python_install_mirror,
1631                args.pypy_install_mirror,
1632                globals.python_preference,
1633                globals.python_downloads,
1634                &client_builder.subcommand(vec!["python".to_owned(), "list".to_owned()]),
1635                &cache,
1636                printer,
1637                globals.preview,
1638            )
1639            .await
1640        }
1641        Commands::Python(PythonNamespace {
1642            command: PythonCommand::Install(args),
1643        }) => {
1644            // Resolve the settings from the command-line arguments and workspace configuration.
1645            let args = settings::PythonInstallSettings::resolve(args, filesystem, environment);
1646            show_settings!(args);
1647
1648            // Initialize the cache.
1649            let cache = cache.init().await?;
1650
1651            commands::python_install(
1652                &project_dir,
1653                args.install_dir,
1654                args.targets,
1655                args.reinstall,
1656                args.upgrade,
1657                args.bin,
1658                args.registry,
1659                args.force,
1660                args.python_install_mirror,
1661                args.pypy_install_mirror,
1662                args.python_downloads_json_url,
1663                client_builder.subcommand(vec!["python".to_owned(), "install".to_owned()]),
1664                args.default,
1665                globals.python_downloads,
1666                cli.top_level.no_config,
1667                args.compile_bytecode,
1668                &globals.concurrency,
1669                &cache,
1670                globals.preview,
1671                printer,
1672            )
1673            .await
1674        }
1675        Commands::Python(PythonNamespace {
1676            command: PythonCommand::Upgrade(args),
1677        }) => {
1678            // Resolve the settings from the command-line arguments and workspace configuration.
1679            let args = settings::PythonUpgradeSettings::resolve(args, filesystem, environment);
1680            show_settings!(args);
1681            let upgrade = commands::PythonUpgrade::Enabled(commands::PythonUpgradeSource::Upgrade);
1682
1683            // Initialize the cache.
1684            let cache = cache.init().await?;
1685
1686            commands::python_install(
1687                &project_dir,
1688                args.install_dir,
1689                args.targets,
1690                args.reinstall,
1691                upgrade,
1692                args.bin,
1693                args.registry,
1694                args.force,
1695                args.python_install_mirror,
1696                args.pypy_install_mirror,
1697                args.python_downloads_json_url,
1698                client_builder.subcommand(vec!["python".to_owned(), "upgrade".to_owned()]),
1699                args.default,
1700                globals.python_downloads,
1701                cli.top_level.no_config,
1702                args.compile_bytecode,
1703                &globals.concurrency,
1704                &cache,
1705                globals.preview,
1706                printer,
1707            )
1708            .await
1709        }
1710        Commands::Python(PythonNamespace {
1711            command: PythonCommand::Uninstall(args),
1712        }) => {
1713            // Resolve the settings from the command-line arguments and workspace configuration.
1714            let args = settings::PythonUninstallSettings::resolve(args, filesystem);
1715            show_settings!(args);
1716
1717            commands::python_uninstall(
1718                args.install_dir,
1719                args.targets,
1720                args.all,
1721                printer,
1722                globals.preview,
1723            )
1724            .await
1725        }
1726        Commands::Python(PythonNamespace {
1727            command: PythonCommand::Find(args),
1728        }) => {
1729            // Resolve the settings from the command-line arguments and workspace configuration.
1730            let args = settings::PythonFindSettings::resolve(args, filesystem, environment);
1731
1732            // Initialize the cache.
1733            let cache = cache.init().await?;
1734
1735            if let Some(Pep723Item::Script(script)) = script {
1736                commands::python_find_script(
1737                    (&script).into(),
1738                    args.show_version,
1739                    // TODO(zsol): is this the right thing to do here?
1740                    &client_builder.subcommand(vec!["python".to_owned(), "find".to_owned()]),
1741                    globals.python_preference,
1742                    globals.python_downloads,
1743                    cli.top_level.no_config,
1744                    &cache,
1745                    printer,
1746                    globals.preview,
1747                )
1748                .await
1749            } else {
1750                commands::python_find(
1751                    &project_dir,
1752                    args.request,
1753                    args.show_version,
1754                    args.no_project,
1755                    cli.top_level.no_config,
1756                    args.system,
1757                    globals.python_preference,
1758                    args.python_downloads_json_url.as_deref(),
1759                    &client_builder.subcommand(vec!["python".to_owned(), "find".to_owned()]),
1760                    &cache,
1761                    printer,
1762                    globals.preview,
1763                )
1764                .await
1765            }
1766        }
1767        Commands::Python(PythonNamespace {
1768            command: PythonCommand::Pin(args),
1769        }) => {
1770            // Resolve the settings from the command-line arguments and workspace configuration.
1771            let args = settings::PythonPinSettings::resolve(args, filesystem, environment);
1772
1773            // Initialize the cache.
1774            let cache = cache.init().await?;
1775
1776            commands::python_pin(
1777                &project_dir,
1778                args.request,
1779                args.resolved,
1780                globals.python_preference,
1781                globals.python_downloads,
1782                args.no_project,
1783                args.global,
1784                args.rm,
1785                args.install_mirrors,
1786                client_builder.subcommand(vec!["python".to_owned(), "pin".to_owned()]),
1787                &cache,
1788                printer,
1789                globals.preview,
1790            )
1791            .await
1792        }
1793        Commands::Python(PythonNamespace {
1794            command: PythonCommand::Dir(args),
1795        }) => {
1796            // Resolve the settings from the command-line arguments and workspace configuration.
1797            let args = settings::PythonDirSettings::resolve(args, filesystem);
1798            show_settings!(args);
1799
1800            commands::python_dir(args.bin, printer)?;
1801            Ok(ExitStatus::Success)
1802        }
1803        Commands::Python(PythonNamespace {
1804            command: PythonCommand::UpdateShell,
1805        }) => {
1806            commands::python_update_shell(printer).await?;
1807            Ok(ExitStatus::Success)
1808        }
1809        Commands::Publish(args) => {
1810            show_settings!(args);
1811
1812            if args.skip_existing {
1813                bail!(
1814                    "`uv publish` does not support `--skip-existing` because there is not a \
1815                    reliable way to identify when an upload fails due to an existing \
1816                    distribution. Instead, use `--check-url` to provide the URL to the simple \
1817                    API for your index. uv will check the index for existing distributions before \
1818                    attempting uploads."
1819                );
1820            }
1821
1822            // Resolve the settings from the command-line arguments and workspace configuration.
1823            let PublishSettings {
1824                files,
1825                username,
1826                password,
1827                dry_run,
1828                no_attestations,
1829                direct,
1830                publish_url,
1831                trusted_publishing,
1832                keyring_provider,
1833                check_url,
1834                index,
1835                index_locations,
1836            } = PublishSettings::resolve(args, filesystem);
1837
1838            commands::publish(
1839                files,
1840                publish_url,
1841                trusted_publishing,
1842                keyring_provider,
1843                &environment,
1844                &client_builder.subcommand(vec!["publish".to_owned()]),
1845                username,
1846                password,
1847                check_url,
1848                index,
1849                index_locations,
1850                dry_run,
1851                no_attestations,
1852                direct,
1853                globals.preview,
1854                &cache,
1855                printer,
1856            )
1857            .await
1858        }
1859        Commands::Workspace(WorkspaceNamespace { command }) => match command {
1860            WorkspaceCommand::Metadata(_args) => {
1861                commands::metadata(&project_dir, globals.preview, printer).await
1862            }
1863            WorkspaceCommand::Dir(args) => {
1864                commands::dir(args.package, &project_dir, globals.preview, printer).await
1865            }
1866            WorkspaceCommand::List(args) => {
1867                commands::list(&project_dir, args.paths, globals.preview, printer).await
1868            }
1869        },
1870        Commands::BuildBackend { command } => spawn_blocking(move || match command {
1871            BuildBackendCommand::BuildSdist { sdist_directory } => {
1872                commands::build_backend::build_sdist(&sdist_directory)
1873            }
1874            BuildBackendCommand::BuildWheel {
1875                wheel_directory,
1876                metadata_directory,
1877            } => commands::build_backend::build_wheel(
1878                &wheel_directory,
1879                metadata_directory.as_deref(),
1880            ),
1881            BuildBackendCommand::BuildEditable {
1882                wheel_directory,
1883                metadata_directory,
1884            } => commands::build_backend::build_editable(
1885                &wheel_directory,
1886                metadata_directory.as_deref(),
1887            ),
1888            BuildBackendCommand::GetRequiresForBuildSdist => {
1889                commands::build_backend::get_requires_for_build_sdist()
1890            }
1891            BuildBackendCommand::GetRequiresForBuildWheel => {
1892                commands::build_backend::get_requires_for_build_wheel()
1893            }
1894            BuildBackendCommand::PrepareMetadataForBuildWheel { wheel_directory } => {
1895                commands::build_backend::prepare_metadata_for_build_wheel(&wheel_directory)
1896            }
1897            BuildBackendCommand::GetRequiresForBuildEditable => {
1898                commands::build_backend::get_requires_for_build_editable()
1899            }
1900            BuildBackendCommand::PrepareMetadataForBuildEditable { wheel_directory } => {
1901                commands::build_backend::prepare_metadata_for_build_editable(&wheel_directory)
1902            }
1903        })
1904        .await
1905        .expect("tokio threadpool exited unexpectedly"),
1906    }
1907}
1908
1909/// Run a [`ProjectCommand`].
1910async fn run_project(
1911    project_command: Box<ProjectCommand>,
1912    project_dir: &Path,
1913    command: Option<RunCommand>,
1914    script: Option<Pep723Item>,
1915    globals: GlobalSettings,
1916    // TODO(zanieb): Determine a better story for passing `no_config` in here
1917    no_config: bool,
1918    explicit_project: bool,
1919    client_builder: BaseClientBuilder<'_>,
1920    filesystem: Option<FilesystemOptions>,
1921    cache: Cache,
1922    printer: Printer,
1923) -> Result<ExitStatus> {
1924    // Write out any resolved settings.
1925    macro_rules! show_settings {
1926        ($arg:expr) => {
1927            if globals.show_settings {
1928                writeln!(printer.stdout(), "{:#?}", $arg)?;
1929                return Ok(ExitStatus::Success);
1930            }
1931        };
1932    }
1933
1934    // Load environment variables not handled by Clap
1935    let environment = EnvironmentOptions::new()?;
1936
1937    match *project_command {
1938        ProjectCommand::Init(args) => {
1939            // Resolve the settings from the command-line arguments and workspace configuration.
1940            let args = settings::InitSettings::resolve(args, filesystem, environment);
1941            show_settings!(args);
1942
1943            // The `--project` arg is being deprecated for `init` with a warning now and an error in preview.
1944            if explicit_project {
1945                if globals
1946                    .preview
1947                    .is_enabled(PreviewFeatures::INIT_PROJECT_FLAG)
1948                {
1949                    bail!(
1950                        "The `--project` option cannot be used in `uv init`. {}",
1951                        if args.path.is_some() {
1952                            "Use `--directory` instead."
1953                        } else {
1954                            "Use `--directory` or a positional path instead."
1955                        }
1956                    )
1957                }
1958
1959                warn_user!(
1960                    "Use of the `--project` option in `uv init` is deprecated and will be removed in a future release. {}",
1961                    if args.path.is_some() {
1962                        "Since a positional path was provided, the `--project` option has no effect. Consider using `--directory` instead."
1963                    } else {
1964                        "Consider using `uv init <PATH>` instead."
1965                    }
1966                );
1967            }
1968
1969            // Initialize the cache.
1970            let cache = cache.init().await?;
1971
1972            commands::init(
1973                project_dir,
1974                args.path,
1975                args.name,
1976                args.package,
1977                args.kind,
1978                args.bare,
1979                args.description,
1980                args.no_description,
1981                args.vcs,
1982                args.build_backend,
1983                args.no_readme,
1984                args.author_from,
1985                args.pin_python,
1986                args.python,
1987                args.install_mirrors,
1988                args.no_workspace,
1989                &client_builder.subcommand(vec!["init".to_owned()]),
1990                globals.python_preference,
1991                globals.python_downloads,
1992                no_config,
1993                &cache,
1994                printer,
1995                globals.preview,
1996            )
1997            .await
1998        }
1999        ProjectCommand::Run(args) => {
2000            // Resolve the settings from the command-line arguments and workspace configuration.
2001            let args = settings::RunSettings::resolve(args, filesystem, environment);
2002            show_settings!(args);
2003
2004            // Check for conflicts between offline and refresh.
2005            globals
2006                .network_settings
2007                .check_refresh_conflict(&args.refresh);
2008
2009            // Initialize the cache.
2010            let cache = cache.init().await?.with_refresh(
2011                args.refresh
2012                    .combine(Refresh::from(args.settings.reinstall.clone()))
2013                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
2014            );
2015
2016            let mut requirements = Vec::with_capacity(
2017                args.with.len() + args.with_editable.len() + args.with_requirements.len(),
2018            );
2019            for package in args.with {
2020                requirements.push(RequirementsSource::from_with_package_argument(&package)?);
2021            }
2022            for package in args.with_editable {
2023                requirements.push(RequirementsSource::from_editable(&package)?);
2024            }
2025            requirements.extend(
2026                args.with_requirements
2027                    .into_iter()
2028                    .map(RequirementsSource::from_requirements_file)
2029                    .collect::<Result<Vec<_>, _>>()?,
2030            );
2031
2032            Box::pin(commands::run(
2033                project_dir,
2034                script,
2035                command,
2036                requirements,
2037                args.show_resolution || globals.verbose > 0,
2038                args.lock_check,
2039                args.frozen,
2040                args.active,
2041                args.no_sync,
2042                args.isolated,
2043                args.all_packages,
2044                args.package,
2045                args.no_project,
2046                no_config,
2047                args.extras,
2048                args.groups,
2049                args.editable,
2050                args.modifications,
2051                args.python,
2052                args.python_platform,
2053                args.install_mirrors,
2054                args.settings,
2055                client_builder.subcommand(vec!["run".to_owned()]),
2056                globals.python_preference,
2057                globals.python_downloads,
2058                globals.installer_metadata,
2059                globals.concurrency,
2060                cache,
2061                printer,
2062                args.env_file,
2063                globals.preview,
2064                args.max_recursion_depth,
2065            ))
2066            .await
2067        }
2068        ProjectCommand::Sync(args) => {
2069            // Resolve the settings from the command-line arguments and workspace configuration.
2070            let args = settings::SyncSettings::resolve(args, filesystem, environment);
2071            show_settings!(args);
2072
2073            // Check for conflicts between offline and refresh.
2074            globals
2075                .network_settings
2076                .check_refresh_conflict(&args.refresh);
2077
2078            // Initialize the cache.
2079            let cache = cache.init().await?.with_refresh(
2080                args.refresh
2081                    .combine(Refresh::from(args.settings.reinstall.clone()))
2082                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
2083            );
2084
2085            // Unwrap the script.
2086            let script = script.map(|script| match script {
2087                Pep723Item::Script(script) => script,
2088                Pep723Item::Stdin(..) => unreachable!("`uv lock` does not support stdin"),
2089                Pep723Item::Remote(..) => unreachable!("`uv lock` does not support remote files"),
2090            });
2091
2092            Box::pin(commands::sync(
2093                project_dir,
2094                args.lock_check,
2095                args.frozen,
2096                args.dry_run,
2097                args.active,
2098                args.all_packages,
2099                args.package,
2100                args.extras,
2101                args.groups,
2102                args.editable,
2103                args.install_options,
2104                args.modifications,
2105                args.python,
2106                args.python_platform,
2107                args.install_mirrors,
2108                globals.python_preference,
2109                globals.python_downloads,
2110                args.settings,
2111                client_builder.subcommand(vec!["sync".to_owned()]),
2112                script,
2113                globals.installer_metadata,
2114                globals.concurrency,
2115                no_config,
2116                &cache,
2117                printer,
2118                globals.preview,
2119                args.output_format,
2120            ))
2121            .await
2122        }
2123        ProjectCommand::Lock(args) => {
2124            // Resolve the settings from the command-line arguments and workspace configuration.
2125            let args = settings::LockSettings::resolve(args, filesystem, environment);
2126            show_settings!(args);
2127
2128            // Check for conflicts between offline and refresh.
2129            globals
2130                .network_settings
2131                .check_refresh_conflict(&args.refresh);
2132
2133            // Initialize the cache.
2134            let cache = cache.init().await?.with_refresh(
2135                args.refresh
2136                    .clone()
2137                    .combine(Refresh::from(args.settings.upgrade.clone())),
2138            );
2139
2140            // If the script already exists, use it; otherwise, propagate the file path and we'll
2141            // initialize it later on.
2142            let script = script
2143                .map(|script| match script {
2144                    Pep723Item::Script(script) => script,
2145                    Pep723Item::Stdin(..) => unreachable!("`uv add` does not support stdin"),
2146                    Pep723Item::Remote(..) => {
2147                        unreachable!("`uv add` does not support remote files")
2148                    }
2149                })
2150                .map(ScriptPath::Script)
2151                .or(args.script.map(ScriptPath::Path));
2152
2153            Box::pin(commands::lock(
2154                project_dir,
2155                args.lock_check,
2156                args.frozen,
2157                args.dry_run,
2158                args.refresh,
2159                args.python,
2160                args.install_mirrors,
2161                args.settings,
2162                client_builder.subcommand(vec!["lock".to_owned()]),
2163                script,
2164                globals.python_preference,
2165                globals.python_downloads,
2166                globals.concurrency,
2167                no_config,
2168                &cache,
2169                printer,
2170                globals.preview,
2171            ))
2172            .await
2173        }
2174        ProjectCommand::Add(args) => {
2175            // Resolve the settings from the command-line arguments and workspace configuration.
2176            let mut args = settings::AddSettings::resolve(args, filesystem, environment);
2177            show_settings!(args);
2178
2179            // If the script already exists, use it; otherwise, propagate the file path and we'll
2180            // initialize it later on.
2181            let script = script
2182                .map(|script| match script {
2183                    Pep723Item::Script(script) => script,
2184                    Pep723Item::Stdin(..) => unreachable!("`uv add` does not support stdin"),
2185                    Pep723Item::Remote(..) => {
2186                        unreachable!("`uv add` does not support remote files")
2187                    }
2188                })
2189                .map(ScriptPath::Script)
2190                .or(args.script.map(ScriptPath::Path));
2191
2192            let requirements = args
2193                .packages
2194                .iter()
2195                .map(String::as_str)
2196                .map(RequirementsSource::from_package_argument)
2197                .chain(
2198                    args.requirements
2199                        .into_iter()
2200                        .map(RequirementsSource::from_requirements_file),
2201                )
2202                .collect::<Result<Vec<_>>>()?;
2203
2204            // Special-case: any local source trees specified on the command-line are automatically
2205            // reinstalled.
2206            for requirement in &requirements {
2207                let requirement = match requirement {
2208                    RequirementsSource::Package(requirement) => requirement,
2209                    RequirementsSource::Editable(requirement) => requirement,
2210                    _ => continue,
2211                };
2212                match requirement {
2213                    RequirementsTxtRequirement::Named(requirement) => {
2214                        if let Some(VersionOrUrl::Url(url)) = requirement.version_or_url.as_ref() {
2215                            if let ParsedUrl::Directory(ParsedDirectoryUrl {
2216                                install_path, ..
2217                            }) = &url.parsed_url
2218                            {
2219                                debug!(
2220                                    "Marking explicit source tree for reinstall: `{}`",
2221                                    install_path.display()
2222                                );
2223                                args.settings.reinstall = args
2224                                    .settings
2225                                    .reinstall
2226                                    .with_package(requirement.name.clone());
2227                            }
2228                        }
2229                    }
2230                    RequirementsTxtRequirement::Unnamed(requirement) => {
2231                        if let ParsedUrl::Directory(ParsedDirectoryUrl { install_path, .. }) =
2232                            &requirement.url.parsed_url
2233                        {
2234                            debug!(
2235                                "Marking explicit source tree for reinstall: `{}`",
2236                                install_path.display()
2237                            );
2238                            args.settings.reinstall =
2239                                args.settings.reinstall.with_path(install_path.clone());
2240                        }
2241                    }
2242                }
2243            }
2244
2245            // Check for conflicts between offline and refresh.
2246            globals
2247                .network_settings
2248                .check_refresh_conflict(&args.refresh);
2249
2250            // Initialize the cache.
2251            let cache = cache.init().await?.with_refresh(
2252                args.refresh
2253                    .combine(Refresh::from(args.settings.reinstall.clone()))
2254                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
2255            );
2256
2257            let constraints = args
2258                .constraints
2259                .into_iter()
2260                .map(RequirementsSource::from_constraints_txt)
2261                .collect::<Result<Vec<_>, _>>()?;
2262
2263            Box::pin(commands::add(
2264                project_dir,
2265                args.lock_check,
2266                args.frozen,
2267                args.active,
2268                args.no_sync,
2269                args.no_install_project,
2270                args.only_install_project,
2271                args.no_install_workspace,
2272                args.only_install_workspace,
2273                args.no_install_local,
2274                args.only_install_local,
2275                args.no_install_package,
2276                args.only_install_package,
2277                requirements,
2278                constraints,
2279                args.marker,
2280                args.editable,
2281                args.dependency_type,
2282                args.raw,
2283                args.bounds,
2284                args.indexes,
2285                args.rev,
2286                args.tag,
2287                args.branch,
2288                args.lfs,
2289                args.extras,
2290                args.package,
2291                args.python,
2292                args.workspace,
2293                args.install_mirrors,
2294                args.settings,
2295                client_builder.subcommand(vec!["add".to_owned()]),
2296                script,
2297                globals.python_preference,
2298                globals.python_downloads,
2299                globals.installer_metadata,
2300                globals.concurrency,
2301                no_config,
2302                &cache,
2303                printer,
2304                globals.preview,
2305            ))
2306            .await
2307        }
2308        ProjectCommand::Remove(args) => {
2309            // Resolve the settings from the command-line arguments and workspace configuration.
2310            let args = settings::RemoveSettings::resolve(args, filesystem, environment);
2311            show_settings!(args);
2312
2313            // Check for conflicts between offline and refresh.
2314            globals
2315                .network_settings
2316                .check_refresh_conflict(&args.refresh);
2317
2318            // Initialize the cache.
2319            let cache = cache.init().await?.with_refresh(
2320                args.refresh
2321                    .combine(Refresh::from(args.settings.reinstall.clone()))
2322                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
2323            );
2324
2325            // Unwrap the script.
2326            let script = script.map(|script| match script {
2327                Pep723Item::Script(script) => script,
2328                Pep723Item::Stdin(..) => unreachable!("`uv remove` does not support stdin"),
2329                Pep723Item::Remote(..) => unreachable!("`uv remove` does not support remote files"),
2330            });
2331
2332            Box::pin(commands::remove(
2333                project_dir,
2334                args.lock_check,
2335                args.frozen,
2336                args.active,
2337                args.no_sync,
2338                args.packages,
2339                args.dependency_type,
2340                args.package,
2341                args.python,
2342                args.install_mirrors,
2343                args.settings,
2344                client_builder.subcommand(vec!["remove".to_owned()]),
2345                script,
2346                globals.python_preference,
2347                globals.python_downloads,
2348                globals.installer_metadata,
2349                globals.concurrency,
2350                no_config,
2351                &cache,
2352                printer,
2353                globals.preview,
2354            ))
2355            .await
2356        }
2357        ProjectCommand::Version(args) => {
2358            // Resolve the settings from the command-line arguments and workspace configuration.
2359            let args = settings::VersionSettings::resolve(args, filesystem, environment);
2360            show_settings!(args);
2361
2362            // Check for conflicts between offline and refresh.
2363            globals
2364                .network_settings
2365                .check_refresh_conflict(&args.refresh);
2366
2367            // Initialize the cache.
2368            let cache = cache.init().await?.with_refresh(
2369                args.refresh
2370                    .combine(Refresh::from(args.settings.reinstall.clone()))
2371                    .combine(Refresh::from(args.settings.resolver.upgrade.clone())),
2372            );
2373
2374            Box::pin(commands::project_version(
2375                args.value,
2376                args.bump,
2377                args.short,
2378                args.output_format,
2379                project_dir,
2380                args.package,
2381                explicit_project,
2382                args.dry_run,
2383                args.lock_check,
2384                args.frozen,
2385                args.active,
2386                args.no_sync,
2387                args.python,
2388                args.install_mirrors,
2389                args.settings,
2390                client_builder.subcommand(vec!["version".to_owned()]),
2391                globals.python_preference,
2392                globals.python_downloads,
2393                globals.installer_metadata,
2394                globals.concurrency,
2395                no_config,
2396                &cache,
2397                printer,
2398                globals.preview,
2399            ))
2400            .await
2401        }
2402        ProjectCommand::Tree(args) => {
2403            // Resolve the settings from the command-line arguments and workspace configuration.
2404            let args = settings::TreeSettings::resolve(args, filesystem, environment);
2405            show_settings!(args);
2406
2407            // Initialize the cache.
2408            let cache = cache.init().await?;
2409
2410            // Unwrap the script.
2411            let script = script.map(|script| match script {
2412                Pep723Item::Script(script) => script,
2413                Pep723Item::Stdin(..) => unreachable!("`uv tree` does not support stdin"),
2414                Pep723Item::Remote(..) => unreachable!("`uv tree` does not support remote files"),
2415            });
2416
2417            Box::pin(commands::tree(
2418                project_dir,
2419                args.groups,
2420                args.lock_check,
2421                args.frozen,
2422                args.universal,
2423                args.depth,
2424                args.prune,
2425                args.package,
2426                args.no_dedupe,
2427                args.invert,
2428                args.outdated,
2429                args.show_sizes,
2430                args.python_version,
2431                args.python_platform,
2432                args.python,
2433                args.install_mirrors,
2434                args.resolver,
2435                &client_builder.subcommand(vec!["tree".to_owned()]),
2436                script,
2437                globals.python_preference,
2438                globals.python_downloads,
2439                globals.concurrency,
2440                no_config,
2441                &cache,
2442                printer,
2443                globals.preview,
2444            ))
2445            .await
2446        }
2447        ProjectCommand::Export(args) => {
2448            // Resolve the settings from the command-line arguments and workspace configuration.
2449            let args = settings::ExportSettings::resolve(args, filesystem, environment);
2450            show_settings!(args);
2451
2452            // Initialize the cache.
2453            let cache = cache.init().await?;
2454
2455            // Unwrap the script.
2456            let script = script.map(|script| match script {
2457                Pep723Item::Script(script) => script,
2458                Pep723Item::Stdin(..) => unreachable!("`uv export` does not support stdin"),
2459                Pep723Item::Remote(..) => unreachable!("`uv export` does not support remote files"),
2460            });
2461
2462            commands::export(
2463                project_dir,
2464                args.format,
2465                args.all_packages,
2466                args.package,
2467                args.prune,
2468                args.hashes,
2469                args.install_options,
2470                args.output_file,
2471                args.extras,
2472                args.groups,
2473                args.editable,
2474                args.lock_check,
2475                args.frozen,
2476                args.include_annotations,
2477                args.include_header,
2478                script,
2479                args.python,
2480                args.install_mirrors,
2481                args.settings,
2482                client_builder.subcommand(vec!["export".to_owned()]),
2483                globals.python_preference,
2484                globals.python_downloads,
2485                globals.concurrency,
2486                no_config,
2487                globals.quiet > 0,
2488                &cache,
2489                printer,
2490                globals.preview,
2491            )
2492            .boxed_local()
2493            .await
2494        }
2495        ProjectCommand::Format(args) => {
2496            // Resolve the settings from the command-line arguments and workspace configuration.
2497            let args = settings::FormatSettings::resolve(args, filesystem);
2498            show_settings!(args);
2499
2500            // Initialize the cache.
2501            let cache = cache.init().await?;
2502
2503            Box::pin(commands::format(
2504                project_dir,
2505                args.check,
2506                args.diff,
2507                args.extra_args,
2508                args.version,
2509                client_builder.subcommand(vec!["format".to_owned()]),
2510                cache,
2511                printer,
2512                globals.preview,
2513                args.no_project,
2514            ))
2515            .await
2516        }
2517    }
2518}
2519
2520/// The main entry point for a uv invocation.
2521///
2522/// # Usage
2523///
2524/// This entry point is not recommended for external consumption, the uv binary interface is the
2525/// official public API.
2526///
2527/// When using this entry point, uv assumes it is running in a process it controls and that the
2528/// entire process lifetime is managed by uv. Unexpected behavior may be encountered if this entry
2529/// point is called multiple times in a single process.
2530///
2531/// # Safety
2532///
2533/// It is only safe to call this routine when it is known that multiple threads are not running.
2534#[allow(unsafe_code)]
2535pub unsafe fn main<I, T>(args: I) -> ExitCode
2536where
2537    I: IntoIterator<Item = T>,
2538    T: Into<OsString> + Clone,
2539{
2540    #[cfg(windows)]
2541    windows_exception::setup();
2542
2543    // Set the `UV` variable to the current executable so it is implicitly propagated to all child
2544    // processes, e.g., in `uv run`.
2545    if let Ok(current_exe) = std::env::current_exe() {
2546        // SAFETY: The proof obligation must be satisfied by the caller.
2547        unsafe {
2548            // This will become unsafe in Rust 2024
2549            // See https://doc.rust-lang.org/std/env/fn.set_var.html#safety
2550            std::env::set_var(EnvVars::UV, current_exe);
2551        }
2552    }
2553
2554    // `std::env::args` is not `Send` so we parse before passing to our runtime
2555    // https://github.com/rust-lang/rust/pull/48005
2556    let cli = match Cli::try_parse_from(args) {
2557        Ok(cli) => cli,
2558        Err(mut err) => {
2559            if let Some(ContextValue::String(subcommand)) = err.get(ContextKind::InvalidSubcommand)
2560            {
2561                match subcommand.as_str() {
2562                    "compile" => {
2563                        err.insert(
2564                            ContextKind::SuggestedSubcommand,
2565                            ContextValue::String("uv pip compile".to_string()),
2566                        );
2567                    }
2568                    "install" => {
2569                        err.insert(
2570                            ContextKind::SuggestedSubcommand,
2571                            ContextValue::String("uv pip install".to_string()),
2572                        );
2573                    }
2574                    "uninstall" => {
2575                        err.insert(
2576                            ContextKind::SuggestedSubcommand,
2577                            ContextValue::String("uv pip uninstall".to_string()),
2578                        );
2579                    }
2580                    "freeze" => {
2581                        err.insert(
2582                            ContextKind::SuggestedSubcommand,
2583                            ContextValue::String("uv pip freeze".to_string()),
2584                        );
2585                    }
2586                    "list" => {
2587                        err.insert(
2588                            ContextKind::SuggestedSubcommand,
2589                            ContextValue::String("uv pip list".to_string()),
2590                        );
2591                    }
2592                    "show" => {
2593                        err.insert(
2594                            ContextKind::SuggestedSubcommand,
2595                            ContextValue::String("uv pip show".to_string()),
2596                        );
2597                    }
2598                    _ => {}
2599                }
2600            }
2601            err.exit()
2602        }
2603    };
2604
2605    // See `min_stack_size` doc comment about `main2`
2606    let min_stack_size = min_stack_size();
2607    let main2 = move || {
2608        let runtime = tokio::runtime::Builder::new_current_thread()
2609            .enable_all()
2610            .thread_stack_size(min_stack_size)
2611            .build()
2612            .expect("Failed building the Runtime");
2613        // Box the large main future to avoid stack overflows.
2614        let result = runtime.block_on(Box::pin(run(cli)));
2615        // Avoid waiting for pending tasks to complete.
2616        //
2617        // The resolver may have kicked off HTTP requests during resolution that
2618        // turned out to be unnecessary. Waiting for those to complete can cause
2619        // the CLI to hang before exiting.
2620        runtime.shutdown_background();
2621        result
2622    };
2623    let result = std::thread::Builder::new()
2624        .name("main2".to_owned())
2625        .stack_size(min_stack_size)
2626        .spawn(main2)
2627        .expect("Tokio executor failed, was there a panic?")
2628        .join()
2629        .expect("Tokio executor failed, was there a panic?");
2630
2631    match result {
2632        Ok(code) => code.into(),
2633        Err(err) => {
2634            trace!("Error trace: {err:?}");
2635            let mut causes = err.chain();
2636            eprintln!(
2637                "{}: {}",
2638                "error".red().bold(),
2639                causes.next().unwrap().to_string().trim()
2640            );
2641            for err in causes {
2642                eprintln!("  {}: {}", "Caused by".red().bold(), err.to_string().trim());
2643            }
2644            ExitStatus::Error.into()
2645        }
2646    }
2647}