Add persistent configuration for non-pip APIs (#4294)

## Summary

This PR introduces top-level configuration for uv, such that you can do:

```toml
[tool.uv]
index-url = "https://test.pypi.org/simple"
```

And `uv pip compile`, `uv run`, `uv tool run`, etc., will all respect
that configuration.

The settings that were escalated to the top-level remain on
`tool.uv.pip` too, but they're only respected in `uv pip` commands. If
they're specified in both places, then the `pip` settings win out.

While making this change, I also wired up some of the global options,
like `connectivity` and `native_tls`, through to all the relevant
places.

Closes #4250.
This commit is contained in:
Charlie Marsh 2024-06-13 17:56:38 -07:00 committed by GitHub
parent b74de31967
commit e0a389032f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 1702 additions and 664 deletions

View file

@ -471,7 +471,6 @@ pub enum Error {
stderr: String, stderr: String,
path: PathBuf, path: PathBuf,
}, },
#[error("Querying Python at `{}` failed with exit status {code}\n--- stdout:\n{stdout}\n--- stderr:\n{stderr}\n---", path.display())] #[error("Querying Python at `{}` failed with exit status {code}\n--- stdout:\n{stdout}\n--- stderr:\n{stderr}\n---", path.display())]
StatusCode { StatusCode {
code: ExitStatus, code: ExitStatus,

View file

@ -7,7 +7,7 @@ use uv_configuration::{ConfigSettings, IndexStrategy, KeyringProviderType, Targe
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode}; use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
use uv_toolchain::PythonVersion; use uv_toolchain::PythonVersion;
use crate::{Options, PipOptions, Workspace}; use crate::{GlobalOptions, Options, PipOptions, ResolverInstallerOptions, Workspace};
pub trait Combine { pub trait Combine {
/// Combine two values, preferring the values in `self`. /// Combine two values, preferring the values in `self`.
@ -41,11 +41,8 @@ impl Combine for Option<Workspace> {
impl Combine for Options { impl Combine for Options {
fn combine(self, other: Options) -> Options { fn combine(self, other: Options) -> Options {
Options { Options {
native_tls: self.native_tls.combine(other.native_tls), globals: self.globals.combine(other.globals),
offline: self.offline.combine(other.offline), top_level: self.top_level.combine(other.top_level),
no_cache: self.no_cache.combine(other.no_cache),
preview: self.preview.combine(other.preview),
cache_dir: self.cache_dir.combine(other.cache_dir),
pip: self.pip.combine(other.pip), pip: self.pip.combine(other.pip),
override_dependencies: self override_dependencies: self
.override_dependencies .override_dependencies
@ -54,6 +51,37 @@ impl Combine for Options {
} }
} }
impl Combine for GlobalOptions {
fn combine(self, other: GlobalOptions) -> GlobalOptions {
GlobalOptions {
native_tls: self.native_tls.combine(other.native_tls),
offline: self.offline.combine(other.offline),
no_cache: self.no_cache.combine(other.no_cache),
cache_dir: self.cache_dir.combine(other.cache_dir),
preview: self.preview.combine(other.preview),
}
}
}
impl Combine for ResolverInstallerOptions {
fn combine(self, other: ResolverInstallerOptions) -> ResolverInstallerOptions {
ResolverInstallerOptions {
index_url: self.index_url.combine(other.index_url),
extra_index_url: self.extra_index_url.combine(other.extra_index_url),
no_index: self.no_index.combine(other.no_index),
find_links: self.find_links.combine(other.find_links),
index_strategy: self.index_strategy.combine(other.index_strategy),
keyring_provider: self.keyring_provider.combine(other.keyring_provider),
resolution: self.resolution.combine(other.resolution),
prerelease: self.prerelease.combine(other.prerelease),
config_settings: self.config_settings.combine(other.config_settings),
exclude_newer: self.exclude_newer.combine(other.exclude_newer),
link_mode: self.link_mode.combine(other.link_mode),
compile_bytecode: self.compile_bytecode.combine(other.compile_bytecode),
}
}
}
impl Combine for Option<PipOptions> { impl Combine for Option<PipOptions> {
fn combine(self, other: Option<PipOptions>) -> Option<PipOptions> { fn combine(self, other: Option<PipOptions>) -> Option<PipOptions> {
match (self, other) { match (self, other) {

View file

@ -32,11 +32,10 @@ pub(crate) struct Tools {
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Options { pub struct Options {
pub native_tls: Option<bool>, #[serde(flatten)]
pub offline: Option<bool>, pub globals: GlobalOptions,
pub no_cache: Option<bool>, #[serde(flatten)]
pub preview: Option<bool>, pub top_level: ResolverInstallerOptions,
pub cache_dir: Option<PathBuf>,
pub pip: Option<PipOptions>, pub pip: Option<PipOptions>,
#[cfg_attr( #[cfg_attr(
feature = "schemars", feature = "schemars",
@ -48,6 +47,76 @@ pub struct Options {
pub override_dependencies: Option<Vec<pep508_rs::Requirement<VerbatimParsedUrl>>>, pub override_dependencies: Option<Vec<pep508_rs::Requirement<VerbatimParsedUrl>>>,
} }
/// Global settings, relevant to all invocations.
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct GlobalOptions {
pub native_tls: Option<bool>,
pub offline: Option<bool>,
pub no_cache: Option<bool>,
pub cache_dir: Option<PathBuf>,
pub preview: Option<bool>,
}
/// Settings relevant to all installer operations.
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct InstallerOptions {
pub index_url: Option<IndexUrl>,
pub extra_index_url: Option<Vec<IndexUrl>>,
pub no_index: Option<bool>,
pub find_links: Option<Vec<FlatIndexLocation>>,
pub index_strategy: Option<IndexStrategy>,
pub keyring_provider: Option<KeyringProviderType>,
pub config_settings: Option<ConfigSettings>,
pub link_mode: Option<LinkMode>,
pub compile_bytecode: Option<bool>,
}
/// Settings relevant to all resolver operations.
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ResolverOptions {
pub index_url: Option<IndexUrl>,
pub extra_index_url: Option<Vec<IndexUrl>>,
pub no_index: Option<bool>,
pub find_links: Option<Vec<FlatIndexLocation>>,
pub index_strategy: Option<IndexStrategy>,
pub keyring_provider: Option<KeyringProviderType>,
pub resolution: Option<ResolutionMode>,
pub prerelease: Option<PreReleaseMode>,
pub config_settings: Option<ConfigSettings>,
pub exclude_newer: Option<ExcludeNewer>,
pub link_mode: Option<LinkMode>,
}
/// Shared settings, relevant to all operations that must resolve and install dependencies. The
/// union of [`InstallerOptions`] and [`ResolverOptions`].
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ResolverInstallerOptions {
pub index_url: Option<IndexUrl>,
pub extra_index_url: Option<Vec<IndexUrl>>,
pub no_index: Option<bool>,
pub find_links: Option<Vec<FlatIndexLocation>>,
pub index_strategy: Option<IndexStrategy>,
pub keyring_provider: Option<KeyringProviderType>,
pub resolution: Option<ResolutionMode>,
pub prerelease: Option<PreReleaseMode>,
pub config_settings: Option<ConfigSettings>,
pub exclude_newer: Option<ExcludeNewer>,
pub link_mode: Option<LinkMode>,
pub compile_bytecode: Option<bool>,
}
/// A `[tool.uv.pip]` section. /// A `[tool.uv.pip]` section.
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)] #[derive(Debug, Clone, Default, Deserialize)]
@ -99,3 +168,120 @@ pub struct PipOptions {
pub concurrent_builds: Option<NonZeroUsize>, pub concurrent_builds: Option<NonZeroUsize>,
pub concurrent_installs: Option<NonZeroUsize>, pub concurrent_installs: Option<NonZeroUsize>,
} }
impl Options {
/// Return the `pip` section, with any top-level options merged in. If options are repeated
/// between the top-level and the `pip` section, the `pip` options are preferred.
///
/// For example, prefers `tool.uv.pip.index-url` over `tool.uv.index-url`.
pub fn pip(self) -> PipOptions {
let PipOptions {
python,
system,
break_system_packages,
target,
prefix,
index_url,
extra_index_url,
no_index,
find_links,
index_strategy,
keyring_provider,
no_build,
no_binary,
only_binary,
no_build_isolation,
strict,
extra,
all_extras,
no_deps,
resolution,
prerelease,
output_file,
no_strip_extras,
no_annotate,
no_header,
custom_compile_command,
generate_hashes,
legacy_setup_py,
config_settings,
python_version,
python_platform,
exclude_newer,
no_emit_package,
emit_index_url,
emit_find_links,
emit_marker_expression,
emit_index_annotation,
annotation_style,
link_mode,
compile_bytecode,
require_hashes,
concurrent_builds,
concurrent_downloads,
concurrent_installs,
} = self.pip.unwrap_or_default();
let ResolverInstallerOptions {
index_url: top_level_index_url,
extra_index_url: top_level_extra_index_url,
no_index: top_level_no_index,
find_links: top_level_find_links,
index_strategy: top_level_index_strategy,
keyring_provider: top_level_keyring_provider,
resolution: top_level_resolution,
prerelease: top_level_prerelease,
config_settings: top_level_config_settings,
exclude_newer: top_level_exclude_newer,
link_mode: top_level_link_mode,
compile_bytecode: top_level_compile_bytecode,
} = self.top_level;
PipOptions {
python,
system,
break_system_packages,
target,
prefix,
index_url: index_url.or(top_level_index_url),
extra_index_url: extra_index_url.or(top_level_extra_index_url),
no_index: no_index.or(top_level_no_index),
find_links: find_links.or(top_level_find_links),
index_strategy: index_strategy.or(top_level_index_strategy),
keyring_provider: keyring_provider.or(top_level_keyring_provider),
no_build,
no_binary,
only_binary,
no_build_isolation,
strict,
extra,
all_extras,
no_deps,
resolution: resolution.or(top_level_resolution),
prerelease: prerelease.or(top_level_prerelease),
output_file,
no_strip_extras,
no_annotate,
no_header,
custom_compile_command,
generate_hashes,
legacy_setup_py,
config_settings: config_settings.or(top_level_config_settings),
python_version,
python_platform,
exclude_newer: exclude_newer.or(top_level_exclude_newer),
no_emit_package,
emit_index_url,
emit_find_links,
emit_marker_expression,
emit_index_annotation,
annotation_style,
link_mode: link_mode.or(top_level_link_mode),
compile_bytecode: compile_bytecode.or(top_level_compile_bytecode),
require_hashes,
concurrent_builds,
concurrent_downloads,
concurrent_installs,
}
}
}

View file

@ -342,6 +342,9 @@ pub(crate) struct PipCompileArgs {
#[arg(long, overrides_with("all_extras"), hide = true)] #[arg(long, overrides_with("all_extras"), hide = true)]
pub(crate) no_all_extras: bool, pub(crate) no_all_extras: bool,
#[command(flatten)]
pub(crate) resolver: ResolverArgs,
/// Ignore package dependencies, instead only add those packages explicitly listed /// Ignore package dependencies, instead only add those packages explicitly listed
/// on the command line to the resulting the requirements file. /// on the command line to the resulting the requirements file.
#[arg(long)] #[arg(long)]
@ -350,24 +353,6 @@ pub(crate) struct PipCompileArgs {
#[arg(long, overrides_with("no_deps"), hide = true)] #[arg(long, overrides_with("no_deps"), hide = true)]
pub(crate) deps: bool, pub(crate) deps: bool,
/// 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<ResolutionMode>,
/// The strategy to use when considering pre-release versions.
///
/// By default, `uv` will accept pre-releases for packages that _only_ publish pre-releases,
/// 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<PreReleaseMode>,
#[arg(long, hide = true)]
pub(crate) pre: bool,
/// Write the compiled requirements to the given `requirements.txt` file. /// Write the compiled requirements to the given `requirements.txt` file.
#[arg(long, short)] #[arg(long, short)]
pub(crate) output_file: Option<PathBuf>, pub(crate) output_file: Option<PathBuf>,
@ -423,36 +408,6 @@ pub(crate) struct PipCompileArgs {
#[arg(long)] #[arg(long)]
pub(crate) refresh_package: Vec<PackageName>, pub(crate) refresh_package: Vec<PackageName>,
/// The method to use when installing packages from the global cache.
///
/// This option is only used when building source distributions.
///
/// 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<install_wheel_rs::linker::LinkMode>,
#[command(flatten)]
pub(crate) index_args: IndexArgs,
/// The strategy to use when resolving against multiple index URLs.
///
/// By default, `uv` will stop at the first index on which a given package is available, and
/// limit resolutions to those present on that first index (`first-match`. This prevents
/// "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<IndexStrategy>,
/// Attempt to use `keyring` for authentication for index URLs.
///
/// At present, only `--keyring-provider subprocess` is supported, which configures `uv` to
/// use the `keyring` CLI to handle authentication.
///
/// Defaults to `disabled`.
#[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")]
pub(crate) keyring_provider: Option<KeyringProviderType>,
/// The Python interpreter against which to compile the requirements. /// The Python interpreter against which to compile the requirements.
/// ///
/// By default, `uv` uses the virtual environment in the current working directory or any parent /// By default, `uv` uses the virtual environment in the current working directory or any parent
@ -570,10 +525,6 @@ pub(crate) struct PipCompileArgs {
#[arg(long, conflicts_with = "no_build")] #[arg(long, conflicts_with = "no_build")]
pub(crate) only_binary: Option<Vec<PackageNameSpecifier>>, pub(crate) only_binary: Option<Vec<PackageNameSpecifier>>,
/// 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<Vec<ConfigSettingEntry>>,
/// The minimum Python version that should be supported by the compiled requirements (e.g., /// The minimum Python version that should be supported by the compiled requirements (e.g.,
/// `3.7` or `3.7.9`). /// `3.7` or `3.7.9`).
/// ///
@ -590,13 +541,6 @@ pub(crate) struct PipCompileArgs {
#[arg(long)] #[arg(long)]
pub(crate) python_platform: Option<TargetTriple>, pub(crate) python_platform: Option<TargetTriple>,
/// 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<ExcludeNewer>,
/// Specify a package to omit from the output resolution. Its dependencies will still be /// 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. /// included in the resolution. Equivalent to pip-compile's `--unsafe-package` option.
#[arg(long, alias = "unsafe-package")] #[arg(long, alias = "unsafe-package")]
@ -662,6 +606,16 @@ pub(crate) struct PipSyncArgs {
#[arg(long, short, env = "UV_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)] #[arg(long, short, env = "UV_CONSTRAINT", value_delimiter = ' ', value_parser = parse_maybe_file_path)]
pub(crate) constraint: Vec<Maybe<PathBuf>>, pub(crate) constraint: Vec<Maybe<PathBuf>>,
#[command(flatten)]
pub(crate) installer: InstallerArgs,
/// 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<ExcludeNewer>,
/// Reinstall all packages, regardless of whether they're already installed. /// Reinstall all packages, regardless of whether they're already installed.
#[arg(long, alias = "force-reinstall", overrides_with("no_reinstall"))] #[arg(long, alias = "force-reinstall", overrides_with("no_reinstall"))]
pub(crate) reinstall: bool, pub(crate) reinstall: bool,
@ -689,25 +643,6 @@ pub(crate) struct PipSyncArgs {
#[arg(long)] #[arg(long)]
pub(crate) refresh_package: Vec<PackageName>, pub(crate) refresh_package: Vec<PackageName>,
/// 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<install_wheel_rs::linker::LinkMode>,
#[command(flatten)]
pub(crate) index_args: IndexArgs,
/// The strategy to use when resolving against multiple index URLs.
///
/// By default, `uv` will stop at the first index on which a given package is available, and
/// limit resolutions to those present on that first index (`first-match`. This prevents
/// "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<IndexStrategy>,
/// Require a matching hash for each requirement. /// Require a matching hash for each requirement.
/// ///
/// Hash-checking mode is all or nothing. If enabled, _all_ requirements must be provided /// Hash-checking mode is all or nothing. If enabled, _all_ requirements must be provided
@ -726,15 +661,6 @@ pub(crate) struct PipSyncArgs {
#[arg(long, overrides_with("require_hashes"), hide = true)] #[arg(long, overrides_with("require_hashes"), hide = true)]
pub(crate) no_require_hashes: bool, pub(crate) no_require_hashes: bool,
/// Attempt to use `keyring` for authentication for index URLs.
///
/// Function's similar to `pip`'s `--keyring-provider subprocess` argument,
/// `uv` will try to use `keyring` via CLI when this flag is used.
///
/// Defaults to `disabled`.
#[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")]
pub(crate) keyring_provider: Option<KeyringProviderType>,
/// The Python interpreter into which packages should be installed. /// The Python interpreter into which packages should be installed.
/// ///
/// By default, `uv` installs into the virtual environment in the current working directory or /// By default, `uv` installs into the virtual environment in the current working directory or
@ -869,30 +795,6 @@ pub(crate) struct PipSyncArgs {
#[arg(long, conflicts_with = "no_build")] #[arg(long, conflicts_with = "no_build")]
pub(crate) only_binary: Option<Vec<PackageNameSpecifier>>, pub(crate) only_binary: Option<Vec<PackageNameSpecifier>>,
/// Compile Python files to bytecode.
///
/// By default, does not compile Python (`.py`) files to bytecode (`__pycache__/*.pyc`), instead
/// Python lazily does the compilation the first time a module is imported. In cases where the
/// first start time matters, such as CLI applications and docker containers, this option can
/// trade longer install time for faster startup.
///
/// 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,
#[arg(
long,
alias = "no_compile",
overrides_with("compile_bytecode"),
hide = true
)]
pub(crate) no_compile_bytecode: 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<Vec<ConfigSettingEntry>>,
/// The minimum Python version that should be supported by the requirements (e.g., /// The minimum Python version that should be supported by the requirements (e.g.,
/// `3.7` or `3.7.9`). /// `3.7` or `3.7.9`).
/// ///
@ -923,13 +825,6 @@ pub(crate) struct PipSyncArgs {
#[arg(long, overrides_with("strict"), hide = true)] #[arg(long, overrides_with("strict"), hide = true)]
pub(crate) no_strict: bool, pub(crate) no_strict: bool,
/// 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<ExcludeNewer>,
/// Perform a dry run, i.e., don't actually install anything but resolve the dependencies and /// Perform a dry run, i.e., don't actually install anything but resolve the dependencies and
/// print the resulting plan. /// print the resulting plan.
#[arg(long)] #[arg(long)]
@ -995,6 +890,9 @@ pub(crate) struct PipInstallArgs {
#[arg(long, overrides_with("all_extras"), hide = true)] #[arg(long, overrides_with("all_extras"), hide = true)]
pub(crate) no_all_extras: bool, pub(crate) no_all_extras: bool,
#[command(flatten)]
pub(crate) installer: ResolverInstallerArgs,
/// Allow package upgrades. /// Allow package upgrades.
#[arg(long, short = 'U', overrides_with("no_upgrade"))] #[arg(long, short = 'U', overrides_with("no_upgrade"))]
pub(crate) upgrade: bool, pub(crate) upgrade: bool,
@ -1041,43 +939,6 @@ pub(crate) struct PipInstallArgs {
#[arg(long, overrides_with("no_deps"), hide = true)] #[arg(long, overrides_with("no_deps"), hide = true)]
pub(crate) deps: bool, pub(crate) deps: bool,
/// 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<install_wheel_rs::linker::LinkMode>,
/// 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<ResolutionMode>,
/// The strategy to use when considering pre-release versions.
///
/// By default, `uv` will accept pre-releases for packages that _only_ publish pre-releases,
/// 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<PreReleaseMode>,
#[arg(long, hide = true)]
pub(crate) pre: bool,
#[command(flatten)]
pub(crate) index_args: IndexArgs,
/// The strategy to use when resolving against multiple index URLs.
///
/// By default, `uv` will stop at the first index on which a given package is available, and
/// limit resolutions to those present on that first index (`first-match`. This prevents
/// "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<IndexStrategy>,
/// Require a matching hash for each requirement. /// Require a matching hash for each requirement.
/// ///
/// Hash-checking mode is all or nothing. If enabled, _all_ requirements must be provided /// Hash-checking mode is all or nothing. If enabled, _all_ requirements must be provided
@ -1100,15 +961,6 @@ pub(crate) struct PipInstallArgs {
#[arg(long, overrides_with("require_hashes"), hide = true)] #[arg(long, overrides_with("require_hashes"), hide = true)]
pub(crate) no_require_hashes: bool, pub(crate) no_require_hashes: bool,
/// Attempt to use `keyring` for authentication for index URLs.
///
/// At present, only `--keyring-provider subprocess` is supported, which configures `uv` to
/// use the `keyring` CLI to handle authentication.
///
/// Defaults to `disabled`.
#[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")]
pub(crate) keyring_provider: Option<KeyringProviderType>,
/// The Python interpreter into which packages should be installed. /// The Python interpreter into which packages should be installed.
/// ///
/// By default, `uv` installs into the virtual environment in the current working directory or /// By default, `uv` installs into the virtual environment in the current working directory or
@ -1243,30 +1095,6 @@ pub(crate) struct PipInstallArgs {
#[arg(long, conflicts_with = "no_build")] #[arg(long, conflicts_with = "no_build")]
pub(crate) only_binary: Option<Vec<PackageNameSpecifier>>, pub(crate) only_binary: Option<Vec<PackageNameSpecifier>>,
/// Compile Python files to bytecode.
///
/// By default, does not compile Python (`.py`) files to bytecode (`__pycache__/*.pyc`), instead
/// Python lazily does the compilation the first time a module is imported. In cases where the
/// first start time matters, such as CLI applications and docker containers, this option can
/// trade longer install time for faster startup.
///
/// 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,
#[arg(
long,
alias = "no_compile",
overrides_with("compile_bytecode"),
hide = true
)]
pub(crate) no_compile_bytecode: 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<Vec<ConfigSettingEntry>>,
/// The minimum Python version that should be supported by the requirements (e.g., /// The minimum Python version that should be supported by the requirements (e.g.,
/// `3.7` or `3.7.9`). /// `3.7` or `3.7.9`).
/// ///
@ -1297,13 +1125,6 @@ pub(crate) struct PipInstallArgs {
#[arg(long, overrides_with("strict"), hide = true)] #[arg(long, overrides_with("strict"), hide = true)]
pub(crate) no_strict: bool, pub(crate) no_strict: bool,
/// 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<ExcludeNewer>,
/// Perform a dry run, i.e., don't actually install anything but resolve the dependencies and /// Perform a dry run, i.e., don't actually install anything but resolve the dependencies and
/// print the resulting plan. /// print the resulting plan.
#[arg(long)] #[arg(long)]
@ -1673,48 +1494,8 @@ pub(crate) struct VenvArgs {
#[arg(long)] #[arg(long)]
pub(crate) system_site_packages: bool, pub(crate) system_site_packages: bool,
/// The method to use when installing packages from the global cache. #[command(flatten)]
/// pub(crate) index_args: IndexArgs,
/// This option is only used for installing seed packages.
///
/// 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<install_wheel_rs::linker::LinkMode>,
/// The URL of the Python package index (by default: <https://pypi.org/simple>).
///
/// Accepts either a repository compliant with PEP 503 (the simple repository API), or a local
/// directory laid out in the same format.
///
/// The index given by this flag is given lower priority than all other
/// indexes specified via the `--extra-index-url` flag.
///
/// Unlike `pip`, `uv` will stop looking for versions of a package as soon
/// as it finds it in an index. That is, it isn't possible for `uv` to
/// consider versions of the same package across multiple indexes.
#[arg(long, short, env = "UV_INDEX_URL", value_parser = parse_index_url)]
pub(crate) index_url: Option<Maybe<IndexUrl>>,
/// Extra URLs of package indexes to use, in addition to `--index-url`.
///
/// Accepts either a repository compliant with PEP 503 (the simple repository API), or a local
/// directory laid out in the same format.
///
/// All indexes given via this flag take priority over the index
/// in `--index-url` (which defaults to PyPI). And when multiple
/// `--extra-index-url` flags are given, earlier values take priority.
///
/// Unlike `pip`, `uv` will stop looking for versions of a package as soon
/// as it finds it in an index. That is, it isn't possible for `uv` to
/// consider versions of the same package across multiple indexes.
#[arg(long, env = "UV_EXTRA_INDEX_URL", value_delimiter = ' ', value_parser = parse_index_url)]
pub(crate) extra_index_url: Option<Vec<Maybe<IndexUrl>>>,
/// 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,
/// The strategy to use when resolving against multiple index URLs. /// The strategy to use when resolving against multiple index URLs.
/// ///
@ -1741,6 +1522,15 @@ pub(crate) struct VenvArgs {
#[arg(long, env = "UV_EXCLUDE_NEWER")] #[arg(long, env = "UV_EXCLUDE_NEWER")]
pub(crate) exclude_newer: Option<ExcludeNewer>, pub(crate) exclude_newer: Option<ExcludeNewer>,
/// The method to use when installing packages from the global cache.
///
/// This option is only used for installing seed packages.
///
/// 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<install_wheel_rs::linker::LinkMode>,
#[command(flatten)] #[command(flatten)]
pub(crate) compat_args: compat::VenvCompatArgs, pub(crate) compat_args: compat::VenvCompatArgs,
} }
@ -1808,7 +1598,7 @@ pub(crate) struct RunArgs {
pub(crate) upgrade_package: Vec<PackageName>, pub(crate) upgrade_package: Vec<PackageName>,
#[command(flatten)] #[command(flatten)]
pub(crate) index_args: IndexArgs, pub(crate) installer: ResolverInstallerArgs,
/// The Python interpreter to use to build the run environment. /// The Python interpreter to use to build the run environment.
/// ///
@ -1824,13 +1614,6 @@ pub(crate) struct RunArgs {
#[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)]
pub(crate) python: Option<String>, pub(crate) python: Option<String>,
/// 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<ExcludeNewer>,
/// Run the command in a different package in the workspace. /// Run the command in a different package in the workspace.
#[arg(long, conflicts_with = "isolated")] #[arg(long, conflicts_with = "isolated")]
pub(crate) package: Option<PackageName>, pub(crate) package: Option<PackageName>,
@ -1877,7 +1660,7 @@ pub(crate) struct SyncArgs {
pub(crate) refresh_package: Vec<PackageName>, pub(crate) refresh_package: Vec<PackageName>,
#[command(flatten)] #[command(flatten)]
pub(crate) index_args: IndexArgs, pub(crate) installer: InstallerArgs,
/// The Python interpreter to use to build the run environment. /// The Python interpreter to use to build the run environment.
/// ///
@ -1925,7 +1708,7 @@ pub(crate) struct LockArgs {
pub(crate) upgrade_package: Vec<PackageName>, pub(crate) upgrade_package: Vec<PackageName>,
#[command(flatten)] #[command(flatten)]
pub(crate) index_args: IndexArgs, pub(crate) resolver: ResolverArgs,
/// The Python interpreter to use to build the run environment. /// The Python interpreter to use to build the run environment.
/// ///
@ -1940,13 +1723,6 @@ pub(crate) struct LockArgs {
/// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path. /// - `/home/ferris/.local/bin/python3.10` uses the exact Python at the given path.
#[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)] #[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)]
pub(crate) python: Option<String>, pub(crate) python: Option<String>,
/// 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<ExcludeNewer>,
} }
#[derive(Args)] #[derive(Args)]
@ -2028,7 +1804,7 @@ pub(crate) struct ToolRunArgs {
pub(crate) with: Vec<String>, pub(crate) with: Vec<String>,
#[command(flatten)] #[command(flatten)]
pub(crate) index_args: IndexArgs, pub(crate) installer: ResolverInstallerArgs,
/// The Python interpreter to use to build the run environment. /// The Python interpreter to use to build the run environment.
/// ///
@ -2098,10 +1874,6 @@ pub(crate) struct IndexArgs {
/// ///
/// The index given by this flag is given lower priority than all other /// The index given by this flag is given lower priority than all other
/// indexes specified via the `--extra-index-url` flag. /// indexes specified via the `--extra-index-url` flag.
///
/// Unlike `pip`, `uv` will stop looking for versions of a package as soon
/// as it finds it in an index. That is, it isn't possible for `uv` to
/// consider versions of the same package across multiple indexes.
#[arg(long, short, env = "UV_INDEX_URL", value_parser = parse_index_url)] #[arg(long, short, env = "UV_INDEX_URL", value_parser = parse_index_url)]
pub(crate) index_url: Option<Maybe<IndexUrl>>, pub(crate) index_url: Option<Maybe<IndexUrl>>,
@ -2113,10 +1885,6 @@ pub(crate) struct IndexArgs {
/// All indexes given via this flag take priority over the index /// All indexes given via this flag take priority over the index
/// in `--index-url` (which defaults to PyPI). And when multiple /// in `--index-url` (which defaults to PyPI). And when multiple
/// `--extra-index-url` flags are given, earlier values take priority. /// `--extra-index-url` flags are given, earlier values take priority.
///
/// Unlike `pip`, `uv` will stop looking for versions of a package as soon
/// as it finds it in an index. That is, it isn't possible for `uv` to
/// consider versions of the same package across multiple indexes.
#[arg(long, env = "UV_EXTRA_INDEX_URL", value_delimiter = ' ', value_parser = parse_index_url)] #[arg(long, env = "UV_EXTRA_INDEX_URL", value_delimiter = ' ', value_parser = parse_index_url)]
pub(crate) extra_index_url: Option<Vec<Maybe<IndexUrl>>>, pub(crate) extra_index_url: Option<Vec<Maybe<IndexUrl>>>,
@ -2134,3 +1902,203 @@ pub(crate) struct IndexArgs {
#[arg(long)] #[arg(long)]
pub(crate) no_index: bool, pub(crate) no_index: bool,
} }
/// Arguments that are used by commands that need to install (but not resolve) packages.
#[derive(Args)]
pub(crate) struct InstallerArgs {
#[command(flatten)]
pub(crate) index_args: IndexArgs,
/// The strategy to use when resolving against multiple index URLs.
///
/// By default, `uv` will stop at the first index on which a given package is available, and
/// limit resolutions to those present on that first index (`first-match`. This prevents
/// "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<IndexStrategy>,
/// Attempt to use `keyring` for authentication for index URLs.
///
/// At present, only `--keyring-provider subprocess` is supported, which configures `uv` to
/// use the `keyring` CLI to handle authentication.
///
/// Defaults to `disabled`.
#[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")]
pub(crate) keyring_provider: Option<KeyringProviderType>,
/// 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<Vec<ConfigSettingEntry>>,
/// 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<install_wheel_rs::linker::LinkMode>,
/// Compile Python files to bytecode.
///
/// By default, does not compile Python (`.py`) files to bytecode (`__pycache__/*.pyc`), instead
/// Python lazily does the compilation the first time a module is imported. In cases where the
/// first start time matters, such as CLI applications and docker containers, this option can
/// trade longer install time for faster startup.
///
/// 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,
#[arg(
long,
alias = "no_compile",
overrides_with("compile_bytecode"),
hide = true
)]
pub(crate) no_compile_bytecode: bool,
}
/// Arguments that are used by commands that need to resolve (but not install) packages.
#[derive(Args)]
pub(crate) struct ResolverArgs {
#[command(flatten)]
pub(crate) index_args: IndexArgs,
/// The strategy to use when resolving against multiple index URLs.
///
/// By default, `uv` will stop at the first index on which a given package is available, and
/// limit resolutions to those present on that first index (`first-match`. This prevents
/// "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<IndexStrategy>,
/// Attempt to use `keyring` for authentication for index URLs.
///
/// At present, only `--keyring-provider subprocess` is supported, which configures `uv` to
/// use the `keyring` CLI to handle authentication.
///
/// Defaults to `disabled`.
#[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")]
pub(crate) keyring_provider: Option<KeyringProviderType>,
/// 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<ResolutionMode>,
/// The strategy to use when considering pre-release versions.
///
/// By default, `uv` will accept pre-releases for packages that _only_ publish pre-releases,
/// 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<PreReleaseMode>,
#[arg(long, hide = true)]
pub(crate) 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<Vec<ConfigSettingEntry>>,
/// 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<ExcludeNewer>,
/// The method to use when installing packages from the global cache.
///
/// This option is only used when building source distributions.
///
/// 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<install_wheel_rs::linker::LinkMode>,
}
/// Arguments that are used by commands that need to resolve and install packages.
#[derive(Args)]
pub(crate) struct ResolverInstallerArgs {
#[command(flatten)]
pub(crate) index_args: IndexArgs,
/// The strategy to use when resolving against multiple index URLs.
///
/// By default, `uv` will stop at the first index on which a given package is available, and
/// limit resolutions to those present on that first index (`first-match`. This prevents
/// "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<IndexStrategy>,
/// Attempt to use `keyring` for authentication for index URLs.
///
/// At present, only `--keyring-provider subprocess` is supported, which configures `uv` to
/// use the `keyring` CLI to handle authentication.
///
/// Defaults to `disabled`.
#[arg(long, value_enum, env = "UV_KEYRING_PROVIDER")]
pub(crate) keyring_provider: Option<KeyringProviderType>,
/// 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<ResolutionMode>,
/// The strategy to use when considering pre-release versions.
///
/// By default, `uv` will accept pre-releases for packages that _only_ publish pre-releases,
/// 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<PreReleaseMode>,
#[arg(long, hide = true)]
pub(crate) 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<Vec<ConfigSettingEntry>>,
/// 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<ExcludeNewer>,
/// 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<install_wheel_rs::linker::LinkMode>,
/// Compile Python files to bytecode.
///
/// By default, does not compile Python (`.py`) files to bytecode (`__pycache__/*.pyc`), instead
/// Python lazily does the compilation the first time a module is imported. In cases where the
/// first start time matters, such as CLI applications and docker containers, this option can
/// trade longer install time for faster startup.
///
/// 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,
#[arg(
long,
alias = "no_compile",
overrides_with("compile_bytecode"),
hide = true
)]
pub(crate) no_compile_bytecode: bool,
}

View file

@ -1,16 +1,18 @@
use anyhow::Result;
use std::str::FromStr; use std::str::FromStr;
use uv_distribution::pyproject_mut::PyProjectTomlMut;
use distribution_types::IndexLocations; use anyhow::Result;
use pep508_rs::Requirement; use pep508_rs::Requirement;
use uv_cache::Cache; use uv_cache::Cache;
use uv_configuration::{ExtrasSpecification, PreviewMode, Upgrade}; use uv_client::Connectivity;
use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode, Upgrade};
use uv_distribution::pyproject_mut::PyProjectTomlMut;
use uv_distribution::ProjectWorkspace; use uv_distribution::ProjectWorkspace;
use uv_warnings::warn_user; use uv_warnings::warn_user;
use crate::commands::{project, ExitStatus}; use crate::commands::{project, ExitStatus};
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::{InstallerSettings, ResolverSettings};
/// Add one or more packages to the project requirements. /// Add one or more packages to the project requirements.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -18,6 +20,9 @@ pub(crate) async fn add(
requirements: Vec<String>, requirements: Vec<String>,
python: Option<String>, python: Option<String>,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
@ -43,9 +48,9 @@ pub(crate) async fn add(
// Discover or create the virtual environment. // Discover or create the virtual environment.
let venv = project::init_environment(project.workspace(), python.as_deref(), cache, printer)?; let venv = project::init_environment(project.workspace(), python.as_deref(), cache, printer)?;
let index_locations = IndexLocations::default(); // Use the default settings.
let settings = ResolverSettings::default();
let upgrade = Upgrade::default(); let upgrade = Upgrade::default();
let exclude_newer = None;
// Lock and sync the environment. // Lock and sync the environment.
let root_project_name = project let root_project_name = project
@ -59,10 +64,19 @@ pub(crate) async fn add(
root_project_name, root_project_name,
project.workspace(), project.workspace(),
venv.interpreter(), venv.interpreter(),
&index_locations,
upgrade, upgrade,
exclude_newer, &settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.resolution,
&settings.prerelease,
&settings.config_setting,
settings.exclude_newer.as_ref(),
&settings.link_mode,
preview, preview,
connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
) )
@ -70,6 +84,7 @@ pub(crate) async fn add(
// Perform a full sync, because we don't know what exactly is affected by the removal. // Perform a full sync, because we don't know what exactly is affected by the removal.
// TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here? // TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here?
let settings = InstallerSettings::default();
let extras = ExtrasSpecification::All; let extras = ExtrasSpecification::All;
let dev = true; let dev = true;
@ -78,10 +93,18 @@ pub(crate) async fn add(
project.workspace().root(), project.workspace().root(),
&venv, &venv,
&lock, &lock,
&index_locations,
extras, extras,
dev, dev,
&settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.config_setting,
&settings.link_mode,
&settings.compile_bytecode,
preview, preview,
connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
) )

View file

@ -5,17 +5,20 @@ use anstream::eprint;
use distribution_types::{IndexLocations, UnresolvedRequirementSpecification}; use distribution_types::{IndexLocations, UnresolvedRequirementSpecification};
use install_wheel_rs::linker::LinkMode; use install_wheel_rs::linker::LinkMode;
use uv_cache::Cache; use uv_cache::Cache;
use uv_client::{FlatIndexClient, RegistryClientBuilder}; use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{ use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, ExtrasSpecification, PreviewMode, Reinstall, BuildOptions, Concurrency, ConfigSettings, ExtrasSpecification, IndexStrategy,
SetupPyStrategy, Upgrade, KeyringProviderType, PreviewMode, Reinstall, SetupPyStrategy, Upgrade,
}; };
use uv_dispatch::BuildDispatch; use uv_dispatch::BuildDispatch;
use uv_distribution::{Workspace, DEV_DEPENDENCIES}; use uv_distribution::{Workspace, DEV_DEPENDENCIES};
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_requirements::upgrade::{read_lockfile, LockedRequirements}; use uv_requirements::upgrade::{read_lockfile, LockedRequirements};
use uv_resolver::{ExcludeNewer, FlatIndex, InMemoryIndex, Lock, OptionsBuilder, RequiresPython}; use uv_resolver::{
ExcludeNewer, FlatIndex, InMemoryIndex, Lock, OptionsBuilder, PreReleaseMode, RequiresPython,
ResolutionMode,
};
use uv_toolchain::Interpreter; use uv_toolchain::Interpreter;
use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight}; use uv_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
use uv_warnings::warn_user; use uv_warnings::warn_user;
@ -23,15 +26,18 @@ use uv_warnings::warn_user;
use crate::commands::project::{find_requires_python, ProjectError}; use crate::commands::project::{find_requires_python, ProjectError};
use crate::commands::{pip, project, ExitStatus}; use crate::commands::{pip, project, ExitStatus};
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::ResolverSettings;
/// Resolve the project requirements into a lockfile. /// Resolve the project requirements into a lockfile.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) async fn lock( pub(crate) async fn lock(
index_locations: IndexLocations,
upgrade: Upgrade, upgrade: Upgrade,
exclude_newer: Option<ExcludeNewer>,
python: Option<String>, python: Option<String>,
settings: ResolverSettings,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> anyhow::Result<ExitStatus> { ) -> anyhow::Result<ExitStatus> {
@ -57,10 +63,19 @@ pub(crate) async fn lock(
root_project_name, root_project_name,
&workspace, &workspace,
&interpreter, &interpreter,
&index_locations,
upgrade, upgrade,
exclude_newer, &settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.resolution,
&settings.prerelease,
&settings.config_setting,
settings.exclude_newer.as_ref(),
&settings.link_mode,
preview, preview,
connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
) )
@ -85,10 +100,19 @@ pub(super) async fn do_lock(
root_project_name: Option<PackageName>, root_project_name: Option<PackageName>,
workspace: &Workspace, workspace: &Workspace,
interpreter: &Interpreter, interpreter: &Interpreter,
index_locations: &IndexLocations,
upgrade: Upgrade, upgrade: Upgrade,
exclude_newer: Option<ExcludeNewer>, index_locations: &IndexLocations,
index_strategy: &IndexStrategy,
keyring_provider: &KeyringProviderType,
resolution: &ResolutionMode,
prerelease: &PreReleaseMode,
config_setting: &ConfigSettings,
exclude_newer: Option<&ExcludeNewer>,
link_mode: &LinkMode,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<Lock, ProjectError> { ) -> Result<Lock, ProjectError> {
@ -137,28 +161,36 @@ pub(super) async fn do_lock(
}; };
// Initialize the registry client. // Initialize the registry client.
// TODO(zanieb): Support client options e.g. offline, tls, etc.
let client = RegistryClientBuilder::new(cache.clone()) let client = RegistryClientBuilder::new(cache.clone())
.native_tls(native_tls)
.connectivity(connectivity)
.index_urls(index_locations.index_urls()) .index_urls(index_locations.index_urls())
.index_strategy(*index_strategy)
.keyring(*keyring_provider)
.markers(interpreter.markers()) .markers(interpreter.markers())
.platform(interpreter.platform()) .platform(interpreter.platform())
.build(); .build();
// TODO(charlie): Respect project configuration. let options = OptionsBuilder::new()
let build_isolation = BuildIsolation::default(); .resolution_mode(*resolution)
let concurrency = Concurrency::default(); .prerelease_mode(*prerelease)
let config_settings = ConfigSettings::default(); .exclude_newer(exclude_newer.copied())
let extras = ExtrasSpecification::default(); .index_strategy(*index_strategy)
.build();
let hasher = HashStrategy::Generate;
// Initialize any shared state.
let in_flight = InFlight::default(); let in_flight = InFlight::default();
let index = InMemoryIndex::default(); let index = InMemoryIndex::default();
let link_mode = LinkMode::default();
// TODO(charlie): These are all default values. We should consider whether we want to make them
// optional on the downstream APIs.
let build_isolation = BuildIsolation::default();
let build_options = BuildOptions::default(); let build_options = BuildOptions::default();
let extras = ExtrasSpecification::default();
let reinstall = Reinstall::default(); let reinstall = Reinstall::default();
let setup_py = SetupPyStrategy::default(); let setup_py = SetupPyStrategy::default();
let hasher = HashStrategy::Generate;
let options = OptionsBuilder::new().exclude_newer(exclude_newer).build();
// Resolve the flat indexes from `--find-links`. // Resolve the flat indexes from `--find-links`.
let flat_index = { let flat_index = {
let client = FlatIndexClient::new(&client, cache); let client = FlatIndexClient::new(&client, cache);
@ -183,9 +215,9 @@ pub(super) async fn do_lock(
&git, &git,
&in_flight, &in_flight,
setup_py, setup_py,
&config_settings, config_setting,
build_isolation, build_isolation,
link_mode, *link_mode,
&build_options, &build_options,
concurrency, concurrency,
preview, preview,

View file

@ -5,14 +5,13 @@ use itertools::Itertools;
use owo_colors::OwoColorize; use owo_colors::OwoColorize;
use tracing::debug; use tracing::debug;
use distribution_types::{IndexLocations, Resolution}; use distribution_types::Resolution;
use install_wheel_rs::linker::LinkMode;
use pep440_rs::Version; use pep440_rs::Version;
use uv_cache::Cache; use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity, RegistryClientBuilder}; use uv_client::{BaseClientBuilder, Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{ use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, ExtrasSpecification, PreviewMode, Reinstall, BuildOptions, Concurrency, ExtrasSpecification, PreviewMode, Reinstall, SetupPyStrategy,
SetupPyStrategy, Upgrade, Upgrade,
}; };
use uv_dispatch::BuildDispatch; use uv_dispatch::BuildDispatch;
use uv_distribution::Workspace; use uv_distribution::Workspace;
@ -20,7 +19,7 @@ use uv_fs::Simplified;
use uv_git::GitResolver; use uv_git::GitResolver;
use uv_installer::{SatisfiesResult, SitePackages}; use uv_installer::{SatisfiesResult, SitePackages};
use uv_requirements::{RequirementsSource, RequirementsSpecification}; use uv_requirements::{RequirementsSource, RequirementsSpecification};
use uv_resolver::{FlatIndex, InMemoryIndex, Options, RequiresPython}; use uv_resolver::{FlatIndex, InMemoryIndex, OptionsBuilder, RequiresPython};
use uv_toolchain::{ use uv_toolchain::{
Interpreter, PythonEnvironment, SystemPython, Toolchain, ToolchainRequest, VersionRequest, Interpreter, PythonEnvironment, SystemPython, Toolchain, ToolchainRequest, VersionRequest,
}; };
@ -29,6 +28,7 @@ use uv_warnings::warn_user;
use crate::commands::pip; use crate::commands::pip;
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings;
pub(crate) mod add; pub(crate) mod add;
pub(crate) mod lock; pub(crate) mod lock;
@ -256,17 +256,35 @@ pub(crate) fn init_environment(
} }
/// Update a [`PythonEnvironment`] to satisfy a set of [`RequirementsSource`]s. /// Update a [`PythonEnvironment`] to satisfy a set of [`RequirementsSource`]s.
#[allow(clippy::too_many_arguments)]
pub(crate) async fn update_environment( pub(crate) async fn update_environment(
venv: PythonEnvironment, venv: PythonEnvironment,
requirements: &[RequirementsSource], requirements: &[RequirementsSource],
index_locations: &IndexLocations, settings: &ResolverInstallerSettings,
preview: PreviewMode,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
preview: PreviewMode,
) -> Result<PythonEnvironment> { ) -> Result<PythonEnvironment> {
// TODO(zanieb): Support client configuration // Extract the project settings.
let client_builder = BaseClientBuilder::default().connectivity(connectivity); let ResolverInstallerSettings {
index_locations,
index_strategy,
keyring_provider,
resolution,
prerelease,
config_setting,
exclude_newer,
link_mode,
compile_bytecode,
} = settings;
let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
.native_tls(native_tls)
.keyring(*keyring_provider);
// Read all requirements from the provided sources. // Read all requirements from the provided sources.
// TODO(zanieb): Consider allowing constraints and extras // TODO(zanieb): Consider allowing constraints and extras
@ -304,35 +322,48 @@ pub(crate) async fn update_environment(
let markers = venv.interpreter().markers(); let markers = venv.interpreter().markers();
// Initialize the registry client. // Initialize the registry client.
// TODO(zanieb): Support client options e.g. offline, tls, etc.
let client = RegistryClientBuilder::new(cache.clone()) let client = RegistryClientBuilder::new(cache.clone())
.native_tls(native_tls)
.connectivity(connectivity) .connectivity(connectivity)
.index_urls(index_locations.index_urls()) .index_urls(index_locations.index_urls())
.index_strategy(*index_strategy)
.keyring(*keyring_provider)
.markers(markers) .markers(markers)
.platform(venv.interpreter().platform()) .platform(interpreter.platform())
.build(); .build();
// TODO(charlie): Respect project configuration. let options = OptionsBuilder::new()
let build_isolation = BuildIsolation::default(); .resolution_mode(*resolution)
let compile = false; .prerelease_mode(*prerelease)
let concurrency = Concurrency::default(); .exclude_newer(*exclude_newer)
let config_settings = ConfigSettings::default(); .index_strategy(*index_strategy)
let dry_run = false; .build();
let extras = ExtrasSpecification::default();
let flat_index = FlatIndex::default(); // Initialize any shared state.
let git = GitResolver::default(); let git = GitResolver::default();
let dev = Vec::default();
let hasher = HashStrategy::default();
let in_flight = InFlight::default(); let in_flight = InFlight::default();
let index = InMemoryIndex::default(); let index = InMemoryIndex::default();
let link_mode = LinkMode::default();
// TODO(charlie): These are all default values. We should consider whether we want to make them
// optional on the downstream APIs.
let build_isolation = BuildIsolation::default();
let build_options = BuildOptions::default(); let build_options = BuildOptions::default();
let options = Options::default(); let dev = Vec::default();
let dry_run = false;
let extras = ExtrasSpecification::default();
let hasher = HashStrategy::default();
let preferences = Vec::default(); let preferences = Vec::default();
let reinstall = Reinstall::default(); let reinstall = Reinstall::default();
let setup_py = SetupPyStrategy::default(); let setup_py = SetupPyStrategy::default();
let upgrade = Upgrade::default(); let upgrade = Upgrade::default();
// Resolve the flat indexes from `--find-links`.
let flat_index = {
let client = FlatIndexClient::new(&client, cache);
let entries = client.fetch(index_locations.flat_index()).await?;
FlatIndex::from_entries(entries, Some(tags), &hasher, &build_options)
};
// Create a build dispatch. // Create a build dispatch.
let resolve_dispatch = BuildDispatch::new( let resolve_dispatch = BuildDispatch::new(
&client, &client,
@ -344,9 +375,9 @@ pub(crate) async fn update_environment(
&git, &git,
&in_flight, &in_flight,
setup_py, setup_py,
&config_settings, config_setting,
build_isolation, build_isolation,
link_mode, *link_mode,
&build_options, &build_options,
concurrency, concurrency,
preview, preview,
@ -403,9 +434,9 @@ pub(crate) async fn update_environment(
&git, &git,
&in_flight, &in_flight,
setup_py, setup_py,
&config_settings, config_setting,
build_isolation, build_isolation,
link_mode, *link_mode,
&build_options, &build_options,
concurrency, concurrency,
preview, preview,
@ -419,8 +450,8 @@ pub(crate) async fn update_environment(
pip::operations::Modifications::Sufficient, pip::operations::Modifications::Sufficient,
&reinstall, &reinstall,
&build_options, &build_options,
link_mode, *link_mode,
compile, *compile_bytecode,
index_locations, index_locations,
&hasher, &hasher,
tags, tags,

View file

@ -2,14 +2,15 @@ use anyhow::Result;
use pep508_rs::PackageName; use pep508_rs::PackageName;
use uv_distribution::pyproject_mut::PyProjectTomlMut; use uv_distribution::pyproject_mut::PyProjectTomlMut;
use distribution_types::IndexLocations;
use uv_cache::Cache; use uv_cache::Cache;
use uv_configuration::{ExtrasSpecification, PreviewMode, Upgrade}; use uv_client::Connectivity;
use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode, Upgrade};
use uv_distribution::ProjectWorkspace; use uv_distribution::ProjectWorkspace;
use uv_warnings::warn_user; use uv_warnings::warn_user;
use crate::commands::{project, ExitStatus}; use crate::commands::{project, ExitStatus};
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::{InstallerSettings, ResolverSettings};
/// Remove one or more packages from the project requirements. /// Remove one or more packages from the project requirements.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -17,6 +18,9 @@ pub(crate) async fn remove(
requirements: Vec<PackageName>, requirements: Vec<PackageName>,
python: Option<String>, python: Option<String>,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
@ -46,9 +50,9 @@ pub(crate) async fn remove(
// Discover or create the virtual environment. // Discover or create the virtual environment.
let venv = project::init_environment(project.workspace(), python.as_deref(), cache, printer)?; let venv = project::init_environment(project.workspace(), python.as_deref(), cache, printer)?;
let index_locations = IndexLocations::default(); // Use the default settings.
let upgrade = Upgrade::None; let settings = ResolverSettings::default();
let exclude_newer = None; let upgrade = Upgrade::default();
// Lock and sync the environment. // Lock and sync the environment.
let root_project_name = project let root_project_name = project
@ -62,10 +66,19 @@ pub(crate) async fn remove(
root_project_name, root_project_name,
project.workspace(), project.workspace(),
venv.interpreter(), venv.interpreter(),
&index_locations,
upgrade, upgrade,
exclude_newer, &settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.resolution,
&settings.prerelease,
&settings.config_setting,
settings.exclude_newer.as_ref(),
&settings.link_mode,
preview, preview,
connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
) )
@ -73,6 +86,7 @@ pub(crate) async fn remove(
// Perform a full sync, because we don't know what exactly is affected by the removal. // Perform a full sync, because we don't know what exactly is affected by the removal.
// TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here? // TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here?
let settings = InstallerSettings::default();
let extras = ExtrasSpecification::All; let extras = ExtrasSpecification::All;
let dev = true; let dev = true;
@ -81,10 +95,18 @@ pub(crate) async fn remove(
project.workspace().root(), project.workspace().root(),
&venv, &venv,
&lock, &lock,
&index_locations,
extras, extras,
dev, dev,
&settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.config_setting,
&settings.link_mode,
&settings.compile_bytecode,
preview, preview,
connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
) )

View file

@ -2,28 +2,26 @@ use std::ffi::OsString;
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use distribution_types::IndexLocations;
use itertools::Itertools; use itertools::Itertools;
use tokio::process::Command; use tokio::process::Command;
use tracing::debug; use tracing::debug;
use uv_cache::Cache; use uv_cache::Cache;
use uv_client::{BaseClientBuilder, Connectivity}; use uv_client::{BaseClientBuilder, Connectivity};
use uv_configuration::{ExtrasSpecification, PreviewMode, Upgrade}; use uv_configuration::{Concurrency, ExtrasSpecification, PreviewMode, Upgrade};
use uv_distribution::{ProjectWorkspace, Workspace}; use uv_distribution::{ProjectWorkspace, Workspace};
use uv_normalize::PackageName; use uv_normalize::PackageName;
use uv_requirements::RequirementsSource; use uv_requirements::RequirementsSource;
use uv_resolver::ExcludeNewer;
use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain}; use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain};
use uv_warnings::warn_user; use uv_warnings::warn_user;
use crate::commands::{project, ExitStatus}; use crate::commands::{project, ExitStatus};
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings;
/// Run a command. /// Run a command.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) async fn run( pub(crate) async fn run(
index_locations: IndexLocations,
extras: ExtrasSpecification, extras: ExtrasSpecification,
dev: bool, dev: bool,
target: Option<String>, target: Option<String>,
@ -31,16 +29,16 @@ pub(crate) async fn run(
requirements: Vec<RequirementsSource>, requirements: Vec<RequirementsSource>,
python: Option<String>, python: Option<String>,
upgrade: Upgrade, upgrade: Upgrade,
exclude_newer: Option<ExcludeNewer>,
package: Option<PackageName>, package: Option<PackageName>,
settings: ResolverInstallerSettings,
isolated: bool, isolated: bool,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
let client_builder = BaseClientBuilder::new().connectivity(connectivity);
if preview.is_disabled() { if preview.is_disabled() {
warn_user!("`uv run` is experimental and may change without warning."); warn_user!("`uv run` is experimental and may change without warning.");
} }
@ -76,10 +74,19 @@ pub(crate) async fn run(
root_project_name, root_project_name,
project.workspace(), project.workspace(),
venv.interpreter(), venv.interpreter(),
&index_locations,
upgrade, upgrade,
exclude_newer, &settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.resolution,
&settings.prerelease,
&settings.config_setting,
settings.exclude_newer.as_ref(),
&settings.link_mode,
preview, preview,
connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
) )
@ -89,10 +96,18 @@ pub(crate) async fn run(
project.workspace().root(), project.workspace().root(),
&venv, &venv,
&lock, &lock,
&index_locations,
extras, extras,
dev, dev,
&settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.config_setting,
&settings.link_mode,
&settings.compile_bytecode,
preview, preview,
connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
) )
@ -108,6 +123,10 @@ pub(crate) async fn run(
} else { } else {
debug!("Syncing ephemeral environment."); debug!("Syncing ephemeral environment.");
let client_builder = BaseClientBuilder::new()
.connectivity(connectivity)
.native_tls(native_tls);
// Discover an interpreter. // Discover an interpreter.
let interpreter = if let Some(project_env) = &project_env { let interpreter = if let Some(project_env) = &project_env {
project_env.interpreter().clone() project_env.interpreter().clone()
@ -144,11 +163,13 @@ pub(crate) async fn run(
project::update_environment( project::update_environment(
venv, venv,
&requirements, &requirements,
&index_locations, &settings,
preview,
connectivity, connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
preview,
) )
.await?, .await?,
) )

View file

@ -1,13 +1,14 @@
use anyhow::Result;
use std::path::Path; use std::path::Path;
use anyhow::Result;
use distribution_types::IndexLocations; use distribution_types::IndexLocations;
use install_wheel_rs::linker::LinkMode; use install_wheel_rs::linker::LinkMode;
use uv_cache::Cache; use uv_cache::Cache;
use uv_client::{FlatIndexClient, RegistryClientBuilder}; use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{ use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, ExtrasSpecification, PreviewMode, Reinstall, BuildOptions, Concurrency, ConfigSettings, ExtrasSpecification, IndexStrategy,
SetupPyStrategy, KeyringProviderType, PreviewMode, Reinstall, SetupPyStrategy,
}; };
use uv_dispatch::BuildDispatch; use uv_dispatch::BuildDispatch;
use uv_distribution::{ProjectWorkspace, DEV_DEPENDENCIES}; use uv_distribution::{ProjectWorkspace, DEV_DEPENDENCIES};
@ -23,15 +24,19 @@ use crate::commands::pip::operations::Modifications;
use crate::commands::project::ProjectError; use crate::commands::project::ProjectError;
use crate::commands::{pip, project, ExitStatus}; use crate::commands::{pip, project, ExitStatus};
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::InstallerSettings;
/// Sync the project environment. /// Sync the project environment.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub(crate) async fn sync( pub(crate) async fn sync(
index_locations: IndexLocations,
extras: ExtrasSpecification, extras: ExtrasSpecification,
dev: bool, dev: bool,
python: Option<String>, python: Option<String>,
settings: InstallerSettings,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
@ -58,10 +63,18 @@ pub(crate) async fn sync(
project.workspace().root(), project.workspace().root(),
&venv, &venv,
&lock, &lock,
&index_locations,
extras, extras,
dev, dev,
&settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.config_setting,
&settings.link_mode,
&settings.compile_bytecode,
preview, preview,
connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
) )
@ -77,10 +90,18 @@ pub(super) async fn do_sync(
workspace_root: &Path, workspace_root: &Path,
venv: &PythonEnvironment, venv: &PythonEnvironment,
lock: &Lock, lock: &Lock,
index_locations: &IndexLocations,
extras: ExtrasSpecification, extras: ExtrasSpecification,
dev: bool, dev: bool,
index_locations: &IndexLocations,
index_strategy: &IndexStrategy,
keyring_provider: &KeyringProviderType,
config_setting: &ConfigSettings,
link_mode: &LinkMode,
compile_bytecode: &bool,
preview: PreviewMode, preview: PreviewMode,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<(), ProjectError> { ) -> Result<(), ProjectError> {
@ -109,25 +130,27 @@ pub(super) async fn do_sync(
lock.to_resolution(workspace_root, markers, tags, project_name, &extras, &dev)?; lock.to_resolution(workspace_root, markers, tags, project_name, &extras, &dev)?;
// Initialize the registry client. // Initialize the registry client.
// TODO(zanieb): Support client options e.g. offline, tls, etc.
let client = RegistryClientBuilder::new(cache.clone()) let client = RegistryClientBuilder::new(cache.clone())
.native_tls(native_tls)
.connectivity(connectivity)
.index_urls(index_locations.index_urls()) .index_urls(index_locations.index_urls())
.index_strategy(*index_strategy)
.keyring(*keyring_provider)
.markers(markers) .markers(markers)
.platform(venv.interpreter().platform()) .platform(venv.interpreter().platform())
.build(); .build();
// TODO(charlie): Respect project configuration. // Initialize any shared state.
let build_isolation = BuildIsolation::default();
let compile = false;
let concurrency = Concurrency::default();
let config_settings = ConfigSettings::default();
let dry_run = false;
let git = GitResolver::default(); let git = GitResolver::default();
let hasher = HashStrategy::default();
let in_flight = InFlight::default(); let in_flight = InFlight::default();
let index = InMemoryIndex::default(); let index = InMemoryIndex::default();
let link_mode = LinkMode::default();
// TODO(charlie): These are all default values. We should consider whether we want to make them
// optional on the downstream APIs.
let build_isolation = BuildIsolation::default();
let build_options = BuildOptions::default(); let build_options = BuildOptions::default();
let dry_run = false;
let hasher = HashStrategy::default();
let reinstall = Reinstall::default(); let reinstall = Reinstall::default();
let setup_py = SetupPyStrategy::default(); let setup_py = SetupPyStrategy::default();
@ -149,9 +172,9 @@ pub(super) async fn do_sync(
&git, &git,
&in_flight, &in_flight,
setup_py, setup_py,
&config_settings, config_setting,
build_isolation, build_isolation,
link_mode, *link_mode,
&build_options, &build_options,
concurrency, concurrency,
preview, preview,
@ -166,8 +189,8 @@ pub(super) async fn do_sync(
Modifications::Sufficient, Modifications::Sufficient,
&reinstall, &reinstall,
&build_options, &build_options,
link_mode, *link_mode,
compile, *compile_bytecode,
index_locations, index_locations,
&hasher, &hasher,
tags, tags,

View file

@ -6,10 +6,9 @@ use itertools::Itertools;
use tokio::process::Command; use tokio::process::Command;
use tracing::debug; use tracing::debug;
use distribution_types::IndexLocations;
use uv_cache::Cache; use uv_cache::Cache;
use uv_client::Connectivity; use uv_client::Connectivity;
use uv_configuration::PreviewMode; use uv_configuration::{Concurrency, PreviewMode};
use uv_requirements::RequirementsSource; use uv_requirements::RequirementsSource;
use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain}; use uv_toolchain::{PythonEnvironment, SystemPython, Toolchain};
use uv_warnings::warn_user; use uv_warnings::warn_user;
@ -17,6 +16,7 @@ use uv_warnings::warn_user;
use crate::commands::project::update_environment; use crate::commands::project::update_environment;
use crate::commands::ExitStatus; use crate::commands::ExitStatus;
use crate::printer::Printer; use crate::printer::Printer;
use crate::settings::ResolverInstallerSettings;
/// Run a command. /// Run a command.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -26,10 +26,12 @@ pub(crate) async fn run(
python: Option<String>, python: Option<String>,
from: Option<String>, from: Option<String>,
with: Vec<String>, with: Vec<String>,
settings: ResolverInstallerSettings,
_isolated: bool, _isolated: bool,
preview: PreviewMode, preview: PreviewMode,
index_locations: IndexLocations,
connectivity: Connectivity, connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache, cache: &Cache,
printer: Printer, printer: Printer,
) -> Result<ExitStatus> { ) -> Result<ExitStatus> {
@ -75,11 +77,13 @@ pub(crate) async fn run(
update_environment( update_environment(
venv, venv,
&requirements, &requirements,
&index_locations, &settings,
preview,
connectivity, connectivity,
concurrency,
native_tls,
cache, cache,
printer, printer,
preview,
) )
.await?, .await?,
); );

View file

@ -12,6 +12,7 @@ use tracing::{debug, instrument};
use cli::{ToolCommand, ToolNamespace, ToolchainCommand, ToolchainNamespace}; use cli::{ToolCommand, ToolNamespace, ToolchainCommand, ToolchainNamespace};
use uv_cache::Cache; use uv_cache::Cache;
use uv_configuration::Concurrency;
use uv_requirements::RequirementsSource; use uv_requirements::RequirementsSource;
use uv_workspace::Combine; use uv_workspace::Combine;
@ -123,6 +124,8 @@ async fn run() -> Result<ExitStatus> {
} else if cli.global_args.isolated { } else if cli.global_args.isolated {
None None
} else { } else {
// TODO(charlie): This needs to discover settings from the workspace _root_. Right now, it
// discovers the closest `pyproject.toml`, which could be a workspace _member_.
let project = uv_workspace::Workspace::find(env::current_dir()?)?; let project = uv_workspace::Workspace::find(env::current_dir()?)?;
let user = uv_workspace::Workspace::user()?; let user = uv_workspace::Workspace::user()?;
project.combine(user) project.combine(user)
@ -189,7 +192,7 @@ async fn run() -> Result<ExitStatus> {
// Resolve the settings from the command-line arguments and workspace configuration. // Resolve the settings from the command-line arguments and workspace configuration.
let args = PipCompileSettings::resolve(args, workspace); let args = PipCompileSettings::resolve(args, workspace);
rayon::ThreadPoolBuilder::new() rayon::ThreadPoolBuilder::new()
.num_threads(args.pip.concurrency.installs) .num_threads(args.settings.concurrency.installs)
.build_global() .build_global()
.expect("failed to initialize global rayon pool"); .expect("failed to initialize global rayon pool");
@ -217,39 +220,39 @@ async fn run() -> Result<ExitStatus> {
&constraints, &constraints,
&overrides, &overrides,
args.overrides_from_workspace, args.overrides_from_workspace,
args.pip.extras, args.settings.extras,
args.pip.output_file.as_deref(), args.settings.output_file.as_deref(),
args.pip.resolution, args.settings.resolution,
args.pip.prerelease, args.settings.prerelease,
args.pip.dependency_mode, args.settings.dependency_mode,
args.upgrade, args.upgrade,
args.pip.generate_hashes, args.settings.generate_hashes,
args.pip.no_emit_package, args.settings.no_emit_package,
args.pip.no_strip_extras, args.settings.no_strip_extras,
!args.pip.no_annotate, !args.settings.no_annotate,
!args.pip.no_header, !args.settings.no_header,
args.pip.custom_compile_command, args.settings.custom_compile_command,
args.pip.emit_index_url, args.settings.emit_index_url,
args.pip.emit_find_links, args.settings.emit_find_links,
args.pip.emit_marker_expression, args.settings.emit_marker_expression,
args.pip.emit_index_annotation, args.settings.emit_index_annotation,
args.pip.index_locations, args.settings.index_locations,
args.pip.index_strategy, args.settings.index_strategy,
args.pip.keyring_provider, args.settings.keyring_provider,
args.pip.setup_py, args.settings.setup_py,
args.pip.config_setting, args.settings.config_setting,
globals.connectivity, globals.connectivity,
args.pip.no_build_isolation, args.settings.no_build_isolation,
args.pip.no_build, args.settings.no_build,
args.pip.no_binary, args.settings.no_binary,
args.pip.python_version, args.settings.python_version,
args.pip.python_platform, args.settings.python_platform,
args.pip.exclude_newer, args.settings.exclude_newer,
args.pip.annotation_style, args.settings.annotation_style,
args.pip.link_mode, args.settings.link_mode,
args.pip.python, args.settings.python,
args.pip.system, args.settings.system,
args.pip.concurrency, args.settings.concurrency,
globals.native_tls, globals.native_tls,
globals.quiet, globals.quiet,
globals.preview, globals.preview,
@ -266,7 +269,7 @@ async fn run() -> Result<ExitStatus> {
// Resolve the settings from the command-line arguments and workspace configuration. // Resolve the settings from the command-line arguments and workspace configuration.
let args = PipSyncSettings::resolve(args, workspace); let args = PipSyncSettings::resolve(args, workspace);
rayon::ThreadPoolBuilder::new() rayon::ThreadPoolBuilder::new()
.num_threads(args.pip.concurrency.installs) .num_threads(args.settings.concurrency.installs)
.build_global() .build_global()
.expect("failed to initialize global rayon pool"); .expect("failed to initialize global rayon pool");
@ -288,28 +291,28 @@ async fn run() -> Result<ExitStatus> {
&requirements, &requirements,
&constraints, &constraints,
&args.reinstall, &args.reinstall,
args.pip.link_mode, args.settings.link_mode,
args.pip.compile_bytecode, args.settings.compile_bytecode,
args.pip.require_hashes, args.settings.require_hashes,
args.pip.index_locations, args.settings.index_locations,
args.pip.index_strategy, args.settings.index_strategy,
args.pip.keyring_provider, args.settings.keyring_provider,
args.pip.setup_py, args.settings.setup_py,
globals.connectivity, globals.connectivity,
&args.pip.config_setting, &args.settings.config_setting,
args.pip.no_build_isolation, args.settings.no_build_isolation,
args.pip.no_build, args.settings.no_build,
args.pip.no_binary, args.settings.no_binary,
args.pip.python_version, args.settings.python_version,
args.pip.python_platform, args.settings.python_platform,
args.pip.strict, args.settings.strict,
args.pip.exclude_newer, args.settings.exclude_newer,
args.pip.python, args.settings.python,
args.pip.system, args.settings.system,
args.pip.break_system_packages, args.settings.break_system_packages,
args.pip.target, args.settings.target,
args.pip.prefix, args.settings.prefix,
args.pip.concurrency, args.settings.concurrency,
globals.native_tls, globals.native_tls,
globals.preview, globals.preview,
cache, cache,
@ -326,7 +329,7 @@ async fn run() -> Result<ExitStatus> {
// Resolve the settings from the command-line arguments and workspace configuration. // Resolve the settings from the command-line arguments and workspace configuration.
let args = PipInstallSettings::resolve(args, workspace); let args = PipInstallSettings::resolve(args, workspace);
rayon::ThreadPoolBuilder::new() rayon::ThreadPoolBuilder::new()
.num_threads(args.pip.concurrency.installs) .num_threads(args.settings.concurrency.installs)
.build_global() .build_global()
.expect("failed to initialize global rayon pool"); .expect("failed to initialize global rayon pool");
@ -359,34 +362,34 @@ async fn run() -> Result<ExitStatus> {
&constraints, &constraints,
&overrides, &overrides,
args.overrides_from_workspace, args.overrides_from_workspace,
&args.pip.extras, &args.settings.extras,
args.pip.resolution, args.settings.resolution,
args.pip.prerelease, args.settings.prerelease,
args.pip.dependency_mode, args.settings.dependency_mode,
args.upgrade, args.upgrade,
args.pip.index_locations, args.settings.index_locations,
args.pip.index_strategy, args.settings.index_strategy,
args.pip.keyring_provider, args.settings.keyring_provider,
args.reinstall, args.reinstall,
args.pip.link_mode, args.settings.link_mode,
args.pip.compile_bytecode, args.settings.compile_bytecode,
args.pip.require_hashes, args.settings.require_hashes,
args.pip.setup_py, args.settings.setup_py,
globals.connectivity, globals.connectivity,
&args.pip.config_setting, &args.settings.config_setting,
args.pip.no_build_isolation, args.settings.no_build_isolation,
args.pip.no_build, args.settings.no_build,
args.pip.no_binary, args.settings.no_binary,
args.pip.python_version, args.settings.python_version,
args.pip.python_platform, args.settings.python_platform,
args.pip.strict, args.settings.strict,
args.pip.exclude_newer, args.settings.exclude_newer,
args.pip.python, args.settings.python,
args.pip.system, args.settings.system,
args.pip.break_system_packages, args.settings.break_system_packages,
args.pip.target, args.settings.target,
args.pip.prefix, args.settings.prefix,
args.pip.concurrency, args.settings.concurrency,
globals.native_tls, globals.native_tls,
globals.preview, globals.preview,
cache, cache,
@ -416,16 +419,16 @@ async fn run() -> Result<ExitStatus> {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
commands::pip_uninstall( commands::pip_uninstall(
&sources, &sources,
args.pip.python, args.settings.python,
args.pip.system, args.settings.system,
args.pip.break_system_packages, args.settings.break_system_packages,
args.pip.target, args.settings.target,
args.pip.prefix, args.settings.prefix,
cache, cache,
globals.connectivity, globals.connectivity,
globals.native_tls, globals.native_tls,
globals.preview, globals.preview,
args.pip.keyring_provider, args.settings.keyring_provider,
printer, printer,
) )
.await .await
@ -441,9 +444,9 @@ async fn run() -> Result<ExitStatus> {
commands::pip_freeze( commands::pip_freeze(
args.exclude_editable, args.exclude_editable,
args.pip.strict, args.settings.strict,
args.pip.python.as_deref(), args.settings.python.as_deref(),
args.pip.system, args.settings.system,
globals.preview, globals.preview,
&cache, &cache,
printer, printer,
@ -465,9 +468,9 @@ async fn run() -> Result<ExitStatus> {
args.exclude_editable, args.exclude_editable,
&args.exclude, &args.exclude,
&args.format, &args.format,
args.pip.strict, args.settings.strict,
args.pip.python.as_deref(), args.settings.python.as_deref(),
args.pip.system, args.settings.system,
globals.preview, globals.preview,
&cache, &cache,
printer, printer,
@ -484,9 +487,9 @@ async fn run() -> Result<ExitStatus> {
commands::pip_show( commands::pip_show(
args.package, args.package,
args.pip.strict, args.settings.strict,
args.pip.python.as_deref(), args.settings.python.as_deref(),
args.pip.system, args.settings.system,
globals.preview, globals.preview,
&cache, &cache,
printer, printer,
@ -502,8 +505,8 @@ async fn run() -> Result<ExitStatus> {
let cache = cache.init()?; let cache = cache.init()?;
commands::pip_check( commands::pip_check(
args.shared.python.as_deref(), args.settings.python.as_deref(),
args.shared.system, args.settings.system,
globals.preview, globals.preview,
&cache, &cache,
printer, printer,
@ -542,17 +545,17 @@ async fn run() -> Result<ExitStatus> {
commands::venv( commands::venv(
&args.name, &args.name,
args.pip.python.as_deref(), args.settings.python.as_deref(),
args.pip.link_mode, args.settings.link_mode,
&args.pip.index_locations, &args.settings.index_locations,
args.pip.index_strategy, args.settings.index_strategy,
args.pip.keyring_provider, args.settings.keyring_provider,
uv_virtualenv::Prompt::from_args(prompt), uv_virtualenv::Prompt::from_args(prompt),
args.system_site_packages, args.system_site_packages,
globals.connectivity, globals.connectivity,
args.seed, args.seed,
args.allow_existing, args.allow_existing,
args.pip.exclude_newer, args.settings.exclude_newer,
globals.native_tls, globals.native_tls,
globals.preview, globals.preview,
&cache, &cache,
@ -588,7 +591,6 @@ async fn run() -> Result<ExitStatus> {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
commands::run( commands::run(
args.index_locations,
args.extras, args.extras,
args.dev, args.dev,
args.target, args.target,
@ -596,11 +598,13 @@ async fn run() -> Result<ExitStatus> {
requirements, requirements,
args.python, args.python,
args.upgrade, args.upgrade,
args.exclude_newer,
args.package, args.package,
args.settings,
globals.isolated, globals.isolated,
globals.preview, globals.preview,
globals.connectivity, globals.connectivity,
Concurrency::default(),
globals.native_tls,
&cache, &cache,
printer, printer,
) )
@ -614,11 +618,14 @@ async fn run() -> Result<ExitStatus> {
let cache = cache.init()?.with_refresh(args.refresh); let cache = cache.init()?.with_refresh(args.refresh);
commands::sync( commands::sync(
args.index_locations,
args.extras, args.extras,
args.dev, args.dev,
args.python, args.python,
args.settings,
globals.preview, globals.preview,
globals.connectivity,
Concurrency::default(),
globals.native_tls,
&cache, &cache,
printer, printer,
) )
@ -632,11 +639,13 @@ async fn run() -> Result<ExitStatus> {
let cache = cache.init()?.with_refresh(args.refresh); let cache = cache.init()?.with_refresh(args.refresh);
commands::lock( commands::lock(
args.index_locations,
args.upgrade, args.upgrade,
args.exclude_newer,
args.python, args.python,
args.settings,
globals.preview, globals.preview,
globals.connectivity,
Concurrency::default(),
globals.native_tls,
&cache, &cache,
printer, printer,
) )
@ -653,6 +662,9 @@ async fn run() -> Result<ExitStatus> {
args.requirements, args.requirements,
args.python, args.python,
globals.preview, globals.preview,
globals.connectivity,
Concurrency::default(),
globals.native_tls,
&cache, &cache,
printer, printer,
) )
@ -669,6 +681,9 @@ async fn run() -> Result<ExitStatus> {
args.requirements, args.requirements,
args.python, args.python,
globals.preview, globals.preview,
globals.connectivity,
Concurrency::default(),
globals.native_tls,
&cache, &cache,
printer, printer,
) )
@ -701,10 +716,12 @@ async fn run() -> Result<ExitStatus> {
args.python, args.python,
args.from, args.from,
args.with, args.with,
args.settings,
globals.isolated, globals.isolated,
globals.preview, globals.preview,
args.index_locations,
globals.connectivity, globals.connectivity,
Concurrency::default(),
globals.native_tls,
&cache, &cache,
printer, printer,
) )

File diff suppressed because it is too large Load diff

View file

@ -9272,6 +9272,229 @@ fn resolve_configuration() -> Result<()> {
"### "###
); );
// Write out to the top-level (`tool.uv`, rather than `tool.uv.pip`).
pyproject.write_str(indoc::indoc! {r#"
[project]
name = "example"
version = "0.0.0"
[tool.uv]
resolution = "lowest-direct"
"#})?;
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("anyio>3.0.0")?;
uv_snapshot!(context.compile()
.arg("requirements.in"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
anyio==3.0.1
# via -r requirements.in
idna==3.6
# via anyio
sniffio==1.3.1
# via anyio
----- stderr -----
Resolved 3 packages in [TIME]
"###
);
// Write out to both the top-level (`tool.uv`) and the pip section (`tool.uv.pip`). The
// `tool.uv.pip` section should take precedence.
pyproject.write_str(indoc::indoc! {r#"
[project]
name = "example"
version = "0.0.0"
[tool.uv]
resolution = "lowest-direct"
[tool.uv.pip]
resolution = "highest"
"#})?;
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("anyio>3.0.0")?;
uv_snapshot!(context.compile()
.arg("requirements.in"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
anyio==4.3.0
# via -r requirements.in
idna==3.6
# via anyio
sniffio==1.3.1
# via anyio
----- stderr -----
Resolved 3 packages in [TIME]
"###
);
// But the command-line should take precedence over both.
uv_snapshot!(context.compile()
.arg("requirements.in")
.arg("--resolution=lowest-direct"), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in --resolution=lowest-direct
anyio==3.0.1
# via -r requirements.in
idna==3.6
# via anyio
sniffio==1.3.1
# via anyio
----- stderr -----
Resolved 3 packages in [TIME]
"###
);
Ok(())
}
/// Verify that user configuration is respected.
#[test]
#[cfg(not(windows))]
fn resolve_user_configuration() -> Result<()> {
// Create a temporary directory to store the user configuration.
let xdg = assert_fs::TempDir::new().expect("Failed to create temp dir");
let uv = xdg.child("uv");
let config = uv.child("uv.toml");
config.write_str(indoc::indoc! {r#"
[pip]
resolution = "lowest-direct"
"#})?;
let context = TestContext::new("3.12");
let requirements_in = context.temp_dir.child("requirements.in");
requirements_in.write_str("anyio>3.0.0")?;
// Resolution should use the lowest direct version.
uv_snapshot!(context.compile()
.arg("requirements.in")
.env("XDG_CONFIG_HOME", xdg.path()), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
anyio==3.0.1
# via -r requirements.in
idna==3.6
# via anyio
sniffio==1.3.1
# via anyio
----- stderr -----
Resolved 3 packages in [TIME]
"###
);
// Add a local configuration to generate hashes.
let config = context.temp_dir.child("uv.toml");
config.write_str(indoc::indoc! {r"
[pip]
generate-hashes = true
"})?;
// Resolution should use the lowest direct version and generate hashes.
uv_snapshot!(context.compile()
.arg("requirements.in")
.env("XDG_CONFIG_HOME", xdg.path()), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
anyio==3.0.1 \
--hash=sha256:1ef7622396ab55829d4236a6f75e2199df6d26a4ba79bea0cb942a5fd2f79a23 \
--hash=sha256:ed71f7542ef39875b65def219794d9dcb0a48c571317b13612c12b1f292701b5
# via -r requirements.in
idna==3.6 \
--hash=sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca \
--hash=sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f
# via anyio
sniffio==1.3.1 \
--hash=sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2 \
--hash=sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc
# via anyio
----- stderr -----
Resolved 3 packages in [TIME]
"###
);
// Add a local configuration to override the user configuration.
let config = context.temp_dir.child("uv.toml");
config.write_str(indoc::indoc! {r#"
[pip]
resolution = "highest"
"#})?;
// Resolution should use the highest version.
uv_snapshot!(context.compile()
.arg("requirements.in")
.env("XDG_CONFIG_HOME", xdg.path()), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
anyio==4.3.0
# via -r requirements.in
idna==3.6
# via anyio
sniffio==1.3.1
# via anyio
----- stderr -----
Resolved 3 packages in [TIME]
"###
);
// However, the user-level `tool.uv.pip` settings override the project-level `tool.uv` settings.
// This is awkward, but we merge the user configuration into the workspace configuration, so
// the resulting configuration has both `tool.uv.pip.resolution` (from the user configuration)
// and `tool.uv.resolution` (from the workspace settings), so we choose the former.
let config = context.temp_dir.child("uv.toml");
config.write_str(indoc::indoc! {r#"
resolution = "highest"
"#})?;
// Resolution should use the highest version.
uv_snapshot!(context.compile()
.arg("requirements.in")
.env("XDG_CONFIG_HOME", xdg.path()), @r###"
success: true
exit_code: 0
----- stdout -----
# This file was autogenerated by uv via the following command:
# uv pip compile --cache-dir [CACHE_DIR] --exclude-newer 2024-03-25T00:00:00Z requirements.in
anyio==3.0.1
# via -r requirements.in
idna==3.6
# via anyio
sniffio==1.3.1
# via anyio
----- stderr -----
Resolved 3 packages in [TIME]
"###
);
Ok(()) Ok(())
} }

110
uv.schema.json generated
View file

@ -10,6 +10,22 @@
"null" "null"
] ]
}, },
"compile-bytecode": {
"type": [
"boolean",
"null"
]
},
"config-settings": {
"anyOf": [
{
"$ref": "#/definitions/ConfigSettings"
},
{
"type": "null"
}
]
},
"dev-dependencies": { "dev-dependencies": {
"description": "PEP 508-style requirements, e.g., `flask==3.0.0`, or `black @ https://...`.", "description": "PEP 508-style requirements, e.g., `flask==3.0.0`, or `black @ https://...`.",
"type": [ "type": [
@ -20,6 +36,74 @@
"type": "string" "type": "string"
} }
}, },
"exclude-newer": {
"anyOf": [
{
"$ref": "#/definitions/ExcludeNewer"
},
{
"type": "null"
}
]
},
"extra-index-url": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/IndexUrl"
}
},
"find-links": {
"type": [
"array",
"null"
],
"items": {
"$ref": "#/definitions/FlatIndexLocation"
}
},
"index-strategy": {
"anyOf": [
{
"$ref": "#/definitions/IndexStrategy"
},
{
"type": "null"
}
]
},
"index-url": {
"anyOf": [
{
"$ref": "#/definitions/IndexUrl"
},
{
"type": "null"
}
]
},
"keyring-provider": {
"anyOf": [
{
"$ref": "#/definitions/KeyringProviderType"
},
{
"type": "null"
}
]
},
"link-mode": {
"anyOf": [
{
"$ref": "#/definitions/LinkMode"
},
{
"type": "null"
}
]
},
"native-tls": { "native-tls": {
"type": [ "type": [
"boolean", "boolean",
@ -32,6 +116,12 @@
"null" "null"
] ]
}, },
"no-index": {
"type": [
"boolean",
"null"
]
},
"offline": { "offline": {
"type": [ "type": [
"boolean", "boolean",
@ -58,12 +148,32 @@
} }
] ]
}, },
"prerelease": {
"anyOf": [
{
"$ref": "#/definitions/PreReleaseMode"
},
{
"type": "null"
}
]
},
"preview": { "preview": {
"type": [ "type": [
"boolean", "boolean",
"null" "null"
] ]
}, },
"resolution": {
"anyOf": [
{
"$ref": "#/definitions/ResolutionMode"
},
{
"type": "null"
}
]
},
"sources": { "sources": {
"type": [ "type": [
"object", "object",