Skip to content

Commit c459b17

Browse files
authored
Rollup merge of #135950 - Kobzol:tidy-python-improvements, r=onur-ozkan
Tidy Python improvements Fixes display of Python formatting diffs in tidy, and refactors the code to make it simpler and more robust. Also documents Python formatting and linting in the Rustc dev guide. Fixes: #135942 r? `@onur-ozkan`
2 parents e96bb6a + 0f334ec commit c459b17

File tree

2 files changed

+110
-59
lines changed

2 files changed

+110
-59
lines changed

Diff for: src/doc/rustc-dev-guide/src/conventions.md

+25-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
This file offers some tips on the coding conventions for rustc. This
1+
This file offers some tips on the coding conventions for rustc. This
22
chapter covers [formatting](#formatting), [coding for correctness](#cc),
33
[using crates from crates.io](#cio), and some tips on
44
[structuring your PR for easy review](#er).
@@ -25,6 +25,7 @@ pass the <!-- date-check: nov 2022 --> `--edition=2021` argument yourself when c
2525
`rustfmt` directly.
2626

2727
[fmt]: https://github.com/rust-dev-tools/fmt-rfcs
28+
2829
[`rustfmt`]:https://github.com/rust-lang/rustfmt
2930

3031
## Formatting C++ code
@@ -40,6 +41,26 @@ When modifying that code, use this command to format it:
4041
This uses a pinned version of `clang-format`, to avoid relying on the local
4142
environment.
4243

44+
## Formatting and linting Python code
45+
46+
The Rust repository contains quite a lof of Python code. We try to keep
47+
it both linted and formatted by the [ruff][ruff] tool.
48+
49+
When modifying Python code, use this command to format it:
50+
```sh
51+
./x test tidy --extra-checks=py:fmt --bless
52+
```
53+
54+
and the following command to run lints:
55+
```sh
56+
./x test tidy --extra-checks=py:lint
57+
```
58+
59+
This uses a pinned version of `ruff`, to avoid relying on the local
60+
environment.
61+
62+
[ruff]: https://github.com/astral-sh/ruff
63+
4364
<a id="copyright"></a>
4465

4566
<!-- REUSE-IgnoreStart -->
@@ -84,7 +105,7 @@ Using `_` in a match is convenient, but it means that when new
84105
variants are added to the enum, they may not get handled correctly.
85106
Ask yourself: if a new variant were added to this enum, what's the
86107
chance that it would want to use the `_` code, versus having some
87-
other treatment? Unless the answer is "low", then prefer an
108+
other treatment? Unless the answer is "low", then prefer an
88109
exhaustive match. (The same advice applies to `if let` and `while
89110
let`, which are effectively tests for a single variant.)
90111

@@ -124,7 +145,7 @@ See the [crates.io dependencies][crates] section.
124145
# How to structure your PR
125146

126147
How you prepare the commits in your PR can make a big difference for the
127-
reviewer. Here are some tips.
148+
reviewer. Here are some tips.
128149

129150
**Isolate "pure refactorings" into their own commit.** For example, if
130151
you rename a method, then put that rename into its own commit, along
@@ -165,4 +186,5 @@ to the compiler.
165186
crate-related, often the spelling is changed to `krate`.
166187

167188
[tcx]: ./ty.md
189+
168190
[crates]: ./crates-io.md

Diff for: src/tools/tidy/src/ext_tool_checks.rs

+85-56
Original file line numberDiff line numberDiff line change
@@ -89,74 +89,45 @@ fn check_impl(
8989

9090
if python_lint {
9191
eprintln!("linting python files");
92-
let mut cfg_args_ruff = cfg_args.clone();
93-
let mut file_args_ruff = file_args.clone();
94-
95-
let mut cfg_path = root_path.to_owned();
96-
cfg_path.extend(RUFF_CONFIG_PATH);
97-
let mut cache_dir = outdir.to_owned();
98-
cache_dir.extend(RUFF_CACHE_PATH);
99-
100-
cfg_args_ruff.extend([
101-
"--config".as_ref(),
102-
cfg_path.as_os_str(),
103-
"--cache-dir".as_ref(),
104-
cache_dir.as_os_str(),
105-
]);
106-
107-
if file_args_ruff.is_empty() {
108-
file_args_ruff.push(root_path.as_os_str());
109-
}
110-
111-
let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
112-
args.insert(0, "check".as_ref());
113-
let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
92+
let py_path = py_path.as_ref().unwrap();
93+
let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &["check".as_ref()]);
11494

11595
if res.is_err() && show_diff {
11696
eprintln!("\npython linting failed! Printing diff suggestions:");
11797

118-
args.insert(1, "--diff".as_ref());
119-
let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
98+
let _ = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &[
99+
"check".as_ref(),
100+
"--diff".as_ref(),
101+
]);
120102
}
121103
// Rethrow error
122104
let _ = res?;
123105
}
124106

125107
if python_fmt {
126-
let mut cfg_args_ruff = cfg_args.clone();
127-
let mut file_args_ruff = file_args.clone();
128-
108+
let mut args: Vec<&OsStr> = vec!["format".as_ref()];
129109
if bless {
130110
eprintln!("formatting python files");
131111
} else {
132112
eprintln!("checking python file formatting");
133-
cfg_args_ruff.push("--check".as_ref());
134-
}
135-
136-
let mut cfg_path = root_path.to_owned();
137-
cfg_path.extend(RUFF_CONFIG_PATH);
138-
let mut cache_dir = outdir.to_owned();
139-
cache_dir.extend(RUFF_CACHE_PATH);
140-
141-
cfg_args_ruff.extend(["--config".as_ref(), cfg_path.as_os_str()]);
142-
143-
if file_args_ruff.is_empty() {
144-
file_args_ruff.push(root_path.as_os_str());
113+
args.push("--check".as_ref());
145114
}
146115

147-
let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
148-
args.insert(0, "format".as_ref());
149-
let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
116+
let py_path = py_path.as_ref().unwrap();
117+
let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &args);
150118

151-
if res.is_err() && show_diff {
152-
eprintln!("\npython formatting does not match! Printing diff:");
153-
154-
args.insert(0, "--diff".as_ref());
155-
let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
156-
}
157119
if res.is_err() && !bless {
120+
if show_diff {
121+
eprintln!("\npython formatting does not match! Printing diff:");
122+
123+
let _ = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &[
124+
"format".as_ref(),
125+
"--diff".as_ref(),
126+
]);
127+
}
158128
eprintln!("rerun tidy with `--extra-checks=py:fmt --bless` to reformat Python code");
159129
}
130+
160131
// Rethrow error
161132
let _ = res?;
162133
}
@@ -247,6 +218,38 @@ fn check_impl(
247218
Ok(())
248219
}
249220

221+
fn run_ruff(
222+
root_path: &Path,
223+
outdir: &Path,
224+
py_path: &Path,
225+
cfg_args: &[&OsStr],
226+
file_args: &[&OsStr],
227+
ruff_args: &[&OsStr],
228+
) -> Result<(), Error> {
229+
let mut cfg_args_ruff = cfg_args.into_iter().copied().collect::<Vec<_>>();
230+
let mut file_args_ruff = file_args.into_iter().copied().collect::<Vec<_>>();
231+
232+
let mut cfg_path = root_path.to_owned();
233+
cfg_path.extend(RUFF_CONFIG_PATH);
234+
let mut cache_dir = outdir.to_owned();
235+
cache_dir.extend(RUFF_CACHE_PATH);
236+
237+
cfg_args_ruff.extend([
238+
"--config".as_ref(),
239+
cfg_path.as_os_str(),
240+
"--cache-dir".as_ref(),
241+
cache_dir.as_os_str(),
242+
]);
243+
244+
if file_args_ruff.is_empty() {
245+
file_args_ruff.push(root_path.as_os_str());
246+
}
247+
248+
let mut args: Vec<&OsStr> = ruff_args.into_iter().copied().collect();
249+
args.extend(merge_args(&cfg_args_ruff, &file_args_ruff));
250+
py_runner(py_path, true, None, "ruff", &args)
251+
}
252+
250253
/// Helper to create `cfg1 cfg2 -- file1 file2` output
251254
fn merge_args<'a>(cfg_args: &[&'a OsStr], file_args: &[&'a OsStr]) -> Vec<&'a OsStr> {
252255
let mut args = cfg_args.to_owned();
@@ -321,8 +324,16 @@ fn get_or_create_venv(venv_path: &Path, src_reqs_path: &Path) -> Result<PathBuf,
321324
fn create_venv_at_path(path: &Path) -> Result<(), Error> {
322325
/// Preferred python versions in order. Newest to oldest then current
323326
/// development versions
324-
const TRY_PY: &[&str] =
325-
&["python3.11", "python3.10", "python3.9", "python3", "python", "python3.12", "python3.13"];
327+
const TRY_PY: &[&str] = &[
328+
"python3.13",
329+
"python3.12",
330+
"python3.11",
331+
"python3.10",
332+
"python3.9",
333+
"python3",
334+
"python",
335+
"python3.14",
336+
];
326337

327338
let mut sys_py = None;
328339
let mut found = Vec::new();
@@ -357,22 +368,40 @@ fn create_venv_at_path(path: &Path) -> Result<(), Error> {
357368
return Err(ret);
358369
};
359370

360-
eprintln!("creating virtual environment at '{}' using '{sys_py}'", path.display());
361-
let out = Command::new(sys_py).args(["-m", "virtualenv"]).arg(path).output().unwrap();
371+
// First try venv, which should be packaged in the Python3 standard library.
372+
// If it is not available, try to create the virtual environment using the
373+
// virtualenv package.
374+
if try_create_venv(sys_py, path, "venv").is_ok() {
375+
return Ok(());
376+
}
377+
try_create_venv(sys_py, path, "virtualenv")
378+
}
379+
380+
fn try_create_venv(python: &str, path: &Path, module: &str) -> Result<(), Error> {
381+
eprintln!(
382+
"creating virtual environment at '{}' using '{python}' and '{module}'",
383+
path.display()
384+
);
385+
let out = Command::new(python).args(["-m", module]).arg(path).output().unwrap();
362386

363387
if out.status.success() {
364388
return Ok(());
365389
}
366390

367391
let stderr = String::from_utf8_lossy(&out.stderr);
368-
let err = if stderr.contains("No module named virtualenv") {
392+
let err = if stderr.contains(&format!("No module named {module}")) {
369393
Error::Generic(format!(
370-
"virtualenv not found: you may need to install it \
371-
(`{sys_py} -m pip install virtualenv`)"
394+
r#"{module} not found: you may need to install it:
395+
`{python} -m pip install {module}`
396+
If you see an error about "externally managed environment" when running the above command,
397+
either install `{module}` using your system package manager
398+
(e.g. `sudo apt-get install {python}-{module}`) or create a virtual environment manually, install
399+
`{module}` in it and then activate it before running tidy.
400+
"#
372401
))
373402
} else {
374403
Error::Generic(format!(
375-
"failed to create venv at '{}' using {sys_py}: {stderr}",
404+
"failed to create venv at '{}' using {python} -m {module}: {stderr}",
376405
path.display()
377406
))
378407
};

0 commit comments

Comments
 (0)