Skip to content

Commit 7921667

Browse files
authored
Emit dedicated error message for Conda environment.yml files (#12669)
<!-- Thank you for contributing to uv! To help us out with reviewing, please consider the following: - Does this pull request include a summary of the change? (See below.) - Does this pull request include a descriptive title? - Does this pull request include references to any relevant issues? --> ## Summary Fixes #12606. Two options considered, thanks to @zanieb's guidance are: 1. Special-casing on parse error and encountering the `environment.yml` filename, possibly at `RequirementsTxt::parse` 2. Adding a new `RequirementsSource::EnvironmentYml` variant and erroring on `RequirementSpecification::from_source` I went with the latter for the following reasons: - This edge case is explicitly modelled within the type system. However, it changes the semantics of `RequirementsSource` to also model _unsupported_ sources. - (**Separation of concerns**) The special-casing would occur in the `uv-requirements-txt` crate, which seems to be relatively deep in the guts of the codebase. In my opinion, maintainers working in `uv-requirements-txt` would reasonably assume the input file to be a `requirements.txt` file, instead of having to be concerned with it being another file format (`environment.yml`, `pyproject.toml`, etc.) <!-- What's the purpose of the change? What does it do, and why? --> ## Test Plan Manually tested as follows: ```sh >>> cargo run -- pip install -r environment.yml error: Conda environment file `environment.yml` is not supported >>> cargo run -- add -r environment.yml error: Conda environment file `environment.yml` is not supported ``` If you can point me to the appropriate test module, I can write up tests for these to use `insta`. <!-- How was it tested? -->
1 parent 335af98 commit 7921667

File tree

4 files changed

+72
-0
lines changed

4 files changed

+72
-0
lines changed

crates/uv-requirements/src/sources.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ pub enum RequirementsSource {
2323
SetupCfg(PathBuf),
2424
/// Dependencies were provided via a path to a source tree (e.g., `pip install .`).
2525
SourceTree(PathBuf),
26+
/// Dependencies were provided via an unsupported Conda `environment.yml` file (e.g., `pip install -r environment.yml`).
27+
EnvironmentYml(PathBuf),
2628
}
2729

2830
impl RequirementsSource {
@@ -35,6 +37,8 @@ impl RequirementsSource {
3537
Self::SetupPy(path)
3638
} else if path.ends_with("setup.cfg") {
3739
Self::SetupCfg(path)
40+
} else if path.ends_with("environment.yml") {
41+
Self::EnvironmentYml(path)
3842
} else {
3943
Self::RequirementsTxt(path)
4044
}
@@ -217,6 +221,7 @@ impl std::fmt::Display for RequirementsSource {
217221
| Self::PyprojectToml(path)
218222
| Self::SetupPy(path)
219223
| Self::SetupCfg(path)
224+
| Self::EnvironmentYml(path)
220225
| Self::SourceTree(path) => {
221226
write!(f, "{}", path.simplified_display())
222227
}

crates/uv-requirements/src/specification.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,12 @@ impl RequirementsSpecification {
212212
..Self::default()
213213
}
214214
}
215+
RequirementsSource::EnvironmentYml(path) => {
216+
return Err(anyhow::anyhow!(
217+
"Conda environment files (i.e. `{}`) are not supported",
218+
path.user_display()
219+
))
220+
}
215221
})
216222
}
217223

crates/uv/tests/it/edit.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3662,6 +3662,41 @@ fn add_error() -> Result<()> {
36623662
Ok(())
36633663
}
36643664

3665+
/// Emit dedicated error message when adding Conda `environment.yml`
3666+
#[test]
3667+
fn add_environment_yml_error() -> Result<()> {
3668+
let context = TestContext::new("3.12");
3669+
3670+
let pyproject_toml = context.temp_dir.child("pyproject.toml");
3671+
pyproject_toml.write_str(indoc! {r#"
3672+
[project]
3673+
name = "project"
3674+
version = "0.1.0"
3675+
requires-python = ">=3.12"
3676+
dependencies = []
3677+
"#})?;
3678+
3679+
let environment_yml = context.temp_dir.child("environment.yml");
3680+
environment_yml.write_str(indoc! {r"
3681+
name: test-env
3682+
channels:
3683+
- conda-forge
3684+
dependencies:
3685+
- python>=3.12
3686+
"})?;
3687+
3688+
uv_snapshot!(context.filters(), context.add().arg("-r").arg("environment.yml"), @r"
3689+
success: false
3690+
exit_code: 2
3691+
----- stdout -----
3692+
3693+
----- stderr -----
3694+
error: Conda environment files (i.e. `environment.yml`) are not supported
3695+
");
3696+
3697+
Ok(())
3698+
}
3699+
36653700
/// Set a lower bound when adding unconstrained dependencies.
36663701
#[test]
36673702
fn add_lower_bound() -> Result<()> {

crates/uv/tests/it/pip_install.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8049,6 +8049,32 @@ fn install_incompatible_python_version_interpreter_broken_in_path() -> Result<()
80498049
Ok(())
80508050
}
80518051

8052+
/// Emit dedicated error message when installing Conda `environment.yml`
8053+
#[test]
8054+
fn install_unsupported_environment_yml() -> Result<()> {
8055+
let context = TestContext::new("3.12");
8056+
8057+
let environment_yml = context.temp_dir.child("environment.yml");
8058+
environment_yml.write_str(indoc! {r"
8059+
name: test-env
8060+
channels:
8061+
- conda-forge
8062+
dependencies:
8063+
- python>=3.12
8064+
"})?;
8065+
8066+
uv_snapshot!(context.filters(), context.pip_install().arg("-r").arg("environment.yml"), @r"
8067+
success: false
8068+
exit_code: 2
8069+
----- stdout -----
8070+
8071+
----- stderr -----
8072+
error: Conda environment files (i.e. `environment.yml`) are not supported
8073+
");
8074+
8075+
Ok(())
8076+
}
8077+
80528078
/// Include a `build_constraints.txt` file with an incompatible constraint.
80538079
#[test]
80548080
fn incompatible_build_constraint() -> Result<()> {

0 commit comments

Comments
 (0)