diff --git a/crates/puffin-cli/src/commands/pip_compile.rs b/crates/puffin-cli/src/commands/pip_compile.rs index 430fb2de4..f4a961b77 100644 --- a/crates/puffin-cli/src/commands/pip_compile.rs +++ b/crates/puffin-cli/src/commands/pip_compile.rs @@ -42,6 +42,17 @@ pub(crate) async fn pip_compile( ) -> Result { let start = std::time::Instant::now(); + // If the user requests `extras` but does not provide a pyproject toml source + if !matches!(extras, ExtrasSpecification::None) + && !requirements + .iter() + .any(|source| matches!(source, RequirementsSource::PyprojectToml(_))) + { + return Err(anyhow!( + "Requesting extras requires a pyproject.toml input file." + )); + } + // Read all requirements from the provided sources. let RequirementsSpecification { project, diff --git a/crates/puffin-cli/tests/pip_compile.rs b/crates/puffin-cli/tests/pip_compile.rs index 102ed5d26..c54e773c5 100644 --- a/crates/puffin-cli/tests/pip_compile.rs +++ b/crates/puffin-cli/tests/pip_compile.rs @@ -460,6 +460,51 @@ optional-dependencies.foo = [ Ok(()) } +/// Request extras when using a `requirements.in` file which does not support extras. +#[test] +fn compile_requirements_file_extra() -> Result<()> { + let temp_dir = assert_fs::TempDir::new()?; + let cache_dir = assert_fs::TempDir::new()?; + let venv = temp_dir.child(".venv"); + + Command::new(get_cargo_bin(BIN_NAME)) + .arg("venv") + .arg(venv.as_os_str()) + .arg("--cache-dir") + .arg(cache_dir.path()) + .current_dir(&temp_dir) + .assert() + .success(); + venv.assert(predicates::path::is_dir()); + + let requirements_in = temp_dir.child("requirements.in"); + requirements_in.touch()?; + requirements_in.write_str("django==5.0b1")?; + + insta::with_settings!({ + filters => INSTA_FILTERS.to_vec() + }, { + assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)) + .arg("pip-compile") + .arg("requirements.in") + .arg("--cache-dir") + .arg(cache_dir.path()) + .arg("--all-extras") + .env("VIRTUAL_ENV", venv.as_os_str()) + .current_dir(&temp_dir), + @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: Requesting extras requires a pyproject.toml input file. + "###); + }); + + Ok(()) +} + /// Request an extra with a name that does not conform to the specification. #[test] fn invalid_extra_name() -> Result<()> {