use std::fs;
use std::process::Command;
use std::str;
use anyhow::Result;
use insta_cmd::{assert_cmd_snapshot, get_cargo_bin};
use crate::CliTest;
const BIN_NAME: &str = "ruff";
const STDIN_BASE_OPTIONS: &[&str] = &["check", "--no-cache", "--output-format", "concise"];
impl CliTest {
fn check_command(&self) -> Command {
let mut command = self.command();
command.args(STDIN_BASE_OPTIONS);
command
}
}
#[test]
fn top_level_options() -> Result<()> {
let test = CliTest::new()?;
test.write_file(
"ruff.toml",
r#"
extend-select = ["B", "Q"]
[flake8-quotes]
inline-quotes = "single"
"#,
)?;
assert_cmd_snapshot!(test.check_command()
.arg("--config")
.arg("ruff.toml")
.args(["--stdin-filename", "test.py"])
.arg("-")
.pass_stdin(r#"a = "abcba".strip("aba")"#), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:5: Q000 [*] Double quotes found but single quotes preferred
test.py:1:5: B005 Using `.strip()` with multi-character strings is misleading
test.py:1:19: Q000 [*] Double quotes found but single quotes preferred
Found 3 errors.
[*] 2 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `ruff.toml`:
- 'extend-select' -> 'lint.extend-select'
- 'flake8-quotes' -> 'lint.flake8-quotes'
");
Ok(())
}
#[test]
fn lint_options() -> Result<()> {
let case = CliTest::with_file(
"ruff.toml",
r#"
[lint]
extend-select = ["B", "Q"]
[lint.flake8-quotes]
inline-quotes = "single"
"#,
)?;
assert_cmd_snapshot!(
case.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"a = "abcba".strip("aba")"#), @"
success: false
exit_code: 1
----- stdout -----
-:1:5: Q000 [*] Double quotes found but single quotes preferred
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
-:1:19: Q000 [*] Double quotes found but single quotes preferred
Found 3 errors.
[*] 2 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn mixed_levels() -> Result<()> {
let test = CliTest::new()?;
test.write_file(
"ruff.toml",
r#"
extend-select = ["B", "Q"]
[lint.flake8-quotes]
inline-quotes = "single"
"#,
)?;
assert_cmd_snapshot!(test.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"a = "abcba".strip("aba")"#), @"
success: false
exit_code: 1
----- stdout -----
-:1:5: Q000 [*] Double quotes found but single quotes preferred
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
-:1:19: Q000 [*] Double quotes found but single quotes preferred
Found 3 errors.
[*] 2 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `ruff.toml`:
- 'extend-select' -> 'lint.extend-select'
");
Ok(())
}
#[test]
fn precedence() -> Result<()> {
let test = CliTest::new()?;
test.write_file(
"ruff.toml",
r#"
[lint]
extend-select = ["B", "Q"]
[flake8-quotes]
inline-quotes = "double"
[lint.flake8-quotes]
inline-quotes = "single"
"#,
)?;
assert_cmd_snapshot!(test.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"a = "abcba".strip("aba")"#), @"
success: false
exit_code: 1
----- stdout -----
-:1:5: Q000 [*] Double quotes found but single quotes preferred
-:1:5: B005 Using `.strip()` with multi-character strings is misleading
-:1:19: Q000 [*] Double quotes found but single quotes preferred
Found 3 errors.
[*] 2 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `ruff.toml`:
- 'flake8-quotes' -> 'lint.flake8-quotes'
");
Ok(())
}
#[test]
fn exclude() -> Result<()> {
let case = CliTest::new()?;
case.write_file(
"ruff.toml",
r#"
extend-select = ["B", "Q"]
extend-exclude = ["out"]
[lint]
exclude = ["test.py", "generated.py"]
[lint.flake8-quotes]
inline-quotes = "single"
"#,
)?;
case.write_file(
"main.py",
r#"from test import say_hy
if __name__ == "__main__":
say_hy("dear Ruff contributor")
"#,
)?;
case.write_file(
"test.py",
r#"def say_hy(name: str):
print(f"Hy {name}")"#,
)?;
case.write_file(
"generated.py",
r#"NUMBERS = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19
]
OTHER = "OTHER"
"#,
)?;
case.write_file("out/a.py", r#"a = "a""#)?;
assert_cmd_snapshot!(
case.check_command()
.args(["--config", "ruff.toml"])
.arg("test.py")
.arg("."), @"
success: false
exit_code: 1
----- stdout -----
main.py:3:16: Q000 [*] Double quotes found but single quotes preferred
main.py:4:12: Q000 [*] Double quotes found but single quotes preferred
test.py:2:15: Q000 [*] Double quotes found but single quotes preferred
Found 3 errors.
[*] 3 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `ruff.toml`:
- 'extend-select' -> 'lint.extend-select'
");
Ok(())
}
#[test]
fn deduplicate_directory_and_explicit_file() -> Result<()> {
let case = CliTest::new()?;
case.write_file(
"ruff.toml",
r#"
[lint]
exclude = ["main.py"]
"#,
)?;
case.write_file("main.py", "import os\n")?;
assert_cmd_snapshot!(
case.check_command()
.args(["--config", "ruff.toml"])
.arg(".")
.arg("main.py"),
@"
success: false
exit_code: 1
----- stdout -----
main.py:1:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
"
);
Ok(())
}
#[test]
fn exclude_stdin() -> Result<()> {
let case = CliTest::with_file(
"ruff.toml",
r#"
extend-select = ["B", "Q"]
[lint]
exclude = ["generated.py"]
[lint.flake8-quotes]
inline-quotes = "single"
"#,
)?;
assert_cmd_snapshot!(
case.check_command()
.args(["--config", "ruff.toml"])
.args(["--stdin-filename", "generated.py"])
.arg("-")
.pass_stdin(r#"
from test import say_hy
if __name__ == "__main__":
say_hy("dear Ruff contributor")
"#), @"
success: false
exit_code: 1
----- stdout -----
generated.py:4:16: Q000 [*] Double quotes found but single quotes preferred
generated.py:5:12: Q000 [*] Double quotes found but single quotes preferred
Found 2 errors.
[*] 2 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `ruff.toml`:
- 'extend-select' -> 'lint.extend-select'
");
Ok(())
}
#[test]
fn line_too_long_width_override() -> Result<()> {
let test = CliTest::new()?;
test.write_file(
"ruff.toml",
r#"
line-length = 80
select = ["E501"]
[pycodestyle]
max-line-length = 100
"#,
)?;
assert_cmd_snapshot!(test.check_command()
.arg("--config")
.arg("ruff.toml")
.args(["--stdin-filename", "test.py"])
.arg("-")
.pass_stdin(r#"
# longer than 80, but less than 100
_ = "---------------------------------------------------------------------------亜亜亜亜亜亜"
# longer than 100
_ = "---------------------------------------------------------------------------亜亜亜亜亜亜亜亜亜亜亜亜亜亜"
"#), @"
success: false
exit_code: 1
----- stdout -----
test.py:5:91: E501 Line too long (109 > 100)
Found 1 error.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `ruff.toml`:
- 'select' -> 'lint.select'
- 'pycodestyle' -> 'lint.pycodestyle'
");
Ok(())
}
#[test]
fn per_file_ignores_stdin() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
extend-select = ["B", "Q"]
[lint.flake8-quotes]
inline-quotes = "single"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.args(["--stdin-filename", "generated.py"])
.args(["--per-file-ignores", "generated.py:Q"])
.arg("-")
.pass_stdin(r#"
import os
from test import say_hy
if __name__ == "__main__":
say_hy("dear Ruff contributor")
"#), @"
success: false
exit_code: 1
----- stdout -----
generated.py:2:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `ruff.toml`:
- 'extend-select' -> 'lint.extend-select'
");
Ok(())
}
#[test]
fn extend_per_file_ignores_stdin() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
extend-select = ["B", "Q"]
[lint.flake8-quotes]
inline-quotes = "single"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.args(["--stdin-filename", "generated.py"])
.args(["--extend-per-file-ignores", "generated.py:Q"])
.arg("-")
.pass_stdin(r#"
import os
from test import say_hy
if __name__ == "__main__":
say_hy("dear Ruff contributor")
"#), @"
success: false
exit_code: 1
----- stdout -----
generated.py:2:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in `ruff.toml`:
- 'extend-select' -> 'lint.extend-select'
");
Ok(())
}
#[test]
fn parent_configuration_override() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["ALL"]
"#,
)?;
fixture.write_file(
"subdirectory/ruff.toml",
r#"
[lint]
ignore = ["D203", "D212"]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.current_dir(fixture.root().join("subdirectory"))
, @"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
warning: No Python files found under the given path(s)
");
Ok(())
}
#[test]
fn nonexistent_config_file() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--config", "foo.toml", "."]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'foo.toml' for '--config <CONFIG_OPTION>'
tip: A `--config` flag must either be a path to a `.toml` configuration file
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
option
It looks like you were trying to pass a path to a configuration file.
The path `foo.toml` does not point to a configuration file
For more information, try '--help'.
");
}
#[test]
fn config_override_rejected_if_invalid_toml() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--config", "foo = bar", "."]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'foo = bar' for '--config <CONFIG_OPTION>'
tip: A `--config` flag must either be a path to a `.toml` configuration file
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
option
The supplied argument is not valid TOML:
TOML parse error at line 1, column 7
|
1 | foo = bar
| ^^^
string values must be quoted, expected literal string
For more information, try '--help'.
");
}
#[test]
fn too_many_config_files() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("ruff.toml", "")?;
fixture.write_file("ruff2.toml", "")?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("--config")
.arg("ruff2.toml")
.arg("."), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: You cannot specify more than one configuration file on the command line.
tip: remove either `--config=ruff.toml` or `--config=ruff2.toml`.
For more information, try `--help`.
");
Ok(())
}
#[test]
fn extend_passed_via_config_argument() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--config", "extend = 'foo.toml'", "."]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'extend = 'foo.toml'' for '--config <CONFIG_OPTION>'
tip: Cannot include `extend` in a --config flag value
For more information, try '--help'.
");
}
#[test]
fn nonexistent_extend_file() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
extend = "ruff2.toml"
"#,
)?;
fixture.write_file(
"ruff2.toml",
r#"
extend = "ruff3.toml"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command(), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load extended configuration `[TMP]/ruff3.toml` (`[TMP]/ruff.toml` extends `[TMP]/ruff2.toml` extends `[TMP]/ruff3.toml`)
Cause: Failed to read [TMP]/ruff3.toml
Cause: No such file or directory (os error 2)
");
Ok(())
}
#[test]
fn circular_extend() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
extend = "ruff2.toml"
"#,
)?;
fixture.write_file(
"ruff2.toml",
r#"
extend = "ruff3.toml"
"#,
)?;
fixture.write_file(
"ruff3.toml",
r#"
extend = "ruff.toml"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command(),
@"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Circular configuration detected: `[TMP]/ruff.toml` extends `[TMP]/ruff2.toml` extends `[TMP]/ruff3.toml` extends `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn parse_error_extends() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
extend = "ruff2.toml"
"#,
)?;
fixture.write_file(
"ruff2.toml",
r#"
[lint]
select = [E501]
"#,
)?;
assert_cmd_snapshot!(
fixture.check_command(),
@"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load extended configuration `[TMP]/ruff2.toml` (`[TMP]/ruff.toml` extends `[TMP]/ruff2.toml`)
Cause: Failed to parse [TMP]/ruff2.toml
Cause: TOML parse error at line 3, column 11
|
3 | select = [E501]
| ^^^^
string values must be quoted, expected literal string
");
Ok(())
}
#[test]
fn config_file_and_isolated() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("ruff.toml", "")?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("--isolated")
.arg("."), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: The argument `--config=ruff.toml` cannot be used with `--isolated`
tip: You cannot specify a configuration file and also specify `--isolated`,
as `--isolated` causes ruff to ignore all configuration files.
For more information, try `--help`.
");
Ok(())
}
#[test]
fn config_override_via_cli() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
line-length = 100
[lint]
select = ["I"]
[lint.isort]
combine-as-imports = true
"#,
)?;
let test_code = r#"
from foo import (
aaaaaaaaaaaaaaaaaaa,
bbbbbbbbbbb as bbbbbbbbbbbbbbbb,
cccccccccccccccc,
ddddddddddd as ddddddddddddd,
eeeeeeeeeeeeeee,
ffffffffffff as ffffffffffffff,
ggggggggggggg,
hhhhhhh as hhhhhhhhhhh,
iiiiiiiiiiiiii,
jjjjjjjjjjjjj as jjjjjj,
)
x = "longer_than_90_charactersssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss"
"#;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.args(["--config", "line-length=90"])
.args(["--config", "lint.extend-select=['E501', 'F841']"])
.args(["--config", "lint.isort.combine-as-imports = false"])
.arg("-")
.pass_stdin(test_code), @"
success: false
exit_code: 1
----- stdout -----
-:2:1: I001 [*] Import block is un-sorted or un-formatted
-:15:91: E501 Line too long (97 > 90)
Found 2 errors.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
fn unknown_rule_selector_test() -> Result<CliTest> {
CliTest::with_file("test.py", "import os\n")
}
#[test]
fn unknown_rule_selectors_select_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--select", "F481"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `select` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_extend_select_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--extend-select", "F481"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `extend-select` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ignore_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--ignore", "F481"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `ignore` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_fixable_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--fixable", "F481"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `fixable` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_extend_fixable_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--extend-fixable", "F481"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `extend-fixable` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_unfixable_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--unfixable", "F481"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `unfixable` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_config_ignore_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--config", "lint.ignore=['F481']"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `ignore` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ruff_toml_extend_safe_fixes_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file("ruff.toml", r#"lint = { extend-safe-fixes = ["F481"] }"#)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `extend-safe-fixes` from `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ruff_toml_extend_unsafe_fixes_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file("ruff.toml", r#"lint = { extend-unsafe-fixes = ["F481"] }"#)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `extend-unsafe-fixes` from `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn unknown_rule_selectors_per_file_ignores_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--per-file-ignores", "test.py:F481"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `per-file-ignores` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_extend_per_file_ignores_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--extend-per-file-ignores", "test.py:F481"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `per-file-ignores` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ruff_toml_per_file_ignores_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file(
"ruff.toml",
r#"lint = { per-file-ignores = { "test.py" = ["F481"] } }"#,
)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `per-file-ignores` from `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ruff_toml_extend_per_file_ignores_f481() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file(
"ruff.toml",
r#"lint = { extend-per-file-ignores = { "test.py" = ["F481"] } }"#,
)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Unknown rule selector `F481` in `per-file-ignores` from `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn unknown_rule_selectors_select_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--select", "F481"]), @"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
warning: Unknown rule selector `F481` in `select` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_extend_select_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--extend-select", "F481"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `extend-select` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ignore_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--ignore", "F481"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `ignore` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_fixable_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--fixable", "F481"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: `os` imported but unused
Found 1 error.
----- stderr -----
warning: Unknown rule selector `F481` in `fixable` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_extend_fixable_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--extend-fixable", "F481"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `extend-fixable` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_unfixable_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--unfixable", "F481"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `unfixable` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_config_ignore_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--config", "lint.ignore=['F481']"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `ignore` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ruff_toml_extend_safe_fixes_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file(
"ruff.toml",
r#"lint = { preview = true, extend-safe-fixes = ["F481"] }"#,
)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `extend-safe-fixes` from `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ruff_toml_extend_unsafe_fixes_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file(
"ruff.toml",
r#"lint = { preview = true, extend-unsafe-fixes = ["F481"] }"#,
)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `extend-unsafe-fixes` from `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn unknown_rule_selectors_per_file_ignores_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--per-file-ignores", "test.py:F481"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `per-file-ignores` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_extend_per_file_ignores_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--preview", "--extend-per-file-ignores", "test.py:F481"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `per-file-ignores` from the CLI
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ruff_toml_per_file_ignores_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file(
"ruff.toml",
r#"lint = { preview = true, per-file-ignores = { "test.py" = ["F481"] } }"#,
)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `per-file-ignores` from `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn unknown_rule_selectors_ruff_toml_extend_per_file_ignores_f481_preview() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file(
"ruff.toml",
r#"lint = { preview = true, extend-per-file-ignores = { "test.py" = ["F481"] } }"#,
)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
warning: Unknown rule selector `F481` in `per-file-ignores` from `[TMP]/ruff.toml`
");
Ok(())
}
#[test]
fn rule_name_selector_cli_preview_disabled() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--select", "unused-import"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Invalid selector `unused-import` in `select` from the CLI. Selecting rules by name requires preview mode
");
Ok(())
}
#[test]
fn rule_name_selector_cli_preview_enabled() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
assert_cmd_snapshot!(fixture.check_command().args(["--select", "unused-import", "--preview"]), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn rule_name_selector_config_preview_disabled() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file("ruff.toml", r#"lint = { select = ["unused-import"] }"#)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Invalid selector `unused-import` in `select` from `[TMP]/ruff.toml`. Selecting rules by name requires preview mode
");
Ok(())
}
#[test]
fn rule_name_selector_config_preview_enabled() -> Result<()> {
let fixture = unknown_rule_selector_test()?;
fixture.write_file(
"ruff.toml",
r#"lint = { preview = true, select = ["unused-import"] }"#,
)?;
assert_cmd_snapshot!(fixture.check_command(), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:8: unused-import: [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn valid_toml_but_nonexistent_option_provided_via_config_argument() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args([".", "--config", "extend-selection=['F401']"]), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'extend-selection=['F401']' for '--config <CONFIG_OPTION>'
tip: A `--config` flag must either be a path to a `.toml` configuration file
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
option
Could not parse the supplied argument as a `ruff.toml` configuration option:
unknown field `extend-selection`
For more information, try '--help'.
");
}
#[test]
fn each_toml_option_requires_a_new_flag_1() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args([".", "--config", "extend-select=['F841'], line-length=90"]),
@"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'extend-select=['F841'], line-length=90' for '--config <CONFIG_OPTION>'
tip: A `--config` flag must either be a path to a `.toml` configuration file
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
option
The supplied argument is not valid TOML:
TOML parse error at line 1, column 23
|
1 | extend-select=['F841'], line-length=90
| ^
unexpected key or value, expected newline, `#`
For more information, try '--help'.
");
}
#[test]
fn each_toml_option_requires_a_new_flag_2() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args([".", "--config", "extend-select=['F841'] line-length=90"]),
@"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'extend-select=['F841'] line-length=90' for '--config <CONFIG_OPTION>'
tip: A `--config` flag must either be a path to a `.toml` configuration file
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
option
The supplied argument is not valid TOML:
TOML parse error at line 1, column 24
|
1 | extend-select=['F841'] line-length=90
| ^
unexpected key or value, expected newline, `#`
For more information, try '--help'.
");
}
#[test]
fn value_given_to_table_key_is_not_inline_table_1() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args([".", "--config", r#"lint.flake8-pytest-style="csv""#]),
@r#"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'lint.flake8-pytest-style="csv"' for '--config <CONFIG_OPTION>'
tip: A `--config` flag must either be a path to a `.toml` configuration file
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
option
`lint.flake8-pytest-style` is a table of configuration options.
Did you want to override one of the table's subkeys?
Possible choices:
- `lint.flake8-pytest-style.fixture-parentheses`
- `lint.flake8-pytest-style.parametrize-names-type`
- `lint.flake8-pytest-style.parametrize-values-type`
- `lint.flake8-pytest-style.parametrize-values-row-type`
- `lint.flake8-pytest-style.raises-require-match-for`
- `lint.flake8-pytest-style.raises-extend-require-match-for`
- `lint.flake8-pytest-style.mark-parentheses`
- `lint.flake8-pytest-style.warns-require-match-for`
- `lint.flake8-pytest-style.warns-extend-require-match-for`
For more information, try '--help'.
"#);
}
#[test]
fn value_given_to_table_key_is_not_inline_table_2() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args([".", "--config", r#"lint=123"#]),
@"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: invalid value 'lint=123' for '--config <CONFIG_OPTION>'
tip: A `--config` flag must either be a path to a `.toml` configuration file
or a TOML `<KEY> = <VALUE>` pair overriding a specific configuration
option
`lint` is a table of configuration options.
Did you want to override one of the table's subkeys?
Possible choices:
- `lint.allowed-confusables`
- `lint.dummy-variable-rgx`
- `lint.extend-ignore`
- `lint.extend-select`
- `lint.extend-fixable`
- `lint.external`
- `lint.fixable`
- `lint.ignore`
- `lint.extend-safe-fixes`
- `lint.extend-unsafe-fixes`
- `lint.ignore-init-module-imports`
- `lint.logger-objects`
- `lint.select`
- `lint.explicit-preview-rules`
- `lint.task-tags`
- `lint.typing-modules`
- `lint.unfixable`
- `lint.per-file-ignores`
- `lint.extend-per-file-ignores`
- `lint.exclude`
- `lint.preview`
- `lint.typing-extensions`
- `lint.future-annotations`
For more information, try '--help'.
");
}
#[test]
fn config_doubly_overridden_via_cli() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
line-length = 100
[lint]
select=["E501"]
"#,
)?;
let test_code = "x = 'longer_than_90_charactersssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss'";
assert_cmd_snapshot!(fixture
.check_command()
.args(["--line-length", "90"])
.arg("--config")
.arg("ruff.toml")
.args(["--config", "line-length=110"])
.arg("-")
.pass_stdin(test_code), @"
success: false
exit_code: 1
----- stdout -----
-:1:91: E501 Line too long (97 > 90)
Found 1 error.
----- stderr -----
");
Ok(())
}
#[test]
fn complex_config_setting_overridden_via_cli() -> Result<()> {
let fixture = CliTest::with_file("ruff.toml", "lint.select = ['N801']")?;
let test_code = "class violates_n801: pass";
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.args(["--config", "lint.per-file-ignores = {'generated.py' = ['N801']}"])
.args(["--stdin-filename", "generated.py"])
.arg("-")
.pass_stdin(test_code), @"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
");
Ok(())
}
#[test]
fn deprecated_config_option_overridden_via_cli() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--config", "select=['N801']", "-"])
.pass_stdin("class lowercase: ..."),
@"
success: false
exit_code: 1
----- stdout -----
-:1:7: N801 Class name `lowercase` should use CapWords convention
Found 1 error.
----- stderr -----
warning: The top-level linter settings are deprecated in favour of their counterparts in the `lint` section. Please update the following options in your `--config` CLI arguments:
- 'select' -> 'lint.select'
");
}
#[test]
fn extension() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
include = ["*.ipy"]
"#,
)?;
fixture.write_file(
"main.ipy",
r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "ad6f36d9-4b7d-4562-8d00-f15a0f1fbb6d",
"metadata": {},
"outputs": [],
"source": [
"import os"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.args(["--extension", "ipy:ipynb"])
.arg("."), @"
success: false
exit_code: 1
----- stdout -----
main.ipy:cell 1:1:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn required_version_fails_to_parse() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
required-version = "pikachu"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command(), @r#"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load configuration `[TMP]/ruff.toml`
Cause: Failed to parse [TMP]/ruff.toml
Cause: TOML parse error at line 2, column 20
|
2 | required-version = "pikachu"
| ^^^^^^^^^
Failed to parse version: Unexpected end of version specifier, expected operator:
pikachu
^^^^^^^
"#);
Ok(())
}
#[test]
fn required_version_exact_mismatch() -> Result<()> {
let version = env!("CARGO_PKG_VERSION");
let fixture = CliTest::with_file(
"ruff.toml",
r#"
required-version = "0.1.0"
"#,
)?;
let mut settings = insta::Settings::clone_current();
settings.add_filter(version, "[VERSION]");
settings.bind(|| {
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"
import os
"#), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load configuration `[TMP]/ruff.toml`
Cause: Required version `==0.1.0` does not match the running version `[VERSION]`
");
});
Ok(())
}
#[test]
fn required_version_exact_match() -> Result<()> {
let version = env!("CARGO_PKG_VERSION");
let fixture = CliTest::with_file(
"ruff.toml",
&format!(
r#"
required-version = "{version}"
"#
),
)?;
insta::with_settings!({
filters => vec![(version, "[VERSION]")]
}, {
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"
import os
"#), @"
success: false
exit_code: 1
----- stdout -----
-:2:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
});
Ok(())
}
#[test]
fn required_version_bound_mismatch() -> Result<()> {
let version = env!("CARGO_PKG_VERSION");
let fixture = CliTest::with_file(
"ruff.toml",
&format!(
r#"
required-version = ">{version}"
"#
),
)?;
let mut settings = insta::Settings::clone_current();
settings.add_filter(version, "[VERSION]");
settings.bind(|| {
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"
import os
"#), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load configuration `[TMP]/ruff.toml`
Cause: Required version `>[VERSION]` does not match the running version `[VERSION]`
");
});
Ok(())
}
#[test]
fn required_version_precedes_rule_validation() -> Result<()> {
let version = env!("CARGO_PKG_VERSION");
let fixture = CliTest::with_file(
"ruff.toml",
&format!(
r#"
required-version = ">{version}"
[lint]
select = ["RUF999"]
"#
),
)?;
let mut settings = insta::Settings::clone_current();
settings.add_filter(version, "[VERSION]");
settings.bind(|| {
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"
import os
"#), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load configuration `[TMP]/ruff.toml`
Cause: Required version `>[VERSION]` does not match the running version `[VERSION]`
");
});
Ok(())
}
#[test]
fn required_version_bound_match() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
required-version = ">=0.1.0"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin(r#"
import os
"#), @"
success: false
exit_code: 1
----- stdout -----
-:2:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn config_expand() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
[lint]
select = ["F"]
ignore = ["F841"]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("${NAME}.toml")
.env("NAME", "ruff")
.arg("-")
.pass_stdin(r#"
import os
def func():
x = 1
"#), @"
success: false
exit_code: 1
----- stdout -----
-:2:8: F401 [*] `os` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn negated_per_file_ignores() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint.per-file-ignores]
"!selected.py" = ["RUF"]
"#,
)?;
fixture.write_file("selected.py", "")?;
fixture.write_file("ignored.py", "")?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("--select")
.arg("RUF901")
, @"
success: false
exit_code: 1
----- stdout -----
selected.py:1:1: RUF901 [*] Hey this is a stable test rule with a safe fix.
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn negated_per_file_ignores_absolute() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint.per-file-ignores]
"!src/**.py" = ["RUF"]
"#,
)?;
fixture.write_file("src/selected.py", "")?;
fixture.write_file("ignored.py", "")?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("--select")
.arg("RUF901")
, @"
success: false
exit_code: 1
----- stdout -----
src/selected.py:1:1: RUF901 [*] Hey this is a stable test rule with a safe fix.
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn negated_per_file_ignores_overlap() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint.per-file-ignores]
"*.py" = ["RUF"]
"!foo.py" = ["RUF"]
"#,
)?;
fixture.write_file("foo.py", "")?;
fixture.write_file("bar.py", "")?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("--select")
.arg("RUF901")
, @"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
");
Ok(())
}
#[test]
fn unused_interaction() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
[lint]
select = ["F"]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.args(["--stdin-filename", "test.py"])
.arg("--fix")
.arg("-")
.pass_stdin(r#"
import os # F401
def function():
import os # F811
print(os.name)
"#), @"
success: true
exit_code: 0
----- stdout -----
import os # F401
def function():
print(os.name)
----- stderr -----
Found 1 error (1 fixed, 0 remaining).
");
Ok(())
}
#[test]
fn ignore_noqa() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["F401"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
import os # noqa: F401
# ruff: disable[F401]
import sys
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py"),
@"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
");
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.args(["--ignore-noqa"]),
@"
success: false
exit_code: 1
----- stdout -----
noqa.py:2:8: F401 [*] `os` imported but unused
noqa.py:5:8: F401 [*] `sys` imported but unused
Found 2 errors.
[*] 2 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn add_noqa() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["RUF015"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
def first_square():
return [x * x for x in range(20)][0]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.args(["--add-noqa"])
.arg("-")
.pass_stdin(r#"
"#), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 noqa directive.
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @"
def first_square():
return [x * x for x in range(20)][0] # noqa: RUF015
");
Ok(())
}
#[test]
fn add_noqa_multiple_codes() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["ANN001", "ANN201", "ARG001", "D103"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
def unused(x):
pass
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.arg("--preview")
.args(["--add-noqa"])
.arg("-")
.pass_stdin(r#"
"#), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 noqa directive.
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @"
def unused(x): # noqa: ANN001, ANN201, D103
pass
");
Ok(())
}
#[test]
fn add_noqa_multiline_diagnostic() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["I"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
import z
import c
import a
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.args(["--add-noqa"])
.arg("-")
.pass_stdin(r#"
"#), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 noqa directive.
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @"
import z # noqa: I001
import c
import a
");
Ok(())
}
#[test]
fn add_noqa_existing_noqa() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["ANN001", "ANN201", "ARG001", "D103"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
def unused(x): # noqa: ANN001, ARG001, D103
pass
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.arg("--preview")
.args(["--add-noqa"])
.arg("-")
.pass_stdin(r#"
"#), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 noqa directive.
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @"
def unused(x): # noqa: ANN001, ANN201, ARG001, D103
pass
");
Ok(())
}
#[test]
fn add_noqa_existing_file_level_noqa() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["F401"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
# ruff: noqa F401
import os
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.arg("--preview")
.args(["--add-noqa"])
.arg("-")
.pass_stdin(r#"
"#), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @"
# ruff: noqa F401
import os
");
Ok(())
}
#[test]
fn add_noqa_existing_range_suppression() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["F401"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
# ruff: disable[F401]
import os
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.arg("--preview")
.args(["--add-noqa"])
.arg("-")
.pass_stdin(r#"
"#), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @"
# ruff: disable[F401]
import os
");
Ok(())
}
#[test]
fn add_noqa_multiline_comment() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["UP031"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
print(
"""First line
second line
third line
%s"""
% name
)
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.arg("--preview")
.args(["--add-noqa"])
.arg("-")
.pass_stdin(r#"
"#), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 noqa directive.
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @r#"
print(
"""First line
second line
third line
%s""" # noqa: UP031
% name
)
"#);
Ok(())
}
#[test]
fn add_noqa_top_of_file() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["D100"]
"#,
)?;
fixture.write_file(
"noqa.py", r"
",
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.arg("--preview")
.args(["--add-noqa"])
, @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 noqa directive.
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @"# noqa: D100");
Ok(())
}
#[test]
fn add_noqa_top_of_file_with_shebang() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["D100"]
"#,
)?;
fixture.write_file(
"noqa.py",
r"#!/usr/bin/env fake command
",
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--config", "ruff.toml"])
.arg("noqa.py")
.arg("--preview")
.args(["--add-noqa"])
, @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 noqa directive.
");
let test_code =
fs::read_to_string(fixture.root().join("noqa.py")).expect("should read test file");
insta::assert_snapshot!(test_code, @"
#!/usr/bin/env fake command
# noqa: D100
");
Ok(())
}
#[test]
fn add_noqa_exclude() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
exclude = ["excluded.py"]
select = ["RUF015"]
"#,
)?;
fixture.write_file(
"noqa.py",
r#"
def first_square():
return [x * x for x in range(20)][0]
"#,
)?;
fixture.write_file(
"excluded.py",
r#"
def first_square():
return [x * x for x in range(20)][0]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--add-noqa"]), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 noqa directive.
");
Ok(())
}
#[test]
fn add_noqa_parent() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"noqa.py",
r#"
from foo import ( # noqa: F401
bar
)
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--add-noqa")
.arg("--select=F401")
.arg("noqa.py"), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
");
Ok(())
}
#[test]
fn add_noqa_with_reason() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"test.py",
r#"import os
def foo():
x = 1
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--add-noqa=TODO: fix")
.arg("--select=F401,F841")
.arg("test.py"), @"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 2 noqa directives.
");
let content = fs::read_to_string(fixture.root().join("test.py"))?;
insta::assert_snapshot!(content, @"
import os # noqa: F401 TODO: fix
def foo():
x = 1 # noqa: F841 TODO: fix
");
Ok(())
}
#[test]
fn add_noqa_with_newline_in_reason() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("test.py", "import os\n")?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--add-noqa=line1\nline2")
.arg("--select=F401")
.arg("test.py"), @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: --add-noqa <reason> cannot contain newline characters
");
Ok(())
}
#[test]
fn add_ignore() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"noqa.py",
r#"
def first_square():
return [x * x for x in range(20)][0]
"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--select=RUF015")
.arg("--preview")
.arg("--add-ignore"),
@"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
Added 1 ignore comment.
",
);
let test_code = fixture.read_file("noqa.py")?;
insta::assert_snapshot!(
test_code,
@"
def first_square():
return [x * x for x in range(20)][0] # ruff:ignore[unnecessary-iterable-allocation-for-first-element]
",
);
Ok(())
}
#[test]
fn add_ignore_requires_preview() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("noqa.py", "import os\n")?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--select=F401")
.arg("--add-ignore"),
@"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: `--add-ignore` requires preview mode, but preview is disabled for `[TMP]/noqa.py`
",
);
let test_code = fixture.read_file("noqa.py")?;
insta::assert_snapshot!(test_code, @"import os");
Ok(())
}
#[test]
fn add_noqa_existing_ignore() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"noqa.py",
r#"
def unused(x): # ruff:ignore[ANN001, ARG001, D103]
pass
"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--select=ANN001,ANN201,ARG001,D103")
.arg("--add-noqa"),
@"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
warning: #ruff:ignore comment found but not active, enable preview mode
Added 1 noqa directive.
",
);
let test_code = fixture.read_file("noqa.py")?;
insta::assert_snapshot!(
test_code,
@"
def unused(x): # ruff:ignore[ANN001, ARG001, D103] # noqa: ANN001, ANN201, D103
pass
",
);
Ok(())
}
#[test]
fn requires_python() -> Result<()> {
let fixture = CliTest::with_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
[tool.ruff.lint]
select = ["UP006"]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("pyproject.toml")
.args(["--stdin-filename", "test.py"])
.arg("-")
.pass_stdin(r#"from typing import List; foo: List[int]"#), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:31: UP006 [*] Use `list` instead of `List` for type annotation
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
let fixture2 = CliTest::with_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.8"
[tool.ruff.lint]
select = ["UP006"]
"#,
)?;
assert_cmd_snapshot!(fixture2
.check_command()
.arg("--config")
.arg("pyproject.toml")
.args(["--stdin-filename", "test.py"])
.arg("-")
.pass_stdin(r#"from typing import List; foo: List[int]"#), @"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
");
Ok(())
}
#[test]
fn requires_python_patch() -> Result<()> {
let fixture = CliTest::with_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11.4"
[tool.ruff.lint]
select = ["UP006"]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("pyproject.toml")
.args(["--stdin-filename", "test.py"])
.arg("-")
.pass_stdin(r#"from typing import List; foo: List[int]"#), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:31: UP006 [*] Use `list` instead of `List` for type annotation
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn requires_python_equals() -> Result<()> {
let fixture = CliTest::with_file(
"pyproject.toml",
r#"[project]
requires-python = "== 3.11"
[tool.ruff.lint]
select = ["UP006"]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("pyproject.toml")
.args(["--stdin-filename", "test.py"])
.arg("-")
.pass_stdin(r#"from typing import List; foo: List[int]"#), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:31: UP006 [*] Use `list` instead of `List` for type annotation
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn requires_python_equals_patch() -> Result<()> {
let fixture = CliTest::with_file(
"pyproject.toml",
r#"[project]
requires-python = "== 3.11.4"
[tool.ruff.lint]
select = ["UP006"]
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("pyproject.toml")
.args(["--stdin-filename", "test.py"])
.arg("-")
.pass_stdin(r#"from typing import List; foo: List[int]"#), @"
success: false
exit_code: 1
----- stdout -----
test.py:1:31: UP006 [*] Use `list` instead of `List` for type annotation
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn requires_python_no_tool() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"test.py",
r#"from typing import Union;foo: Union[int, str] = 1"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--show-settings")
.args(["--select", "UP007"])
.arg("test.py")
.arg("-")
);
Ok(())
}
#[test]
fn requires_python_no_tool_preview_enabled() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"test.py",
r#"from typing import Union;foo: Union[int, str] = 1"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--preview")
.arg("--show-settings")
.args(["--select", "UP007"])
.arg("test.py")
.arg("-")
);
Ok(())
}
#[test]
fn requires_python_no_tool_target_version_override() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"test.py",
r#"from typing import Union;foo: Union[int, str] = 1"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--show-settings")
.args(["--select", "UP007"])
.args(["--target-version", "py310"])
.arg("test.py")
.arg("-")
);
Ok(())
}
#[test]
fn requires_python_no_tool_with_check() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"test.py",
r#"from typing import Union;foo: Union[int, str] = 1"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.args(["--select","UP007"])
.arg(".")
, @"
success: false
exit_code: 1
----- stdout -----
test.py:1:31: UP007 [*] Use `X | Y` for type annotations
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn requires_python_ruff_toml_no_target_fallback() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"[lint]
select = ["UP007"]
"#,
)?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"test.py",
r#"
from typing import Union;foo: Union[int, str] = 1
"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("test.py")
.arg("--show-settings"),
);
Ok(())
}
#[test]
fn requires_python_ruff_toml_no_target_fallback_check() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"[lint]
select = ["UP007"]
"#,
)?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"test.py",
r#"
from typing import Union;foo: Union[int, str] = 1"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("test.py")
, @"
success: false
exit_code: 1
----- stdout -----
test.py:2:31: UP007 [*] Use `X | Y` for type annotations
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn requires_python_pyproject_toml_above() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"foo/pyproject.toml",
r#"[project]
"#,
)?;
fixture.write_file(
"foo/test.py",
r#"
from typing import Union;foo: Union[int, str] = 1
"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--show-settings")
.args(["--select", "UP007"])
.arg("foo/test.py"),
);
Ok(())
}
#[test]
fn requires_python_pyproject_toml_above_with_tool() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"foo/pyproject.toml",
r#"
[tool.ruff]
target-version = "py310"
"#,
)?;
fixture.write_file(
"foo/test.py",
r#"
from typing import Union;foo: Union[int, str] = 1
"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--show-settings")
.args(["--select", "UP007"])
.arg("foo/test.py"),
);
Ok(())
}
#[test]
fn requires_python_ruff_toml_above() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
[lint]
select = ["UP007"]
"#,
)?;
fixture.write_file(
"foo/pyproject.toml",
r#"[project]
requires-python = ">= 3.11"
"#,
)?;
fixture.write_file(
"foo/test.py",
r#"
from typing import Union;foo: Union[int, str] = 1
"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--show-settings")
.arg("foo/test.py"),
);
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--show-settings")
.arg("test.py")
.current_dir(fixture.root().join("foo")),
);
Ok(())
}
#[test]
fn requires_python_extend_from_shared_config() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"ruff.toml",
r#"
extend = "./shared/base_config.toml"
[lint]
select = ["UP007"]
"#,
)?;
fixture.write_file(
"pyproject.toml",
r#"[project]
requires-python = ">= 3.10"
"#,
)?;
fixture.write_file(
"shared/base_config.toml",
r#"
target-version = "py311"
"#,
)?;
fixture.write_file(
"test.py",
r#"
from typing import Union;foo: Union[int, str] = 1
"#,
)?;
assert_cmd_snapshot!(
fixture
.check_command()
.arg("--show-settings")
.arg("test.py"),
);
Ok(())
}
#[test]
fn checks_notebooks_in_stable() -> anyhow::Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"main.ipynb",
r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "ad6f36d9-4b7d-4562-8d00-f15a0f1fbb6d",
"metadata": {},
"outputs": [],
"source": [
"import random"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--select")
.arg("F401")
, @"
success: false
exit_code: 1
----- stdout -----
main.ipynb:cell 1:1:8: F401 [*] `random` imported but unused
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn nested_implicit_namespace_package() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("foo/__init__.py", "")?;
fixture.write_file("foo/bar/baz/__init__.py", "")?;
fixture.write_file("foo/bar/baz/bop.py", "")?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--select")
.arg("INP")
, @"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
");
assert_cmd_snapshot!(fixture
.check_command()
.arg("--select")
.arg("INP")
.arg("--preview")
, @"
success: false
exit_code: 1
----- stdout -----
foo/bar/baz/__init__.py:1:1: implicit-namespace-package: File `foo/bar/baz/__init__.py` declares a package, but is nested under an implicit namespace package. Add an `__init__.py` to `foo/bar`.
Found 1 error.
----- stderr -----
");
Ok(())
}
#[test]
fn flake8_import_convention_invalid_aliases_config_alias_name() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
[lint.flake8-import-conventions.aliases]
"module.name" = "invalid.alias"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin("")
, @r#"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load configuration `[TMP]/ruff.toml`
Cause: Failed to parse [TMP]/ruff.toml
Cause: TOML parse error at line 3, column 17
|
3 | "module.name" = "invalid.alias"
| ^^^^^^^^^^^^^^^
invalid value: string "invalid.alias", expected a Python identifier
"#);
Ok(())
}
#[test]
fn flake8_import_convention_invalid_aliases_config_extend_alias_name() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
[lint.flake8-import-conventions.extend-aliases]
"module.name" = "__debug__"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin("")
, @r#"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load configuration `[TMP]/ruff.toml`
Cause: Failed to parse [TMP]/ruff.toml
Cause: TOML parse error at line 3, column 17
|
3 | "module.name" = "__debug__"
| ^^^^^^^^^^^
invalid value: string "__debug__", expected an assignable Python identifier
"#);
Ok(())
}
#[test]
fn flake8_import_convention_invalid_aliases_config_module_name() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
[lint.flake8-import-conventions.aliases]
"module..invalid" = "alias"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin("")
, @r#"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Failed to load configuration `[TMP]/ruff.toml`
Cause: Failed to parse [TMP]/ruff.toml
Cause: TOML parse error at line 3, column 1
|
3 | "module..invalid" = "alias"
| ^^^^^^^^^^^^^^^^^
invalid value: string "module..invalid", expected a sequence of Python identifiers delimited by periods
"#);
Ok(())
}
#[test]
fn flake8_import_convention_nfkc_normalization() -> Result<()> {
let fixture = CliTest::with_file(
"ruff.toml",
r#"
[lint.flake8-import-conventions.aliases]
"test.module" = "_﹏𝘥𝘦𝘣𝘶𝘨﹏﹏"
"#,
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--config")
.arg("ruff.toml")
.arg("-")
.pass_stdin("")
, @"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
ruff failed
Cause: Invalid alias for module 'test.module': alias normalizes to '__debug__', which is not allowed.
");
Ok(())
}
#[test]
fn flake8_import_convention_unused_aliased_import() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.arg("--config")
.arg(r#"lint.isort.required-imports = ["import pandas"]"#)
.args(["--select", "I002,ICN001,F401"])
.args(["--stdin-filename", "test.py"])
.arg("--unsafe-fixes")
.arg("--fix")
.arg("-")
.pass_stdin("1")
);
}
#[test]
fn flake8_import_convention_unused_aliased_import_no_conflict() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.arg("--config")
.arg(r#"lint.isort.required-imports = ["import pandas as pd"]"#)
.args(["--select", "I002,ICN001,F401"])
.args(["--stdin-filename", "test.py"])
.arg("--unsafe-fixes")
.arg("--fix")
.arg("-")
.pass_stdin("1")
);
}
#[test]
fn required_import_set_conflicts_with_pyi025() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.arg("--config")
.arg(r#"lint.isort.required-imports = ["from collections.abc import Set"]"#)
.args(["--select", "I002,PYI025"])
.arg("-")
.pass_stdin("1")
);
}
#[test]
fn required_import_set_aliased_as_abstract_set_no_conflict() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.arg("--config")
.arg(r#"lint.isort.required-imports = ["from collections.abc import Set as AbstractSet"]"#)
.args(["--select", "I002,PYI025"])
.arg("-")
.pass_stdin("1")
);
}
#[test]
fn required_import_set_without_pyi025_no_conflict() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.arg("--config")
.arg(r#"lint.isort.required-imports = ["from collections.abc import Set"]"#)
.args(["--select", "I002"])
.arg("-")
.pass_stdin("1")
);
}
#[test]
fn pyupgrade_up026_respects_isort_required_import_fix() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.arg("--isolated")
.arg("check")
.arg("-")
.args(["--select", "I002,UP026"])
.arg("--config")
.arg(r#"lint.isort.required-imports=["import mock"]"#)
.arg("--fix")
.arg("--no-cache")
.pass_stdin("1\n"),
@"
success: true
exit_code: 0
----- stdout -----
import mock
1
----- stderr -----
Found 1 error (1 fixed, 0 remaining).
"
);
}
#[test]
fn pyupgrade_up026_respects_isort_required_import_from_fix() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.arg("--isolated")
.arg("check")
.arg("-")
.args(["--select", "I002,UP026"])
.arg("--config")
.arg(r#"lint.isort.required-imports = ["from mock import mock"]"#)
.arg("--fix")
.arg("--no-cache")
.pass_stdin("from mock import mock\n"),
@"
success: true
exit_code: 0
----- stdout -----
from mock import mock
----- stderr -----
All checks passed!
"
);
}
#[test]
fn flake8_pyi_redundant_none_literal() {
let snippet = r#"
from typing import Literal
# For each of these expressions, Ruff provides a fix for one of the `Literal[None]` elements
# but not both, as if both were autofixed it would result in `None | None`,
# which leads to a `TypeError` at runtime.
a: Literal[None,] | Literal[None,]
b: Literal[None] | Literal[None]
c: Literal[None] | Literal[None,]
d: Literal[None,] | Literal[None]
"#;
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--select", "PYI061"])
.args(["--stdin-filename", "test.py"])
.arg("--preview")
.arg("--diff")
.arg("--unsafe-fixes")
.arg("-")
.pass_stdin(snippet), @"
success: false
exit_code: 1
----- stdout -----
--- test.py
+++ test.py
@@ -4,7 +4,7 @@
# For each of these expressions, Ruff provides a fix for one of the `Literal[None]` elements
# but not both, as if both were autofixed it would result in `None | None`,
# which leads to a `TypeError` at runtime.
-a: Literal[None,] | Literal[None,]
-b: Literal[None] | Literal[None]
-c: Literal[None] | Literal[None,]
-d: Literal[None,] | Literal[None]
+a: None | Literal[None,]
+b: None | Literal[None]
+c: None | Literal[None,]
+d: None | Literal[None]
----- stderr -----
Would fix 4 errors.
");
}
#[test]
fn pep695_generic_rename() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--select", "F401,PYI018,UP046,UP047,UP049"])
.args(["--stdin-filename", "test.py"])
.arg("--unsafe-fixes")
.arg("--fix")
.arg("--preview")
.arg("--target-version=py312")
.arg("-")
.pass_stdin(
r#"
from typing import Generic, TypeVar
_T = TypeVar("_T")
class OldStyle(Generic[_T]):
var: _T
def func(t: _T) -> _T:
x: _T
return x
"#
),
@"
success: true
exit_code: 0
----- stdout -----
class OldStyle[T]:
var: T
def func[T](t: T) -> T:
x: T
return x
----- stderr -----
Found 7 errors (7 fixed, 0 remaining).
"
);
}
#[test]
fn type_parameter_rename_isolation() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--select", "UP049"])
.args(["--stdin-filename", "test.py"])
.arg("--unsafe-fixes")
.arg("--fix")
.arg("--preview")
.arg("--target-version=py312")
.arg("-")
.pass_stdin(
r#"
class Foo[_T, __T]:
pass
"#
),
@"
success: false
exit_code: 1
----- stdout -----
class Foo[T, __T]:
pass
----- stderr -----
test.py:2:14: private-type-parameter: Generic class uses private type parameters
Found 2 errors (1 fixed, 1 remaining).
"
);
}
fn create_a005_module_structure(fixture: &CliTest) -> Result<()> {
fixture.write_file("abc/__init__.py", "")?;
fixture.write_file("collections/__init__.py", "")?;
fixture.write_file("collections/abc/__init__.py", "")?;
fixture.write_file("collections/foobar/__init__.py", "")?;
fixture.write_file("foobar/__init__.py", "")?;
fixture.write_file("foobar/abc/__init__.py", "")?;
fixture.write_file("foobar/collections/__init__.py", "")?;
fixture.write_file("foobar/collections/abc/__init__.py", "")?;
fixture.write_file("urlparse/__init__.py", "")?;
fixture.write_file("ruff.toml", "")?;
Ok(())
}
#[test]
fn a005_module_shadowing_strict() -> Result<()> {
let fixture = CliTest::new()?;
create_a005_module_structure(&fixture)?;
assert_cmd_snapshot!(fixture.check_command()
.arg("--config")
.arg(r#"lint.flake8-builtins.strict-checking = true"#)
.args(["--select", "A005"]),
@"
success: false
exit_code: 1
----- stdout -----
abc/__init__.py:1:1: A005 Module `abc` shadows a Python standard-library module
collections/__init__.py:1:1: A005 Module `collections` shadows a Python standard-library module
collections/abc/__init__.py:1:1: A005 Module `abc` shadows a Python standard-library module
foobar/abc/__init__.py:1:1: A005 Module `abc` shadows a Python standard-library module
foobar/collections/__init__.py:1:1: A005 Module `collections` shadows a Python standard-library module
foobar/collections/abc/__init__.py:1:1: A005 Module `abc` shadows a Python standard-library module
Found 6 errors.
----- stderr -----
");
Ok(())
}
#[test]
fn a005_module_shadowing_non_strict() -> Result<()> {
let fixture = CliTest::new()?;
create_a005_module_structure(&fixture)?;
assert_cmd_snapshot!(fixture.check_command()
.arg("--config")
.arg(r#"lint.flake8-builtins.strict-checking = false"#)
.args(["--select", "A005"]),
@"
success: false
exit_code: 1
----- stdout -----
abc/__init__.py:1:1: A005 Module `abc` shadows a Python standard-library module
collections/__init__.py:1:1: A005 Module `collections` shadows a Python standard-library module
Found 2 errors.
----- stderr -----
");
Ok(())
}
#[test]
fn a005_module_shadowing_strict_default() -> Result<()> {
let fixture = CliTest::new()?;
create_a005_module_structure(&fixture)?;
assert_cmd_snapshot!(fixture.check_command()
.args(["--select", "A005"]),
@"
success: false
exit_code: 1
----- stdout -----
abc/__init__.py:1:1: A005 Module `abc` shadows a Python standard-library module
collections/__init__.py:1:1: A005 Module `collections` shadows a Python standard-library module
Found 2 errors.
----- stderr -----
");
Ok(())
}
#[test]
fn per_file_target_version_linter() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--target-version", "py312"])
.args(["--select", "UP046"]) .args(["--stdin-filename", "test.py"])
.arg("--preview")
.arg("-")
.pass_stdin(r#"
from typing import Generic, TypeVar
T = TypeVar("T")
class A(Generic[T]):
var: T
"#),
@"
success: false
exit_code: 1
----- stdout -----
test.py:6:9: non-pep695-generic-class: Generic class `A` uses `Generic` subclass instead of type parameters
Found 1 error.
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
----- stderr -----
"
);
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--target-version", "py312"])
.args(["--config", r#"per-file-target-version = {"test.py" = "py311"}"#])
.args(["--select", "UP046"]) .args(["--stdin-filename", "test.py"])
.arg("--preview")
.arg("-")
.pass_stdin(r#"
from typing import Generic, TypeVar
T = TypeVar("T")
class A(Generic[T]):
var: T
"#),
@"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
"
);
}
#[test]
fn walrus_before_py38() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--stdin-filename", "test.py"])
.arg("--target-version=py38")
.arg("-")
.pass_stdin(r#"(x := 1)"#),
@"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
"
);
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--stdin-filename", "test.py"])
.arg("--target-version=py37")
.arg("-")
.pass_stdin(r#"(x := 1)"#),
@"
success: false
exit_code: 1
----- stdout -----
test.py:1:2: invalid-syntax: Cannot use named assignment expression (`:=`) on Python 3.7 (syntax was added in Python 3.8)
Found 1 error.
----- stderr -----
"
);
}
#[test]
fn match_before_py310() {
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--stdin-filename", "test.py"])
.arg("--target-version=py310")
.arg("-")
.pass_stdin(
r#"
match 2:
case 1:
print("it's one")
"#
),
@"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
"
);
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--stdin-filename", "test.py"])
.arg("--target-version=py39")
.arg("-")
.pass_stdin(
r#"
match 2:
case 1:
print("it's one")
"#
),
@"
success: false
exit_code: 1
----- stdout -----
test.py:2:1: invalid-syntax: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
Found 1 error.
----- stderr -----
"
);
assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--stdin-filename", "test.py"])
.arg("--target-version=py39")
.arg("--preview")
.arg("-")
.pass_stdin(
r#"
match 2:
case 1:
print("it's one")
"#
),
@"
success: false
exit_code: 1
----- stdout -----
test.py:2:1: invalid-syntax: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
Found 1 error.
----- stderr -----
"
);
}
#[test]
fn cache_syntax_errors() -> Result<()> {
let fixture = CliTest::with_file("main.py", "match 2:\n case 1: ...")?;
let mut cmd = fixture.command();
cmd.args(["check", "--output-format", "concise"])
.arg("--target-version=py39")
.arg("--preview")
.arg("--quiet");
assert_cmd_snapshot!(
cmd,
@"
success: false
exit_code: 1
----- stdout -----
main.py:1:1: invalid-syntax: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
----- stderr -----
"
);
assert_cmd_snapshot!(
cmd,
@"
success: false
exit_code: 1
----- stdout -----
main.py:1:1: invalid-syntax: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
----- stderr -----
"
);
Ok(())
}
#[test]
fn cookiecutter_globbing() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file(
"{{cookiecutter.repo_name}}/pyproject.toml",
r#"tool.ruff.lint.per-file-ignores = { "tests/*" = ["F811"] }"#,
)?;
fixture.write_file(
"{{cookiecutter.repo_name}}/tests/maintest.py",
"import foo\nimport bar\nimport foo",
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--select=F811"), @"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
");
fs::remove_file(
fixture
.root()
.join("{{cookiecutter.repo_name}}/pyproject.toml"),
)?;
assert_cmd_snapshot!(fixture
.check_command()
.arg("--select=F811"), @"
success: false
exit_code: 1
----- stdout -----
{{cookiecutter.repo_name}}/tests/maintest.py:3:8: F811 [*] Redefinition of unused `foo` from line 1: `foo` redefined here
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn cookiecutter_globbing_no_project_root() -> Result<()> {
let fixture = CliTest::new()?;
fs::create_dir(fixture.root().join("{{cookiecutter.repo_name}}"))?;
assert_cmd_snapshot!(fixture
.check_command()
.current_dir(fixture.root().join("{{cookiecutter.repo_name}}"))
.args(["--extend-per-file-ignores", "generated.py:Q"]), @"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
warning: No Python files found under the given path(s)
");
Ok(())
}
#[test]
fn semantic_syntax_errors() -> Result<()> {
let fixture = CliTest::with_file("main.py", "[(x := 1) for x in foo]")?;
let contents = "[(x := 1) for x in foo]";
let mut cmd = fixture.command();
cmd.args(["check", "--output-format", "concise"])
.arg("--preview")
.arg("--quiet");
assert_cmd_snapshot!(
cmd,
@"
success: false
exit_code: 1
----- stdout -----
main.py:1:3: invalid-syntax: assignment expression cannot rebind comprehension variable
main.py:1:20: undefined-name: Undefined name `foo`
----- stderr -----
"
);
assert_cmd_snapshot!(
cmd,
@"
success: false
exit_code: 1
----- stdout -----
main.py:1:3: invalid-syntax: assignment expression cannot rebind comprehension variable
main.py:1:20: undefined-name: Undefined name `foo`
----- stderr -----
"
);
assert_cmd_snapshot!(
fixture.check_command()
.args(["--config", "lint.select = []"])
.arg("--preview")
.arg("-")
.pass_stdin(contents),
@"
success: false
exit_code: 1
----- stdout -----
-:1:3: invalid-syntax: assignment expression cannot rebind comprehension variable
Found 1 error.
----- stderr -----
"
);
Ok(())
}
#[test]
fn combine_typing_extensions_config() {
let contents = "
from typing import TypeVar
T = TypeVar('T')
class Foo:
def f(self: T) -> T: ...
";
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--config", "lint.typing-extensions = false"])
.arg("--select=PYI019")
.arg("--target-version=py39")
.arg("-")
.pass_stdin(contents),
@"
success: true
exit_code: 0
----- stdout -----
All checks passed!
----- stderr -----
"
);
}
#[test_case::test_case("concise")]
#[test_case::test_case("full")]
#[test_case::test_case("json")]
#[test_case::test_case("json-lines")]
#[test_case::test_case("junit")]
#[test_case::test_case("grouped")]
#[test_case::test_case("github")]
#[test_case::test_case("gitlab")]
#[test_case::test_case("pylint")]
#[test_case::test_case("rdjson")]
#[test_case::test_case("azure")]
#[test_case::test_case("sarif")]
fn output_format(output_format: &str) -> Result<()> {
const CONTENT: &str = "\
import os # F401
x = y # F821
match 42: # invalid-syntax
case _: ...
";
let fixture = CliTest::with_settings(|_project_dir, mut settings| {
settings.add_filter(r#""[^"]+\\?/?input.py"#, r#""[TMP]/input.py"#);
settings
})?;
fixture.write_file("input.py", CONTENT)?;
let snapshot = format!("output_format_{output_format}");
assert_cmd_snapshot!(
snapshot,
fixture.command().args([
"check",
"--no-cache",
"--output-format",
output_format,
"--select",
"F401,F821",
"--target-version",
"py39",
"input.py",
])
);
Ok(())
}
#[test_case::test_case("concise")]
#[test_case::test_case("full")]
#[test_case::test_case("json")]
#[test_case::test_case("json-lines")]
#[test_case::test_case("junit")]
#[test_case::test_case("grouped")]
#[test_case::test_case("github")]
#[test_case::test_case("gitlab")]
#[test_case::test_case("pylint")]
#[test_case::test_case("rdjson")]
#[test_case::test_case("azure")]
#[test_case::test_case("sarif")]
fn output_format_preview(output_format: &str) -> Result<()> {
const CONTENT: &str = "\
import os # F401
x = y # F821
match 42: # invalid-syntax
case _: ...
";
let fixture = CliTest::with_settings(|_project_dir, mut settings| {
settings.add_filter(r#""[^"]+\\?/?input.py"#, r#""[TMP]/input.py"#);
settings
})?;
fixture.write_file("input.py", CONTENT)?;
let snapshot = format!("output_format_preview_{output_format}");
assert_cmd_snapshot!(
snapshot,
fixture.command().args([
"check",
"--no-cache",
"--output-format",
output_format,
"--select",
"F401,F821",
"--target-version",
"py39",
"--preview",
"input.py",
])
);
Ok(())
}
#[test_case::test_case("concise"; "concise_show_fixes")]
#[test_case::test_case("full"; "full_show_fixes")]
#[test_case::test_case("grouped"; "grouped_show_fixes")]
fn output_format_show_fixes(output_format: &str) -> Result<()> {
let fixture = CliTest::with_file("input.py", "import os # F401")?;
let snapshot = format!("output_format_show_fixes_{output_format}");
assert_cmd_snapshot!(
snapshot,
fixture.command().args([
"check",
"--no-cache",
"--output-format",
output_format,
"--select",
"F401",
"--fix",
"--show-fixes",
"input.py",
])
);
Ok(())
}
#[test]
fn show_fixes_preview() -> Result<()> {
let fixture = CliTest::with_file("input.py", "import os # F401")?;
assert_cmd_snapshot!(
fixture.check_command().args([
"--select",
"F401",
"--fix",
"--show-fixes",
"--preview",
"input.py",
]),
@"
success: true
exit_code: 0
----- stdout -----
Fixed 1 error:
- input.py:
1 × unused-import (F401)
Found 1 error (1 fixed, 0 remaining).
----- stderr -----
"
);
Ok(())
}
#[test]
fn up045_nested_optional_flatten_all() {
let contents = "\
from typing import Optional
nested_optional: Optional[Optional[Optional[str]]] = None
";
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(STDIN_BASE_OPTIONS)
.args(["--select", "UP045", "--diff", "--target-version", "py312"])
.arg("-")
.pass_stdin(contents),
@"
success: false
exit_code: 1
----- stdout -----
@@ -1,2 +1,2 @@
from typing import Optional
-nested_optional: Optional[Optional[Optional[str]]] = None
+nested_optional: str | None = None
----- stderr -----
Would fix 1 error.
",
);
}
#[test]
fn show_fixes_in_full_output_with_preview_enabled() {
assert_cmd_snapshot!(
Command::new(get_cargo_bin(BIN_NAME))
.args(["check", "--no-cache", "--output-format", "full"])
.args(["--select", "F401"])
.arg("--preview")
.arg("-")
.pass_stdin("import math"),
@"
success: false
exit_code: 1
----- stdout -----
unused-import: [*] `math` imported but unused
--> -:1:8
|
1 | import math
| ^^^^
|
help: Remove unused import: `math`
|
- import math
|
Found 1 error.
[*] 1 fixable with the `--fix` option.
----- stderr -----
",
);
}
#[test]
fn rule_panic_mixed_results_concise() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("normal.py", "import os")?;
fixture.write_file("panic.py", "print('hello, world!')")?;
assert_cmd_snapshot!(
fixture.check_command()
.args(["--select", "RUF9", "--preview"])
.args(["normal.py", "panic.py"]),
@"
success: false
exit_code: 2
----- stdout -----
normal.py:1:1: stable-test-rule: Hey this is a stable test rule.
normal.py:1:1: stable-test-rule-safe-fix: [*] Hey this is a stable test rule with a safe fix.
normal.py:1:1: stable-test-rule-unsafe-fix: Hey this is a stable test rule with an unsafe fix.
normal.py:1:1: stable-test-rule-display-only-fix: Hey this is a stable test rule with a display only fix.
normal.py:1:1: preview-test-rule: Hey this is a preview test rule.
normal.py:1:1: redirected-to-test-rule: Hey this is a test rule that was redirected from another.
panic.py: panic: Panicked at <location> when checking `[TMP]/panic.py`: `This is a fake panic for testing.`
Found 7 errors.
[*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option).
----- stderr -----
error: Panic during linting indicates a bug in Ruff. If you could open an issue at:
https://github.com/astral-sh/ruff/issues/new?title=%5BLinter%20panic%5D
...with the relevant file contents, the `pyproject.toml` settings, and the stack trace above, we'd be very appreciative!
");
Ok(())
}
#[test]
fn rule_panic_mixed_results_full() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("normal.py", "import os")?;
fixture.write_file("panic.py", "print('hello, world!')")?;
assert_cmd_snapshot!(
fixture.command()
.args(["check", "--select", "RUF9", "--preview", "--output-format=full", "--no-cache"])
.args(["normal.py", "panic.py"]),
@"
success: false
exit_code: 2
----- stdout -----
stable-test-rule: Hey this is a stable test rule.
--> normal.py:1:1
stable-test-rule-safe-fix: [*] Hey this is a stable test rule with a safe fix.
--> normal.py:1:1
|
1 + # fix from stable-test-rule-safe-fix
2 | import os
|
stable-test-rule-unsafe-fix: Hey this is a stable test rule with an unsafe fix.
--> normal.py:1:1
stable-test-rule-display-only-fix: Hey this is a stable test rule with a display only fix.
--> normal.py:1:1
preview-test-rule: Hey this is a preview test rule.
--> normal.py:1:1
redirected-to-test-rule: Hey this is a test rule that was redirected from another.
--> normal.py:1:1
panic: Panicked at <location> when checking `[TMP]/panic.py`: `This is a fake panic for testing.`
--> panic.py:1:1
info: This indicates a bug in Ruff.
info: If you could open an issue at https://github.com/astral-sh/ruff/issues/new?title=%5Bpanic%5D, we'd be very appreciative!
info: run with `RUST_BACKTRACE=1` environment variable to show the full backtrace information
Found 7 errors.
[*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option).
----- stderr -----
error: Panic during linting indicates a bug in Ruff. If you could open an issue at:
https://github.com/astral-sh/ruff/issues/new?title=%5BLinter%20panic%5D
...with the relevant file contents, the `pyproject.toml` settings, and the stack trace above, we'd be very appreciative!
");
Ok(())
}
#[test]
fn supported_file_extensions() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("src/thing.txt", "hello world\n")?;
fixture.write_file("src/thing.py", "import os\nprint('hello world')\n")?;
fixture.write_file("src/thing.pyi", "import os\nclass foo:\n ...\n")?;
fixture.write_file("src/thing.pyw", "import os\nprint('hello world')\n")?;
fixture.write_file(
"src/thing.pyx",
"import os\ncdef int add(int a, int b):\n return a + b\n",
)?;
fixture.write_file(
"src/thing.ipynb",
r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "ad6f36d9-4b7d-4562-8d00-f15a0f1fbb6d",
"metadata": {},
"outputs": [],
"source": [
"import os"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#,
)?;
assert_cmd_snapshot!(
fixture.check_command()
.args(["--select", "F401"])
.arg("src"),
@"
success: false
exit_code: 1
----- stdout -----
src/thing.ipynb:cell 1:1:8: F401 [*] `os` imported but unused
src/thing.py:1:8: F401 [*] `os` imported but unused
src/thing.pyi:1:8: F401 [*] `os` imported but unused
Found 3 errors.
[*] 3 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn supported_file_extensions_preview_enabled() -> Result<()> {
let fixture = CliTest::new()?;
fixture.write_file("src/thing.txt", "hello world\n")?;
fixture.write_file("src/thing.py", "import os\nprint('hello world')\n")?;
fixture.write_file("src/thing.pyi", "import os\nclass foo:\n ...\n")?;
fixture.write_file("src/thing.pyw", "import os\nprint('hello world')\n")?;
fixture.write_file(
"src/thing.pyx",
"import os\ncdef int add(int a, int b):\n return a + b\n",
)?;
fixture.write_file(
"src/thing.ipynb",
r#"
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "ad6f36d9-4b7d-4562-8d00-f15a0f1fbb6d",
"metadata": {},
"outputs": [],
"source": [
"import os"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
"#,
)?;
assert_cmd_snapshot!(
fixture.check_command()
.args(["--select", "F401", "--preview"])
.arg("src"),
@"
success: false
exit_code: 1
----- stdout -----
src/thing.ipynb:cell 1:1:8: unused-import: [*] `os` imported but unused
src/thing.py:1:8: unused-import: [*] `os` imported but unused
src/thing.pyi:1:8: unused-import: [*] `os` imported but unused
src/thing.pyw:1:8: unused-import: [*] `os` imported but unused
Found 4 errors.
[*] 4 fixable with the `--fix` option.
----- stderr -----
");
Ok(())
}
#[test]
fn preview_default_rules() -> Result<()> {
let test = CliTest::with_settings(|_path, mut settings| {
settings.add_filter(r"(?s).*(linter\.rules\.enabled[^]]+]).*", "$1");
settings
})?;
test.write_file("try.py", "1")?;
assert_cmd_snapshot!(
test.check_command().args(["--preview", "--show-settings"]),
@"
linter.rules.enabled = [
sys-version-slice3 (YTT101),
sys-version2 (YTT102),
sys-version-cmp-str3 (YTT103),
sys-version-info0-eq3 (YTT201),
six-py3 (YTT202),
sys-version-info1-cmp-int (YTT203),
sys-version-info-minor-cmp-int (YTT204),
sys-version0 (YTT301),
sys-version-cmp-str10 (YTT302),
sys-version-slice1 (YTT303),
cancel-scope-no-checkpoint (ASYNC100),
trio-sync-call (ASYNC105),
async-zero-sleep (ASYNC115),
long-sleep-not-forever (ASYNC116),
blocking-http-call-in-async-function (ASYNC210),
create-subprocess-in-async-function (ASYNC220),
run-process-in-async-function (ASYNC221),
wait-for-process-in-async-function (ASYNC222),
blocking-open-call-in-async-function (ASYNC230),
blocking-sleep-in-async-function (ASYNC251),
exec-builtin (S102),
try-except-pass (S110),
try-except-continue (S112),
blind-except (BLE001),
unary-prefix-increment-decrement (B002),
assignment-to-os-environ (B003),
unreliable-callable-check (B004),
strip-with-multi-characters (B005),
mutable-argument-default (B006),
function-call-in-default-argument (B008),
get-attr-with-constant (B009),
set-attr-with-constant (B010),
jump-statement-in-finally (B012),
redundant-tuple-in-exception-handler (B013),
duplicate-handler-exception (B014),
useless-comparison (B015),
raise-literal (B016),
assert-raises-exception (B017),
useless-expression (B018),
cached-instance-method (B019),
loop-variable-overrides-iterator (B020),
f-string-docstring (B021),
useless-contextlib-suppress (B022),
function-uses-loop-variable (B023),
duplicate-try-block-exception (B025),
star-arg-unpacking-after-keyword-arg (B026),
except-with-empty-tuple (B029),
except-with-non-exception-classes (B030),
reuse-of-groupby-generator (B031),
unintentional-type-annotation (B032),
duplicate-value (B033),
static-key-dict-comprehension (B035),
mutable-contextvar-default (B039),
unnecessary-generator-list (C400),
unnecessary-generator-set (C401),
unnecessary-generator-dict (C402),
unnecessary-list-comprehension-set (C403),
unnecessary-list-comprehension-dict (C404),
unnecessary-literal-set (C405),
unnecessary-literal-dict (C406),
unnecessary-collection-call (C408),
unnecessary-literal-within-tuple-call (C409),
unnecessary-literal-within-list-call (C410),
unnecessary-list-call (C411),
unnecessary-call-around-sorted (C413),
unnecessary-double-cast-or-process (C414),
unnecessary-subscript-reversal (C415),
unnecessary-map (C417),
unnecessary-literal-within-dict-call (C418),
unnecessary-comprehension-in-call (C419),
call-datetime-without-tzinfo (DTZ001),
call-datetime-today (DTZ002),
call-datetime-utcnow (DTZ003),
call-datetime-utcfromtimestamp (DTZ004),
call-datetime-now-without-tzinfo (DTZ005),
call-datetime-fromtimestamp (DTZ006),
call-datetime-strptime-without-zone (DTZ007),
call-date-today (DTZ011),
call-date-fromtimestamp (DTZ012),
datetime-min-max (DTZ901),
debugger (T100),
shebang-not-executable (EXE001),
shebang-missing-executable-file (EXE002),
shebang-leading-whitespace (EXE004),
shebang-not-first-line (EXE005),
future-rewritable-type-annotation (FA100),
future-required-type-annotation (FA102),
f-string-in-get-text-func-call (INT001),
format-in-get-text-func-call (INT002),
printf-in-get-text-func-call (INT003),
direct-logger-instantiation (LOG001),
invalid-get-logger-argument (LOG002),
undocumented-warn (LOG009),
exc-info-outside-except-handler (LOG014),
root-logger-call (LOG015),
logging-warn (G010),
logging-extra-attr-clash (G101),
logging-exc-info (G201),
logging-redundant-exc-info (G202),
unnecessary-placeholder (PIE790),
duplicate-class-field-definition (PIE794),
non-unique-enums (PIE796),
unnecessary-spread (PIE800),
unnecessary-dict-kwargs (PIE804),
reimplemented-container-builtin (PIE807),
unnecessary-range-start (PIE808),
multiple-starts-ends-with (PIE810),
unprefixed-type-param (PYI001),
complex-if-statement-in-stub (PYI002),
unrecognized-version-info-check (PYI003),
patch-version-comparison (PYI004),
wrong-tuple-length-version-comparison (PYI005),
bad-version-info-comparison (PYI006),
unrecognized-platform-check (PYI007),
unrecognized-platform-name (PYI008),
pass-statement-stub-body (PYI009),
non-empty-stub-body (PYI010),
pass-in-class-body (PYI012),
ellipsis-in-non-empty-class-body (PYI013),
assignment-default-in-stub (PYI015),
duplicate-union-member (PYI016),
complex-assignment-in-stub (PYI017),
unused-private-type-var (PYI018),
custom-type-var-for-self (PYI019),
quoted-annotation-in-stub (PYI020),
unaliased-collections-abc-set-import (PYI025),
type-alias-without-annotation (PYI026),
str-or-repr-defined-in-stub (PYI029),
unnecessary-literal-union (PYI030),
any-eq-ne-annotation (PYI032),
legacy-type-comment (PYI033),
non-self-return-type (PYI034),
unassigned-special-variable-in-stub (PYI035),
bad-exit-annotation (PYI036),
redundant-numeric-union (PYI041),
snake-case-type-alias (PYI042),
t-suffixed-type-alias (PYI043),
future-annotations-in-stub (PYI044),
iter-method-return-iterable (PYI045),
unused-private-protocol (PYI046),
unused-private-type-alias (PYI047),
stub-body-multiple-statements (PYI048),
unused-private-typed-dict (PYI049),
no-return-argument-annotation-in-stub (PYI050),
unannotated-assignment-in-stub (PYI052),
unnecessary-type-union (PYI055),
byte-string-usage (PYI057),
generator-return-from-iter-method (PYI058),
generic-not-last-base-class (PYI059),
redundant-none-literal (PYI061),
duplicate-literal-member (PYI062),
pep484-style-positional-only-parameter (PYI063),
redundant-final-literal (PYI064),
bad-version-info-order (PYI066),
pytest-raises-without-exception (PT010),
pytest-duplicate-parametrize-test-cases (PT014),
pytest-deprecated-yield-fixture (PT020),
pytest-erroneous-use-fixtures-on-fixture (PT025),
pytest-use-fixtures-without-parameters (PT026),
pytest-warns-with-multiple-statements (PT031),
unnecessary-return-none (RET501),
duplicate-isinstance-call (SIM101),
collapsible-if (SIM102),
needless-bool (SIM103),
return-in-try-except-finally (SIM107),
enumerate-for-loop (SIM113),
if-with-same-arms (SIM114),
open-file-with-context-handler (SIM115),
multiple-with-statements (SIM117),
in-dict-keys (SIM118),
negate-equal-op (SIM201),
negate-not-equal-op (SIM202),
double-negation (SIM208),
if-expr-with-true-false (SIM210),
if-expr-with-false-true (SIM211),
expr-and-not-expr (SIM220),
expr-or-not-expr (SIM221),
expr-or-true (SIM222),
expr-and-false (SIM223),
if-else-block-instead-of-dict-get (SIM401),
split-static-string (SIM905),
zip-dict-keys-and-values (SIM911),
runtime-import-in-type-checking-block (TC004),
empty-type-checking-block (TC005),
unquoted-type-alias (TC007),
runtime-string-union (TC010),
py-path (PTH124),
invalid-pathlib-with-suffix (PTH210),
static-join-to-f-string (FLY002),
unsorted-imports (I001),
invalid-module-name (N999),
unnecessary-list-cast (PERF101),
incorrect-dict-iterator (PERF102),
manual-list-copy (PERF402),
bare-except (E722),
io-error (E902),
invalid-escape-sequence (W605),
empty-docstring (D419),
unused-import (F401),
import-shadowed-by-loop-var (F402),
late-future-import (F404),
future-feature-not-defined (F407),
percent-format-invalid-format (F501),
percent-format-expected-mapping (F502),
percent-format-expected-sequence (F503),
percent-format-extra-named-arguments (F504),
percent-format-missing-argument (F505),
percent-format-mixed-positional-and-named (F506),
percent-format-positional-count-mismatch (F507),
percent-format-star-requires-sequence (F508),
percent-format-unsupported-format-character (F509),
string-dot-format-invalid-format (F521),
string-dot-format-extra-named-arguments (F522),
string-dot-format-extra-positional-arguments (F523),
string-dot-format-missing-arguments (F524),
string-dot-format-mixing-automatic (F525),
f-string-missing-placeholders (F541),
multi-value-repeated-key-literal (F601),
multi-value-repeated-key-variable (F602),
expressions-in-star-assignment (F621),
multiple-starred-expressions (F622),
assert-tuple (F631),
is-literal (F632),
invalid-print-syntax (F633),
if-tuple (F634),
break-outside-loop (F701),
continue-outside-loop (F702),
yield-outside-function (F704),
return-outside-function (F706),
default-except-not-last (F707),
redefined-while-unused (F811),
undefined-name (F821),
undefined-export (F822),
undefined-local (F823),
unused-variable (F841),
unused-annotation (F842),
raise-not-implemented (F901),
invalid-mock-access (PGH005),
type-name-incorrect-variance (PLC0105),
type-bivariance (PLC0131),
type-param-name-mismatch (PLC0132),
single-string-slots (PLC0205),
dict-index-missing-items (PLC0206),
iteration-over-set (PLC0208),
useless-import-alias (PLC0414),
unnecessary-direct-lambda-call (PLC3002),
yield-in-init (PLE0100),
return-in-init (PLE0101),
nonlocal-and-global (PLE0115),
continue-in-finally (PLE0116),
nonlocal-without-binding (PLE0117),
load-before-global-declaration (PLE0118),
invalid-length-return-type (PLE0303),
invalid-index-return-type (PLE0305),
invalid-str-return-type (PLE0307),
invalid-bytes-return-type (PLE0308),
invalid-hash-return-type (PLE0309),
invalid-all-object (PLE0604),
invalid-all-format (PLE0605),
potential-index-error (PLE0643),
misplaced-bare-raise (PLE0704),
repeated-keyword-argument (PLE1132),
await-outside-async (PLE1142),
logging-too-many-args (PLE1205),
logging-too-few-args (PLE1206),
bad-string-format-character (PLE1300),
bad-string-format-type (PLE1307),
bad-str-strip-call (PLE1310),
invalid-envvar-value (PLE1507),
singledispatch-method (PLE1519),
singledispatchmethod-function (PLE1520),
yield-from-in-async-function (PLE1700),
bidirectional-unicode (PLE2502),
invalid-character-backspace (PLE2510),
invalid-character-sub (PLE2512),
invalid-character-esc (PLE2513),
invalid-character-nul (PLE2514),
invalid-character-zero-width-space (PLE2515),
comparison-with-itself (PLR0124),
comparison-of-constant (PLR0133),
property-with-parameters (PLR0206),
manual-from-import (PLR0402),
redefined-argument-from-local (PLR1704),
useless-return (PLR1711),
boolean-chained-comparison (PLR1716),
sys-exit-alias (PLR1722),
if-stmt-min-max (PLR1730),
unnecessary-dict-index-lookup (PLR1733),
unnecessary-list-index-lookup (PLR1736),
empty-comment (PLR2044),
useless-else-on-loop (PLW0120),
self-assigning-variable (PLW0127),
redeclared-assigned-name (PLW0128),
assert-on-string-literal (PLW0129),
named-expr-without-context (PLW0131),
useless-exception-statement (PLW0133),
nan-comparison (PLW0177),
bad-staticmethod-argument (PLW0211),
super-without-brackets (PLW0245),
import-self (PLW0406),
global-variable-not-assigned (PLW0602),
global-at-module-level (PLW0604),
self-or-cls-assignment (PLW0642),
binary-op-exception (PLW0711),
bad-open-mode (PLW1501),
shallow-copy-environ (PLW1507),
invalid-envvar-default (PLW1508),
subprocess-popen-preexec-fn (PLW1509),
subprocess-run-without-check (PLW1510),
useless-with-lock (PLW2101),
useless-metaclass-type (UP001),
type-of-primitive (UP003),
useless-object-inheritance (UP004),
deprecated-unittest-alias (UP005),
non-pep585-annotation (UP006),
non-pep604-annotation-union (UP007),
super-call-with-parameters (UP008),
utf8-encoding-declaration (UP009),
unnecessary-future-import (UP010),
lru-cache-without-parameters (UP011),
unnecessary-encode-utf8 (UP012),
convert-named-tuple-functional-to-class (UP014),
datetime-timezone-utc (UP017),
native-literals (UP018),
typing-text-str-alias (UP019),
open-alias (UP020),
replace-universal-newlines (UP021),
replace-stdout-stderr (UP022),
deprecated-c-element-tree (UP023),
os-error-alias (UP024),
unicode-kind-prefix (UP025),
deprecated-mock-import (UP026),
yield-in-for-loop (UP028),
unnecessary-builtin-import (UP029),
format-literals (UP030),
printf-string-formatting (UP031),
f-string (UP032),
lru-cache-with-maxsize-none (UP033),
extraneous-parentheses (UP034),
deprecated-import (UP035),
outdated-version-block (UP036),
quoted-annotation (UP037),
unnecessary-class-parentheses (UP039),
non-pep695-type-alias (UP040),
timeout-error-alias (UP041),
unnecessary-default-type-args (UP043),
non-pep646-unpack (UP044),
non-pep604-annotation-optional (UP045),
non-pep695-generic-class (UP046),
non-pep695-generic-function (UP047),
private-type-parameter (UP049),
useless-class-metaclass-type (UP050),
print-empty-string (FURB105),
for-loop-writes (FURB122),
readlines-in-for (FURB129),
check-and-remove-from-set (FURB132),
if-expr-min-max (FURB136),
verbose-decimal-constructor (FURB157),
bit-count (FURB161),
fromisoformat-replace-z (FURB162),
redundant-log-base (FURB163),
int-on-sliced-str (FURB166),
regex-flag-alias (FURB167),
isinstance-type-none (FURB168),
type-none-comparison (FURB169),
implicit-cwd (FURB177),
hashlib-digest-hex (FURB181),
slice-to-remove-prefix-or-suffix (FURB188),
zip-instead-of-pairwise (RUF007),
mutable-dataclass-default (RUF008),
function-call-in-dataclass-default-argument (RUF009),
explicit-f-string-type-conversion (RUF010),
mutable-class-default (RUF012),
implicit-optional (RUF013),
unnecessary-iterable-allocation-for-first-element (RUF015),
invalid-index-type (RUF016),
quadratic-list-summation (RUF017),
assignment-in-assert (RUF018),
unnecessary-key-check (RUF019),
never-union (RUF020),
unsorted-dunder-all (RUF022),
unsorted-dunder-slots (RUF023),
mutable-fromkeys-value (RUF024),
default-factory-kwarg (RUF026),
invalid-formatter-suppression-comment (RUF028),
assert-with-print-message (RUF030),
decimal-from-float-literal (RUF032),
post-init-default (RUF033),
useless-if-else (RUF034),
invalid-assert-message-literal-argument (RUF040),
unnecessary-nested-literal (RUF041),
unnecessary-cast-to-int (RUF046),
map-int-version-parsing (RUF048),
dataclass-enum (RUF049),
if-key-in-dict-del (RUF051),
class-with-mixed-type-vars (RUF053),
unnecessary-round (RUF057),
starmap-zip (RUF058),
unused-unpacked-variable (RUF059),
unused-noqa (RUF100),
redirected-noqa (RUF101),
invalid-pyproject-toml (RUF200),
raise-vanilla-class (TRY002),
type-check-without-type-error (TRY004),
verbose-raise (TRY201),
useless-try-except (TRY203),
verbose-log-message (TRY401),
]
",
);
Ok(())
}