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,
};
use uv_torch::TorchMode;
use uv_workspace::pyproject::ExtraBuildDependencies;
use uv_workspace::pyproject::{ExtraBuildDependencies, ToolUvSources};
use uv_workspace::pyproject_mut::AddBoundsKind;
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,
conflicts,
workspace,
sources,
sources: _,
dev_dependencies,
default_groups,
dependency_groups,
@ -236,9 +236,6 @@ fn validate_uv_toml(path: &Path, options: &Options) -> Result<(), Error> {
if workspace.is_some() {
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() {
return Err(Error::PyprojectOnlyField(
path.to_path_buf(),

View File

@ -139,8 +139,11 @@ pub struct Options {
#[cfg_attr(feature = "schemars", schemars(skip))]
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))]
pub sources: Option<serde::de::IgnoredAny>,
pub sources: Option<uv_workspace::pyproject::ToolUvSources>,
#[cfg_attr(feature = "schemars", schemars(skip))]
pub dev_dependencies: Option<serde::de::IgnoredAny>,
@ -2117,12 +2120,15 @@ pub struct OptionsWire {
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.
// They're only respected in `pyproject.toml` files, and should be rejected in `uv.toml` files.
conflicts: 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>,
r#package: 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)]
#[cfg_attr(test, derive(Serialize))]
#[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
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 the command line take precedence over both.

View File

@ -216,6 +216,34 @@ dependencies = ["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:
- [Index](#index): A package resolved from a specific package index.