Skip to main content

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