diff --git a/crates/uv/src/commands/project/tree.rs b/crates/uv/src/commands/project/tree.rs index 1acbd7cf02c4a..178aa1828ec79 100644 --- a/crates/uv/src/commands/project/tree.rs +++ b/crates/uv/src/commands/project/tree.rs @@ -15,7 +15,7 @@ use uv_python::{PythonDownloads, PythonPreference, PythonRequest, PythonVersion} use uv_resolver::{PackageMap, TreeDisplay}; use uv_scripts::Pep723Script; use uv_settings::PythonInstallMirrors; -use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceCache}; +use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceCache}; use crate::commands::pip::latest::LatestClient; use crate::commands::pip::loggers::DefaultResolveLogger; @@ -65,18 +65,18 @@ pub(crate) async fn tree( ) -> Result { // Find the project requirements. let workspace_cache = WorkspaceCache::default(); - let workspace; + let virtual_project; let target = if let Some(script) = script.as_ref() { LockTarget::Script(script) } else { - workspace = Workspace::discover( + virtual_project = VirtualProject::discover( project_dir, &DiscoveryOptions::default(), cache, &workspace_cache, ) .await?; - LockTarget::Workspace(&workspace) + LockTarget::Workspace(virtual_project.workspace()) }; // Determine the groups to include. diff --git a/crates/uv/tests/project/tree.rs b/crates/uv/tests/project/tree.rs index cfd4fb3726570..a35c56069946f 100644 --- a/crates/uv/tests/project/tree.rs +++ b/crates/uv/tests/project/tree.rs @@ -2226,6 +2226,41 @@ fn non_project() -> Result<()> { Ok(()) } +/// A pyproject.toml with only `[dependency-groups]` (no `[project]`, no `[tool.uv.workspace]`) +/// is valid per PEP 735, and `uv tree` must handle it. +#[test] +fn dependency_groups_only() -> Result<()> { + let context = uv_test::test_context!("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [dependency-groups] + async = ["anyio"] + "#, + )?; + + uv_snapshot!(context.filters(), context.tree().arg("--universal").arg("--group").arg("async"), @" + success: true + exit_code: 0 + ----- stdout ----- + anyio v4.3.0 (group: async) + ├── idna v3.6 + └── sniffio v1.3.1 + + ----- stderr ----- + warning: No `requires-python` value found in the workspace. Defaulting to `>=3.12`. + Resolved 3 packages in [TIME] + " + ); + + // `uv tree` should update the lockfile + let lock = context.read("uv.lock"); + assert!(!lock.is_empty()); + + Ok(()) +} + #[test] fn non_project_group_selection_with_extras() -> Result<()> { let context = uv_test::test_context!("3.12");