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