mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Load configuration options from workspace root (#4295)
## Summary In a workspace, we now read configuration from the workspace root. Previously, we read configuration from the first `pyproject.toml` or `uv.toml` file in path -- but in a workspace, that would often be the _project_ rather than the workspace configuration. We need to read configuration from the workspace root, rather than its members, because we lock the workspace globally, so all configuration applies to the workspace globally. As part of this change, the `uv-workspace` crate has been renamed to `uv-settings` and its purpose has been narrowed significantly (it no longer discovers a workspace; instead, it just reads the settings from a directory). If a user has a `uv.toml` in their directory or in a parent directory but is _not_ in a workspace, we will still respect that use-case as before. Closes #4249.
This commit is contained in:
parent
e0a389032f
commit
cacd1a2b5a
15 changed files with 338 additions and 357 deletions
49
Cargo.lock
generated
49
Cargo.lock
generated
|
@ -4427,11 +4427,11 @@ dependencies = [
|
|||
"uv-normalize",
|
||||
"uv-requirements",
|
||||
"uv-resolver",
|
||||
"uv-settings",
|
||||
"uv-toolchain",
|
||||
"uv-types",
|
||||
"uv-virtualenv",
|
||||
"uv-warnings",
|
||||
"uv-workspace",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -4618,9 +4618,9 @@ dependencies = [
|
|||
"uv-git",
|
||||
"uv-installer",
|
||||
"uv-resolver",
|
||||
"uv-settings",
|
||||
"uv-toolchain",
|
||||
"uv-types",
|
||||
"uv-workspace",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
|
@ -4886,6 +4886,28 @@ dependencies = [
|
|||
"uv-warnings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uv-settings"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
"distribution-types",
|
||||
"fs-err",
|
||||
"install-wheel-rs",
|
||||
"pep508_rs",
|
||||
"pypi-types",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"tracing",
|
||||
"uv-configuration",
|
||||
"uv-fs",
|
||||
"uv-normalize",
|
||||
"uv-resolver",
|
||||
"uv-toolchain",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uv-state"
|
||||
version = "0.0.1"
|
||||
|
@ -4990,29 +5012,6 @@ dependencies = [
|
|||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uv-workspace"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
"distribution-types",
|
||||
"fs-err",
|
||||
"install-wheel-rs",
|
||||
"pep508_rs",
|
||||
"pypi-types",
|
||||
"schemars",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"toml",
|
||||
"tracing",
|
||||
"uv-configuration",
|
||||
"uv-fs",
|
||||
"uv-normalize",
|
||||
"uv-resolver",
|
||||
"uv-toolchain",
|
||||
"uv-warnings",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -38,16 +38,16 @@ uv-extract = { path = "crates/uv-extract" }
|
|||
uv-fs = { path = "crates/uv-fs" }
|
||||
uv-git = { path = "crates/uv-git" }
|
||||
uv-installer = { path = "crates/uv-installer" }
|
||||
uv-toolchain = { path = "crates/uv-toolchain" }
|
||||
uv-normalize = { path = "crates/uv-normalize" }
|
||||
uv-requirements = { path = "crates/uv-requirements" }
|
||||
uv-resolver = { path = "crates/uv-resolver" }
|
||||
uv-settings = { path = "crates/uv-settings" }
|
||||
uv-state = { path = "crates/uv-state" }
|
||||
uv-toolchain = { path = "crates/uv-toolchain" }
|
||||
uv-types = { path = "crates/uv-types" }
|
||||
uv-version = { path = "crates/uv-version" }
|
||||
uv-virtualenv = { path = "crates/uv-virtualenv" }
|
||||
uv-warnings = { path = "crates/uv-warnings" }
|
||||
uv-workspace = { path = "crates/uv-workspace" }
|
||||
|
||||
anstream = { version = "0.6.13" }
|
||||
anyhow = { version = "1.0.80" }
|
||||
|
|
|
@ -30,10 +30,10 @@ uv-distribution = { workspace = true, features = ["schemars"] }
|
|||
uv-fs = { workspace = true }
|
||||
uv-git = { workspace = true }
|
||||
uv-installer = { workspace = true }
|
||||
uv-toolchain = { workspace = true }
|
||||
uv-resolver = { workspace = true }
|
||||
uv-settings = { workspace = true, features = ["schemars"] }
|
||||
uv-toolchain = { workspace = true }
|
||||
uv-types = { workspace = true }
|
||||
uv-workspace = { workspace = true, features = ["schemars"] }
|
||||
|
||||
# Any dependencies that are exclusively used in `uv-dev` should be listed as non-workspace
|
||||
# dependencies, to ensure that we're forced to think twice before including them in other crates.
|
||||
|
|
|
@ -6,7 +6,7 @@ use pretty_assertions::StrComparison;
|
|||
use schemars::{schema_for, JsonSchema};
|
||||
use serde::Deserialize;
|
||||
|
||||
use uv_workspace::Options;
|
||||
use uv_settings::Options;
|
||||
|
||||
use crate::ROOT_DIR;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[package]
|
||||
name = "uv-workspace"
|
||||
name = "uv-settings"
|
||||
version = "0.0.1"
|
||||
edition = { workspace = true }
|
||||
rust-version = { workspace = true }
|
||||
|
@ -22,7 +22,6 @@ uv-fs = { workspace = true }
|
|||
uv-normalize = { workspace = true, features = ["schemars"] }
|
||||
uv-resolver = { workspace = true, features = ["schemars"] }
|
||||
uv-toolchain = { workspace = true, features = ["schemars"] }
|
||||
uv-warnings = { workspace = true }
|
||||
|
||||
dirs-sys = { workspace = true }
|
||||
fs-err = { workspace = true }
|
|
@ -7,7 +7,7 @@ use uv_configuration::{ConfigSettings, IndexStrategy, KeyringProviderType, Targe
|
|||
use uv_resolver::{AnnotationStyle, ExcludeNewer, PreReleaseMode, ResolutionMode};
|
||||
use uv_toolchain::PythonVersion;
|
||||
|
||||
use crate::{GlobalOptions, Options, PipOptions, ResolverInstallerOptions, Workspace};
|
||||
use crate::{FilesystemOptions, GlobalOptions, Options, PipOptions, ResolverInstallerOptions};
|
||||
|
||||
pub trait Combine {
|
||||
/// Combine two values, preferring the values in `self`.
|
||||
|
@ -25,14 +25,13 @@ pub trait Combine {
|
|||
fn combine(self, other: Self) -> Self;
|
||||
}
|
||||
|
||||
impl Combine for Option<Workspace> {
|
||||
/// Combine the options used in two [`Workspace`]s. Retains the root of `self`.
|
||||
fn combine(self, other: Option<Workspace>) -> Option<Workspace> {
|
||||
impl Combine for Option<FilesystemOptions> {
|
||||
/// Combine the options used in two [`FilesystemOptions`]s. Retains the root of `self`.
|
||||
fn combine(self, other: Option<FilesystemOptions>) -> Option<FilesystemOptions> {
|
||||
match (self, other) {
|
||||
(Some(mut a), Some(b)) => {
|
||||
a.options = a.options.combine(b.options);
|
||||
Some(a)
|
||||
}
|
||||
(Some(a), Some(b)) => Some(FilesystemOptions(
|
||||
a.into_options().combine(b.into_options()),
|
||||
)),
|
||||
(a, b) => a.or(b),
|
||||
}
|
||||
}
|
167
crates/uv-settings/src/lib.rs
Normal file
167
crates/uv-settings/src/lib.rs
Normal file
|
@ -0,0 +1,167 @@
|
|||
use std::ops::Deref;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
use uv_fs::Simplified;
|
||||
|
||||
pub use crate::combine::*;
|
||||
pub use crate::settings::*;
|
||||
|
||||
mod combine;
|
||||
mod settings;
|
||||
|
||||
/// The [`Options`] as loaded from a configuration file on disk.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FilesystemOptions(Options);
|
||||
|
||||
impl FilesystemOptions {
|
||||
/// Convert the [`FilesystemOptions`] into [`Options`].
|
||||
pub fn into_options(self) -> Options {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for FilesystemOptions {
|
||||
type Target = Options;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FilesystemOptions {
|
||||
/// Load the user [`FilesystemOptions`].
|
||||
pub fn user() -> Result<Option<Self>, Error> {
|
||||
let Some(dir) = config_dir() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let root = dir.join("uv");
|
||||
let file = root.join("uv.toml");
|
||||
|
||||
debug!("Loading user configuration from: `{}`", file.display());
|
||||
match read_file(&file) {
|
||||
Ok(options) => Ok(Some(Self(options))),
|
||||
Err(Error::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||
Err(_) if !dir.is_dir() => {
|
||||
// Ex) `XDG_CONFIG_HOME=/dev/null`
|
||||
debug!(
|
||||
"User configuration directory `{}` does not exist or is not a directory",
|
||||
dir.display()
|
||||
);
|
||||
Ok(None)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the [`FilesystemOptions`] for the given path.
|
||||
///
|
||||
/// The search starts at the given path and goes up the directory tree until a `uv.toml` file is
|
||||
/// found.
|
||||
pub fn find(path: impl AsRef<Path>) -> Result<Option<Self>, Error> {
|
||||
for ancestor in path.as_ref().ancestors() {
|
||||
// Read a `uv.toml` file in the current directory.
|
||||
let path = ancestor.join("uv.toml");
|
||||
match fs_err::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
let options: Options = toml::from_str(&content)
|
||||
.map_err(|err| Error::UvToml(path.user_display().to_string(), err))?;
|
||||
|
||||
debug!("Found workspace configuration at `{}`", path.display());
|
||||
return Ok(Some(Self(options)));
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Load a [`FilesystemOptions`] from a directory, preferring a `uv.toml` file over a
|
||||
/// `pyproject.toml` file.
|
||||
pub fn from_directory(dir: impl AsRef<Path>) -> Result<Option<Self>, Error> {
|
||||
// Read a `uv.toml` file in the current directory.
|
||||
let path = dir.as_ref().join("uv.toml");
|
||||
match fs_err::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
let options: Options = toml::from_str(&content)
|
||||
.map_err(|err| Error::UvToml(path.user_display().to_string(), err))?;
|
||||
|
||||
debug!("Found workspace configuration at `{}`", path.display());
|
||||
return Ok(Some(Self(options)));
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
|
||||
// Read a `pyproject.toml` file in the current directory.
|
||||
let path = dir.as_ref().join("pyproject.toml");
|
||||
match fs_err::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
// Parse, but skip any `pyproject.toml` that doesn't have a `[tool.uv]` section.
|
||||
let pyproject: PyProjectToml = toml::from_str(&content)
|
||||
.map_err(|err| Error::PyprojectToml(path.user_display().to_string(), err))?;
|
||||
let Some(tool) = pyproject.tool else {
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(options) = tool.uv else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
debug!("Found workspace configuration at `{}`", path.display());
|
||||
return Ok(Some(Self(options)));
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Load a [`FilesystemOptions`] from a `uv.toml` file.
|
||||
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
|
||||
Ok(Self(read_file(path.as_ref())?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the user configuration directory.
|
||||
///
|
||||
/// This is similar to the `config_dir()` returned by the `dirs` crate, but it uses the
|
||||
/// `XDG_CONFIG_HOME` environment variable on both Linux _and_ macOS, rather than the
|
||||
/// `Application Support` directory on macOS.
|
||||
fn config_dir() -> Option<PathBuf> {
|
||||
// On Windows, use, e.g., C:\Users\Alice\AppData\Roaming
|
||||
#[cfg(windows)]
|
||||
{
|
||||
dirs_sys::known_folder_roaming_app_data()
|
||||
}
|
||||
|
||||
// On Linux and macOS, use, e.g., /home/alice/.config.
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
std::env::var_os("XDG_CONFIG_HOME")
|
||||
.and_then(dirs_sys::is_absolute_path)
|
||||
.or_else(|| dirs_sys::home_dir().map(|path| path.join(".config")))
|
||||
}
|
||||
}
|
||||
|
||||
/// Load [`Options`] from a `uv.toml` file.
|
||||
fn read_file(path: &Path) -> Result<Options, Error> {
|
||||
let content = fs_err::read_to_string(path)?;
|
||||
let options: Options = toml::from_str(&content)
|
||||
.map_err(|err| Error::UvToml(path.user_display().to_string(), err))?;
|
||||
Ok(options)
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Failed to parse: `{0}`")]
|
||||
PyprojectToml(String, #[source] toml::de::Error),
|
||||
|
||||
#[error("Failed to parse: `{0}`")]
|
||||
UvToml(String, #[source] toml::de::Error),
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
pub use crate::combine::*;
|
||||
pub use crate::settings::*;
|
||||
pub use crate::workspace::*;
|
||||
|
||||
mod combine;
|
||||
mod settings;
|
||||
mod workspace;
|
|
@ -1,168 +0,0 @@
|
|||
use std::path::{Path, PathBuf};
|
||||
use tracing::debug;
|
||||
|
||||
use uv_fs::Simplified;
|
||||
use uv_warnings::warn_user;
|
||||
|
||||
use crate::{Options, PyProjectToml};
|
||||
|
||||
/// Represents a project workspace that contains a set of options and a root path.
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Workspace {
|
||||
pub options: Options,
|
||||
pub root: PathBuf,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
/// Load the user [`Workspace`].
|
||||
pub fn user() -> Result<Option<Self>, WorkspaceError> {
|
||||
let Some(dir) = config_dir() else {
|
||||
return Ok(None);
|
||||
};
|
||||
let root = dir.join("uv");
|
||||
let file = root.join("uv.toml");
|
||||
|
||||
debug!("Loading user configuration from: `{}`", file.display());
|
||||
match read_file(&file) {
|
||||
Ok(options) => Ok(Some(Self { options, root })),
|
||||
Err(WorkspaceError::Io(err)) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||
Err(_) if !dir.is_dir() => {
|
||||
// Ex) `XDG_CONFIG_HOME=/dev/null`
|
||||
debug!(
|
||||
"User configuration directory `{}` does not exist or is not a directory",
|
||||
dir.display()
|
||||
);
|
||||
Ok(None)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the [`Workspace`] for the given path.
|
||||
///
|
||||
/// The search starts at the given path and goes up the directory tree until a workspace is
|
||||
/// found.
|
||||
pub fn find(path: impl AsRef<Path>) -> Result<Option<Self>, WorkspaceError> {
|
||||
for ancestor in path.as_ref().ancestors() {
|
||||
match find_in_directory(ancestor) {
|
||||
Ok(Some(options)) => {
|
||||
return Ok(Some(Self {
|
||||
options,
|
||||
root: ancestor.to_path_buf(),
|
||||
}))
|
||||
}
|
||||
Ok(None) => {
|
||||
// Continue traversing the directory tree.
|
||||
}
|
||||
Err(WorkspaceError::PyprojectToml(file, err)) => {
|
||||
// If we see an invalid `pyproject.toml`, warn but continue.
|
||||
warn_user!("Failed to parse `{file}`: {err}");
|
||||
}
|
||||
Err(err) => {
|
||||
// Otherwise, warn and stop.
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Load a [`Workspace`] from a `uv.toml` file.
|
||||
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, WorkspaceError> {
|
||||
Ok(Self {
|
||||
options: read_file(path.as_ref())?,
|
||||
root: path.as_ref().parent().unwrap().to_path_buf(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path to the user configuration directory.
|
||||
///
|
||||
/// This is similar to the `config_dir()` returned by the `dirs` crate, but it uses the
|
||||
/// `XDG_CONFIG_HOME` environment variable on both Linux _and_ macOS, rather than the
|
||||
/// `Application Support` directory on macOS.
|
||||
fn config_dir() -> Option<PathBuf> {
|
||||
// On Windows, use, e.g., C:\Users\Alice\AppData\Roaming
|
||||
#[cfg(windows)]
|
||||
{
|
||||
dirs_sys::known_folder_roaming_app_data()
|
||||
}
|
||||
|
||||
// On Linux and macOS, use, e.g., /home/alice/.config.
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
std::env::var_os("XDG_CONFIG_HOME")
|
||||
.and_then(dirs_sys::is_absolute_path)
|
||||
.or_else(|| dirs_sys::home_dir().map(|path| path.join(".config")))
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a `uv.toml` or `pyproject.toml` file in the given directory.
|
||||
fn find_in_directory(dir: &Path) -> Result<Option<Options>, WorkspaceError> {
|
||||
// Read a `uv.toml` file in the current directory.
|
||||
let path = dir.join("uv.toml");
|
||||
match fs_err::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
let options: Options = toml::from_str(&content)
|
||||
.map_err(|err| WorkspaceError::UvToml(path.user_display().to_string(), err))?;
|
||||
|
||||
debug!("Found workspace configuration at `{}`", path.display());
|
||||
return Ok(Some(options));
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
|
||||
// Read a `pyproject.toml` file in the current directory.
|
||||
let path = dir.join("pyproject.toml");
|
||||
match fs_err::read_to_string(&path) {
|
||||
Ok(content) => {
|
||||
// Parse, but skip any `pyproject.toml` that doesn't have a `[tool.uv]` section.
|
||||
let pyproject: PyProjectToml = toml::from_str(&content).map_err(|err| {
|
||||
WorkspaceError::PyprojectToml(path.user_display().to_string(), err)
|
||||
})?;
|
||||
let Some(tool) = pyproject.tool else {
|
||||
debug!(
|
||||
"Skipping `pyproject.toml` in `{}` (no `[tool]` section)",
|
||||
dir.display()
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
let Some(options) = tool.uv else {
|
||||
debug!(
|
||||
"Skipping `pyproject.toml` in `{}` (no `[tool.uv]` section)",
|
||||
dir.display()
|
||||
);
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
debug!("Found workspace configuration at `{}`", path.display());
|
||||
return Ok(Some(options));
|
||||
}
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => {}
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Load [`Options`] from a `uv.toml` file.
|
||||
fn read_file(path: &Path) -> Result<Options, WorkspaceError> {
|
||||
let content = fs_err::read_to_string(path)?;
|
||||
let options: Options = toml::from_str(&content)
|
||||
.map_err(|err| WorkspaceError::UvToml(path.user_display().to_string(), err))?;
|
||||
Ok(options)
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum WorkspaceError {
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
|
||||
#[error("Failed to parse: `{0}`")]
|
||||
PyprojectToml(String, #[source] toml::de::Error),
|
||||
|
||||
#[error("Failed to parse: `{0}`")]
|
||||
UvToml(String, #[source] toml::de::Error),
|
||||
}
|
|
@ -29,14 +29,14 @@ uv-distribution = { workspace = true }
|
|||
uv-fs = { workspace = true }
|
||||
uv-git = { workspace = true }
|
||||
uv-installer = { workspace = true }
|
||||
uv-toolchain = { workspace = true }
|
||||
uv-normalize = { workspace = true }
|
||||
uv-requirements = { workspace = true }
|
||||
uv-resolver = { workspace = true, features = ["clap"] }
|
||||
uv-settings = { workspace = true, features = ["schemars"] }
|
||||
uv-toolchain = { workspace = true }
|
||||
uv-types = { workspace = true }
|
||||
uv-virtualenv = { workspace = true }
|
||||
uv-warnings = { workspace = true }
|
||||
uv-workspace = { workspace = true, features = ["schemars"] }
|
||||
|
||||
anstream = { workspace = true }
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
@ -13,8 +13,9 @@ use tracing::{debug, instrument};
|
|||
use cli::{ToolCommand, ToolNamespace, ToolchainCommand, ToolchainNamespace};
|
||||
use uv_cache::Cache;
|
||||
use uv_configuration::Concurrency;
|
||||
use uv_distribution::Workspace;
|
||||
use uv_requirements::RequirementsSource;
|
||||
use uv_workspace::Combine;
|
||||
use uv_settings::Combine;
|
||||
|
||||
use crate::cli::{
|
||||
CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace, ProjectCommand,
|
||||
|
@ -115,24 +116,30 @@ async fn run() -> Result<ExitStatus> {
|
|||
uv_warnings::enable();
|
||||
}
|
||||
|
||||
// Load the workspace settings, prioritizing (in order):
|
||||
// Load configuration from the filesystem, prioritizing (in order):
|
||||
// 1. The configuration file specified on the command-line.
|
||||
// 2. The configuration file in the current directory.
|
||||
// 3. The user configuration file.
|
||||
let workspace = if let Some(config_file) = cli.config_file.as_ref() {
|
||||
Some(uv_workspace::Workspace::from_file(config_file)?)
|
||||
// 2. The configuration file in the current workspace (i.e., the `pyproject.toml` or `uv.toml`
|
||||
// file in the workspace root directory). If found, this file is combined with the user
|
||||
// configuration file.
|
||||
// 3. The nearest `uv.toml` file in the directory tree, starting from the current directory. If
|
||||
// found, this file is combined with the user configuration file. In this case, we don't
|
||||
// search for `pyproject.toml` files, since we're not in a workspace.
|
||||
let filesystem = if let Some(config_file) = cli.config_file.as_ref() {
|
||||
Some(uv_settings::FilesystemOptions::from_file(config_file)?)
|
||||
} else if cli.global_args.isolated {
|
||||
None
|
||||
} else if let Ok(project) = Workspace::discover(&env::current_dir()?, None).await {
|
||||
let project = uv_settings::FilesystemOptions::from_directory(project.root())?;
|
||||
let user = uv_settings::FilesystemOptions::user()?;
|
||||
project.combine(user)
|
||||
} 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()?;
|
||||
let project = uv_settings::FilesystemOptions::find(env::current_dir()?)?;
|
||||
let user = uv_settings::FilesystemOptions::user()?;
|
||||
project.combine(user)
|
||||
};
|
||||
|
||||
// Resolve the global settings.
|
||||
let globals = GlobalSettings::resolve(cli.global_args, workspace.as_ref());
|
||||
let globals = GlobalSettings::resolve(cli.global_args, filesystem.as_ref());
|
||||
|
||||
// Configure the `tracing` crate, which controls internal logging.
|
||||
#[cfg(feature = "tracing-durations-export")]
|
||||
|
@ -180,7 +187,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
debug!("uv {}", version::version());
|
||||
|
||||
// Resolve the cache settings.
|
||||
let cache = CacheSettings::resolve(cli.cache_args, workspace.as_ref());
|
||||
let cache = CacheSettings::resolve(cli.cache_args, filesystem.as_ref());
|
||||
let cache = Cache::from_settings(cache.no_cache, cache.cache_dir)?;
|
||||
|
||||
match cli.command {
|
||||
|
@ -190,7 +197,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
args.compat_args.validate()?;
|
||||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipCompileSettings::resolve(args, workspace);
|
||||
let args = PipCompileSettings::resolve(args, filesystem);
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(args.settings.concurrency.installs)
|
||||
.build_global()
|
||||
|
@ -267,7 +274,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
args.compat_args.validate()?;
|
||||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipSyncSettings::resolve(args, workspace);
|
||||
let args = PipSyncSettings::resolve(args, filesystem);
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(args.settings.concurrency.installs)
|
||||
.build_global()
|
||||
|
@ -327,7 +334,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
args.compat_args.validate()?;
|
||||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipInstallSettings::resolve(args, workspace);
|
||||
let args = PipInstallSettings::resolve(args, filesystem);
|
||||
rayon::ThreadPoolBuilder::new()
|
||||
.num_threads(args.settings.concurrency.installs)
|
||||
.build_global()
|
||||
|
@ -402,7 +409,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
command: PipCommand::Uninstall(args),
|
||||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipUninstallSettings::resolve(args, workspace);
|
||||
let args = PipUninstallSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -437,7 +444,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
command: PipCommand::Freeze(args),
|
||||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipFreezeSettings::resolve(args, workspace);
|
||||
let args = PipFreezeSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -458,7 +465,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
args.compat_args.validate()?;
|
||||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipListSettings::resolve(args, workspace);
|
||||
let args = PipListSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -480,7 +487,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
command: PipCommand::Show(args),
|
||||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipShowSettings::resolve(args, workspace);
|
||||
let args = PipShowSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -499,7 +506,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
command: PipCommand::Check(args),
|
||||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = PipCheckSettings::resolve(args, workspace);
|
||||
let args = PipCheckSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -529,7 +536,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
args.compat_args.validate()?;
|
||||
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::VenvSettings::resolve(args, workspace);
|
||||
let args = settings::VenvSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -565,7 +572,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}
|
||||
Commands::Project(ProjectCommand::Run(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::RunSettings::resolve(args, workspace);
|
||||
let args = settings::RunSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?.with_refresh(args.refresh);
|
||||
|
@ -612,7 +619,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}
|
||||
Commands::Project(ProjectCommand::Sync(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::SyncSettings::resolve(args, workspace);
|
||||
let args = settings::SyncSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?.with_refresh(args.refresh);
|
||||
|
@ -633,7 +640,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}
|
||||
Commands::Project(ProjectCommand::Lock(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::LockSettings::resolve(args, workspace);
|
||||
let args = settings::LockSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?.with_refresh(args.refresh);
|
||||
|
@ -653,7 +660,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}
|
||||
Commands::Project(ProjectCommand::Add(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::AddSettings::resolve(args, workspace);
|
||||
let args = settings::AddSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -672,7 +679,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
}
|
||||
Commands::Project(ProjectCommand::Remove(args)) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::RemoveSettings::resolve(args, workspace);
|
||||
let args = settings::RemoveSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -705,7 +712,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
command: ToolCommand::Run(args),
|
||||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::ToolRunSettings::resolve(args, workspace);
|
||||
let args = settings::ToolRunSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -731,7 +738,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
command: ToolchainCommand::List(args),
|
||||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::ToolchainListSettings::resolve(args, workspace);
|
||||
let args = settings::ToolchainListSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
@ -750,7 +757,7 @@ async fn run() -> Result<ExitStatus> {
|
|||
command: ToolchainCommand::Install(args),
|
||||
}) => {
|
||||
// Resolve the settings from the command-line arguments and workspace configuration.
|
||||
let args = settings::ToolchainInstallSettings::resolve(args, workspace);
|
||||
let args = settings::ToolchainInstallSettings::resolve(args, filesystem);
|
||||
|
||||
// Initialize the cache.
|
||||
let cache = cache.init()?;
|
||||
|
|
|
@ -17,10 +17,11 @@ use uv_configuration::{
|
|||
};
|
||||
use uv_normalize::PackageName;
|
||||
use uv_resolver::{AnnotationStyle, DependencyMode, ExcludeNewer, PreReleaseMode, ResolutionMode};
|
||||
use uv_toolchain::{Prefix, PythonVersion, Target};
|
||||
use uv_workspace::{
|
||||
Combine, InstallerOptions, PipOptions, ResolverInstallerOptions, ResolverOptions, Workspace,
|
||||
use uv_settings::{
|
||||
Combine, FilesystemOptions, InstallerOptions, Options, PipOptions, ResolverInstallerOptions,
|
||||
ResolverOptions,
|
||||
};
|
||||
use uv_toolchain::{Prefix, PythonVersion, Target};
|
||||
|
||||
use crate::cli::{
|
||||
AddArgs, ColorChoice, GlobalArgs, IndexArgs, InstallerArgs, LockArgs, Maybe, PipCheckArgs,
|
||||
|
@ -44,8 +45,8 @@ pub(crate) struct GlobalSettings {
|
|||
}
|
||||
|
||||
impl GlobalSettings {
|
||||
/// Resolve the [`GlobalSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: GlobalArgs, workspace: Option<&Workspace>) -> Self {
|
||||
/// Resolve the [`GlobalSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: GlobalArgs, workspace: Option<&FilesystemOptions>) -> Self {
|
||||
Self {
|
||||
quiet: args.quiet,
|
||||
verbose: args.verbose,
|
||||
|
@ -67,10 +68,10 @@ impl GlobalSettings {
|
|||
args.color
|
||||
},
|
||||
native_tls: flag(args.native_tls, args.no_native_tls)
|
||||
.combine(workspace.and_then(|workspace| workspace.options.globals.native_tls))
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.native_tls))
|
||||
.unwrap_or(false),
|
||||
connectivity: if flag(args.offline, args.no_offline)
|
||||
.combine(workspace.and_then(|workspace| workspace.options.globals.offline))
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.offline))
|
||||
.unwrap_or(false)
|
||||
{
|
||||
Connectivity::Offline
|
||||
|
@ -80,7 +81,7 @@ impl GlobalSettings {
|
|||
isolated: args.isolated,
|
||||
preview: PreviewMode::from(
|
||||
flag(args.preview, args.no_preview)
|
||||
.combine(workspace.and_then(|workspace| workspace.options.globals.preview))
|
||||
.combine(workspace.and_then(|workspace| workspace.globals.preview))
|
||||
.unwrap_or(false),
|
||||
),
|
||||
}
|
||||
|
@ -96,16 +97,16 @@ pub(crate) struct CacheSettings {
|
|||
}
|
||||
|
||||
impl CacheSettings {
|
||||
/// Resolve the [`CacheSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: CacheArgs, workspace: Option<&Workspace>) -> Self {
|
||||
/// Resolve the [`CacheSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: CacheArgs, workspace: Option<&FilesystemOptions>) -> Self {
|
||||
Self {
|
||||
no_cache: args.no_cache
|
||||
|| workspace
|
||||
.and_then(|workspace| workspace.options.globals.no_cache)
|
||||
.and_then(|workspace| workspace.globals.no_cache)
|
||||
.unwrap_or(false),
|
||||
cache_dir: args.cache_dir.or_else(|| {
|
||||
workspace.and_then(|workspace| workspace.options.globals.cache_dir.clone())
|
||||
}),
|
||||
cache_dir: args
|
||||
.cache_dir
|
||||
.or_else(|| workspace.and_then(|workspace| workspace.globals.cache_dir.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -127,9 +128,9 @@ pub(crate) struct RunSettings {
|
|||
}
|
||||
|
||||
impl RunSettings {
|
||||
/// Resolve the [`RunSettings`] from the CLI and workspace configuration.
|
||||
/// Resolve the [`RunSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: RunArgs, workspace: Option<Workspace>) -> Self {
|
||||
pub(crate) fn resolve(args: RunArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let RunArgs {
|
||||
extra,
|
||||
all_extras,
|
||||
|
@ -165,7 +166,7 @@ impl RunSettings {
|
|||
package,
|
||||
settings: ResolverInstallerSettings::combine(
|
||||
ResolverInstallerOptions::from(installer),
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -184,9 +185,9 @@ pub(crate) struct ToolRunSettings {
|
|||
}
|
||||
|
||||
impl ToolRunSettings {
|
||||
/// Resolve the [`ToolRunSettings`] from the CLI and workspace configuration.
|
||||
/// Resolve the [`ToolRunSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: ToolRunArgs, workspace: Option<Workspace>) -> Self {
|
||||
pub(crate) fn resolve(args: ToolRunArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let ToolRunArgs {
|
||||
target,
|
||||
args,
|
||||
|
@ -204,7 +205,7 @@ impl ToolRunSettings {
|
|||
python,
|
||||
settings: ResolverInstallerSettings::combine(
|
||||
ResolverInstallerOptions::from(installer),
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -227,9 +228,9 @@ pub(crate) struct ToolchainListSettings {
|
|||
}
|
||||
|
||||
impl ToolchainListSettings {
|
||||
/// Resolve the [`ToolchainListSettings`] from the CLI and workspace configuration.
|
||||
/// Resolve the [`ToolchainListSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: ToolchainListArgs, _workspace: Option<Workspace>) -> Self {
|
||||
pub(crate) fn resolve(args: ToolchainListArgs, _filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let ToolchainListArgs {
|
||||
all_versions,
|
||||
all_platforms,
|
||||
|
@ -259,9 +260,12 @@ pub(crate) struct ToolchainInstallSettings {
|
|||
}
|
||||
|
||||
impl ToolchainInstallSettings {
|
||||
/// Resolve the [`ToolchainInstallSettings`] from the CLI and workspace configuration.
|
||||
/// Resolve the [`ToolchainInstallSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: ToolchainInstallArgs, _workspace: Option<Workspace>) -> Self {
|
||||
pub(crate) fn resolve(
|
||||
args: ToolchainInstallArgs,
|
||||
_filesystem: Option<FilesystemOptions>,
|
||||
) -> Self {
|
||||
let ToolchainInstallArgs { target, force } = args;
|
||||
|
||||
Self { target, force }
|
||||
|
@ -280,9 +284,9 @@ pub(crate) struct SyncSettings {
|
|||
}
|
||||
|
||||
impl SyncSettings {
|
||||
/// Resolve the [`SyncSettings`] from the CLI and workspace configuration.
|
||||
/// Resolve the [`SyncSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: SyncArgs, workspace: Option<Workspace>) -> Self {
|
||||
pub(crate) fn resolve(args: SyncArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let SyncArgs {
|
||||
extra,
|
||||
all_extras,
|
||||
|
@ -304,7 +308,7 @@ impl SyncSettings {
|
|||
),
|
||||
dev: flag(dev, no_dev).unwrap_or(true),
|
||||
python,
|
||||
settings: InstallerSettings::combine(InstallerOptions::from(installer), workspace),
|
||||
settings: InstallerSettings::combine(InstallerOptions::from(installer), filesystem),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,9 +324,9 @@ pub(crate) struct LockSettings {
|
|||
}
|
||||
|
||||
impl LockSettings {
|
||||
/// Resolve the [`LockSettings`] from the CLI and workspace configuration.
|
||||
/// Resolve the [`LockSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: LockArgs, workspace: Option<Workspace>) -> Self {
|
||||
pub(crate) fn resolve(args: LockArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let LockArgs {
|
||||
refresh,
|
||||
no_refresh,
|
||||
|
@ -338,7 +342,7 @@ impl LockSettings {
|
|||
refresh: Refresh::from_args(flag(refresh, no_refresh), refresh_package),
|
||||
upgrade: Upgrade::from_args(flag(upgrade, no_upgrade), upgrade_package),
|
||||
python,
|
||||
settings: ResolverSettings::combine(ResolverOptions::from(resolver), workspace),
|
||||
settings: ResolverSettings::combine(ResolverOptions::from(resolver), filesystem),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,9 +356,9 @@ pub(crate) struct AddSettings {
|
|||
}
|
||||
|
||||
impl AddSettings {
|
||||
/// Resolve the [`AddSettings`] from the CLI and workspace configuration.
|
||||
/// Resolve the [`AddSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: AddArgs, _workspace: Option<Workspace>) -> Self {
|
||||
pub(crate) fn resolve(args: AddArgs, _filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let AddArgs {
|
||||
requirements,
|
||||
python,
|
||||
|
@ -376,9 +380,9 @@ pub(crate) struct RemoveSettings {
|
|||
}
|
||||
|
||||
impl RemoveSettings {
|
||||
/// Resolve the [`RemoveSettings`] from the CLI and workspace configuration.
|
||||
/// Resolve the [`RemoveSettings`] from the CLI and filesystem configuration.
|
||||
#[allow(clippy::needless_pass_by_value)]
|
||||
pub(crate) fn resolve(args: RemoveArgs, _workspace: Option<Workspace>) -> Self {
|
||||
pub(crate) fn resolve(args: RemoveArgs, _filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let RemoveArgs {
|
||||
requirements,
|
||||
python,
|
||||
|
@ -405,8 +409,8 @@ pub(crate) struct PipCompileSettings {
|
|||
}
|
||||
|
||||
impl PipCompileSettings {
|
||||
/// Resolve the [`PipCompileSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: PipCompileArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipCompileSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: PipCompileArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipCompileArgs {
|
||||
src_file,
|
||||
constraint,
|
||||
|
@ -459,9 +463,8 @@ impl PipCompileSettings {
|
|||
compat_args: _,
|
||||
} = args;
|
||||
|
||||
let overrides_from_workspace = if let Some(workspace) = &workspace {
|
||||
workspace
|
||||
.options
|
||||
let overrides_from_workspace = if let Some(configuration) = &filesystem {
|
||||
configuration
|
||||
.override_dependencies
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
|
@ -515,7 +518,7 @@ impl PipCompileSettings {
|
|||
concurrent_installs: env(env::CONCURRENT_INSTALLS),
|
||||
..PipOptions::from(resolver)
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -534,8 +537,8 @@ pub(crate) struct PipSyncSettings {
|
|||
}
|
||||
|
||||
impl PipSyncSettings {
|
||||
/// Resolve the [`PipSyncSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: PipSyncArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipSyncSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: PipSyncArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipSyncArgs {
|
||||
src_file,
|
||||
constraint,
|
||||
|
@ -603,7 +606,7 @@ impl PipSyncSettings {
|
|||
concurrent_installs: env(env::CONCURRENT_INSTALLS),
|
||||
..PipOptions::from(installer)
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -627,8 +630,8 @@ pub(crate) struct PipInstallSettings {
|
|||
}
|
||||
|
||||
impl PipInstallSettings {
|
||||
/// Resolve the [`PipInstallSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: PipInstallArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipInstallSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: PipInstallArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipInstallArgs {
|
||||
package,
|
||||
requirement,
|
||||
|
@ -675,9 +678,8 @@ impl PipInstallSettings {
|
|||
compat_args: _,
|
||||
} = args;
|
||||
|
||||
let overrides_from_workspace = if let Some(workspace) = &workspace {
|
||||
workspace
|
||||
.options
|
||||
let overrides_from_workspace = if let Some(configuration) = &filesystem {
|
||||
configuration
|
||||
.override_dependencies
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
|
@ -728,7 +730,7 @@ impl PipInstallSettings {
|
|||
concurrent_installs: env(env::CONCURRENT_INSTALLS),
|
||||
..PipOptions::from(installer)
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -744,8 +746,8 @@ pub(crate) struct PipUninstallSettings {
|
|||
}
|
||||
|
||||
impl PipUninstallSettings {
|
||||
/// Resolve the [`PipUninstallSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: PipUninstallArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipUninstallSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: PipUninstallArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipUninstallArgs {
|
||||
package,
|
||||
requirement,
|
||||
|
@ -772,7 +774,7 @@ impl PipUninstallSettings {
|
|||
keyring_provider,
|
||||
..PipOptions::default()
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -787,8 +789,8 @@ pub(crate) struct PipFreezeSettings {
|
|||
}
|
||||
|
||||
impl PipFreezeSettings {
|
||||
/// Resolve the [`PipFreezeSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: PipFreezeArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipFreezeSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: PipFreezeArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipFreezeArgs {
|
||||
exclude_editable,
|
||||
strict,
|
||||
|
@ -807,7 +809,7 @@ impl PipFreezeSettings {
|
|||
strict: flag(strict, no_strict),
|
||||
..PipOptions::default()
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -825,8 +827,8 @@ pub(crate) struct PipListSettings {
|
|||
}
|
||||
|
||||
impl PipListSettings {
|
||||
/// Resolve the [`PipListSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: PipListArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipListSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: PipListArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipListArgs {
|
||||
editable,
|
||||
exclude_editable,
|
||||
|
@ -852,7 +854,7 @@ impl PipListSettings {
|
|||
strict: flag(strict, no_strict),
|
||||
..PipOptions::default()
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -867,8 +869,8 @@ pub(crate) struct PipShowSettings {
|
|||
}
|
||||
|
||||
impl PipShowSettings {
|
||||
/// Resolve the [`PipShowSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: PipShowArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipShowSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: PipShowArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipShowArgs {
|
||||
package,
|
||||
strict,
|
||||
|
@ -887,7 +889,7 @@ impl PipShowSettings {
|
|||
strict: flag(strict, no_strict),
|
||||
..PipOptions::default()
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -901,8 +903,8 @@ pub(crate) struct PipCheckSettings {
|
|||
}
|
||||
|
||||
impl PipCheckSettings {
|
||||
/// Resolve the [`PipCheckSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: PipCheckArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipCheckSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: PipCheckArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipCheckArgs {
|
||||
python,
|
||||
system,
|
||||
|
@ -916,7 +918,7 @@ impl PipCheckSettings {
|
|||
system: flag(system, no_system),
|
||||
..PipOptions::default()
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -935,8 +937,8 @@ pub(crate) struct VenvSettings {
|
|||
}
|
||||
|
||||
impl VenvSettings {
|
||||
/// Resolve the [`VenvSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn resolve(args: VenvArgs, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`VenvSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn resolve(args: VenvArgs, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let VenvArgs {
|
||||
python,
|
||||
system,
|
||||
|
@ -970,7 +972,7 @@ impl VenvSettings {
|
|||
link_mode,
|
||||
..PipOptions::from(index_args)
|
||||
},
|
||||
workspace,
|
||||
filesystem,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -992,8 +994,8 @@ pub(crate) struct InstallerSettings {
|
|||
}
|
||||
|
||||
impl InstallerSettings {
|
||||
/// Resolve the [`InstallerSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn combine(args: InstallerOptions, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`InstallerSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn combine(args: InstallerOptions, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let ResolverInstallerOptions {
|
||||
index_url,
|
||||
extra_index_url,
|
||||
|
@ -1007,8 +1009,9 @@ impl InstallerSettings {
|
|||
exclude_newer: _,
|
||||
link_mode,
|
||||
compile_bytecode,
|
||||
} = workspace
|
||||
.map(|workspace| workspace.options.top_level)
|
||||
} = filesystem
|
||||
.map(FilesystemOptions::into_options)
|
||||
.map(|options| options.top_level)
|
||||
.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
|
@ -1059,8 +1062,8 @@ pub(crate) struct ResolverSettings {
|
|||
}
|
||||
|
||||
impl ResolverSettings {
|
||||
/// Resolve the [`ResolverSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn combine(args: ResolverOptions, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`ResolverSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn combine(args: ResolverOptions, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let ResolverInstallerOptions {
|
||||
index_url,
|
||||
extra_index_url,
|
||||
|
@ -1074,8 +1077,9 @@ impl ResolverSettings {
|
|||
exclude_newer,
|
||||
link_mode,
|
||||
compile_bytecode: _,
|
||||
} = workspace
|
||||
.map(|workspace| workspace.options.top_level)
|
||||
} = filesystem
|
||||
.map(FilesystemOptions::into_options)
|
||||
.map(|options| options.top_level)
|
||||
.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
|
@ -1127,8 +1131,11 @@ pub(crate) struct ResolverInstallerSettings {
|
|||
}
|
||||
|
||||
impl ResolverInstallerSettings {
|
||||
/// Resolve the [`ResolverInstallerSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn combine(args: ResolverInstallerOptions, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`ResolverInstallerSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn combine(
|
||||
args: ResolverInstallerOptions,
|
||||
filesystem: Option<FilesystemOptions>,
|
||||
) -> Self {
|
||||
let ResolverInstallerOptions {
|
||||
index_url,
|
||||
extra_index_url,
|
||||
|
@ -1142,8 +1149,9 @@ impl ResolverInstallerSettings {
|
|||
exclude_newer,
|
||||
link_mode,
|
||||
compile_bytecode,
|
||||
} = workspace
|
||||
.map(|workspace| workspace.options.top_level)
|
||||
} = filesystem
|
||||
.map(FilesystemOptions::into_options)
|
||||
.map(|options| options.top_level)
|
||||
.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
|
@ -1226,8 +1234,8 @@ pub(crate) struct PipSettings {
|
|||
}
|
||||
|
||||
impl PipSettings {
|
||||
/// Resolve the [`PipSettings`] from the CLI and workspace configuration.
|
||||
pub(crate) fn combine(args: PipOptions, workspace: Option<Workspace>) -> Self {
|
||||
/// Resolve the [`PipSettings`] from the CLI and filesystem configuration.
|
||||
pub(crate) fn combine(args: PipOptions, filesystem: Option<FilesystemOptions>) -> Self {
|
||||
let PipOptions {
|
||||
python,
|
||||
system,
|
||||
|
@ -1273,8 +1281,9 @@ impl PipSettings {
|
|||
concurrent_builds,
|
||||
concurrent_downloads,
|
||||
concurrent_installs,
|
||||
} = workspace
|
||||
.map(|workspace| workspace.options.pip())
|
||||
} = filesystem
|
||||
.map(FilesystemOptions::into_options)
|
||||
.map(Options::pip)
|
||||
.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
|
|
|
@ -3200,30 +3200,13 @@ fn override_dependency_from_workspace_invalid_syntax() -> Result<()> {
|
|||
.arg("pyproject.toml")
|
||||
.current_dir(&context.temp_dir)
|
||||
, @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- 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 pyproject.toml
|
||||
blinker==1.7.0
|
||||
# via flask
|
||||
click==8.1.7
|
||||
# via flask
|
||||
flask==3.0.0
|
||||
# via example (pyproject.toml)
|
||||
itsdangerous==2.1.2
|
||||
# via flask
|
||||
jinja2==3.1.3
|
||||
# via flask
|
||||
markupsafe==2.1.5
|
||||
# via
|
||||
# jinja2
|
||||
# werkzeug
|
||||
werkzeug==3.0.1
|
||||
# via flask
|
||||
|
||||
----- stderr -----
|
||||
warning: Failed to parse `pyproject.toml`: TOML parse error at line 9, column 29
|
||||
error: Failed to parse: `pyproject.toml`
|
||||
Caused by: TOML parse error at line 9, column 29
|
||||
|
|
||||
9 | override-dependencies = [
|
||||
| ^
|
||||
|
@ -3231,7 +3214,6 @@ fn override_dependency_from_workspace_invalid_syntax() -> Result<()> {
|
|||
werkzeug=2.3.0
|
||||
^^^^^^
|
||||
|
||||
Resolved 7 packages in [TIME]
|
||||
"###
|
||||
);
|
||||
|
||||
|
|
|
@ -143,12 +143,6 @@ fn invalid_pyproject_toml_syntax() -> Result<()> {
|
|||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
warning: Failed to parse `pyproject.toml`: TOML parse error at line 1, column 5
|
||||
|
|
||||
1 | 123 - 456
|
||||
| ^
|
||||
expected `.`, `=`
|
||||
|
||||
error: Failed to parse: `pyproject.toml`
|
||||
Caused by: TOML parse error at line 1, column 5
|
||||
|
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue