Add hint to use uv self version when uv version cannot find a project (#14738)
When users run `uv version` in a directory without a `pyproject.toml` file, they often intend to check uv's own version rather than a project's version. This change adds a helpful hint to guide users to the correct command. **Before:** ``` ❯ uv version error: No `pyproject.toml` found in current directory or any parent directory ``` **After:** ``` ❯ uv version 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 ``` ## Changes - Modified `find_target()` function in `crates/uv/src/commands/project/version.rs` to catch `WorkspaceError::MissingPyprojectToml` specifically and enhance the error message with a helpful hint - Added import for `WorkspaceError` to access the specific error type - Updated existing tests to expect the new hint message in error output - Added new test case `version_get_missing_with_hint()` to verify behavior The hint appears consistently across all scenarios where `uv version` fails to find a project: - `uv version` (normal mode) - `uv version --project .` (explicit project mode) - `uv version --preview` (preview mode) The change maintains all existing functionality - when a `pyproject.toml` is found, `uv version` continues to work normally without showing the hint. Fixes #14730. <!-- START COPILOT CODING AGENT TIPS --> --- 💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more [Copilot coding agent tips](https://gh.io/copilot-coding-agent-tips) in the docs. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: zanieb <2586601+zanieb@users.noreply.github.com>
This commit is contained in:
@@ -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};
|
||||
@@ -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,
|
||||
@@ -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 {
|
||||
@@ -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 {
|
||||
@@ -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"))?,
|
||||
)
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -1058,6 +1058,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,
|
||||
@@ -1659,6 +1660,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,
|
||||
@@ -2050,6 +2052,7 @@ async fn run_project(
|
||||
args.output_format,
|
||||
project_dir,
|
||||
args.package,
|
||||
explicit_project,
|
||||
args.dry_run,
|
||||
args.locked,
|
||||
args.frozen,
|
||||
|
||||
@@ -1607,20 +1607,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(())
|
||||
|
||||
Reference in New Issue
Block a user