Issue 11632 - Add sources override

This commit is contained in:
Matt Wildoer 2025-11-25 15:03:09 -08:00
parent e799a088a5
commit 520f42ea31
6 changed files with 73 additions and 8 deletions

View File

@ -20,7 +20,7 @@ use uv_resolver::{
PrereleaseMode, ResolutionMode, PrereleaseMode, ResolutionMode,
}; };
use uv_torch::TorchMode; use uv_torch::TorchMode;
use uv_workspace::pyproject::ExtraBuildDependencies; use uv_workspace::pyproject::{ExtraBuildDependencies, ToolUvSources};
use uv_workspace::pyproject_mut::AddBoundsKind; use uv_workspace::pyproject_mut::AddBoundsKind;
use crate::{FilesystemOptions, Options, PipOptions}; use crate::{FilesystemOptions, Options, PipOptions};
@ -294,3 +294,23 @@ impl Combine for Option<ExtraBuildVariables> {
} }
} }
} }
impl Combine for ToolUvSources {
fn combine(self, other: Self) -> Self {
// Merge sources from other into self, with self taking precedence
let mut combined = self.into_inner();
for (package, sources) in other.into_inner() {
combined.entry(package).or_insert(sources);
}
combined.into_iter().collect()
}
}
impl Combine for Option<ToolUvSources> {
fn combine(self, other: Self) -> Self {
match (self, other) {
(Some(a), Some(b)) => Some(a.combine(b)),
(a, b) => a.or(b),
}
}
}

View File

@ -219,7 +219,7 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> {
required_environments, required_environments,
conflicts, conflicts,
workspace, workspace,
sources, sources: _,
dev_dependencies, dev_dependencies,
default_groups, default_groups,
dependency_groups, dependency_groups,
@ -236,9 +236,6 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> {
if workspace.is_some() { if workspace.is_some() {
return Err(Error::PyprojectOnlyField(path.to_path_buf(), "workspace")); return Err(Error::PyprojectOnlyField(path.to_path_buf(), "workspace"));
} }
if sources.is_some() {
return Err(Error::PyprojectOnlyField(path.to_path_buf(), "sources"));
}
if dev_dependencies.is_some() { if dev_dependencies.is_some() {
return Err(Error::PyprojectOnlyField( return Err(Error::PyprojectOnlyField(
path.to_path_buf(), path.to_path_buf(),

View File

@ -139,8 +139,11 @@ pub struct Options {
#[cfg_attr(feature = "schemars", schemars(skip))] #[cfg_attr(feature = "schemars", schemars(skip))]
pub workspace: Option<serde::de::IgnoredAny>, pub workspace: Option<serde::de::IgnoredAny>,
// NOTE: Unlike other fields above, `sources` is allowed in both `pyproject.toml`
// and `uv.toml` files to support local development overrides without modifying
// the committed configuration.
#[cfg_attr(feature = "schemars", schemars(skip))] #[cfg_attr(feature = "schemars", schemars(skip))]
pub sources: Option<serde::de::IgnoredAny>, pub sources: Option<uv_workspace::pyproject::ToolUvSources>,
#[cfg_attr(feature = "schemars", schemars(skip))] #[cfg_attr(feature = "schemars", schemars(skip))]
pub dev_dependencies: Option<serde::de::IgnoredAny>, pub dev_dependencies: Option<serde::de::IgnoredAny>,
@ -2117,12 +2120,15 @@ pub struct OptionsWire {
environments: Option<SupportedEnvironments>, environments: Option<SupportedEnvironments>,
required_environments: Option<SupportedEnvironments>, required_environments: Option<SupportedEnvironments>,
// NOTE(charlie): These fields should be kept in-sync with `ToolUv` in // NOTE: These fields should be kept in-sync with `ToolUv` in
// `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct. // `crates/uv-workspace/src/pyproject.rs`. The documentation lives on that struct.
// They're only respected in `pyproject.toml` files, and should be rejected in `uv.toml` files. // They're only respected in `pyproject.toml` files, and should be rejected in `uv.toml` files.
conflicts: Option<serde::de::IgnoredAny>, conflicts: Option<serde::de::IgnoredAny>,
workspace: Option<serde::de::IgnoredAny>, workspace: Option<serde::de::IgnoredAny>,
sources: Option<serde::de::IgnoredAny>, // NOTE: Unlike other fields above, `sources` is allowed in both `pyproject.toml`
// and `uv.toml` files to support local development overrides without modifying
// the committed configuration.
sources: Option<uv_workspace::pyproject::ToolUvSources>,
managed: Option<serde::de::IgnoredAny>, managed: Option<serde::de::IgnoredAny>,
r#package: Option<serde::de::IgnoredAny>, r#package: Option<serde::de::IgnoredAny>,
default_groups: Option<serde::de::IgnoredAny>, default_groups: Option<serde::de::IgnoredAny>,

View File

@ -731,6 +731,12 @@ impl<'de> serde::de::Deserialize<'de> for ToolUvSources {
} }
} }
impl FromIterator<(PackageName, Sources)> for ToolUvSources {
fn from_iter<T: IntoIterator<Item = (PackageName, Sources)>>(iter: T) -> Self {
Self(iter.into_iter().collect())
}
}
#[derive(Default, Debug, Clone, PartialEq, Eq)] #[derive(Default, Debug, Clone, PartialEq, Eq)]
#[cfg_attr(test, derive(Serialize))] #[cfg_attr(test, derive(Serialize))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]

View File

@ -62,6 +62,14 @@ configuration tables, the project-level value will be used, and the user-level v
ignored. If an array is present in both tables, the arrays will be concatenated, with the ignored. If an array is present in both tables, the arrays will be concatenated, with the
project-level settings appearing earlier in the merged array. project-level settings appearing earlier in the merged array.
!!! tip "Local development overrides"
The `[sources]` table can be defined in both `uv.toml` and `pyproject.toml`. When both are
present, sources from `uv.toml` override those from `pyproject.toml` on a per-package basis.
This enables local development workflows where different team members have dependencies at
different filesystem locations. See [Dependency sources](projects/dependencies.md#local-development-overrides-with-uvtoml)
for more details.
Settings provided via environment variables take precedence over persistent configuration, and Settings provided via environment variables take precedence over persistent configuration, and
settings provided via the command line take precedence over both. settings provided via the command line take precedence over both.

View File

@ -216,6 +216,34 @@ dependencies = ["foo"]
foo = { path = "./packages/foo" } foo = { path = "./packages/foo" }
``` ```
### Local development overrides with `uv.toml`
Sources can also be defined in a `uv.toml` file to enable local development overrides without
modifying the committed `pyproject.toml`. This is useful when:
- Different developers have dependencies at different local paths
- You want to temporarily override a dependency without committing the change
- You want to use version control ignore patterns (e.g., `.gitignore`) to exclude local configuration
```toml title="uv.toml"
[sources]
httpx = { path = "../httpx", editable = true }
my-package = { git = "https://github.com/me/my-package", branch = "dev" }
```
!!! note
When both `uv.toml` and `pyproject.toml` define sources for the same package, the `uv.toml`
definition takes precedence. This allows local overrides without modifying the project
configuration.
!!! tip
Add `uv.toml` to your `.gitignore` to maintain local-only development configurations without
affecting other team members.
### Supported source types
The following dependency sources are supported by uv: The following dependency sources are supported by uv:
- [Index](#index): A package resolved from a specific package index. - [Index](#index): A package resolved from a specific package index.