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,
path: PathBuf,
},
#[error("Querying Python at `{}` failed with exit status {code}\n--- stdout:\n{stdout}\n--- stderr:\n{stderr}\n---", path.display())]
StatusCode {
code: ExitStatus,

View file

@ -7,7 +7,7 @@ use uv_configuration::{ConfigSettings, IndexStrategy, KeyringProviderType, Targe
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
use uv_toolchain::PythonVersion;
use crate::{Options, PipOptions, Workspace};
use crate::{GlobalOptions, Options, PipOptions, ResolverInstallerOptions, Workspace};
pub trait Combine {
/// Combine two values, preferring the values in `self`.
@ -41,11 +41,8 @@ impl Combine for Option<Workspace> {
impl Combine for Options {
fn combine(self, other: Options) -> Options {
Options {
native_tls: self.native_tls.combine(other.native_tls),
offline: self.offline.combine(other.offline),
no_cache: self.no_cache.combine(other.no_cache),
preview: self.preview.combine(other.preview),
cache_dir: self.cache_dir.combine(other.cache_dir),
globals: self.globals.combine(other.globals),
top_level: self.top_level.combine(other.top_level),
pip: self.pip.combine(other.pip),
override_dependencies: self
.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> {
fn combine(self, other: Option<PipOptions>) -> Option<PipOptions> {
match (self, other) {

View file

@ -32,11 +32,10 @@ pub(crate) struct Tools {
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Options {
pub native_tls: Option<bool>,
pub offline: Option<bool>,
pub no_cache: Option<bool>,
pub preview: Option<bool>,
pub cache_dir: Option<PathBuf>,
#[serde(flatten)]
pub globals: GlobalOptions,
#[serde(flatten)]
pub top_level: ResolverInstallerOptions,
pub pip: Option<PipOptions>,
#[cfg_attr(
feature = "schemars",
@ -48,6 +47,76 @@ pub struct Options {
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.
#[allow(dead_code)]
#[derive(Debug, Clone, Default, Deserialize)]
@ -99,3 +168,120 @@ pub struct PipOptions {
pub concurrent_builds: 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)]
pub(crate) no_all_extras: bool,
#[command(flatten)]
pub(crate) resolver: ResolverArgs,
/// Ignore package dependencies, instead only add those packages explicitly listed
/// on the command line to the resulting the requirements file.
#[arg(long)]
@ -350,24 +353,6 @@ pub(crate) struct PipCompileArgs {
#[arg(long, overrides_with("no_deps"), hide = true)]
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.
#[arg(long, short)]
pub(crate) output_file: Option<PathBuf>,
@ -423,36 +408,6 @@ pub(crate) struct PipCompileArgs {
#[arg(long)]
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.
///
/// 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")]
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.,
/// `3.7` or `3.7.9`).
///
@ -590,13 +541,6 @@ pub(crate) struct PipCompileArgs {
#[arg(long)]
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
/// included in the resolution. Equivalent to pip-compile's `--unsafe-package` option.
#[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)]
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.
#[arg(long, alias = "force-reinstall", overrides_with("no_reinstall"))]
pub(crate) reinstall: bool,
@ -689,25 +643,6 @@ pub(crate) struct PipSyncArgs {
#[arg(long)]
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.
///
/// 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)]
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.
///
/// 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")]
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.,
/// `3.7` or `3.7.9`).
///
@ -923,13 +825,6 @@ pub(crate) struct PipSyncArgs {
#[arg(long, overrides_with("strict"), hide = true)]
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
/// print the resulting plan.
#[arg(long)]
@ -995,6 +890,9 @@ pub(crate) struct PipInstallArgs {
#[arg(long, overrides_with("all_extras"), hide = true)]
pub(crate) no_all_extras: bool,
#[command(flatten)]
pub(crate) installer: ResolverInstallerArgs,
/// Allow package upgrades.
#[arg(long, short = 'U', overrides_with("no_upgrade"))]
pub(crate) upgrade: bool,
@ -1041,43 +939,6 @@ pub(crate) struct PipInstallArgs {
#[arg(long, overrides_with("no_deps"), hide = true)]
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.
///
/// 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)]
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.
///
/// 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")]
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.,
/// `3.7` or `3.7.9`).
///
@ -1297,13 +1125,6 @@ pub(crate) struct PipInstallArgs {
#[arg(long, overrides_with("strict"), hide = true)]
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
/// print the resulting plan.
#[arg(long)]
@ -1673,48 +1494,8 @@ pub(crate) struct VenvArgs {
#[arg(long)]
pub(crate) system_site_packages: bool,
/// 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>,
/// 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,
#[command(flatten)]
pub(crate) index_args: IndexArgs,
/// The strategy to use when resolving against multiple index URLs.
///
@ -1741,6 +1522,15 @@ pub(crate) struct VenvArgs {
#[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 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)]
pub(crate) compat_args: compat::VenvCompatArgs,
}
@ -1808,7 +1598,7 @@ pub(crate) struct RunArgs {
pub(crate) upgrade_package: Vec<PackageName>,
#[command(flatten)]
pub(crate) index_args: IndexArgs,
pub(crate) installer: ResolverInstallerArgs,
/// 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)]
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.
#[arg(long, conflicts_with = "isolated")]
pub(crate) package: Option<PackageName>,
@ -1877,7 +1660,7 @@ pub(crate) struct SyncArgs {
pub(crate) refresh_package: Vec<PackageName>,
#[command(flatten)]
pub(crate) index_args: IndexArgs,
pub(crate) installer: InstallerArgs,
/// The Python interpreter to use to build the run environment.
///
@ -1925,7 +1708,7 @@ pub(crate) struct LockArgs {
pub(crate) upgrade_package: Vec<PackageName>,
#[command(flatten)]
pub(crate) index_args: IndexArgs,
pub(crate) resolver: ResolverArgs,
/// 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.
#[arg(long, short, env = "UV_PYTHON", verbatim_doc_comment)]
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)]
@ -2028,7 +1804,7 @@ pub(crate) struct ToolRunArgs {
pub(crate) with: Vec<String>,
#[command(flatten)]
pub(crate) index_args: IndexArgs,
pub(crate) installer: ResolverInstallerArgs,
/// 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
/// 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>>,
@ -2113,10 +1885,6 @@ pub(crate) struct IndexArgs {
/// 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>>>,
@ -2134,3 +1902,203 @@ pub(crate) struct IndexArgs {
#[arg(long)]
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 uv_distribution::pyproject_mut::PyProjectTomlMut;
use distribution_types::IndexLocations;
use anyhow::Result;
use pep508_rs::Requirement;
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_warnings::warn_user;
use crate::commands::{project, ExitStatus};
use crate::printer::Printer;
use crate::settings::{InstallerSettings, ResolverSettings};
/// Add one or more packages to the project requirements.
#[allow(clippy::too_many_arguments)]
@ -18,6 +20,9 @@ pub(crate) async fn add(
requirements: Vec<String>,
python: Option<String>,
preview: PreviewMode,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache,
printer: Printer,
) -> Result<ExitStatus> {
@ -43,9 +48,9 @@ pub(crate) async fn add(
// Discover or create the virtual environment.
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 exclude_newer = None;
// Lock and sync the environment.
let root_project_name = project
@ -59,10 +64,19 @@ pub(crate) async fn add(
root_project_name,
project.workspace(),
venv.interpreter(),
&index_locations,
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,
connectivity,
concurrency,
native_tls,
cache,
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.
// TODO(ibraheem): Should we accept CLI overrides for this? Should we even sync here?
let settings = InstallerSettings::default();
let extras = ExtrasSpecification::All;
let dev = true;
@ -78,10 +93,18 @@ pub(crate) async fn add(
project.workspace().root(),
&venv,
&lock,
&index_locations,
extras,
dev,
&settings.index_locations,
&settings.index_strategy,
&settings.keyring_provider,
&settings.config_setting,
&settings.link_mode,
&settings.compile_bytecode,
preview,
connectivity,
concurrency,
native_tls,
cache,
printer,
)

View file

@ -5,17 +5,20 @@ use anstream::eprint;
use distribution_types::{IndexLocations, UnresolvedRequirementSpecification};
use install_wheel_rs::linker::LinkMode;
use uv_cache::Cache;
use uv_client::{FlatIndexClient, RegistryClientBuilder};
use uv_client::{Connectivity, FlatIndexClient, RegistryClientBuilder};
use uv_configuration::{
BuildOptions, Concurrency, ConfigSettings, ExtrasSpecification, PreviewMode, Reinstall,
SetupPyStrategy, Upgrade,
BuildOptions, Concurrency, ConfigSettings, ExtrasSpecification, IndexStrategy,
KeyringProviderType, PreviewMode, Reinstall, SetupPyStrategy, Upgrade,
};
use uv_dispatch::BuildDispatch;
use uv_distribution::{Workspace, DEV_DEPENDENCIES};
use uv_git::GitResolver;
use uv_normalize::PackageName;
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_types::{BuildIsolation, EmptyInstalledPackages, HashStrategy, InFlight};
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::{pip, project, ExitStatus};
use crate::printer::Printer;
use crate::settings::ResolverSettings;
/// Resolve the project requirements into a lockfile.
#[allow(clippy::too_many_arguments)]
pub(crate) async fn lock(
index_locations: IndexLocations,
upgrade: Upgrade,
exclude_newer: Option<ExcludeNewer>,
python: Option<String>,
settings: ResolverSettings,
preview: PreviewMode,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache,
printer: Printer,
) -> anyhow::Result<ExitStatus> {
@ -57,10 +63,19 @@ pub(crate) async fn lock(
root_project_name,
&workspace,
&interpreter,
&index_locations,
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,
connectivity,
concurrency,
native_tls,
cache,
printer,
)
@ -85,10 +100,19 @@ pub(super) async fn do_lock(
root_project_name: Option<PackageName>,
workspace: &Workspace,
interpreter: &Interpreter,
index_locations: &IndexLocations,
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,
connectivity: Connectivity,
concurrency: Concurrency,
native_tls: bool,
cache: &Cache,
printer: Printer,
) -> Result<Lock, ProjectError> {
@ -137,28 +161,36 @@ pub(super) async fn do_lock(
};
// Initialize the registry client.
// TODO(zanieb): Support client options e.g. offline, tls, etc.
let client = RegistryClientBuilder::new(cache.clone())
.native_tls(native_tls)
.connectivity(connectivity)
.index_urls(index_locations.index_urls())
.index_strategy(*index_strategy)
.keyring(*keyring_provider)
.markers(interpreter.markers())
.platform(interpreter.platform())
.build();
// TODO(charlie): Respect project configuration.
let build_isolation = BuildIsolation::default();
let concurrency = Concurrency::default();
let config_settings = ConfigSettings::default();
let extras = ExtrasSpecification::default();
let options = OptionsBuilder::new()
.resolution_mode(*resolution)
.prerelease_mode(*prerelease)
.exclude_newer(exclude_newer.copied())
.index_strategy(*index_strategy)
.build();
let hasher = HashStrategy::Generate;
// Initialize any shared state.
let in_flight = InFlight::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 extras = ExtrasSpecification::default();
let reinstall = Reinstall::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`.
let flat_index = {
let client = FlatIndexClient::new(&client, cache);
@ -183,9 +215,9 @@ pub(super) async fn do_lock(
&git,
&in_flight,
setup_py,
&config_settings,
config_setting,
build_isolation,
link_mode,
*link_mode,
&build_options,
concurrency,
preview,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

110
uv.schema.json generated
View file

@ -10,6 +10,22 @@
"null"
]
},
"compile-bytecode": {
"type": [
"boolean",
"null"
]
},
"config-settings": {
"anyOf": [
{
"$ref": "#/definitions/ConfigSettings"
},
{
"type": "null"
}
]
},
"dev-dependencies": {
"description": "PEP 508-style requirements, e.g., `flask==3.0.0`, or `black @ https://...`.",
"type": [
@ -20,6 +36,74 @@
"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": {
"type": [
"boolean",
@ -32,6 +116,12 @@
"null"
]
},
"no-index": {
"type": [
"boolean",
"null"
]
},
"offline": {
"type": [
"boolean",
@ -58,12 +148,32 @@
}
]
},
"prerelease": {
"anyOf": [
{
"$ref": "#/definitions/PreReleaseMode"
},
{
"type": "null"
}
]
},
"preview": {
"type": [
"boolean",
"null"
]
},
"resolution": {
"anyOf": [
{
"$ref": "#/definitions/ResolutionMode"
},
{
"type": "null"
}
]
},
"sources": {
"type": [
"object",