Run the formatter in the project root instead of the working directory (#15440)

Closes https://github.com/astral-sh/uv/issues/15430

This PR is a branch from https://github.com/astral-sh/uv/pull/15438,
opening it as a draft until that is merged to main and I'll merge main
with this branch to have a cleaner diff

---------

Co-authored-by: Zanie Blue <contact@zanie.dev>
This commit is contained in:
Jorge Hermo 2025-08-22 22:09:42 +02:00 committed by GitHub
parent 6e2fbbc30f
commit e6def43110
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 63 additions and 1 deletions

View File

@ -10,6 +10,7 @@ use uv_client::BaseClientBuilder;
use uv_configuration::{Preview, PreviewFeatures};
use uv_pep440::Version;
use uv_warnings::warn_user;
use uv_workspace::{DiscoveryOptions, VirtualProject, WorkspaceCache};
use crate::child::run_to_completion;
use crate::commands::ExitStatus;
@ -36,6 +37,12 @@ pub(crate) async fn format(
PreviewFeatures::FORMAT
);
}
let workspace_cache = WorkspaceCache::default();
let project =
VirtualProject::discover(project_dir, &DiscoveryOptions::default(), &workspace_cache)
.await?;
// Parse version if provided
let version = version.as_deref().map(Version::from_str).transpose()?;
@ -55,7 +62,8 @@ pub(crate) async fn format(
.context("Failed to install ruff {version}")?;
let mut command = Command::new(&ruff_path);
command.current_dir(project_dir);
// Run ruff in the project root
command.current_dir(project.root());
command.arg("format");
if check {

View File

@ -55,6 +55,60 @@ fn format_project() -> Result<()> {
Ok(())
}
#[test]
fn format_from_project_root() -> Result<()> {
let context = TestContext::new_with_versions(&[]);
let pyproject_toml = context.temp_dir.child("pyproject.toml");
pyproject_toml.write_str(indoc! {r#"
[project]
name = "project"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []
"#})?;
// Create an unformatted Python file
let main_py = context.temp_dir.child("main.py");
main_py.write_str(indoc! {r#"
import sys
def hello():
print( "Hello, World!" )
if __name__=="__main__":
hello( )
"#})?;
let subdir = context.temp_dir.child("subdir");
fs_err::create_dir_all(&subdir)?;
// Using format from a subdirectory should still run in the project root
uv_snapshot!(context.filters(), context.format().current_dir(&subdir), @r"
success: true
exit_code: 0
----- stdout -----
1 file reformatted
----- stderr -----
warning: `uv format` is experimental and may change without warning. Pass `--preview-features format` to disable this warning.
");
// Check that the file was formatted
let formatted_content = fs_err::read_to_string(&main_py)?;
assert_snapshot!(formatted_content, @r#"
import sys
def hello():
print("Hello, World!")
if __name__ == "__main__":
hello()
"#);
Ok(())
}
#[test]
fn format_relative_project() -> Result<()> {
let context = TestContext::new_with_versions(&[]);