Skip to content

Commit a019cd7

Browse files
committed
Support uv --override option (#668)
1 parent 5440506 commit a019cd7

File tree

5 files changed

+49
-10
lines changed

5 files changed

+49
-10
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ that were not yet released.
77

88
_Unreleased_
99

10+
- Support dependency overrides via `tool.rye.override-dependencies` when using uv. #668
11+
1012
<!-- released start -->
1113

1214
## 0.32.0

rye/src/cli/test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,8 @@ pub fn execute(cmd: Args) -> Result<(), Error> {
137137
fn has_pytest_dependency(projects: &[PyProject]) -> Result<bool, Error> {
138138
for project in projects {
139139
for dep in project
140-
.iter_dependencies(DependencyKind::Dev)
141-
.chain(project.iter_dependencies(DependencyKind::Normal))
140+
.iter_dependencies(&DependencyKind::Dev)
141+
.chain(project.iter_dependencies(&DependencyKind::Normal))
142142
{
143143
if let Ok(req) = dep.expand(|name| std::env::var(name).ok()) {
144144
if normalize_package_name(&req.name) == "pytest" {

rye/src/lock.rs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -178,12 +178,15 @@ pub fn update_workspace_lockfile(
178178

179179
req_file.flush()?;
180180

181-
let exclusions = find_exclusions(&projects)?;
181+
let exclusions = find_requirements(&projects, &DependencyKind::Excluded)?;
182+
let overrides = find_requirements(&projects, &DependencyKind::Override)?;
183+
let overrides_file = maybe_write_requirements_to_temp(&overrides)?;
182184
generate_lockfile(
183185
output,
184186
py_ver,
185187
&workspace.path(),
186188
req_file.path(),
189+
overrides_file.as_ref().map(|v| v.path()),
187190
lockfile,
188191
sources,
189192
&lock_options,
@@ -194,6 +197,20 @@ pub fn update_workspace_lockfile(
194197
Ok(())
195198
}
196199

200+
fn maybe_write_requirements_to_temp(requirements: &HashSet<Requirement>) -> Result<Option<NamedTempFile>, Error> {
201+
if requirements.is_empty() {
202+
Ok(None)
203+
}
204+
else {
205+
let mut nt_file = NamedTempFile::new()?;
206+
for dep in requirements {
207+
writeln!(&nt_file, "{}", dep)?;
208+
}
209+
nt_file.flush()?;
210+
Ok(Some(nt_file))
211+
}
212+
}
213+
197214
/// Tries to restore the lock options from the given lockfile.
198215
fn restore_lock_options<'o>(
199216
lockfile: &Path,
@@ -263,10 +280,10 @@ fn collect_workspace_features(
263280
Some(features_by_project)
264281
}
265282

266-
fn find_exclusions(projects: &[PyProject]) -> Result<HashSet<Requirement>, Error> {
283+
fn find_requirements(projects: &[PyProject], kind: &DependencyKind) -> Result<HashSet<Requirement>, Error> {
267284
let mut rv = HashSet::new();
268285
for project in projects {
269-
for dep in project.iter_dependencies(DependencyKind::Excluded) {
286+
for dep in project.iter_dependencies(&kind) {
270287
rv.insert(dep.expand(|name: &str| {
271288
if name == "PROJECT_ROOT" {
272289
Some(project.workspace_path().to_string_lossy().to_string())
@@ -285,7 +302,7 @@ fn dump_dependencies(
285302
out: &mut fs::File,
286303
dep_kind: DependencyKind,
287304
) -> Result<(), Error> {
288-
for dep in pyproject.iter_dependencies(dep_kind) {
305+
for dep in pyproject.iter_dependencies(&dep_kind) {
289306
if let Ok(expanded_dep) = dep.expand(|_| {
290307
// we actually do not care what it expands to much, for as long
291308
// as the end result parses
@@ -334,23 +351,26 @@ pub fn update_single_project_lockfile(
334351
)?;
335352
}
336353

337-
for dep in pyproject.iter_dependencies(DependencyKind::Normal) {
354+
for dep in pyproject.iter_dependencies(&DependencyKind::Normal) {
338355
writeln!(req_file, "{}", dep)?;
339356
}
340357
if lock_mode == LockMode::Dev {
341-
for dep in pyproject.iter_dependencies(DependencyKind::Dev) {
358+
for dep in pyproject.iter_dependencies(&DependencyKind::Dev) {
342359
writeln!(req_file, "{}", dep)?;
343360
}
344361
}
345362

346363
req_file.flush()?;
347364

348-
let exclusions = find_exclusions(std::slice::from_ref(pyproject))?;
365+
let exclusions = find_requirements(std::slice::from_ref(pyproject), &DependencyKind::Excluded)?;
366+
let overrides = find_requirements(std::slice::from_ref(pyproject), &DependencyKind::Override)?;
367+
let overrides_file = maybe_write_requirements_to_temp(&overrides)?;
349368
generate_lockfile(
350369
output,
351370
py_ver,
352371
&pyproject.workspace_path(),
353372
req_file.path(),
373+
overrides_file.as_ref().map(|v| v.path()),
354374
lockfile,
355375
sources,
356376
&lock_options,
@@ -367,6 +387,7 @@ fn generate_lockfile(
367387
py_ver: &PythonVersion,
368388
workspace_path: &Path,
369389
requirements_file_in: &Path,
390+
overrides_file_in: Option<&Path>,
370391
lockfile: &Path,
371392
sources: &ExpandedSources,
372393
lock_options: &LockOptions,
@@ -405,12 +426,16 @@ fn generate_lockfile(
405426
.lockfile(
406427
py_ver,
407428
requirements_file_in,
429+
overrides_file_in,
408430
&requirements_file,
409431
lock_options.pre,
410432
env::var("__RYE_UV_EXCLUDE_NEWER").ok(),
411433
upgrade,
412434
)?;
413435
} else {
436+
if overrides_file_in.is_some() {
437+
bail!("dependency overrides are only supported by uv");
438+
}
414439
let mut cmd = Command::new(get_pip_compile(py_ver, output)?);
415440
// legacy pip tools requires some extra parameters
416441
if get_pip_tools_version(py_ver) == PipToolsVersion::Legacy {

rye/src/pyproject.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub enum DependencyKind<'a> {
5555
Normal,
5656
Dev,
5757
Excluded,
58+
Override,
5859
Optional(Cow<'a, str>),
5960
}
6061

@@ -64,6 +65,7 @@ impl<'a> fmt::Display for DependencyKind<'a> {
6465
DependencyKind::Normal => f.write_str("regular"),
6566
DependencyKind::Dev => f.write_str("dev"),
6667
DependencyKind::Excluded => f.write_str("excluded"),
68+
DependencyKind::Override => f.write_str("overrides"),
6769
DependencyKind::Optional(ref sect) => write!(f, "optional ({})", sect),
6870
}
6971
}
@@ -903,6 +905,7 @@ impl PyProject {
903905
DependencyKind::Normal => &mut self.doc["project"]["dependencies"],
904906
DependencyKind::Dev => &mut self.doc["tool"]["rye"]["dev-dependencies"],
905907
DependencyKind::Excluded => &mut self.doc["tool"]["rye"]["excluded-dependencies"],
908+
DependencyKind::Override => &mut self.doc["tool"]["rye"]["override-dependencies"],
906909
DependencyKind::Optional(ref section) => {
907910
// add this as a proper non-inline table if it's missing
908911
let table = &mut self.doc["project"]["optional-dependencies"];
@@ -934,6 +937,7 @@ impl PyProject {
934937
DependencyKind::Normal => &mut self.doc["project"]["dependencies"],
935938
DependencyKind::Dev => &mut self.doc["tool"]["rye"]["dev-dependencies"],
936939
DependencyKind::Excluded => &mut self.doc["tool"]["rye"]["excluded-dependencies"],
940+
DependencyKind::Override => &mut self.doc["tool"]["rye"]["override-dependencies"],
937941
DependencyKind::Optional(ref section) => {
938942
&mut self.doc["project"]["optional-dependencies"][section as &str]
939943
}
@@ -953,7 +957,7 @@ impl PyProject {
953957
/// Iterates over all dependencies.
954958
pub fn iter_dependencies(
955959
&self,
956-
kind: DependencyKind,
960+
kind: &DependencyKind,
957961
) -> impl Iterator<Item = DependencyRef> + '_ {
958962
let sec = match kind {
959963
DependencyKind::Normal => self.doc.get("project").and_then(|x| x.get("dependencies")),
@@ -967,6 +971,11 @@ impl PyProject {
967971
.get("tool")
968972
.and_then(|x| x.get("rye"))
969973
.and_then(|x| x.get("excluded-dependencies")),
974+
DependencyKind::Override => self
975+
.doc
976+
.get("tool")
977+
.and_then(|x| x.get("rye"))
978+
.and_then(|x| x.get("override-dependencies")),
970979
DependencyKind::Optional(ref section) => self
971980
.doc
972981
.get("project")

rye/src/uv.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ impl Uv {
307307
&self,
308308
py_version: &PythonVersion,
309309
source: &Path,
310+
overrides: Option<&Path>,
310311
target: &Path,
311312
allow_prerelease: bool,
312313
exclude_newer: Option<String>,
@@ -333,6 +334,8 @@ impl Uv {
333334

334335
cmd.arg(source);
335336

337+
overrides.map(|ref value| cmd.arg("--override").arg(value));
338+
336339
let status = cmd.status().with_context(|| {
337340
format!(
338341
"Unable to run uv pip compile and generate {}",

0 commit comments

Comments
 (0)