Skip to content

Commit c2ad31a

Browse files
Respect pyproject.toml credentials from user-provided requirements (#7474)
## Summary When syncing a lockfile, we need to respect credentials defined in the `pyproject.toml`, even if they won't be used for resolution. Unfortunately, this includes credentials in `tool.uv.sources`, `tool.uv.dev-dependencies`, `project.dependencies`, and `project.optional-dependencies`. Closes #7453.
1 parent 08a7c70 commit c2ad31a

File tree

8 files changed

+317
-29
lines changed

8 files changed

+317
-29
lines changed

crates/uv-git/src/credentials.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
1-
use std::collections::HashMap;
2-
use std::sync::{Arc, RwLock};
3-
41
use cache_key::RepositoryUrl;
2+
use std::collections::HashMap;
3+
use std::sync::{Arc, LazyLock, RwLock};
4+
use tracing::trace;
5+
use url::Url;
56
use uv_auth::Credentials;
67

8+
/// Global authentication cache for a uv invocation.
9+
///
10+
/// This is used to share Git credentials within a single process.
11+
pub static GIT_STORE: LazyLock<GitStore> = LazyLock::new(GitStore::default);
12+
713
/// A store for Git credentials.
814
#[derive(Debug, Default)]
915
pub struct GitStore(RwLock<HashMap<RepositoryUrl, Arc<Credentials>>>);
@@ -19,3 +25,16 @@ impl GitStore {
1925
self.0.read().unwrap().get(url).cloned()
2026
}
2127
}
28+
29+
/// Populate the global authentication store with credentials on a Git URL, if there are any.
30+
///
31+
/// Returns `true` if the store was updated.
32+
pub fn store_credentials_from_url(url: &Url) -> bool {
33+
if let Some(credentials) = Credentials::from_url(url) {
34+
trace!("Caching credentials for {url}");
35+
GIT_STORE.insert(RepositoryUrl::new(url), credentials);
36+
true
37+
} else {
38+
false
39+
}
40+
}

crates/uv-git/src/lib.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
use std::sync::LazyLock;
2-
31
use url::Url;
42

5-
use crate::credentials::GitStore;
3+
pub use crate::credentials::{store_credentials_from_url, GIT_STORE};
64
pub use crate::git::GitReference;
75
pub use crate::resolver::{
86
GitResolver, GitResolverError, RepositoryReference, ResolvedRepositoryReference,
@@ -16,11 +14,6 @@ mod resolver;
1614
mod sha;
1715
mod source;
1816

19-
/// Global authentication cache for a uv invocation.
20-
///
21-
/// This is used to share Git credentials within a single process.
22-
pub static GIT_STORE: LazyLock<GitStore> = LazyLock::new(GitStore::default);
23-
2417
/// A URL reference to a Git repository.
2518
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash, Ord)]
2619
pub struct GitUrl {

crates/uv-workspace/src/pyproject.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,8 @@ pub struct Project {
120120
pub version: Option<Version>,
121121
/// The Python versions this project is compatible with.
122122
pub requires_python: Option<VersionSpecifiers>,
123+
/// The dependencies of the project.
124+
pub dependencies: Option<Vec<String>>,
123125
/// The optional dependencies of the project.
124126
pub optional_dependencies: Option<BTreeMap<ExtraName, Vec<String>>>,
125127

crates/uv-workspace/src/workspace.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,22 @@ impl Workspace {
531531
&self.sources
532532
}
533533

534+
/// Returns an iterator over all sources in the workspace.
535+
pub fn iter_sources(&self) -> impl Iterator<Item = &Source> {
536+
self.packages
537+
.values()
538+
.filter_map(|member| {
539+
member.pyproject_toml().tool.as_ref().and_then(|tool| {
540+
tool.uv
541+
.as_ref()
542+
.and_then(|uv| uv.sources.as_ref())
543+
.map(ToolUvSources::inner)
544+
.map(|sources| sources.values())
545+
})
546+
})
547+
.flatten()
548+
}
549+
534550
/// The `pyproject.toml` of the workspace.
535551
pub fn pyproject_toml(&self) -> &PyProjectToml {
536552
&self.pyproject_toml
@@ -1608,6 +1624,9 @@ mod tests {
16081624
"name": "bird-feeder",
16091625
"version": "1.0.0",
16101626
"requires-python": ">=3.12",
1627+
"dependencies": [
1628+
"anyio>=4.3.0,<5"
1629+
],
16111630
"optional-dependencies": null
16121631
},
16131632
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1619,6 +1638,9 @@ mod tests {
16191638
"name": "bird-feeder",
16201639
"version": "1.0.0",
16211640
"requires-python": ">=3.12",
1641+
"dependencies": [
1642+
"anyio>=4.3.0,<5"
1643+
],
16221644
"optional-dependencies": null
16231645
},
16241646
"tool": null
@@ -1653,6 +1675,9 @@ mod tests {
16531675
"name": "bird-feeder",
16541676
"version": "1.0.0",
16551677
"requires-python": ">=3.12",
1678+
"dependencies": [
1679+
"anyio>=4.3.0,<5"
1680+
],
16561681
"optional-dependencies": null
16571682
},
16581683
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1664,6 +1689,9 @@ mod tests {
16641689
"name": "bird-feeder",
16651690
"version": "1.0.0",
16661691
"requires-python": ">=3.12",
1692+
"dependencies": [
1693+
"anyio>=4.3.0,<5"
1694+
],
16671695
"optional-dependencies": null
16681696
},
16691697
"tool": null
@@ -1697,6 +1725,10 @@ mod tests {
16971725
"name": "albatross",
16981726
"version": "0.1.0",
16991727
"requires-python": ">=3.12",
1728+
"dependencies": [
1729+
"bird-feeder",
1730+
"tqdm>=4,<5"
1731+
],
17001732
"optional-dependencies": null
17011733
},
17021734
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1707,6 +1739,10 @@ mod tests {
17071739
"name": "bird-feeder",
17081740
"version": "1.0.0",
17091741
"requires-python": ">=3.8",
1742+
"dependencies": [
1743+
"anyio>=4.3.0,<5",
1744+
"seeds"
1745+
],
17101746
"optional-dependencies": null
17111747
},
17121748
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1717,6 +1753,9 @@ mod tests {
17171753
"name": "seeds",
17181754
"version": "1.0.0",
17191755
"requires-python": ">=3.12",
1756+
"dependencies": [
1757+
"idna==3.6"
1758+
],
17201759
"optional-dependencies": null
17211760
},
17221761
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1732,6 +1771,10 @@ mod tests {
17321771
"name": "albatross",
17331772
"version": "0.1.0",
17341773
"requires-python": ">=3.12",
1774+
"dependencies": [
1775+
"bird-feeder",
1776+
"tqdm>=4,<5"
1777+
],
17351778
"optional-dependencies": null
17361779
},
17371780
"tool": {
@@ -1786,6 +1829,10 @@ mod tests {
17861829
"name": "albatross",
17871830
"version": "0.1.0",
17881831
"requires-python": ">=3.12",
1832+
"dependencies": [
1833+
"bird-feeder",
1834+
"tqdm>=4,<5"
1835+
],
17891836
"optional-dependencies": null
17901837
},
17911838
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1796,6 +1843,10 @@ mod tests {
17961843
"name": "bird-feeder",
17971844
"version": "1.0.0",
17981845
"requires-python": ">=3.12",
1846+
"dependencies": [
1847+
"anyio>=4.3.0,<5",
1848+
"seeds"
1849+
],
17991850
"optional-dependencies": null
18001851
},
18011852
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1806,6 +1857,9 @@ mod tests {
18061857
"name": "seeds",
18071858
"version": "1.0.0",
18081859
"requires-python": ">=3.12",
1860+
"dependencies": [
1861+
"idna==3.6"
1862+
],
18091863
"optional-dependencies": null
18101864
},
18111865
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1861,6 +1915,9 @@ mod tests {
18611915
"name": "albatross",
18621916
"version": "0.1.0",
18631917
"requires-python": ">=3.12",
1918+
"dependencies": [
1919+
"tqdm>=4,<5"
1920+
],
18641921
"optional-dependencies": null
18651922
},
18661923
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1872,6 +1929,9 @@ mod tests {
18721929
"name": "albatross",
18731930
"version": "0.1.0",
18741931
"requires-python": ">=3.12",
1932+
"dependencies": [
1933+
"tqdm>=4,<5"
1934+
],
18751935
"optional-dependencies": null
18761936
},
18771937
"tool": null
@@ -1973,6 +2033,9 @@ mod tests {
19732033
"name": "albatross",
19742034
"version": "0.1.0",
19752035
"requires-python": ">=3.12",
2036+
"dependencies": [
2037+
"tqdm>=4,<5"
2038+
],
19762039
"optional-dependencies": null
19772040
},
19782041
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1983,6 +2046,9 @@ mod tests {
19832046
"name": "seeds",
19842047
"version": "1.0.0",
19852048
"requires-python": ">=3.12",
2049+
"dependencies": [
2050+
"idna==3.6"
2051+
],
19862052
"optional-dependencies": null
19872053
},
19882054
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -1994,6 +2060,9 @@ mod tests {
19942060
"name": "albatross",
19952061
"version": "0.1.0",
19962062
"requires-python": ">=3.12",
2063+
"dependencies": [
2064+
"tqdm>=4,<5"
2065+
],
19972066
"optional-dependencies": null
19982067
},
19992068
"tool": {
@@ -2062,6 +2131,9 @@ mod tests {
20622131
"name": "albatross",
20632132
"version": "0.1.0",
20642133
"requires-python": ">=3.12",
2134+
"dependencies": [
2135+
"tqdm>=4,<5"
2136+
],
20652137
"optional-dependencies": null
20662138
},
20672139
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -2072,6 +2144,9 @@ mod tests {
20722144
"name": "seeds",
20732145
"version": "1.0.0",
20742146
"requires-python": ">=3.12",
2147+
"dependencies": [
2148+
"idna==3.6"
2149+
],
20752150
"optional-dependencies": null
20762151
},
20772152
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -2083,6 +2158,9 @@ mod tests {
20832158
"name": "albatross",
20842159
"version": "0.1.0",
20852160
"requires-python": ">=3.12",
2161+
"dependencies": [
2162+
"tqdm>=4,<5"
2163+
],
20862164
"optional-dependencies": null
20872165
},
20882166
"tool": {
@@ -2152,6 +2230,9 @@ mod tests {
21522230
"name": "albatross",
21532231
"version": "0.1.0",
21542232
"requires-python": ">=3.12",
2233+
"dependencies": [
2234+
"tqdm>=4,<5"
2235+
],
21552236
"optional-dependencies": null
21562237
},
21572238
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -2162,6 +2243,9 @@ mod tests {
21622243
"name": "bird-feeder",
21632244
"version": "1.0.0",
21642245
"requires-python": ">=3.12",
2246+
"dependencies": [
2247+
"anyio>=4.3.0,<5"
2248+
],
21652249
"optional-dependencies": null
21662250
},
21672251
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -2172,6 +2256,9 @@ mod tests {
21722256
"name": "seeds",
21732257
"version": "1.0.0",
21742258
"requires-python": ">=3.12",
2259+
"dependencies": [
2260+
"idna==3.6"
2261+
],
21752262
"optional-dependencies": null
21762263
},
21772264
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -2183,6 +2270,9 @@ mod tests {
21832270
"name": "albatross",
21842271
"version": "0.1.0",
21852272
"requires-python": ">=3.12",
2273+
"dependencies": [
2274+
"tqdm>=4,<5"
2275+
],
21862276
"optional-dependencies": null
21872277
},
21882278
"tool": {
@@ -2252,6 +2342,9 @@ mod tests {
22522342
"name": "albatross",
22532343
"version": "0.1.0",
22542344
"requires-python": ">=3.12",
2345+
"dependencies": [
2346+
"tqdm>=4,<5"
2347+
],
22552348
"optional-dependencies": null
22562349
},
22572350
"pyproject_toml": "[PYPROJECT_TOML]"
@@ -2263,6 +2356,9 @@ mod tests {
22632356
"name": "albatross",
22642357
"version": "0.1.0",
22652358
"requires-python": ">=3.12",
2359+
"dependencies": [
2360+
"tqdm>=4,<5"
2361+
],
22662362
"optional-dependencies": null
22672363
},
22682364
"tool": {

0 commit comments

Comments
 (0)