Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions crates/uv/src/commands/project/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use uv_python::{PythonDownloads, PythonPreference, PythonRequest};
use uv_settings::PythonInstallMirrors;
use uv_workspace::pyproject_mut::Error;
use uv_workspace::{
DiscoveryOptions, WorkspaceCache,
DiscoveryOptions, WorkspaceCache, WorkspaceError,
pyproject_mut::{DependencyTarget, PyProjectTomlMut},
};
use uv_workspace::{VirtualProject, Workspace};
Expand Down Expand Up @@ -59,6 +59,7 @@ pub(crate) async fn project_version(
output_format: VersionFormat,
project_dir: &Path,
package: Option<PackageName>,
explicit_project: bool,
dry_run: bool,
locked: bool,
frozen: bool,
Expand All @@ -78,7 +79,7 @@ pub(crate) async fn project_version(
preview: PreviewMode,
) -> Result<ExitStatus> {
// Read the metadata
let project = find_target(project_dir, package.as_ref()).await?;
let project = find_target(project_dir, package.as_ref(), explicit_project).await?;

let pyproject_path = project.root().join("pyproject.toml");
let Some(name) = project.project_name().cloned() else {
Expand Down Expand Up @@ -325,10 +326,30 @@ pub(crate) async fn project_version(
Ok(status)
}

/// Add hint to use `uv self version` when workspace discovery fails due to missing pyproject.toml
/// and --project was not explicitly passed
fn hint_uv_self_version(err: WorkspaceError, explicit_project: bool) -> anyhow::Error {
if matches!(err, WorkspaceError::MissingPyprojectToml) && !explicit_project {
anyhow!(
"{}\n\n{}{} If you meant to view uv's version, use `{}` instead",
err,
"hint".bold().cyan(),
":".bold(),
"uv self version".green()
)
} else {
err.into()
}
}

/// Find the pyproject.toml we're modifying
///
/// Note that `uv version` never needs to support PEP 723 scripts, as those are unversioned.
async fn find_target(project_dir: &Path, package: Option<&PackageName>) -> Result<VirtualProject> {
async fn find_target(
project_dir: &Path,
package: Option<&PackageName>,
explicit_project: bool,
) -> Result<VirtualProject> {
// Find the project in the workspace.
// No workspace caching since `uv version` changes the workspace definition.
let project = if let Some(package) = package {
Expand All @@ -338,7 +359,8 @@ async fn find_target(project_dir: &Path, package: Option<&PackageName>) -> Resul
&DiscoveryOptions::default(),
&WorkspaceCache::default(),
)
.await?
.await
.map_err(|err| hint_uv_self_version(err, explicit_project))?
.with_current_project(package.clone())
.with_context(|| format!("Package `{package}` not found in workspace"))?,
)
Expand All @@ -348,7 +370,8 @@ async fn find_target(project_dir: &Path, package: Option<&PackageName>) -> Resul
&DiscoveryOptions::default(),
&WorkspaceCache::default(),
)
.await?
.await
.map_err(|err| hint_uv_self_version(err, explicit_project))?
};
Ok(project)
}
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
script,
globals,
cli.top_level.no_config,
cli.top_level.global_args.project.is_some(),
filesystem,
cache,
printer,
Expand Down Expand Up @@ -1674,6 +1675,7 @@ async fn run_project(
globals: GlobalSettings,
// TODO(zanieb): Determine a better story for passing `no_config` in here
no_config: bool,
explicit_project: bool,
filesystem: Option<FilesystemOptions>,
cache: Cache,
printer: Printer,
Expand Down Expand Up @@ -2065,6 +2067,7 @@ async fn run_project(
args.output_format,
project_dir,
args.package,
explicit_project,
args.dry_run,
args.locked,
args.frozen,
Expand Down
10 changes: 5 additions & 5 deletions crates/uv/tests/it/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1687,20 +1687,20 @@ fn version_get_fallback_missing_strict() -> Result<()> {
Ok(())
}

// Should error if this pyproject.toml is missing
// and --preview was passed explicitly.
/// Should error with hint if pyproject.toml is missing in normal mode
#[test]
fn version_get_fallback_missing_strict_preview() -> Result<()> {
fn version_get_missing_with_hint() -> Result<()> {
let context = TestContext::new("3.12");

uv_snapshot!(context.filters(), context.version()
.arg("--preview"), @r"
uv_snapshot!(context.filters(), context.version(), @r"
success: false
exit_code: 2
----- stdout -----

----- stderr -----
error: No `pyproject.toml` found in current directory or any parent directory

hint: If you meant to view uv's version, use `uv self version` instead
");

Ok(())
Expand Down
Loading