diff --git a/src/doc/rustc-dev-guide/src/conventions.md b/src/doc/rustc-dev-guide/src/conventions.md
index 37af8121cd1dd..4010e90821f9b 100644
--- a/src/doc/rustc-dev-guide/src/conventions.md
+++ b/src/doc/rustc-dev-guide/src/conventions.md
@@ -1,4 +1,4 @@
-This file offers some tips on the coding conventions for rustc. This
+This file offers some tips on the coding conventions for rustc. This
chapter covers [formatting](#formatting), [coding for correctness](#cc),
[using crates from crates.io](#cio), and some tips on
[structuring your PR for easy review](#er).
@@ -25,6 +25,7 @@ pass the `--edition=2021` argument yourself when c
`rustfmt` directly.
[fmt]: https://github.com/rust-dev-tools/fmt-rfcs
+
[`rustfmt`]:https://github.com/rust-lang/rustfmt
## Formatting C++ code
@@ -40,6 +41,26 @@ When modifying that code, use this command to format it:
This uses a pinned version of `clang-format`, to avoid relying on the local
environment.
+## Formatting and linting Python code
+
+The Rust repository contains quite a lof of Python code. We try to keep
+it both linted and formatted by the [ruff][ruff] tool.
+
+When modifying Python code, use this command to format it:
+```sh
+./x test tidy --extra-checks=py:fmt --bless
+```
+
+and the following command to run lints:
+```sh
+./x test tidy --extra-checks=py:lint
+```
+
+This uses a pinned version of `ruff`, to avoid relying on the local
+environment.
+
+[ruff]: https://github.com/astral-sh/ruff
+
@@ -84,7 +105,7 @@ Using `_` in a match is convenient, but it means that when new
variants are added to the enum, they may not get handled correctly.
Ask yourself: if a new variant were added to this enum, what's the
chance that it would want to use the `_` code, versus having some
-other treatment? Unless the answer is "low", then prefer an
+other treatment? Unless the answer is "low", then prefer an
exhaustive match. (The same advice applies to `if let` and `while
let`, which are effectively tests for a single variant.)
@@ -124,7 +145,7 @@ See the [crates.io dependencies][crates] section.
# How to structure your PR
How you prepare the commits in your PR can make a big difference for the
-reviewer. Here are some tips.
+reviewer. Here are some tips.
**Isolate "pure refactorings" into their own commit.** For example, if
you rename a method, then put that rename into its own commit, along
@@ -165,4 +186,5 @@ to the compiler.
crate-related, often the spelling is changed to `krate`.
[tcx]: ./ty.md
+
[crates]: ./crates-io.md
diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs
index 6269d91c7ecc3..2a4cff1d12ed5 100644
--- a/src/tools/tidy/src/ext_tool_checks.rs
+++ b/src/tools/tidy/src/ext_tool_checks.rs
@@ -89,74 +89,45 @@ fn check_impl(
if python_lint {
eprintln!("linting python files");
- let mut cfg_args_ruff = cfg_args.clone();
- let mut file_args_ruff = file_args.clone();
-
- let mut cfg_path = root_path.to_owned();
- cfg_path.extend(RUFF_CONFIG_PATH);
- let mut cache_dir = outdir.to_owned();
- cache_dir.extend(RUFF_CACHE_PATH);
-
- cfg_args_ruff.extend([
- "--config".as_ref(),
- cfg_path.as_os_str(),
- "--cache-dir".as_ref(),
- cache_dir.as_os_str(),
- ]);
-
- if file_args_ruff.is_empty() {
- file_args_ruff.push(root_path.as_os_str());
- }
-
- let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
- args.insert(0, "check".as_ref());
- let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
+ let py_path = py_path.as_ref().unwrap();
+ let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &["check".as_ref()]);
if res.is_err() && show_diff {
eprintln!("\npython linting failed! Printing diff suggestions:");
- args.insert(1, "--diff".as_ref());
- let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
+ let _ = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &[
+ "check".as_ref(),
+ "--diff".as_ref(),
+ ]);
}
// Rethrow error
let _ = res?;
}
if python_fmt {
- let mut cfg_args_ruff = cfg_args.clone();
- let mut file_args_ruff = file_args.clone();
-
+ let mut args: Vec<&OsStr> = vec!["format".as_ref()];
if bless {
eprintln!("formatting python files");
} else {
eprintln!("checking python file formatting");
- cfg_args_ruff.push("--check".as_ref());
- }
-
- let mut cfg_path = root_path.to_owned();
- cfg_path.extend(RUFF_CONFIG_PATH);
- let mut cache_dir = outdir.to_owned();
- cache_dir.extend(RUFF_CACHE_PATH);
-
- cfg_args_ruff.extend(["--config".as_ref(), cfg_path.as_os_str()]);
-
- if file_args_ruff.is_empty() {
- file_args_ruff.push(root_path.as_os_str());
+ args.push("--check".as_ref());
}
- let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
- args.insert(0, "format".as_ref());
- let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
+ let py_path = py_path.as_ref().unwrap();
+ let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &args);
- if res.is_err() && show_diff {
- eprintln!("\npython formatting does not match! Printing diff:");
-
- args.insert(0, "--diff".as_ref());
- let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
- }
if res.is_err() && !bless {
+ if show_diff {
+ eprintln!("\npython formatting does not match! Printing diff:");
+
+ let _ = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &[
+ "format".as_ref(),
+ "--diff".as_ref(),
+ ]);
+ }
eprintln!("rerun tidy with `--extra-checks=py:fmt --bless` to reformat Python code");
}
+
// Rethrow error
let _ = res?;
}
@@ -247,6 +218,38 @@ fn check_impl(
Ok(())
}
+fn run_ruff(
+ root_path: &Path,
+ outdir: &Path,
+ py_path: &Path,
+ cfg_args: &[&OsStr],
+ file_args: &[&OsStr],
+ ruff_args: &[&OsStr],
+) -> Result<(), Error> {
+ let mut cfg_args_ruff = cfg_args.into_iter().copied().collect::>();
+ let mut file_args_ruff = file_args.into_iter().copied().collect::>();
+
+ let mut cfg_path = root_path.to_owned();
+ cfg_path.extend(RUFF_CONFIG_PATH);
+ let mut cache_dir = outdir.to_owned();
+ cache_dir.extend(RUFF_CACHE_PATH);
+
+ cfg_args_ruff.extend([
+ "--config".as_ref(),
+ cfg_path.as_os_str(),
+ "--cache-dir".as_ref(),
+ cache_dir.as_os_str(),
+ ]);
+
+ if file_args_ruff.is_empty() {
+ file_args_ruff.push(root_path.as_os_str());
+ }
+
+ let mut args: Vec<&OsStr> = ruff_args.into_iter().copied().collect();
+ args.extend(merge_args(&cfg_args_ruff, &file_args_ruff));
+ py_runner(py_path, true, None, "ruff", &args)
+}
+
/// Helper to create `cfg1 cfg2 -- file1 file2` output
fn merge_args<'a>(cfg_args: &[&'a OsStr], file_args: &[&'a OsStr]) -> Vec<&'a OsStr> {
let mut args = cfg_args.to_owned();
@@ -321,8 +324,16 @@ fn get_or_create_venv(venv_path: &Path, src_reqs_path: &Path) -> Result Result<(), Error> {
/// Preferred python versions in order. Newest to oldest then current
/// development versions
- const TRY_PY: &[&str] =
- &["python3.11", "python3.10", "python3.9", "python3", "python", "python3.12", "python3.13"];
+ const TRY_PY: &[&str] = &[
+ "python3.13",
+ "python3.12",
+ "python3.11",
+ "python3.10",
+ "python3.9",
+ "python3",
+ "python",
+ "python3.14",
+ ];
let mut sys_py = None;
let mut found = Vec::new();
@@ -357,22 +368,40 @@ fn create_venv_at_path(path: &Path) -> Result<(), Error> {
return Err(ret);
};
- eprintln!("creating virtual environment at '{}' using '{sys_py}'", path.display());
- let out = Command::new(sys_py).args(["-m", "virtualenv"]).arg(path).output().unwrap();
+ // First try venv, which should be packaged in the Python3 standard library.
+ // If it is not available, try to create the virtual environment using the
+ // virtualenv package.
+ if try_create_venv(sys_py, path, "venv").is_ok() {
+ return Ok(());
+ }
+ try_create_venv(sys_py, path, "virtualenv")
+}
+
+fn try_create_venv(python: &str, path: &Path, module: &str) -> Result<(), Error> {
+ eprintln!(
+ "creating virtual environment at '{}' using '{python}' and '{module}'",
+ path.display()
+ );
+ let out = Command::new(python).args(["-m", module]).arg(path).output().unwrap();
if out.status.success() {
return Ok(());
}
let stderr = String::from_utf8_lossy(&out.stderr);
- let err = if stderr.contains("No module named virtualenv") {
+ let err = if stderr.contains(&format!("No module named {module}")) {
Error::Generic(format!(
- "virtualenv not found: you may need to install it \
- (`{sys_py} -m pip install virtualenv`)"
+ r#"{module} not found: you may need to install it:
+`{python} -m pip install {module}`
+If you see an error about "externally managed environment" when running the above command,
+either install `{module}` using your system package manager
+(e.g. `sudo apt-get install {python}-{module}`) or create a virtual environment manually, install
+`{module}` in it and then activate it before running tidy.
+"#
))
} else {
Error::Generic(format!(
- "failed to create venv at '{}' using {sys_py}: {stderr}",
+ "failed to create venv at '{}' using {python} -m {module}: {stderr}",
path.display()
))
};