From 1eee427c949d74620b4f6693365629097c9b7b63 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Mon, 24 Jun 2024 13:16:22 +0300 Subject: [PATCH] Add standalone CLI crate (#4456) ## Summary This PR moves all the CLI code into its own crate, separate from the `uv` crate. The `uv` crate is iterated on frequently, and the CLI code comprises a significant portion of it but rarely changes. Removing the CLI code reduces the `uv` crate size from 1.4MiB to 1.0MiB. --- Cargo.lock | 26 +- Cargo.toml | 1 + crates/uv-cli/Cargo.toml | 43 ++ crates/{uv => uv-cli}/build.rs | 0 .../compat/mod.rs => uv-cli/src/compat.rs} | 12 +- crates/{uv/src/cli.rs => uv-cli/src/lib.rs} | 711 +++++++++--------- crates/uv-cli/src/options.rs | 351 +++++++++ crates/uv-cli/src/version.rs | 149 ++++ crates/uv/Cargo.toml | 16 +- crates/uv/src/commands/mod.rs | 20 +- crates/uv/src/commands/pip/compile.rs | 14 +- crates/uv/src/commands/pip/list.rs | 2 +- crates/uv/src/commands/project/run.rs | 2 +- crates/uv/src/commands/tool/run.rs | 2 +- crates/uv/src/commands/version.rs | 4 +- crates/uv/src/main.rs | 18 +- crates/uv/src/settings.rs | 358 +-------- pyproject.toml | 1 + 18 files changed, 976 insertions(+), 754 deletions(-) create mode 100644 crates/uv-cli/Cargo.toml rename crates/{uv => uv-cli}/build.rs (100%) rename crates/{uv/src/compat/mod.rs => uv-cli/src/compat.rs} (98%) rename crates/{uv/src/cli.rs => uv-cli/src/lib.rs} (84%) create mode 100644 crates/uv-cli/src/options.rs create mode 100644 crates/uv-cli/src/version.rs diff --git a/Cargo.lock b/Cargo.lock index 32e63d180..744e1597a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4438,7 +4438,6 @@ dependencies = [ "byteorder", "chrono", "clap", - "clap_complete_command", "distribution-types", "filetime", "flate2", @@ -4477,6 +4476,7 @@ dependencies = [ "url", "uv-auth", "uv-cache", + "uv-cli", "uv-client", "uv-configuration", "uv-dispatch", @@ -4570,6 +4570,30 @@ dependencies = [ "walkdir", ] +[[package]] +name = "uv-cli" +version = "0.0.1" +dependencies = [ + "anstream", + "anyhow", + "clap", + "clap_complete_command", + "distribution-types", + "fs-err", + "insta", + "install-wheel-rs", + "serde", + "url", + "uv-cache", + "uv-configuration", + "uv-normalize", + "uv-resolver", + "uv-settings", + "uv-toolchain", + "uv-version", + "uv-warnings", +] + [[package]] name = "uv-client" version = "0.0.1" diff --git a/Cargo.toml b/Cargo.toml index 8d848a65a..ba9261621 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ requirements-txt = { path = "crates/requirements-txt" } uv-auth = { path = "crates/uv-auth" } uv-build = { path = "crates/uv-build" } uv-cache = { path = "crates/uv-cache" } +uv-cli = { path = "crates/uv-cli" } uv-client = { path = "crates/uv-client" } uv-configuration = { path = "crates/uv-configuration" } uv-dispatch = { path = "crates/uv-dispatch" } diff --git a/crates/uv-cli/Cargo.toml b/crates/uv-cli/Cargo.toml new file mode 100644 index 000000000..f6442f34a --- /dev/null +++ b/crates/uv-cli/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "uv-cli" +version = "0.0.1" +description = "The command line interface for the uv binary." +edition = { workspace = true } +rust-version = { workspace = true } +homepage = { workspace = true } +documentation = { workspace = true } +repository = { workspace = true } +authors = { workspace = true } +license = { workspace = true } + +[lints] +workspace = true + +[dependencies] +distribution-types = { workspace = true } +install-wheel-rs = { workspace = true, features = ["clap"], default-features = false } +uv-cache = { workspace = true, features = ["clap"] } +uv-configuration = { workspace = true, features = ["clap"] } +uv-normalize = { workspace = true } +uv-resolver = { workspace = true, features = ["clap"] } +uv-settings = { workspace = true, features = ["schemars"] } +uv-toolchain = { workspace = true, features = ["clap", "schemars"]} +uv-version = { workspace = true } +uv-warnings = { workspace = true } + +anstream = { workspace = true } +anyhow = { workspace = true } +clap = { workspace = true, features = ["derive"] } +clap_complete_command = { workspace = true } +serde = { workspace = true } +url = { workspace = true } + +[dev-dependencies] +insta = { version = "1.36.1", features = ["filters", "json"] } + +[features] +default = [] +self-update = [] + +[build-dependencies] +fs-err = { workspace = true } diff --git a/crates/uv/build.rs b/crates/uv-cli/build.rs similarity index 100% rename from crates/uv/build.rs rename to crates/uv-cli/build.rs diff --git a/crates/uv/src/compat/mod.rs b/crates/uv-cli/src/compat.rs similarity index 98% rename from crates/uv/src/compat/mod.rs rename to crates/uv-cli/src/compat.rs index 128dd57c3..31ec7f6d1 100644 --- a/crates/uv/src/compat/mod.rs +++ b/crates/uv-cli/src/compat.rs @@ -3,7 +3,7 @@ use clap::{Args, ValueEnum}; use uv_warnings::warn_user; -pub(crate) trait CompatArgs { +pub trait CompatArgs { fn validate(&self) -> Result<()>; } @@ -14,7 +14,7 @@ pub(crate) trait CompatArgs { /// nice user experience to warn, rather than fail, when users pass `--allow-unsafe`. #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipCompileCompatArgs { +pub struct PipCompileCompatArgs { #[clap(long, hide = true)] allow_unsafe: bool, @@ -175,7 +175,7 @@ impl CompatArgs for PipCompileCompatArgs { /// These represent a subset of the `pip list` interface that uv supports by default. #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipListCompatArgs { +pub struct PipListCompatArgs { #[clap(long, hide = true)] outdated: bool, } @@ -200,7 +200,7 @@ impl CompatArgs for crate::compat::PipListCompatArgs { /// These represent a subset of the `pip-sync` interface that uv supports by default. #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipSyncCompatArgs { +pub struct PipSyncCompatArgs { #[clap(short, long, hide = true)] ask: bool, @@ -299,7 +299,7 @@ enum Resolver { /// These represent a subset of the `virtualenv` interface that uv supports by default. #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct VenvCompatArgs { +pub struct VenvCompatArgs { #[clap(long, hide = true)] clear: bool, @@ -358,7 +358,7 @@ impl CompatArgs for VenvCompatArgs { /// These represent a subset of the `pip install` interface that uv supports by default. #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipInstallCompatArgs { +pub struct PipInstallCompatArgs { #[clap(long, hide = false)] user: bool, } diff --git a/crates/uv/src/cli.rs b/crates/uv-cli/src/lib.rs similarity index 84% rename from crates/uv/src/cli.rs rename to crates/uv-cli/src/lib.rs index 672950917..9ceedc88c 100644 --- a/crates/uv/src/cli.rs +++ b/crates/uv-cli/src/lib.rs @@ -3,8 +3,7 @@ use std::ops::Deref; use std::path::PathBuf; use std::str::FromStr; -use anyhow::Result; - +use anyhow::{anyhow, Result}; use clap::{Args, Parser, Subcommand}; use distribution_types::{FlatIndexLocation, IndexUrl}; @@ -16,45 +15,75 @@ use uv_normalize::{ExtraName, PackageName}; use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode}; use uv_toolchain::{PythonVersion, ToolchainPreference}; -use crate::commands::{extra_name_with_clap_error, ListFormat, VersionFormat}; -use crate::compat; +pub mod compat; +pub mod options; +pub mod version; + +#[derive(Debug, Clone, Copy, clap::ValueEnum)] +pub enum VersionFormat { + /// Display the version as plain text. + Text, + /// Display the version as JSON. + Json, +} + +#[derive(Debug, Default, Clone, clap::ValueEnum)] +pub enum ListFormat { + /// Display the list of packages in a human-readable table. + #[default] + Columns, + /// Display the list of packages in a `pip freeze`-like format, with one package per line + /// alongside its version. + Freeze, + /// Display the list of packages in a machine-readable JSON format. + Json, +} + +fn extra_name_with_clap_error(arg: &str) -> Result { + ExtraName::from_str(arg).map_err(|_err| { + anyhow!( + "Extra names must start and end with a letter or digit and may only \ + contain -, _, ., and alphanumeric characters" + ) + }) +} #[derive(Parser)] -#[command(author, version, long_version = crate::version::version(), about)] +#[command(name = "uv", author, version = uv_version::version(), long_version = crate::version::version(), about)] #[command(propagate_version = true)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct Cli { +pub struct Cli { #[command(subcommand)] - pub(crate) command: Commands, + pub command: Commands, #[command(flatten)] - pub(crate) global_args: GlobalArgs, + pub global_args: GlobalArgs, #[command(flatten)] - pub(crate) cache_args: CacheArgs, + pub cache_args: CacheArgs, /// The path to a `uv.toml` file to use for configuration. #[arg(global = true, long, env = "UV_CONFIG_FILE")] - pub(crate) config_file: Option, + pub config_file: Option, } #[derive(Parser, Debug, Clone)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct GlobalArgs { +pub struct GlobalArgs { /// Do not print any output. #[arg(global = true, long, short, conflicts_with = "verbose")] - pub(crate) quiet: bool, + pub quiet: bool, /// Use verbose output. /// /// You can configure fine-grained logging using the `RUST_LOG` environment variable. /// () #[arg(global = true, action = clap::ArgAction::Count, long, short, conflicts_with = "quiet")] - pub(crate) verbose: u8, + pub verbose: u8, /// Disable colors; provided for compatibility with `pip`. #[arg(global = true, long, hide = true, conflicts_with = "color")] - pub(crate) no_color: bool, + pub no_color: bool, /// Control colors in output. #[arg( @@ -65,7 +94,7 @@ pub(crate) struct GlobalArgs { conflicts_with = "no_color", value_name = "COLOR_CHOICE" )] - pub(crate) color: ColorChoice, + pub color: ColorChoice, /// Whether to load TLS certificates from the platform's native certificate store. /// @@ -77,41 +106,41 @@ pub(crate) struct GlobalArgs { /// especially if you're relying on a corporate trust root (e.g., for a mandatory proxy) that's /// included in your system's certificate store. #[arg(global = true, long, env = "UV_NATIVE_TLS", value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_native_tls"))] - pub(crate) native_tls: bool, + pub native_tls: bool, #[arg(global = true, long, overrides_with("native_tls"), hide = true)] - pub(crate) no_native_tls: bool, + pub no_native_tls: bool, /// Disable network access, relying only on locally cached data and locally available files. #[arg(global = true, long, overrides_with("no_offline"))] - pub(crate) offline: bool, + pub offline: bool, #[arg(global = true, long, overrides_with("offline"), hide = true)] - pub(crate) no_offline: bool, + pub no_offline: bool, /// Whether to use system or uv-managed Python toolchains. #[arg(global = true, long)] - pub(crate) toolchain_preference: Option, + pub toolchain_preference: Option, /// Whether to enable experimental, preview features. #[arg(global = true, long, hide = true, env = "UV_PREVIEW", value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_preview"))] - pub(crate) preview: bool, + pub preview: bool, #[arg(global = true, long, overrides_with("preview"), hide = true)] - pub(crate) no_preview: bool, + pub no_preview: bool, /// Avoid discovering a `pyproject.toml` or `uv.toml` file in the current directory or any /// parent directories. #[arg(global = true, long, hide = true)] - pub(crate) isolated: bool, + pub isolated: bool, /// Show the resolved settings for the current command. #[arg(global = true, long, hide = true)] - pub(crate) show_settings: bool, + pub show_settings: bool, } #[derive(Debug, Copy, Clone, clap::ValueEnum)] -pub(crate) enum ColorChoice { +pub enum ColorChoice { /// Enables colored output only when the output is going to a terminal or TTY with support. Auto, @@ -134,7 +163,7 @@ impl From for anstream::ColorChoice { #[derive(Subcommand)] #[allow(clippy::large_enum_variant)] -pub(crate) enum Commands { +pub enum Commands { /// Resolve and install Python packages. Pip(PipNamespace), /// Run and manage executable Python packages. @@ -168,27 +197,27 @@ pub(crate) enum Commands { #[derive(Args)] #[cfg(feature = "self-update")] -pub(crate) struct SelfNamespace { +pub struct SelfNamespace { #[command(subcommand)] - pub(crate) command: SelfCommand, + pub command: SelfCommand, } #[derive(Subcommand)] #[cfg(feature = "self-update")] -pub(crate) enum SelfCommand { +pub enum SelfCommand { /// Update `uv` to the latest version. Update, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct CacheNamespace { +pub struct CacheNamespace { #[command(subcommand)] - pub(crate) command: CacheCommand, + pub command: CacheCommand, } #[derive(Subcommand)] -pub(crate) enum CacheCommand { +pub enum CacheCommand { /// Clear the cache, removing all entries or those linked to specific packages. Clean(CleanArgs), /// Prune all unreachable objects from the cache. @@ -199,20 +228,20 @@ pub(crate) enum CacheCommand { #[derive(Args, Debug)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct CleanArgs { +pub struct CleanArgs { /// The packages to remove from the cache. - pub(crate) package: Vec, + pub package: Vec, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipNamespace { +pub struct PipNamespace { #[command(subcommand)] - pub(crate) command: PipCommand, + pub command: PipCommand, } #[derive(Subcommand)] -pub(crate) enum PipCommand { +pub enum PipCommand { /// Compile a `requirements.in` file to a `requirements.txt` file. Compile(PipCompileArgs), /// Sync an environment with a `requirements.txt` file. @@ -234,7 +263,7 @@ pub(crate) enum PipCommand { } #[derive(Subcommand)] -pub(crate) enum ProjectCommand { +pub enum ProjectCommand { /// Run a command in the project environment. #[clap(hide = true)] Run(RunArgs), @@ -255,13 +284,13 @@ pub(crate) enum ProjectCommand { /// A re-implementation of `Option`, used to avoid Clap's automatic `Option` flattening in /// [`parse_index_url`]. #[derive(Debug, Clone)] -pub(crate) enum Maybe { +pub enum Maybe { Some(T), None, } impl Maybe { - pub(crate) fn into_option(self) -> Option { + pub fn into_option(self) -> Option { match self { Maybe::Some(value) => Some(value), Maybe::None => None, @@ -310,7 +339,7 @@ fn parse_maybe_file_path(input: &str) -> Result, String> { #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipCompileArgs { +pub struct PipCompileArgs { /// Include all packages listed in the given `requirements.in` files. /// /// If a `pyproject.toml`, `setup.py`, or `setup.cfg` file is provided, `uv` will @@ -318,7 +347,7 @@ pub(crate) struct PipCompileArgs { /// /// If `-` is provided, then requirements will be read from stdin. #[arg(required(true), value_parser = parse_file_path)] - pub(crate) src_file: Vec, + pub src_file: Vec, /// Constrain versions using the given requirements files. /// @@ -328,7 +357,7 @@ pub(crate) struct PipCompileArgs { /// /// This is equivalent to pip's `--constraint` option. #[arg(long, short, env = "UV_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)] - pub(crate) constraint: Vec>, + pub constraint: Vec>, /// Override versions using the given requirements files. /// @@ -340,38 +369,38 @@ pub(crate) struct PipCompileArgs { /// constituent packages, overrides are _absolute_, in that they completely replace the /// requirements of the constituent packages. #[arg(long, value_parser = parse_file_path)] - pub(crate) r#override: Vec, + pub r#override: Vec, /// Include optional dependencies from the extra group name; may be provided more than once. /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources. #[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)] - pub(crate) extra: Option>, + pub extra: Option>, /// Include all optional dependencies. /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources. #[arg(long, conflicts_with = "extra")] - pub(crate) all_extras: bool, + pub all_extras: bool, #[arg(long, overrides_with("all_extras"), hide = true)] - pub(crate) no_all_extras: bool, + pub no_all_extras: bool, #[command(flatten)] - pub(crate) resolver: ResolverArgs, + pub resolver: ResolverArgs, #[command(flatten)] - pub(crate) refresh: RefreshArgs, + pub refresh: RefreshArgs, /// Ignore package dependencies, instead only add those packages explicitly listed /// on the command line to the resulting the requirements file. #[arg(long)] - pub(crate) no_deps: bool, + pub no_deps: bool, #[arg(long, overrides_with("no_deps"), hide = true)] - pub(crate) deps: bool, + pub deps: bool, /// Write the compiled requirements to the given `requirements.txt` file. #[arg(long, short)] - pub(crate) output_file: Option, + pub output_file: Option, /// Include extras in the output file. /// @@ -379,34 +408,34 @@ pub(crate) struct PipCompileArgs { /// as dependencies in the output file directly. Further, output files generated with /// `--no-strip-extras` cannot be used as constraints files in `install` and `sync` invocations. #[arg(long, overrides_with("strip_extras"))] - pub(crate) no_strip_extras: bool, + pub no_strip_extras: bool, #[arg(long, overrides_with("no_strip_extras"), hide = true)] - pub(crate) strip_extras: bool, + pub strip_extras: bool, /// Exclude comment annotations indicating the source of each package. #[arg(long, overrides_with("annotate"))] - pub(crate) no_annotate: bool, + pub no_annotate: bool, #[arg(long, overrides_with("no_annotate"), hide = true)] - pub(crate) annotate: bool, + pub annotate: bool, /// Exclude the comment header at the top of the generated output file. #[arg(long, overrides_with("header"))] - pub(crate) no_header: bool, + pub no_header: bool, #[arg(long, overrides_with("no_header"), hide = true)] - pub(crate) header: bool, + pub header: bool, /// Choose the style of the annotation comments, which indicate the source of each package. /// /// Defaults to `split`. #[arg(long, value_enum)] - pub(crate) annotation_style: Option, + pub annotation_style: Option, /// Change header comment to reflect custom command wrapping `uv pip compile`. #[arg(long, env = "UV_CUSTOM_COMPILE_COMMAND")] - pub(crate) custom_compile_command: Option, + pub custom_compile_command: Option, /// The Python interpreter against which to compile the requirements. /// @@ -420,7 +449,7 @@ pub(crate) struct PipCompileArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// Install packages into the system Python. /// @@ -434,25 +463,25 @@ pub(crate) struct PipCompileArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, /// Include distribution hashes in the output file. #[arg(long, overrides_with("no_generate_hashes"))] - pub(crate) generate_hashes: bool, + pub generate_hashes: bool, #[arg(long, overrides_with("generate_hashes"), hide = true)] - pub(crate) no_generate_hashes: bool, + pub no_generate_hashes: bool, /// Use legacy `setuptools` behavior when building source distributions without a /// `pyproject.toml`. #[arg(long, overrides_with("no_legacy_setup_py"))] - pub(crate) legacy_setup_py: bool, + pub legacy_setup_py: bool, #[arg(long, overrides_with("legacy_setup_py"), hide = true)] - pub(crate) no_legacy_setup_py: bool, + pub no_legacy_setup_py: bool, /// Disable isolation when building source distributions. /// @@ -463,10 +492,10 @@ pub(crate) struct PipCompileArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("build_isolation") )] - pub(crate) no_build_isolation: bool, + pub no_build_isolation: bool, #[arg(long, overrides_with("no_build_isolation"), hide = true)] - pub(crate) build_isolation: bool, + pub build_isolation: bool, /// Don't build source distributions. /// @@ -481,7 +510,7 @@ pub(crate) struct PipCompileArgs { conflicts_with = "only_binary", overrides_with("build") )] - pub(crate) no_build: bool, + pub no_build: bool, #[arg( long, @@ -490,7 +519,7 @@ pub(crate) struct PipCompileArgs { overrides_with("no_build"), hide = true )] - pub(crate) build: bool, + pub build: bool, /// Don't install pre-built wheels. /// @@ -500,7 +529,7 @@ pub(crate) struct PipCompileArgs { /// Multiple packages may be provided. Disable binaries for all packages with `:all:`. /// Clear previously specified packages with `:none:`. #[arg(long, conflicts_with = "no_build")] - pub(crate) no_binary: Option>, + pub no_binary: Option>, /// Only use pre-built wheels; don't build source distributions. /// @@ -511,7 +540,7 @@ pub(crate) struct PipCompileArgs { /// Multiple packages may be provided. Disable binaries for all packages with `:all:`. /// Clear previously specified packages with `:none:`. #[arg(long, conflicts_with = "no_build")] - pub(crate) only_binary: Option>, + pub only_binary: Option>, /// The minimum Python version that should be supported by the compiled requirements (e.g., /// `3.7` or `3.7.9`). @@ -519,7 +548,7 @@ pub(crate) struct PipCompileArgs { /// If a patch version is omitted, the minimum patch version is assumed. For example, `3.7` is /// mapped to `3.7.0`. #[arg(long, short)] - pub(crate) python_version: Option, + pub python_version: Option, /// The platform for which requirements should be resolved. /// @@ -527,26 +556,26 @@ pub(crate) struct PipCompileArgs { /// its CPU, vendor, and operating system name, like `x86_64-unknown-linux-gnu` or /// `aaarch64-apple-darwin`. #[arg(long)] - pub(crate) python_platform: Option, + pub python_platform: Option, /// Specify a package to omit from the output resolution. Its dependencies will still be /// included in the resolution. Equivalent to pip-compile's `--unsafe-package` option. #[arg(long, alias = "unsafe-package")] - pub(crate) no_emit_package: Option>, + pub no_emit_package: Option>, /// Include `--index-url` and `--extra-index-url` entries in the generated output file. #[arg(long, overrides_with("no_emit_index_url"))] - pub(crate) emit_index_url: bool, + pub emit_index_url: bool, #[arg(long, overrides_with("emit_index_url"), hide = true)] - pub(crate) no_emit_index_url: bool, + pub no_emit_index_url: bool, /// Include `--find-links` entries in the generated output file. #[arg(long, overrides_with("no_emit_find_links"))] - pub(crate) emit_find_links: bool, + pub emit_find_links: bool, #[arg(long, overrides_with("emit_find_links"), hide = true)] - pub(crate) no_emit_find_links: bool, + pub no_emit_find_links: bool, /// Whether to emit a marker string indicating when it is known that the /// resulting set of pinned dependencies is valid. @@ -555,26 +584,26 @@ pub(crate) struct PipCompileArgs { /// false, but when the expression is true, the requirements are known to /// be correct. #[arg(long, overrides_with("no_emit_marker_expression"), hide = true)] - pub(crate) emit_marker_expression: bool, + pub emit_marker_expression: bool, #[arg(long, overrides_with("emit_marker_expression"), hide = true)] - pub(crate) no_emit_marker_expression: bool, + pub no_emit_marker_expression: bool, /// Include comment annotations indicating the index used to resolve each package (e.g., /// `# from https://pypi.org/simple`). #[arg(long, overrides_with("no_emit_index_annotation"))] - pub(crate) emit_index_annotation: bool, + pub emit_index_annotation: bool, #[arg(long, overrides_with("emit_index_annotation"), hide = true)] - pub(crate) no_emit_index_annotation: bool, + pub no_emit_index_annotation: bool, #[command(flatten)] - pub(crate) compat_args: compat::PipCompileCompatArgs, + pub compat_args: compat::PipCompileCompatArgs, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipSyncArgs { +pub struct PipSyncArgs { /// Include all packages listed in the given `requirements.txt` files. /// /// If a `pyproject.toml`, `setup.py`, or `setup.cfg` file is provided, `uv` will @@ -582,7 +611,7 @@ pub(crate) struct PipSyncArgs { /// /// If `-` is provided, then requirements will be read from stdin. #[arg(required(true), value_parser = parse_file_path)] - pub(crate) src_file: Vec, + pub src_file: Vec, /// Constrain versions using the given requirements files. /// @@ -592,20 +621,20 @@ pub(crate) struct PipSyncArgs { /// /// This is equivalent to pip's `--constraint` option. #[arg(long, short, env = "UV_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)] - pub(crate) constraint: Vec>, + pub constraint: Vec>, #[command(flatten)] - pub(crate) installer: InstallerArgs, + pub installer: InstallerArgs, #[command(flatten)] - pub(crate) refresh: RefreshArgs, + pub refresh: RefreshArgs, /// Limit candidate packages to those that were uploaded prior to the given date. /// /// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and UTC dates in the same /// format (e.g., `2006-12-02`). #[arg(long, env = "UV_EXCLUDE_NEWER")] - pub(crate) exclude_newer: Option, + pub exclude_newer: Option, /// Require a matching hash for each requirement. /// @@ -619,11 +648,11 @@ pub(crate) struct PipSyncArgs { /// - Local dependencies are not supported, unless they point to a specific wheel (`.whl`) or /// source archive (`.zip`, `.tar.gz`), as opposed to a directory. #[arg(long, env = "UV_REQUIRE_HASHES", - value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_require_hashes"))] - pub(crate) require_hashes: bool, + value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_require_hashes"))] + pub require_hashes: bool, #[arg(long, overrides_with("require_hashes"), hide = true)] - pub(crate) no_require_hashes: bool, + pub no_require_hashes: bool, /// The Python interpreter into which packages should be installed. /// @@ -638,7 +667,7 @@ pub(crate) struct PipSyncArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// Install packages into the system Python. /// @@ -654,10 +683,10 @@ pub(crate) struct PipSyncArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, /// Allow `uv` to modify an `EXTERNALLY-MANAGED` Python installation. /// @@ -671,16 +700,16 @@ pub(crate) struct PipSyncArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_break_system_packages") )] - pub(crate) break_system_packages: bool, + pub break_system_packages: bool, #[arg(long, overrides_with("break_system_packages"))] - pub(crate) no_break_system_packages: bool, + pub no_break_system_packages: bool, /// Install packages into the specified directory, rather than into the virtual environment /// or system Python interpreter. The packages will be installed at the top-level of the /// directory #[arg(long, conflicts_with = "prefix")] - pub(crate) target: Option, + pub target: Option, /// Install packages into `lib`, `bin`, and other top-level folders under the specified /// directory, as if a virtual environment were created at the specified location. @@ -690,15 +719,15 @@ pub(crate) struct PipSyncArgs { /// interpreter, rather than any interpreter added to the `--prefix` directory, rendering them /// non-portable. #[arg(long, conflicts_with = "target")] - pub(crate) prefix: Option, + pub prefix: Option, /// Use legacy `setuptools` behavior when building source distributions without a /// `pyproject.toml`. #[arg(long, overrides_with("no_legacy_setup_py"))] - pub(crate) legacy_setup_py: bool, + pub legacy_setup_py: bool, #[arg(long, overrides_with("legacy_setup_py"), hide = true)] - pub(crate) no_legacy_setup_py: bool, + pub no_legacy_setup_py: bool, /// Disable isolation when building source distributions. /// @@ -709,10 +738,10 @@ pub(crate) struct PipSyncArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("build_isolation") )] - pub(crate) no_build_isolation: bool, + pub no_build_isolation: bool, #[arg(long, overrides_with("no_build_isolation"), hide = true)] - pub(crate) build_isolation: bool, + pub build_isolation: bool, /// Don't build source distributions. /// @@ -727,7 +756,7 @@ pub(crate) struct PipSyncArgs { conflicts_with = "only_binary", overrides_with("build") )] - pub(crate) no_build: bool, + pub no_build: bool, #[arg( long, @@ -736,7 +765,7 @@ pub(crate) struct PipSyncArgs { overrides_with("no_build"), hide = true )] - pub(crate) build: bool, + pub build: bool, /// Don't install pre-built wheels. /// @@ -746,7 +775,7 @@ pub(crate) struct PipSyncArgs { /// Multiple packages may be provided. Disable binaries for all packages with `:all:`. /// Clear previously specified packages with `:none:`. #[arg(long, conflicts_with = "no_build")] - pub(crate) no_binary: Option>, + pub no_binary: Option>, /// Only use pre-built wheels; don't build source distributions. /// @@ -757,7 +786,7 @@ pub(crate) struct PipSyncArgs { /// Multiple packages may be provided. Disable binaries for all packages with `:all:`. /// Clear previously specified packages with `:none:`. #[arg(long, conflicts_with = "no_build")] - pub(crate) only_binary: Option>, + pub only_binary: Option>, /// The minimum Python version that should be supported by the requirements (e.g., /// `3.7` or `3.7.9`). @@ -765,7 +794,7 @@ pub(crate) struct PipSyncArgs { /// If a patch version is omitted, the minimum patch version is assumed. For example, `3.7` is /// mapped to `3.7.0`. #[arg(long)] - pub(crate) python_version: Option, + pub python_version: Option, /// The platform for which requirements should be installed. /// @@ -779,32 +808,32 @@ pub(crate) struct PipSyncArgs { /// the _target_ platform, as they will be built for the _current_ platform. The /// `--python-platform` option is intended for advanced use cases. #[arg(long)] - pub(crate) python_platform: Option, + pub python_platform: Option, /// Validate the virtual environment after completing the installation, to detect packages with /// missing dependencies or other issues. #[arg(long, overrides_with("no_strict"))] - pub(crate) strict: bool, + pub strict: bool, #[arg(long, overrides_with("strict"), hide = true)] - pub(crate) no_strict: bool, + pub no_strict: bool, /// Perform a dry run, i.e., don't actually install anything but resolve the dependencies and /// print the resulting plan. #[arg(long)] - pub(crate) dry_run: bool, + pub dry_run: bool, #[command(flatten)] - pub(crate) compat_args: compat::PipSyncCompatArgs, + pub compat_args: compat::PipSyncCompatArgs, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] #[command(group = clap::ArgGroup::new("sources").required(true).multiple(true))] -pub(crate) struct PipInstallArgs { +pub struct PipInstallArgs { /// Install all listed packages. #[arg(group = "sources")] - pub(crate) package: Vec, + pub package: Vec, /// Install all packages listed in the given `requirements.txt` files. /// @@ -813,11 +842,11 @@ pub(crate) struct PipInstallArgs { /// /// If `-` is provided, then requirements will be read from stdin. #[arg(long, short, group = "sources", value_parser = parse_file_path)] - pub(crate) requirement: Vec, + pub requirement: Vec, /// Install the editable package based on the provided local file path. #[arg(long, short, group = "sources")] - pub(crate) editable: Vec, + pub editable: Vec, /// Constrain versions using the given requirements files. /// @@ -827,7 +856,7 @@ pub(crate) struct PipInstallArgs { /// /// This is equivalent to pip's `--constraint` option. #[arg(long, short, env = "UV_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)] - pub(crate) constraint: Vec>, + pub constraint: Vec>, /// Override versions using the given requirements files. /// @@ -839,34 +868,34 @@ pub(crate) struct PipInstallArgs { /// constituent packages, overrides are _absolute_, in that they completely replace the /// requirements of the constituent packages. #[arg(long, value_parser = parse_file_path)] - pub(crate) r#override: Vec, + pub r#override: Vec, /// Include optional dependencies from the extra group name; may be provided more than once. /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources. #[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)] - pub(crate) extra: Option>, + pub extra: Option>, /// Include all optional dependencies. /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources. #[arg(long, conflicts_with = "extra", overrides_with = "no_all_extras")] - pub(crate) all_extras: bool, + pub all_extras: bool, #[arg(long, overrides_with("all_extras"), hide = true)] - pub(crate) no_all_extras: bool, + pub no_all_extras: bool, #[command(flatten)] - pub(crate) installer: ResolverInstallerArgs, + pub installer: ResolverInstallerArgs, #[command(flatten)] - pub(crate) refresh: RefreshArgs, + pub refresh: RefreshArgs, /// Ignore package dependencies, instead only installing those packages explicitly listed /// on the command line or in the requirements files. #[arg(long, overrides_with("deps"))] - pub(crate) no_deps: bool, + pub no_deps: bool, #[arg(long, overrides_with("no_deps"), hide = true)] - pub(crate) deps: bool, + pub deps: bool, /// Require a matching hash for each requirement. /// @@ -885,10 +914,10 @@ pub(crate) struct PipInstallArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_require_hashes"), )] - pub(crate) require_hashes: bool, + pub require_hashes: bool, #[arg(long, overrides_with("require_hashes"), hide = true)] - pub(crate) no_require_hashes: bool, + pub no_require_hashes: bool, /// The Python interpreter into which packages should be installed. /// @@ -903,7 +932,7 @@ pub(crate) struct PipInstallArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// Install packages into the system Python. /// @@ -919,10 +948,10 @@ pub(crate) struct PipInstallArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, /// Allow `uv` to modify an `EXTERNALLY-MANAGED` Python installation. /// @@ -936,16 +965,16 @@ pub(crate) struct PipInstallArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_break_system_packages") )] - pub(crate) break_system_packages: bool, + pub break_system_packages: bool, #[arg(long, overrides_with("break_system_packages"))] - pub(crate) no_break_system_packages: bool, + pub no_break_system_packages: bool, /// Install packages into the specified directory, rather than into the virtual environment /// or system Python interpreter. The packages will be installed at the top-level of the /// directory #[arg(long, conflicts_with = "prefix")] - pub(crate) target: Option, + pub target: Option, /// Install packages into `lib`, `bin`, and other top-level folders under the specified /// directory, as if a virtual environment were created at the specified location. @@ -955,15 +984,15 @@ pub(crate) struct PipInstallArgs { /// interpreter, rather than any interpreter added to the `--prefix` directory, rendering them /// non-portable. #[arg(long, conflicts_with = "target")] - pub(crate) prefix: Option, + pub prefix: Option, /// Use legacy `setuptools` behavior when building source distributions without a /// `pyproject.toml`. #[arg(long, overrides_with("no_legacy_setup_py"))] - pub(crate) legacy_setup_py: bool, + pub legacy_setup_py: bool, #[arg(long, overrides_with("legacy_setup_py"), hide = true)] - pub(crate) no_legacy_setup_py: bool, + pub no_legacy_setup_py: bool, /// Disable isolation when building source distributions. /// @@ -974,10 +1003,10 @@ pub(crate) struct PipInstallArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("build_isolation") )] - pub(crate) no_build_isolation: bool, + pub no_build_isolation: bool, #[arg(long, overrides_with("no_build_isolation"), hide = true)] - pub(crate) build_isolation: bool, + pub build_isolation: bool, /// Don't build source distributions. /// @@ -992,7 +1021,7 @@ pub(crate) struct PipInstallArgs { conflicts_with = "only_binary", overrides_with("build") )] - pub(crate) no_build: bool, + pub no_build: bool, #[arg( long, @@ -1001,7 +1030,7 @@ pub(crate) struct PipInstallArgs { overrides_with("no_build"), hide = true )] - pub(crate) build: bool, + pub build: bool, /// Don't install pre-built wheels. /// @@ -1011,7 +1040,7 @@ pub(crate) struct PipInstallArgs { /// Multiple packages may be provided. Disable binaries for all packages with `:all:`. /// Clear previously specified packages with `:none:`. #[arg(long, conflicts_with = "no_build")] - pub(crate) no_binary: Option>, + pub no_binary: Option>, /// Only use pre-built wheels; don't build source distributions. /// @@ -1022,7 +1051,7 @@ pub(crate) struct PipInstallArgs { /// Multiple packages may be provided. Disable binaries for all packages with `:all:`. /// Clear previously specified packages with `:none:`. #[arg(long, conflicts_with = "no_build")] - pub(crate) only_binary: Option>, + pub only_binary: Option>, /// The minimum Python version that should be supported by the requirements (e.g., /// `3.7` or `3.7.9`). @@ -1030,7 +1059,7 @@ pub(crate) struct PipInstallArgs { /// If a patch version is omitted, the minimum patch version is assumed. For example, `3.7` is /// mapped to `3.7.0`. #[arg(long)] - pub(crate) python_version: Option, + pub python_version: Option, /// The platform for which requirements should be installed. /// @@ -1044,36 +1073,36 @@ pub(crate) struct PipInstallArgs { /// the _target_ platform, as they will be built for the _current_ platform. The /// `--python-platform` option is intended for advanced use cases. #[arg(long)] - pub(crate) python_platform: Option, + pub python_platform: Option, /// Validate the virtual environment after completing the installation, to detect packages with /// missing dependencies or other issues. #[arg(long, overrides_with("no_strict"))] - pub(crate) strict: bool, + pub strict: bool, #[arg(long, overrides_with("strict"), hide = true)] - pub(crate) no_strict: bool, + pub no_strict: bool, /// Perform a dry run, i.e., don't actually install anything but resolve the dependencies and /// print the resulting plan. #[arg(long)] - pub(crate) dry_run: bool, + pub dry_run: bool, #[command(flatten)] - pub(crate) compat_args: compat::PipInstallCompatArgs, + pub compat_args: compat::PipInstallCompatArgs, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] #[command(group = clap::ArgGroup::new("sources").required(true).multiple(true))] -pub(crate) struct PipUninstallArgs { +pub struct PipUninstallArgs { /// Uninstall all listed packages. #[arg(group = "sources")] - pub(crate) package: Vec, + pub package: Vec, /// Uninstall all packages listed in the given requirements files. #[arg(long, short, group = "sources", value_parser = parse_file_path)] - pub(crate) requirement: Vec, + pub requirement: Vec, /// The Python interpreter from which packages should be uninstalled. /// @@ -1088,7 +1117,7 @@ pub(crate) struct PipUninstallArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// Attempt to use `keyring` for authentication for remote requirements files. /// @@ -1097,7 +1126,7 @@ pub(crate) struct PipUninstallArgs { /// /// Defaults to `disabled`. #[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")] - pub(crate) keyring_provider: Option, + pub keyring_provider: Option, /// Use the system Python to uninstall packages. /// @@ -1113,10 +1142,10 @@ pub(crate) struct PipUninstallArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, /// Allow `uv` to modify an `EXTERNALLY-MANAGED` Python installation. /// @@ -1130,34 +1159,34 @@ pub(crate) struct PipUninstallArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_break_system_packages") )] - pub(crate) break_system_packages: bool, + pub break_system_packages: bool, #[arg(long, overrides_with("break_system_packages"))] - pub(crate) no_break_system_packages: bool, + pub no_break_system_packages: bool, /// Uninstall packages from the specified `--target` directory. #[arg(long, conflicts_with = "prefix")] - pub(crate) target: Option, + pub target: Option, /// Uninstall packages from the specified `--prefix` directory. #[arg(long, conflicts_with = "target")] - pub(crate) prefix: Option, + pub prefix: Option, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipFreezeArgs { +pub struct PipFreezeArgs { /// Exclude any editable packages from output. #[arg(long)] - pub(crate) exclude_editable: bool, + pub exclude_editable: bool, /// Validate the virtual environment, to detect packages with missing dependencies or other /// issues. #[arg(long, overrides_with("no_strict"))] - pub(crate) strict: bool, + pub strict: bool, #[arg(long, overrides_with("strict"), hide = true)] - pub(crate) no_strict: bool, + pub no_strict: bool, /// The Python interpreter for which packages should be listed. /// @@ -1171,7 +1200,7 @@ pub(crate) struct PipFreezeArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// List packages for the system Python. /// @@ -1188,38 +1217,38 @@ pub(crate) struct PipFreezeArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipListArgs { +pub struct PipListArgs { /// Only include editable projects. #[arg(short, long)] - pub(crate) editable: bool, + pub editable: bool, /// Exclude any editable packages from output. #[arg(long)] - pub(crate) exclude_editable: bool, + pub exclude_editable: bool, /// Exclude the specified package(s) from the output. #[arg(long)] - pub(crate) r#exclude: Vec, + pub r#exclude: Vec, /// Select the output format between: `columns` (default), `freeze`, or `json`. #[arg(long, value_enum, default_value_t = ListFormat::default())] - pub(crate) format: ListFormat, + pub format: ListFormat, /// Validate the virtual environment, to detect packages with missing dependencies or other /// issues. #[arg(long, overrides_with("no_strict"))] - pub(crate) strict: bool, + pub strict: bool, #[arg(long, overrides_with("strict"), hide = true)] - pub(crate) no_strict: bool, + pub no_strict: bool, /// The Python interpreter for which packages should be listed. /// @@ -1233,7 +1262,7 @@ pub(crate) struct PipListArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// List packages for the system Python. /// @@ -1250,18 +1279,18 @@ pub(crate) struct PipListArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, #[command(flatten)] - pub(crate) compat_args: compat::PipListCompatArgs, + pub compat_args: compat::PipListCompatArgs, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipCheckArgs { +pub struct PipCheckArgs { /// The Python interpreter for which packages should be listed. /// /// By default, `uv` lists packages in the currently activated virtual environment, or a virtual @@ -1274,7 +1303,7 @@ pub(crate) struct PipCheckArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// List packages for the system Python. /// @@ -1291,25 +1320,25 @@ pub(crate) struct PipCheckArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipShowArgs { +pub struct PipShowArgs { /// The package(s) to display. - pub(crate) package: Vec, + pub package: Vec, /// Validate the virtual environment, to detect packages with missing dependencies or other /// issues. #[arg(long, overrides_with("no_strict"))] - pub(crate) strict: bool, + pub strict: bool, #[arg(long, overrides_with("strict"), hide = true)] - pub(crate) no_strict: bool, + pub no_strict: bool, /// The Python interpreter for which packages should be listed. /// @@ -1323,7 +1352,7 @@ pub(crate) struct PipShowArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// List packages for the system Python. /// @@ -1340,22 +1369,22 @@ pub(crate) struct PipShowArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct PipTreeArgs { +pub struct PipTreeArgs { /// Validate the virtual environment, to detect packages with missing dependencies or other /// issues. #[arg(long, overrides_with("no_strict"))] - pub(crate) strict: bool, + pub strict: bool, #[arg(long, overrides_with("strict"), hide = true)] - pub(crate) no_strict: bool, + pub no_strict: bool, /// The Python interpreter for which packages should be listed. /// @@ -1369,7 +1398,7 @@ pub(crate) struct PipTreeArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// List packages for the system Python. /// @@ -1386,15 +1415,15 @@ pub(crate) struct PipTreeArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"))] - pub(crate) no_system: bool, + pub no_system: bool, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct VenvArgs { +pub struct VenvArgs { /// The Python interpreter to use for the virtual environment. /// /// Supported formats: @@ -1406,7 +1435,7 @@ pub(crate) struct VenvArgs { /// Note that this is different from `--python-version` in `pip compile`, which takes `3.10` or `3.10.13` and /// doesn't look for a Python interpreter on disk. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// Use the system Python to uninstall packages. /// @@ -1422,14 +1451,14 @@ pub(crate) struct VenvArgs { value_parser = clap::builder::BoolishValueParser::new(), overrides_with("no_system") )] - pub(crate) system: bool, + pub system: bool, #[arg(long, overrides_with("system"), hide = true)] - pub(crate) no_system: bool, + pub no_system: bool, /// Install seed packages (`pip`, `setuptools`, and `wheel`) into the virtual environment. #[arg(long)] - pub(crate) seed: bool, + pub seed: bool, /// Preserve any existing files or directories at the target path. /// @@ -1441,11 +1470,11 @@ pub(crate) struct VenvArgs { /// WARNING: This option can lead to unexpected behavior if the existing virtual environment /// and the newly-created virtual environment are linked to different Python interpreters. #[clap(long)] - pub(crate) allow_existing: bool, + pub allow_existing: bool, /// The path to the virtual environment to create. #[arg(default_value = ".venv")] - pub(crate) name: PathBuf, + pub name: PathBuf, /// Provide an alternative prompt prefix for the virtual environment. /// @@ -1457,7 +1486,7 @@ pub(crate) struct VenvArgs { /// - `.`: Use the current directory name. /// - Any string: Use the given string. #[arg(long, verbatim_doc_comment)] - pub(crate) prompt: Option, + pub prompt: Option, /// Give the virtual environment access to the system site packages directory. /// @@ -1467,10 +1496,10 @@ pub(crate) struct VenvArgs { /// with access to the system site packages directory at runtime, but it will not affect the /// behavior of `uv` commands. #[arg(long)] - pub(crate) system_site_packages: bool, + pub system_site_packages: bool, #[command(flatten)] - pub(crate) index_args: IndexArgs, + pub index_args: IndexArgs, /// The strategy to use when resolving against multiple index URLs. /// @@ -1479,7 +1508,7 @@ pub(crate) struct VenvArgs { /// "dependency confusion" attacks, whereby an attack can upload a malicious package under the /// same name to a secondary #[arg(long, value_enum, env = "UV_INDEX_STRATEGY")] - pub(crate) index_strategy: Option, + pub index_strategy: Option, /// Attempt to use `keyring` for authentication for index URLs. /// @@ -1488,14 +1517,14 @@ pub(crate) struct VenvArgs { /// /// Defaults to `disabled`. #[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")] - pub(crate) keyring_provider: Option, + pub keyring_provider: Option, /// Limit candidate packages to those that were uploaded prior to the given date. /// /// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and UTC dates in the same /// format (e.g., `2006-12-02`). #[arg(long, env = "UV_EXCLUDE_NEWER")] - pub(crate) exclude_newer: Option, + pub exclude_newer: Option, /// The method to use when installing packages from the global cache. /// @@ -1504,14 +1533,14 @@ pub(crate) struct VenvArgs { /// Defaults to `clone` (also known as Copy-on-Write) on macOS, and `hardlink` on Linux and /// Windows. #[arg(long, value_enum, env = "UV_LINK_MODE")] - pub(crate) link_mode: Option, + pub link_mode: Option, #[command(flatten)] - pub(crate) compat_args: compat::VenvCompatArgs, + pub compat_args: compat::VenvCompatArgs, } #[derive(Parser, Debug, Clone)] -pub(crate) enum ExternalCommand { +pub enum ExternalCommand { #[command(external_subcommand)] Cmd(Vec), } @@ -1527,7 +1556,7 @@ impl Deref for ExternalCommand { } impl ExternalCommand { - pub(crate) fn split(&self) -> (Option<&OsString>, &[OsString]) { + pub fn split(&self) -> (Option<&OsString>, &[OsString]) { match self.as_slice() { [] => (None, &[]), [cmd, args @ ..] => (Some(cmd), args), @@ -1537,44 +1566,44 @@ impl ExternalCommand { #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct RunArgs { +pub struct RunArgs { /// Include optional dependencies from the extra group name; may be provided more than once. /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources. #[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)] - pub(crate) extra: Option>, + pub extra: Option>, /// Include all optional dependencies. /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources. #[arg(long, conflicts_with = "extra")] - pub(crate) all_extras: bool, + pub all_extras: bool, #[arg(long, overrides_with("all_extras"), hide = true)] - pub(crate) no_all_extras: bool, + pub no_all_extras: bool, /// Include development dependencies. #[arg(long, overrides_with("no_dev"), hide = true)] - pub(crate) dev: bool, + pub dev: bool, /// Omit development dependencies. #[arg(long, overrides_with("dev"))] - pub(crate) no_dev: bool, + pub no_dev: bool, /// The command to run. #[command(subcommand)] - pub(crate) command: ExternalCommand, + pub command: ExternalCommand, /// Run with the given packages installed. #[arg(long)] - pub(crate) with: Vec, + pub with: Vec, #[command(flatten)] - pub(crate) installer: ResolverInstallerArgs, + pub installer: ResolverInstallerArgs, #[command(flatten)] - pub(crate) build: BuildArgs, + pub build: BuildArgs, #[command(flatten)] - pub(crate) refresh: RefreshArgs, + pub refresh: RefreshArgs, /// The Python interpreter to use to build the run environment. /// @@ -1588,50 +1617,50 @@ pub(crate) struct RunArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, /// Run the command in a different package in the workspace. #[arg(long, conflicts_with = "isolated")] - pub(crate) package: Option, + pub package: Option, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct SyncArgs { +pub struct SyncArgs { /// Include optional dependencies from the extra group name; may be provided more than once. /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources. #[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)] - pub(crate) extra: Option>, + pub extra: Option>, /// Include all optional dependencies. /// Only applies to `pyproject.toml`, `setup.py`, and `setup.cfg` sources. #[arg(long, conflicts_with = "extra")] - pub(crate) all_extras: bool, + pub all_extras: bool, #[arg(long, overrides_with("all_extras"), hide = true)] - pub(crate) no_all_extras: bool, + pub no_all_extras: bool, /// Include development dependencies. #[arg(long, overrides_with("no_dev"), hide = true)] - pub(crate) dev: bool, + pub dev: bool, /// Omit development dependencies. #[arg(long, overrides_with("dev"))] - pub(crate) no_dev: bool, + pub no_dev: bool, /// Does not clean the environment. /// Without this flag any extraneous installations will be removed. #[arg(long)] - pub(crate) no_clean: bool, + pub no_clean: bool, #[command(flatten)] - pub(crate) installer: InstallerArgs, + pub installer: InstallerArgs, #[command(flatten)] - pub(crate) build: BuildArgs, + pub build: BuildArgs, #[command(flatten)] - pub(crate) refresh: RefreshArgs, + pub refresh: RefreshArgs, /// The Python interpreter to use to build the run environment. /// @@ -1645,20 +1674,20 @@ pub(crate) struct SyncArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct LockArgs { +pub struct LockArgs { #[command(flatten)] - pub(crate) resolver: ResolverArgs, + pub resolver: ResolverArgs, #[command(flatten)] - pub(crate) build: BuildArgs, + pub build: BuildArgs, #[command(flatten)] - pub(crate) refresh: RefreshArgs, + pub refresh: RefreshArgs, /// The Python interpreter to use to build the run environment. /// @@ -1672,54 +1701,54 @@ pub(crate) struct LockArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct AddArgs { +pub struct AddArgs { /// The packages to add, as PEP 508 requirements (e.g., `flask==2.2.3`). #[arg(required = true)] - pub(crate) requirements: Vec, + pub requirements: Vec, /// Add the requirements as development dependencies. #[arg(long)] - pub(crate) dev: bool, + pub dev: bool, /// Add the requirements as workspace dependencies. #[arg(long)] - pub(crate) workspace: bool, + pub workspace: bool, /// Add the requirements as editables. #[arg(long, default_missing_value = "true", num_args(0..=1))] - pub(crate) editable: Option, + pub editable: Option, /// Add source requirements to the `project.dependencies` section of the `pyproject.toml`. /// /// Without this flag uv will try to use `tool.uv.sources` for any sources. #[arg(long)] - pub(crate) raw: bool, + pub raw: bool, /// Specific commit to use when adding from Git. #[arg(long)] - pub(crate) rev: Option, + pub rev: Option, /// Tag to use when adding from git. #[arg(long)] - pub(crate) tag: Option, + pub tag: Option, /// Branch to use when adding from git. #[arg(long)] - pub(crate) branch: Option, + pub branch: Option, #[command(flatten)] - pub(crate) installer: ResolverInstallerArgs, + pub installer: ResolverInstallerArgs, #[command(flatten)] - pub(crate) build: BuildArgs, + pub build: BuildArgs, #[command(flatten)] - pub(crate) refresh: RefreshArgs, + pub refresh: RefreshArgs, /// The Python interpreter into which packages should be installed. /// @@ -1734,19 +1763,19 @@ pub(crate) struct AddArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct RemoveArgs { +pub struct RemoveArgs { /// The names of the packages to remove (e.g., `flask`). #[arg(required = true)] - pub(crate) requirements: Vec, + pub requirements: Vec, /// Remove the requirements from development dependencies. #[arg(long)] - pub(crate) dev: bool, + pub dev: bool, /// The Python interpreter into which packages should be installed. /// @@ -1761,47 +1790,47 @@ pub(crate) struct RemoveArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct ToolNamespace { +pub struct ToolNamespace { #[command(subcommand)] - pub(crate) command: ToolCommand, + pub command: ToolCommand, } #[derive(Subcommand)] -pub(crate) enum ToolCommand { +pub enum ToolCommand { /// Run a tool Run(ToolRunArgs), } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct ToolRunArgs { +pub struct ToolRunArgs { /// The command to run. #[command(subcommand)] - pub(crate) command: ExternalCommand, + pub command: ExternalCommand, /// Use the given package to provide the command. /// /// By default, the package name is assumed to match the command name. #[arg(long)] - pub(crate) from: Option, + pub from: Option, /// Include the following extra requirements. #[arg(long)] - pub(crate) with: Vec, + pub with: Vec, #[command(flatten)] - pub(crate) installer: ResolverInstallerArgs, + pub installer: ResolverInstallerArgs, #[command(flatten)] - pub(crate) build: BuildArgs, + pub build: BuildArgs, #[command(flatten)] - pub(crate) refresh: RefreshArgs, + pub refresh: RefreshArgs, /// The Python interpreter to use to build the run environment. /// @@ -1815,18 +1844,18 @@ pub(crate) struct ToolRunArgs { /// - `python3.10` or `python.exe` looks for a binary with the given name in `PATH`. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] - pub(crate) python: Option, + pub python: Option, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct ToolchainNamespace { +pub struct ToolchainNamespace { #[command(subcommand)] - pub(crate) command: ToolchainCommand, + pub command: ToolchainCommand, } #[derive(Subcommand)] -pub(crate) enum ToolchainCommand { +pub enum ToolchainCommand { /// List the available toolchains. List(ToolchainListArgs), @@ -1840,45 +1869,45 @@ pub(crate) enum ToolchainCommand { #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct ToolchainListArgs { +pub struct ToolchainListArgs { /// List all toolchain versions, including outdated patch versions. #[arg(long)] - pub(crate) all_versions: bool, + pub all_versions: bool, /// List toolchains for all platforms. #[arg(long)] - pub(crate) all_platforms: bool, + pub all_platforms: bool, /// Only show installed toolchains, exclude available downloads. #[arg(long)] - pub(crate) only_installed: bool, + pub only_installed: bool, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct ToolchainInstallArgs { +pub struct ToolchainInstallArgs { /// The toolchains to install. /// /// If not provided, the requested toolchain(s) will be read from the `.python-versions` /// or `.python-version` files. If neither file is present, uv will check if it has /// installed any toolchains. If not, it will install the latest stable version of Python. - pub(crate) targets: Vec, + pub targets: Vec, /// Force the installation of the toolchain, even if it is already installed. #[arg(long, short)] - pub(crate) force: bool, + pub force: bool, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct ToolchainFindArgs { +pub struct ToolchainFindArgs { /// The toolchain request. - pub(crate) request: Option, + pub request: Option, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct IndexArgs { +pub struct IndexArgs { /// The URL of the Python package index (by default: ). /// /// Accepts either a repository compliant with PEP 503 (the simple repository API), or a local @@ -1887,7 +1916,7 @@ pub(crate) struct IndexArgs { /// The index given by this flag is given lower priority than all other /// indexes specified via the `--extra-index-url` flag. #[arg(long, short, env = "UV_INDEX_URL", value_parser = parse_index_url)] - pub(crate) index_url: Option>, + pub index_url: Option>, /// Extra URLs of package indexes to use, in addition to `--index-url`. /// @@ -1898,7 +1927,7 @@ pub(crate) struct IndexArgs { /// in `--index-url` (which defaults to PyPI). And when multiple /// `--extra-index-url` flags are given, earlier values take priority. #[arg(long, env = "UV_EXTRA_INDEX_URL", value_delimiter = ' ', value_parser = parse_index_url)] - pub(crate) extra_index_url: Option>>, + pub extra_index_url: Option>>, /// Locations to search for candidate distributions, beyond those found in the indexes. /// @@ -1907,20 +1936,20 @@ pub(crate) struct IndexArgs { /// /// If a URL, the page must contain a flat list of links to package files. #[arg(long, short)] - pub(crate) find_links: Option>, + pub find_links: Option>, /// Ignore the registry index (e.g., PyPI), instead relying on direct URL dependencies and those /// discovered via `--find-links`. #[arg(long)] - pub(crate) no_index: bool, + pub no_index: bool, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct RefreshArgs { +pub struct RefreshArgs { /// Refresh all cached data. #[arg(long, conflicts_with("offline"), overrides_with("no_refresh"))] - pub(crate) refresh: bool, + pub refresh: bool, #[arg( long, @@ -1928,63 +1957,63 @@ pub(crate) struct RefreshArgs { overrides_with("refresh"), hide = true )] - pub(crate) no_refresh: bool, + pub no_refresh: bool, /// Refresh cached data for a specific package. #[arg(long)] - pub(crate) refresh_package: Vec, + pub refresh_package: Vec, } #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct BuildArgs { +pub struct BuildArgs { /// Don't build source distributions. /// /// When enabled, resolving will not run arbitrary code. The cached wheels of already-built /// source distributions will be reused, but operations that require building distributions will /// exit with an error. #[arg(long, overrides_with("build"))] - pub(crate) no_build: bool, + pub no_build: bool, #[arg(long, overrides_with("no_build"), hide = true)] - pub(crate) build: bool, + pub build: bool, /// Don't build source distributions for a specific package. #[arg(long)] - pub(crate) no_build_package: Vec, + pub no_build_package: Vec, /// Don't install pre-built wheels. /// /// The given packages will be installed from a source distribution. The resolver /// will still use pre-built wheels for metadata. #[arg(long, overrides_with("binary"))] - pub(crate) no_binary: bool, + pub no_binary: bool, #[arg(long, overrides_with("no_binary"), hide = true)] - pub(crate) binary: bool, + pub binary: bool, /// Don't install pre-built wheels for a specific package. #[arg(long)] - pub(crate) no_binary_package: Vec, + pub no_binary_package: Vec, } /// Arguments that are used by commands that need to install (but not resolve) packages. #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct InstallerArgs { +pub struct InstallerArgs { #[command(flatten)] - pub(crate) index_args: IndexArgs, + pub index_args: IndexArgs, /// Reinstall all packages, regardless of whether they're already installed. #[arg(long, alias = "force-reinstall", overrides_with("no_reinstall"))] - pub(crate) reinstall: bool, + pub reinstall: bool, #[arg(long, overrides_with("reinstall"), hide = true)] - pub(crate) no_reinstall: bool, + pub no_reinstall: bool, /// Reinstall a specific package, regardless of whether it's already installed. #[arg(long)] - pub(crate) reinstall_package: Vec, + pub reinstall_package: Vec, /// The strategy to use when resolving against multiple index URLs. /// @@ -1993,7 +2022,7 @@ pub(crate) struct InstallerArgs { /// "dependency confusion" attacks, whereby an attack can upload a malicious package under the /// same name to a secondary #[arg(long, value_enum, env = "UV_INDEX_STRATEGY")] - pub(crate) index_strategy: Option, + pub index_strategy: Option, /// Attempt to use `keyring` for authentication for index URLs. /// @@ -2002,18 +2031,18 @@ pub(crate) struct InstallerArgs { /// /// Defaults to `disabled`. #[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")] - pub(crate) keyring_provider: Option, + pub keyring_provider: Option, /// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs. #[arg(long, short = 'C', alias = "config-settings")] - pub(crate) config_setting: Option>, + pub config_setting: Option>, /// The method to use when installing packages from the global cache. /// /// Defaults to `clone` (also known as Copy-on-Write) on macOS, and `hardlink` on Linux and /// Windows. #[arg(long, value_enum, env = "UV_LINK_MODE")] - pub(crate) link_mode: Option, + pub link_mode: Option, /// Compile Python files to bytecode. /// @@ -2025,7 +2054,7 @@ pub(crate) struct InstallerArgs { /// The compile option will process the entire site-packages directory for consistency and /// (like pip) ignore all errors. #[arg(long, alias = "compile", overrides_with("no_compile_bytecode"))] - pub(crate) compile_bytecode: bool, + pub compile_bytecode: bool, #[arg( long, @@ -2033,27 +2062,27 @@ pub(crate) struct InstallerArgs { overrides_with("compile_bytecode"), hide = true )] - pub(crate) no_compile_bytecode: bool, + pub no_compile_bytecode: bool, } /// Arguments that are used by commands that need to resolve (but not install) packages. #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct ResolverArgs { +pub struct ResolverArgs { #[command(flatten)] - pub(crate) index_args: IndexArgs, + pub index_args: IndexArgs, /// Allow package upgrades, ignoring pinned versions in any existing output file. #[arg(long, short = 'U', overrides_with("no_upgrade"))] - pub(crate) upgrade: bool, + pub upgrade: bool, #[arg(long, overrides_with("upgrade"), hide = true)] - pub(crate) no_upgrade: bool, + pub no_upgrade: bool, /// Allow upgrades for a specific package, ignoring pinned versions in any existing output /// file. #[arg(long, short = 'P')] - pub(crate) upgrade_package: Vec, + pub upgrade_package: Vec, /// The strategy to use when resolving against multiple index URLs. /// @@ -2062,7 +2091,7 @@ pub(crate) struct ResolverArgs { /// "dependency confusion" attacks, whereby an attack can upload a malicious package under the /// same name to a secondary #[arg(long, value_enum, env = "UV_INDEX_STRATEGY")] - pub(crate) index_strategy: Option, + pub index_strategy: Option, /// Attempt to use `keyring` for authentication for index URLs. /// @@ -2071,14 +2100,14 @@ pub(crate) struct ResolverArgs { /// /// Defaults to `disabled`. #[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")] - pub(crate) keyring_provider: Option, + pub keyring_provider: Option, /// The strategy to use when selecting between the different compatible versions for a given /// package requirement. /// /// By default, `uv` will use the latest compatible version of each package (`highest`). #[arg(long, value_enum, env = "UV_RESOLUTION")] - pub(crate) resolution: Option, + pub resolution: Option, /// The strategy to use when considering pre-release versions. /// @@ -2086,21 +2115,21 @@ pub(crate) struct ResolverArgs { /// along with first-party requirements that contain an explicit pre-release marker in the /// declared specifiers (`if-necessary-or-explicit`). #[arg(long, value_enum, env = "UV_PRERELEASE")] - pub(crate) prerelease: Option, + pub prerelease: Option, #[arg(long, hide = true)] - pub(crate) pre: bool, + pub pre: bool, /// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs. #[arg(long, short = 'C', alias = "config-settings")] - pub(crate) config_setting: Option>, + pub config_setting: Option>, /// Limit candidate packages to those that were uploaded prior to the given date. /// /// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and UTC dates in the same /// format (e.g., `2006-12-02`). #[arg(long, env = "UV_EXCLUDE_NEWER")] - pub(crate) exclude_newer: Option, + pub exclude_newer: Option, /// The method to use when installing packages from the global cache. /// @@ -2109,38 +2138,38 @@ pub(crate) struct ResolverArgs { /// Defaults to `clone` (also known as Copy-on-Write) on macOS, and `hardlink` on Linux and /// Windows. #[arg(long, value_enum, env = "UV_LINK_MODE")] - pub(crate) link_mode: Option, + pub link_mode: Option, } /// Arguments that are used by commands that need to resolve and install packages. #[derive(Args)] #[allow(clippy::struct_excessive_bools)] -pub(crate) struct ResolverInstallerArgs { +pub struct ResolverInstallerArgs { #[command(flatten)] - pub(crate) index_args: IndexArgs, + pub index_args: IndexArgs, /// Allow package upgrades, ignoring pinned versions in any existing output file. #[arg(long, short = 'U', overrides_with("no_upgrade"))] - pub(crate) upgrade: bool, + pub upgrade: bool, #[arg(long, overrides_with("upgrade"), hide = true)] - pub(crate) no_upgrade: bool, + pub no_upgrade: bool, /// Allow upgrades for a specific package, ignoring pinned versions in any existing output /// file. #[arg(long, short = 'P')] - pub(crate) upgrade_package: Vec, + pub upgrade_package: Vec, /// Reinstall all packages, regardless of whether they're already installed. #[arg(long, alias = "force-reinstall", overrides_with("no_reinstall"))] - pub(crate) reinstall: bool, + pub reinstall: bool, #[arg(long, overrides_with("reinstall"), hide = true)] - pub(crate) no_reinstall: bool, + pub no_reinstall: bool, /// Reinstall a specific package, regardless of whether it's already installed. #[arg(long)] - pub(crate) reinstall_package: Vec, + pub reinstall_package: Vec, /// The strategy to use when resolving against multiple index URLs. /// @@ -2149,7 +2178,7 @@ pub(crate) struct ResolverInstallerArgs { /// "dependency confusion" attacks, whereby an attack can upload a malicious package under the /// same name to a secondary #[arg(long, value_enum, env = "UV_INDEX_STRATEGY")] - pub(crate) index_strategy: Option, + pub index_strategy: Option, /// Attempt to use `keyring` for authentication for index URLs. /// @@ -2158,14 +2187,14 @@ pub(crate) struct ResolverInstallerArgs { /// /// Defaults to `disabled`. #[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")] - pub(crate) keyring_provider: Option, + pub keyring_provider: Option, /// The strategy to use when selecting between the different compatible versions for a given /// package requirement. /// /// By default, `uv` will use the latest compatible version of each package (`highest`). #[arg(long, value_enum, env = "UV_RESOLUTION")] - pub(crate) resolution: Option, + pub resolution: Option, /// The strategy to use when considering pre-release versions. /// @@ -2173,28 +2202,28 @@ pub(crate) struct ResolverInstallerArgs { /// along with first-party requirements that contain an explicit pre-release marker in the /// declared specifiers (`if-necessary-or-explicit`). #[arg(long, value_enum, env = "UV_PRERELEASE")] - pub(crate) prerelease: Option, + pub prerelease: Option, #[arg(long, hide = true)] - pub(crate) pre: bool, + pub pre: bool, /// Settings to pass to the PEP 517 build backend, specified as `KEY=VALUE` pairs. #[arg(long, short = 'C', alias = "config-settings")] - pub(crate) config_setting: Option>, + pub config_setting: Option>, /// Limit candidate packages to those that were uploaded prior to the given date. /// /// Accepts both RFC 3339 timestamps (e.g., `2006-12-02T02:07:43Z`) and UTC dates in the same /// format (e.g., `2006-12-02`). #[arg(long, env = "UV_EXCLUDE_NEWER")] - pub(crate) exclude_newer: Option, + pub exclude_newer: Option, /// The method to use when installing packages from the global cache. /// /// Defaults to `clone` (also known as Copy-on-Write) on macOS, and `hardlink` on Linux and /// Windows. #[arg(long, value_enum, env = "UV_LINK_MODE")] - pub(crate) link_mode: Option, + pub link_mode: Option, /// Compile Python files to bytecode. /// @@ -2206,7 +2235,7 @@ pub(crate) struct ResolverInstallerArgs { /// The compile option will process the entire site-packages directory for consistency and /// (like pip) ignore all errors. #[arg(long, alias = "compile", overrides_with("no_compile_bytecode"))] - pub(crate) compile_bytecode: bool, + pub compile_bytecode: bool, #[arg( long, @@ -2214,5 +2243,5 @@ pub(crate) struct ResolverInstallerArgs { overrides_with("compile_bytecode"), hide = true )] - pub(crate) no_compile_bytecode: bool, + pub no_compile_bytecode: bool, } diff --git a/crates/uv-cli/src/options.rs b/crates/uv-cli/src/options.rs new file mode 100644 index 000000000..d552dbaed --- /dev/null +++ b/crates/uv-cli/src/options.rs @@ -0,0 +1,351 @@ +use uv_cache::Refresh; +use uv_configuration::ConfigSettings; +use uv_resolver::PreReleaseMode; +use uv_settings::{InstallerOptions, PipOptions, ResolverInstallerOptions, ResolverOptions}; + +use crate::{ + BuildArgs, IndexArgs, InstallerArgs, Maybe, RefreshArgs, ResolverArgs, ResolverInstallerArgs, +}; + +/// Given a boolean flag pair (like `--upgrade` and `--no-upgrade`), resolve the value of the flag. +pub fn flag(yes: bool, no: bool) -> Option { + match (yes, no) { + (true, false) => Some(true), + (false, true) => Some(false), + (false, false) => None, + (..) => unreachable!("Clap should make this impossible"), + } +} + +impl From for Refresh { + fn from(value: RefreshArgs) -> Self { + let RefreshArgs { + refresh, + no_refresh, + refresh_package, + } = value; + + Self::from_args(flag(refresh, no_refresh), refresh_package) + } +} + +impl From for PipOptions { + fn from(args: ResolverArgs) -> Self { + let ResolverArgs { + index_args, + upgrade, + no_upgrade, + upgrade_package, + index_strategy, + keyring_provider, + resolution, + prerelease, + pre, + config_setting, + exclude_newer, + link_mode, + } = args; + + Self { + upgrade: flag(upgrade, no_upgrade), + upgrade_package: Some(upgrade_package), + index_strategy, + keyring_provider, + resolution, + prerelease: if pre { + Some(PreReleaseMode::Allow) + } else { + prerelease + }, + config_settings: config_setting + .map(|config_settings| config_settings.into_iter().collect::()), + exclude_newer, + link_mode, + ..PipOptions::from(index_args) + } + } +} + +impl From for PipOptions { + fn from(args: InstallerArgs) -> Self { + let InstallerArgs { + index_args, + reinstall, + no_reinstall, + reinstall_package, + index_strategy, + keyring_provider, + config_setting, + link_mode, + compile_bytecode, + no_compile_bytecode, + } = args; + + Self { + reinstall: flag(reinstall, no_reinstall), + reinstall_package: Some(reinstall_package), + index_strategy, + keyring_provider, + config_settings: config_setting + .map(|config_settings| config_settings.into_iter().collect::()), + link_mode, + compile_bytecode: flag(compile_bytecode, no_compile_bytecode), + ..PipOptions::from(index_args) + } + } +} + +impl From for PipOptions { + fn from(args: ResolverInstallerArgs) -> Self { + let ResolverInstallerArgs { + index_args, + upgrade, + no_upgrade, + upgrade_package, + reinstall, + no_reinstall, + reinstall_package, + index_strategy, + keyring_provider, + resolution, + prerelease, + pre, + config_setting, + exclude_newer, + link_mode, + compile_bytecode, + no_compile_bytecode, + } = args; + + Self { + upgrade: flag(upgrade, no_upgrade), + upgrade_package: Some(upgrade_package), + reinstall: flag(reinstall, no_reinstall), + reinstall_package: Some(reinstall_package), + index_strategy, + keyring_provider, + resolution, + prerelease: if pre { + Some(PreReleaseMode::Allow) + } else { + prerelease + }, + config_settings: config_setting + .map(|config_settings| config_settings.into_iter().collect::()), + exclude_newer, + link_mode, + compile_bytecode: flag(compile_bytecode, no_compile_bytecode), + ..PipOptions::from(index_args) + } + } +} + +impl From for PipOptions { + fn from(args: IndexArgs) -> Self { + let IndexArgs { + index_url, + extra_index_url, + no_index, + find_links, + } = args; + + Self { + index_url: index_url.and_then(Maybe::into_option), + extra_index_url: extra_index_url.map(|extra_index_urls| { + extra_index_urls + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), + no_index: if no_index { Some(true) } else { None }, + find_links, + ..PipOptions::default() + } + } +} + +/// Construct the [`InstallerOptions`] from the [`InstallerArgs`] and [`BuildArgs`]. +pub fn installer_options(installer_args: InstallerArgs, build_args: BuildArgs) -> InstallerOptions { + let InstallerArgs { + index_args, + reinstall, + no_reinstall, + reinstall_package, + index_strategy, + keyring_provider, + config_setting, + link_mode, + compile_bytecode, + no_compile_bytecode, + } = installer_args; + + let BuildArgs { + no_build, + build, + no_build_package, + no_binary, + binary, + no_binary_package, + } = build_args; + + InstallerOptions { + index_url: index_args.index_url.and_then(Maybe::into_option), + extra_index_url: index_args.extra_index_url.map(|extra_index_urls| { + extra_index_urls + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), + no_index: if index_args.no_index { + Some(true) + } else { + None + }, + find_links: index_args.find_links, + reinstall: flag(reinstall, no_reinstall), + reinstall_package: Some(reinstall_package), + index_strategy, + keyring_provider, + config_settings: config_setting + .map(|config_settings| config_settings.into_iter().collect::()), + link_mode, + compile_bytecode: flag(compile_bytecode, no_compile_bytecode), + no_build: flag(no_build, build), + no_build_package: Some(no_build_package), + no_binary: flag(no_binary, binary), + no_binary_package: Some(no_binary_package), + } +} + +/// Construct the [`ResolverOptions`] from the [`ResolverArgs`] and [`BuildArgs`]. +pub fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> ResolverOptions { + let ResolverArgs { + index_args, + upgrade, + no_upgrade, + upgrade_package, + index_strategy, + keyring_provider, + resolution, + prerelease, + pre, + config_setting, + exclude_newer, + link_mode, + } = resolver_args; + + let BuildArgs { + no_build, + build, + no_build_package, + no_binary, + binary, + no_binary_package, + } = build_args; + + ResolverOptions { + index_url: index_args.index_url.and_then(Maybe::into_option), + extra_index_url: index_args.extra_index_url.map(|extra_index_urls| { + extra_index_urls + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), + no_index: if index_args.no_index { + Some(true) + } else { + None + }, + find_links: index_args.find_links, + upgrade: flag(upgrade, no_upgrade), + upgrade_package: Some(upgrade_package), + index_strategy, + keyring_provider, + resolution, + prerelease: if pre { + Some(PreReleaseMode::Allow) + } else { + prerelease + }, + config_settings: config_setting + .map(|config_settings| config_settings.into_iter().collect::()), + exclude_newer, + link_mode, + no_build: flag(no_build, build), + no_build_package: Some(no_build_package), + no_binary: flag(no_binary, binary), + no_binary_package: Some(no_binary_package), + } +} + +/// Construct the [`ResolverInstallerOptions`] from the [`ResolverInstallerArgs`] and [`BuildArgs`]. +pub fn resolver_installer_options( + resolver_installer_args: ResolverInstallerArgs, + build_args: BuildArgs, +) -> ResolverInstallerOptions { + let ResolverInstallerArgs { + index_args, + upgrade, + no_upgrade, + upgrade_package, + reinstall, + no_reinstall, + reinstall_package, + index_strategy, + keyring_provider, + resolution, + prerelease, + pre, + config_setting, + exclude_newer, + link_mode, + compile_bytecode, + no_compile_bytecode, + } = resolver_installer_args; + + let BuildArgs { + no_build, + build, + no_build_package, + no_binary, + binary, + no_binary_package, + } = build_args; + + ResolverInstallerOptions { + index_url: index_args.index_url.and_then(Maybe::into_option), + extra_index_url: index_args.extra_index_url.map(|extra_index_urls| { + extra_index_urls + .into_iter() + .filter_map(Maybe::into_option) + .collect() + }), + no_index: if index_args.no_index { + Some(true) + } else { + None + }, + find_links: index_args.find_links, + upgrade: flag(upgrade, no_upgrade), + upgrade_package: Some(upgrade_package), + reinstall: flag(reinstall, no_reinstall), + reinstall_package: Some(reinstall_package), + index_strategy, + keyring_provider, + resolution, + prerelease: if pre { + Some(PreReleaseMode::Allow) + } else { + prerelease + }, + config_settings: config_setting + .map(|config_settings| config_settings.into_iter().collect::()), + exclude_newer, + link_mode, + compile_bytecode: flag(compile_bytecode, no_compile_bytecode), + no_build: flag(no_build, build), + no_build_package: Some(no_build_package), + no_binary: flag(no_binary, binary), + no_binary_package: Some(no_binary_package), + } +} diff --git a/crates/uv-cli/src/version.rs b/crates/uv-cli/src/version.rs new file mode 100644 index 000000000..87593f787 --- /dev/null +++ b/crates/uv-cli/src/version.rs @@ -0,0 +1,149 @@ +//! Code for representing uv's release version number. +// See also +use std::fmt; + +use serde::Serialize; + +/// Information about the git repository where uv was built from. +#[derive(Serialize)] +pub(crate) struct CommitInfo { + short_commit_hash: String, + commit_hash: String, + commit_date: String, + last_tag: Option, + commits_since_last_tag: u32, +} + +/// uv's version. +#[derive(Serialize)] +pub struct VersionInfo { + /// uv's version, such as "0.5.1" + version: String, + /// Information about the git commit we may have been built from. + /// + /// `None` if not built from a git repo or if retrieval failed. + commit_info: Option, +} + +impl fmt::Display for VersionInfo { + /// Formatted version information: "[+] ( )" + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.version)?; + + if let Some(ref ci) = self.commit_info { + if ci.commits_since_last_tag > 0 { + write!(f, "+{}", ci.commits_since_last_tag)?; + } + write!(f, " ({} {})", ci.short_commit_hash, ci.commit_date)?; + } + + Ok(()) + } +} + +impl From for clap::builder::Str { + fn from(val: VersionInfo) -> Self { + val.to_string().into() + } +} + +/// Returns information about uv's version. +pub fn version() -> VersionInfo { + // Environment variables are only read at compile-time + macro_rules! option_env_str { + ($name:expr) => { + option_env!($name).map(|s| s.to_string()) + }; + } + + // This version is pulled from Cargo.toml and set by Cargo + let version = uv_version::version().to_string(); + + // Commit info is pulled from git and set by `build.rs` + let commit_info = option_env_str!("UV_COMMIT_HASH").map(|commit_hash| CommitInfo { + short_commit_hash: option_env_str!("UV_COMMIT_SHORT_HASH").unwrap(), + commit_hash, + commit_date: option_env_str!("UV_COMMIT_DATE").unwrap(), + last_tag: option_env_str!("UV_LAST_TAG"), + commits_since_last_tag: option_env_str!("UV_LAST_TAG_DISTANCE") + .as_deref() + .map_or(0, |value| value.parse::().unwrap_or(0)), + }); + + VersionInfo { + version, + commit_info, + } +} + +#[cfg(test)] +mod tests { + use insta::{assert_json_snapshot, assert_snapshot}; + + use super::{CommitInfo, VersionInfo}; + + #[test] + fn version_formatting() { + let version = VersionInfo { + version: "0.0.0".to_string(), + commit_info: None, + }; + assert_snapshot!(version, @"0.0.0"); + } + + #[test] + fn version_formatting_with_commit_info() { + let version = VersionInfo { + version: "0.0.0".to_string(), + commit_info: Some(CommitInfo { + short_commit_hash: "53b0f5d92".to_string(), + commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(), + last_tag: Some("v0.0.1".to_string()), + commit_date: "2023-10-19".to_string(), + commits_since_last_tag: 0, + }), + }; + assert_snapshot!(version, @"0.0.0 (53b0f5d92 2023-10-19)"); + } + + #[test] + fn version_formatting_with_commits_since_last_tag() { + let version = VersionInfo { + version: "0.0.0".to_string(), + commit_info: Some(CommitInfo { + short_commit_hash: "53b0f5d92".to_string(), + commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(), + last_tag: Some("v0.0.1".to_string()), + commit_date: "2023-10-19".to_string(), + commits_since_last_tag: 24, + }), + }; + assert_snapshot!(version, @"0.0.0+24 (53b0f5d92 2023-10-19)"); + } + + #[test] + fn version_serializable() { + let version = VersionInfo { + version: "0.0.0".to_string(), + commit_info: Some(CommitInfo { + short_commit_hash: "53b0f5d92".to_string(), + commit_hash: "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7".to_string(), + last_tag: Some("v0.0.1".to_string()), + commit_date: "2023-10-19".to_string(), + commits_since_last_tag: 0, + }), + }; + assert_json_snapshot!(version, @r###" + { + "version": "0.0.0", + "commit_info": { + "short_commit_hash": "53b0f5d92", + "commit_hash": "53b0f5d924110e5b26fbf09f6fd3a03d67b475b7", + "commit_date": "2023-10-19", + "last_tag": "v0.0.1", + "commits_since_last_tag": 0 + } + } + "###); + } +} diff --git a/crates/uv/Cargo.toml b/crates/uv/Cargo.toml index 1521d4fd0..89b06688c 100644 --- a/crates/uv/Cargo.toml +++ b/crates/uv/Cargo.toml @@ -15,15 +15,16 @@ workspace = true [dependencies] distribution-types = { workspace = true } -install-wheel-rs = { workspace = true, features = ["clap"], default-features = false } +install-wheel-rs = { workspace = true, default-features = false } pep440_rs = { workspace = true } pep508_rs = { workspace = true } platform-tags = { workspace = true } pypi-types = { workspace = true } uv-auth = { workspace = true } -uv-cache = { workspace = true, features = ["clap"] } +uv-cache = { workspace = true } +uv-cli = { workspace = true } uv-client = { workspace = true } -uv-configuration = { workspace = true, features = ["clap"] } +uv-configuration = { workspace = true } uv-dispatch = { workspace = true } uv-distribution = { workspace = true } uv-fs = { workspace = true } @@ -31,9 +32,9 @@ uv-git = { workspace = true } uv-installer = { workspace = true } uv-normalize = { workspace = true } uv-requirements = { workspace = true } -uv-resolver = { workspace = true, features = ["clap"] } +uv-resolver = { workspace = true } uv-settings = { workspace = true, features = ["schemars"] } -uv-toolchain = { workspace = true, features = ["clap", "schemars"]} +uv-toolchain = { workspace = true, features = ["schemars"]} uv-types = { workspace = true } uv-virtualenv = { workspace = true } uv-warnings = { workspace = true } @@ -43,7 +44,6 @@ anyhow = { workspace = true } axoupdater = { workspace = true, features = ["github_releases", "tokio"], optional = true } chrono = { workspace = true } clap = { workspace = true, features = ["derive", "string", "wrap_help"] } -clap_complete_command = { workspace = true } flate2 = { workspace = true, default-features = false } fs-err = { workspace = true, features = ["tokio"] } futures = { workspace = true } @@ -100,7 +100,5 @@ pypi = [] # Introduces a dependency on Git. git = [] # Adds self-update functionality. -self-update = ["axoupdater"] +self-update = ["axoupdater", "uv-cli/self-update"] -[build-dependencies] -fs-err = { workspace = true } diff --git a/crates/uv/src/commands/mod.rs b/crates/uv/src/commands/mod.rs index 5dee4eeae..1dcc0fe2c 100644 --- a/crates/uv/src/commands/mod.rs +++ b/crates/uv/src/commands/mod.rs @@ -9,7 +9,7 @@ pub(crate) use cache_dir::cache_dir; pub(crate) use cache_prune::cache_prune; use distribution_types::InstalledMetadata; pub(crate) use pip::check::pip_check; -pub(crate) use pip::compile::{extra_name_with_clap_error, pip_compile}; +pub(crate) use pip::compile::pip_compile; pub(crate) use pip::freeze::pip_freeze; pub(crate) use pip::install::pip_install; pub(crate) use pip::list::pip_list; @@ -111,24 +111,6 @@ pub(super) struct DryRunEvent { kind: ChangeEventKind, } -#[derive(Debug, Clone, Copy, clap::ValueEnum)] -pub(crate) enum VersionFormat { - Text, - Json, -} - -#[derive(Debug, Default, Clone, clap::ValueEnum)] -pub(crate) enum ListFormat { - /// Display the list of packages in a human-readable table. - #[default] - Columns, - /// Display the list of packages in a `pip freeze`-like format, with one package per line - /// alongside its version. - Freeze, - /// Display the list of packages in a machine-readable JSON format. - Json, -} - /// Compile all Python source files in site-packages to bytecode, to speed up the /// initial run of any subsequent executions. /// diff --git a/crates/uv/src/commands/pip/compile.rs b/crates/uv/src/commands/pip/compile.rs index 7bc3447f3..4d6cb1c68 100644 --- a/crates/uv/src/commands/pip/compile.rs +++ b/crates/uv/src/commands/pip/compile.rs @@ -3,13 +3,11 @@ use std::fmt::Write; use std::io::stdout; use std::ops::Deref; use std::path::Path; -use std::str::FromStr; use anstream::{eprint, AutoStream, StripStream}; use anyhow::{anyhow, Result}; use itertools::Itertools; use owo_colors::OwoColorize; -use pypi_types::Requirement; use tracing::debug; use distribution_types::{ @@ -17,6 +15,7 @@ use distribution_types::{ Verbatim, }; use install_wheel_rs::linker::LinkMode; +use pypi_types::Requirement; use uv_auth::store_credentials_from_url; use uv_cache::Cache; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder}; @@ -29,7 +28,7 @@ use uv_dispatch::BuildDispatch; use uv_distribution::DistributionDatabase; use uv_fs::Simplified; use uv_git::GitResolver; -use uv_normalize::{ExtraName, PackageName}; +use uv_normalize::PackageName; use uv_requirements::{ upgrade::read_requirements_txt, LookaheadResolver, NamedRequirementsResolver, RequirementsSource, RequirementsSpecification, SourceTreeResolver, @@ -721,15 +720,6 @@ impl OutputWriter { } } -pub(crate) fn extra_name_with_clap_error(arg: &str) -> Result { - ExtraName::from_str(arg).map_err(|_err| { - anyhow!( - "Extra names must start and end with a letter or digit and may only \ - contain -, _, ., and alphanumeric characters" - ) - }) -} - /// An owned or unowned [`InMemoryIndex`]. enum InMemoryIndexRef<'a> { Owned(InMemoryIndex), diff --git a/crates/uv/src/commands/pip/list.rs b/crates/uv/src/commands/pip/list.rs index d3ed49b76..cd2fc8a74 100644 --- a/crates/uv/src/commands/pip/list.rs +++ b/crates/uv/src/commands/pip/list.rs @@ -10,6 +10,7 @@ use unicode_width::UnicodeWidthStr; use distribution_types::{Diagnostic, InstalledDist, Name}; use uv_cache::Cache; +use uv_cli::ListFormat; use uv_configuration::PreviewMode; use uv_fs::Simplified; use uv_installer::SitePackages; @@ -18,7 +19,6 @@ use uv_toolchain::ToolchainRequest; use uv_toolchain::{EnvironmentPreference, PythonEnvironment}; use crate::commands::ExitStatus; -use crate::commands::ListFormat; use crate::printer::Printer; /// Enumerate the installed packages in the current environment. diff --git a/crates/uv/src/commands/project/run.rs b/crates/uv/src/commands/project/run.rs index 36b887db5..086367297 100644 --- a/crates/uv/src/commands/project/run.rs +++ b/crates/uv/src/commands/project/run.rs @@ -7,6 +7,7 @@ use tokio::process::Command; use tracing::debug; use uv_cache::Cache; +use uv_cli::ExternalCommand; use uv_client::{BaseClientBuilder, Connectivity}; use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode}; use uv_distribution::{ProjectWorkspace, Workspace}; @@ -17,7 +18,6 @@ use uv_toolchain::{ }; use uv_warnings::warn_user; -use crate::cli::ExternalCommand; use crate::commands::pip::operations::Modifications; use crate::commands::{project, ExitStatus}; use crate::printer::Printer; diff --git a/crates/uv/src/commands/tool/run.rs b/crates/uv/src/commands/tool/run.rs index 769210fb3..619c8e686 100644 --- a/crates/uv/src/commands/tool/run.rs +++ b/crates/uv/src/commands/tool/run.rs @@ -6,6 +6,7 @@ use tokio::process::Command; use tracing::debug; use uv_cache::Cache; +use uv_cli::ExternalCommand; use uv_client::Connectivity; use uv_configuration::{Concurrency, PreviewMode}; use uv_requirements::RequirementsSource; @@ -14,7 +15,6 @@ use uv_toolchain::{ }; use uv_warnings::warn_user; -use crate::cli::ExternalCommand; use crate::commands::project::update_environment; use crate::commands::ExitStatus; use crate::printer::Printer; diff --git a/crates/uv/src/commands/version.rs b/crates/uv/src/commands/version.rs index 6a1167a9a..5c9e679fc 100644 --- a/crates/uv/src/commands/version.rs +++ b/crates/uv/src/commands/version.rs @@ -1,10 +1,10 @@ use anyhow::Result; -use crate::commands::VersionFormat; +use uv_cli::VersionFormat; /// Display version information pub(crate) fn version(output_format: VersionFormat, buffer: &mut dyn std::io::Write) -> Result<()> { - let version_info = crate::version::version(); + let version_info = uv_cli::version::version(); match output_format { VersionFormat::Text => { diff --git a/crates/uv/src/main.rs b/crates/uv/src/main.rs index 069405b64..5683d07a2 100644 --- a/crates/uv/src/main.rs +++ b/crates/uv/src/main.rs @@ -9,23 +9,23 @@ use anyhow::Result; use clap::error::{ContextKind, ContextValue}; use clap::{CommandFactory, Parser}; use owo_colors::OwoColorize; -use settings::PipTreeSettings; use tracing::{debug, instrument}; -use cli::{ToolCommand, ToolNamespace, ToolchainCommand, ToolchainNamespace}; +use settings::PipTreeSettings; use uv_cache::Cache; +use uv_cli::{ + compat::CompatArgs, CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace, + ProjectCommand, +}; +#[cfg(feature = "self-update")] +use uv_cli::{SelfCommand, SelfNamespace}; +use uv_cli::{ToolCommand, ToolNamespace, ToolchainCommand, ToolchainNamespace}; use uv_configuration::Concurrency; use uv_distribution::Workspace; use uv_requirements::RequirementsSource; use uv_settings::Combine; -use crate::cli::{ - CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace, ProjectCommand, -}; -#[cfg(feature = "self-update")] -use crate::cli::{SelfCommand, SelfNamespace}; use crate::commands::ExitStatus; -use crate::compat::CompatArgs; use crate::settings::{ CacheSettings, GlobalSettings, PipCheckSettings, PipCompileSettings, PipFreezeSettings, PipInstallSettings, PipListSettings, PipShowSettings, PipSyncSettings, PipUninstallSettings, @@ -47,9 +47,7 @@ static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc; #[global_allocator] static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc; -mod cli; mod commands; -mod compat; mod logging; mod printer; mod settings; diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 6605e552e..0b866546b 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -9,6 +9,13 @@ use install_wheel_rs::linker::LinkMode; use pep508_rs::RequirementOrigin; use pypi_types::Requirement; use uv_cache::{CacheArgs, Refresh}; +use uv_cli::options::{flag, installer_options, resolver_installer_options, resolver_options}; +use uv_cli::{ + AddArgs, ColorChoice, Commands, ExternalCommand, GlobalArgs, ListFormat, LockArgs, Maybe, + PipCheckArgs, PipCompileArgs, PipFreezeArgs, PipInstallArgs, PipListArgs, PipShowArgs, + PipSyncArgs, PipTreeArgs, PipUninstallArgs, RemoveArgs, RunArgs, SyncArgs, ToolRunArgs, + ToolchainFindArgs, ToolchainInstallArgs, ToolchainListArgs, VenvArgs, +}; use uv_client::Connectivity; use uv_configuration::{ BuildOptions, Concurrency, ConfigSettings, ExtrasSpecification, IndexStrategy, @@ -24,15 +31,7 @@ use uv_settings::{ }; use uv_toolchain::{Prefix, PythonVersion, Target, ToolchainPreference}; -use crate::cli::{ - AddArgs, BuildArgs, ColorChoice, Commands, ExternalCommand, GlobalArgs, IndexArgs, - InstallerArgs, LockArgs, Maybe, PipCheckArgs, PipCompileArgs, PipFreezeArgs, PipInstallArgs, - PipListArgs, PipShowArgs, PipSyncArgs, PipTreeArgs, PipUninstallArgs, RefreshArgs, RemoveArgs, - ResolverArgs, ResolverInstallerArgs, RunArgs, SyncArgs, ToolRunArgs, ToolchainFindArgs, - ToolchainInstallArgs, ToolchainListArgs, VenvArgs, -}; use crate::commands::pip::operations::Modifications; -use crate::commands::ListFormat; /// The resolved global settings to use for any invocation of the CLI. #[allow(clippy::struct_excessive_bools)] @@ -1707,346 +1706,3 @@ fn parse_failure(name: &str, expected: &str) -> ! { eprintln!("error: invalid value for {name}, expected {expected}"); process::exit(1) } - -/// Given a boolean flag pair (like `--upgrade` and `--no-upgrade`), resolve the value of the flag. -fn flag(yes: bool, no: bool) -> Option { - match (yes, no) { - (true, false) => Some(true), - (false, true) => Some(false), - (false, false) => None, - (..) => unreachable!("Clap should make this impossible"), - } -} - -impl From for Refresh { - fn from(value: RefreshArgs) -> Self { - let RefreshArgs { - refresh, - no_refresh, - refresh_package, - } = value; - - Self::from_args(flag(refresh, no_refresh), refresh_package) - } -} - -impl From for PipOptions { - fn from(args: ResolverArgs) -> Self { - let ResolverArgs { - index_args, - upgrade, - no_upgrade, - upgrade_package, - index_strategy, - keyring_provider, - resolution, - prerelease, - pre, - config_setting, - exclude_newer, - link_mode, - } = args; - - Self { - upgrade: flag(upgrade, no_upgrade), - upgrade_package: Some(upgrade_package), - index_strategy, - keyring_provider, - resolution, - prerelease: if pre { - Some(PreReleaseMode::Allow) - } else { - prerelease - }, - config_settings: config_setting - .map(|config_settings| config_settings.into_iter().collect::()), - exclude_newer, - link_mode, - ..PipOptions::from(index_args) - } - } -} - -impl From for PipOptions { - fn from(args: InstallerArgs) -> Self { - let InstallerArgs { - index_args, - reinstall, - no_reinstall, - reinstall_package, - index_strategy, - keyring_provider, - config_setting, - link_mode, - compile_bytecode, - no_compile_bytecode, - } = args; - - Self { - reinstall: flag(reinstall, no_reinstall), - reinstall_package: Some(reinstall_package), - index_strategy, - keyring_provider, - config_settings: config_setting - .map(|config_settings| config_settings.into_iter().collect::()), - link_mode, - compile_bytecode: flag(compile_bytecode, no_compile_bytecode), - ..PipOptions::from(index_args) - } - } -} - -impl From for PipOptions { - fn from(args: ResolverInstallerArgs) -> Self { - let ResolverInstallerArgs { - index_args, - upgrade, - no_upgrade, - upgrade_package, - reinstall, - no_reinstall, - reinstall_package, - index_strategy, - keyring_provider, - resolution, - prerelease, - pre, - config_setting, - exclude_newer, - link_mode, - compile_bytecode, - no_compile_bytecode, - } = args; - - Self { - upgrade: flag(upgrade, no_upgrade), - upgrade_package: Some(upgrade_package), - reinstall: flag(reinstall, no_reinstall), - reinstall_package: Some(reinstall_package), - index_strategy, - keyring_provider, - resolution, - prerelease: if pre { - Some(PreReleaseMode::Allow) - } else { - prerelease - }, - config_settings: config_setting - .map(|config_settings| config_settings.into_iter().collect::()), - exclude_newer, - link_mode, - compile_bytecode: flag(compile_bytecode, no_compile_bytecode), - ..PipOptions::from(index_args) - } - } -} - -impl From for PipOptions { - fn from(args: IndexArgs) -> Self { - let IndexArgs { - index_url, - extra_index_url, - no_index, - find_links, - } = args; - - Self { - index_url: index_url.and_then(Maybe::into_option), - extra_index_url: extra_index_url.map(|extra_index_urls| { - extra_index_urls - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), - no_index: if no_index { Some(true) } else { None }, - find_links, - ..PipOptions::default() - } - } -} - -/// Construct the [`InstallerOptions`] from the [`InstallerArgs`] and [`BuildArgs`]. -fn installer_options(installer_args: InstallerArgs, build_args: BuildArgs) -> InstallerOptions { - let InstallerArgs { - index_args, - reinstall, - no_reinstall, - reinstall_package, - index_strategy, - keyring_provider, - config_setting, - link_mode, - compile_bytecode, - no_compile_bytecode, - } = installer_args; - - let BuildArgs { - no_build, - build, - no_build_package, - no_binary, - binary, - no_binary_package, - } = build_args; - - InstallerOptions { - index_url: index_args.index_url.and_then(Maybe::into_option), - extra_index_url: index_args.extra_index_url.map(|extra_index_urls| { - extra_index_urls - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), - no_index: if index_args.no_index { - Some(true) - } else { - None - }, - find_links: index_args.find_links, - reinstall: flag(reinstall, no_reinstall), - reinstall_package: Some(reinstall_package), - index_strategy, - keyring_provider, - config_settings: config_setting - .map(|config_settings| config_settings.into_iter().collect::()), - link_mode, - compile_bytecode: flag(compile_bytecode, no_compile_bytecode), - no_build: flag(no_build, build), - no_build_package: Some(no_build_package), - no_binary: flag(no_binary, binary), - no_binary_package: Some(no_binary_package), - } -} - -/// Construct the [`ResolverOptions`] from the [`ResolverArgs`] and [`BuildArgs`]. -fn resolver_options(resolver_args: ResolverArgs, build_args: BuildArgs) -> ResolverOptions { - let ResolverArgs { - index_args, - upgrade, - no_upgrade, - upgrade_package, - index_strategy, - keyring_provider, - resolution, - prerelease, - pre, - config_setting, - exclude_newer, - link_mode, - } = resolver_args; - - let BuildArgs { - no_build, - build, - no_build_package, - no_binary, - binary, - no_binary_package, - } = build_args; - - ResolverOptions { - index_url: index_args.index_url.and_then(Maybe::into_option), - extra_index_url: index_args.extra_index_url.map(|extra_index_urls| { - extra_index_urls - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), - no_index: if index_args.no_index { - Some(true) - } else { - None - }, - find_links: index_args.find_links, - upgrade: flag(upgrade, no_upgrade), - upgrade_package: Some(upgrade_package), - index_strategy, - keyring_provider, - resolution, - prerelease: if pre { - Some(PreReleaseMode::Allow) - } else { - prerelease - }, - config_settings: config_setting - .map(|config_settings| config_settings.into_iter().collect::()), - exclude_newer, - link_mode, - no_build: flag(no_build, build), - no_build_package: Some(no_build_package), - no_binary: flag(no_binary, binary), - no_binary_package: Some(no_binary_package), - } -} - -/// Construct the [`ResolverInstallerOptions`] from the [`ResolverInstallerArgs`] and [`BuildArgs`]. -fn resolver_installer_options( - resolver_installer_args: ResolverInstallerArgs, - build_args: BuildArgs, -) -> ResolverInstallerOptions { - let ResolverInstallerArgs { - index_args, - upgrade, - no_upgrade, - upgrade_package, - reinstall, - no_reinstall, - reinstall_package, - index_strategy, - keyring_provider, - resolution, - prerelease, - pre, - config_setting, - exclude_newer, - link_mode, - compile_bytecode, - no_compile_bytecode, - } = resolver_installer_args; - - let BuildArgs { - no_build, - build, - no_build_package, - no_binary, - binary, - no_binary_package, - } = build_args; - - ResolverInstallerOptions { - index_url: index_args.index_url.and_then(Maybe::into_option), - extra_index_url: index_args.extra_index_url.map(|extra_index_urls| { - extra_index_urls - .into_iter() - .filter_map(Maybe::into_option) - .collect() - }), - no_index: if index_args.no_index { - Some(true) - } else { - None - }, - find_links: index_args.find_links, - upgrade: flag(upgrade, no_upgrade), - upgrade_package: Some(upgrade_package), - reinstall: flag(reinstall, no_reinstall), - reinstall_package: Some(reinstall_package), - index_strategy, - keyring_provider, - resolution, - prerelease: if pre { - Some(PreReleaseMode::Allow) - } else { - prerelease - }, - config_settings: config_setting - .map(|config_settings| config_settings.into_iter().collect::()), - exclude_newer, - link_mode, - compile_bytecode: flag(compile_bytecode, no_compile_bytecode), - no_build: flag(no_build, build), - no_build_package: Some(no_build_package), - no_binary: flag(no_binary, binary), - no_binary_package: Some(no_binary_package), - } -} diff --git a/pyproject.toml b/pyproject.toml index c55563387..7ad356198 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -66,5 +66,6 @@ changelog_contributors = false version_files = [ "README.md", "crates/uv/Cargo.toml", + "crates/uv-cli/Cargo.toml", "crates/uv-version/Cargo.toml", ]