Skip to content

Commit 5f1a12e

Browse files
authored
Display output severity in preview (astral-sh#23845)
This is just the "aesthetic" part of implementing `warning` severities. Configuration changes are in astral-sh#23846 It's expected that the `preview` ecosystem check times out here.
1 parent 04d8a66 commit 5f1a12e

26 files changed

Lines changed: 253 additions & 177 deletions

crates/ruff/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -359,10 +359,9 @@ pub fn check(args: CheckCommand, global_options: GlobalConfigArgs) -> Result<Exi
359359
);
360360

361361
// the settings should already be combined with the CLI overrides at this point
362-
// TODO(jane): let's make this `PreviewMode`
363362
// TODO: this should reference the global preview mode once https://github.com/astral-sh/ruff/issues/8232
364363
// is resolved.
365-
let preview = pyproject_config.settings.linter.preview.is_enabled();
364+
let preview = pyproject_config.settings.linter.preview;
366365

367366
if cli.watch {
368367
// Configure the file watcher.

crates/ruff/src/printer.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ use ruff_linter::fs::relativize_path;
1414
use ruff_linter::logging::LogLevel;
1515
use ruff_linter::message::{EmitterContext, render_diagnostics};
1616
use ruff_linter::notify_user;
17+
use ruff_linter::preview::is_warning_severity_enabled;
1718
use ruff_linter::settings::flags::{self};
18-
use ruff_linter::settings::types::{OutputFormat, UnsafeFixes};
19+
use ruff_linter::settings::types::{OutputFormat, PreviewMode, UnsafeFixes};
1920

2021
use crate::diagnostics::{Diagnostics, FixMap};
2122

@@ -208,7 +209,7 @@ impl Printer {
208209
&self,
209210
diagnostics: &Diagnostics,
210211
writer: &mut dyn Write,
211-
preview: bool,
212+
preview: PreviewMode,
212213
) -> Result<()> {
213214
if matches!(self.log_level, LogLevel::Silent) {
214215
return Ok(());
@@ -235,12 +236,12 @@ impl Printer {
235236
let fixables = FixableStatistics::try_from(diagnostics, self.unsafe_fixes);
236237

237238
let config = DisplayDiagnosticConfig::new("ruff")
238-
.preview(preview)
239-
.hide_severity(true)
239+
.preview(preview.is_enabled())
240+
.hide_severity(!is_warning_severity_enabled(preview))
240241
.color(!cfg!(test) && colored::control::SHOULD_COLORIZE.should_colorize())
241242
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
242243
.with_fix_applicability(self.unsafe_fixes.required_applicability())
243-
.show_fix_diff(preview);
244+
.show_fix_diff(preview.is_enabled());
244245

245246
render_diagnostics(writer, self.format, config, &context, &diagnostics.inner)?;
246247

@@ -382,7 +383,7 @@ impl Printer {
382383
&self,
383384
writer: &mut dyn Write,
384385
diagnostics: &Diagnostics,
385-
preview: bool,
386+
preview: PreviewMode,
386387
) -> Result<()> {
387388
if matches!(self.log_level, LogLevel::Silent) {
388389
return Ok(());
@@ -409,12 +410,12 @@ impl Printer {
409410

410411
let context = EmitterContext::new(&diagnostics.notebook_indexes);
411412
let config = DisplayDiagnosticConfig::new("ruff")
412-
.preview(preview)
413-
.hide_severity(true)
413+
.preview(preview.is_enabled())
414+
.hide_severity(!is_warning_severity_enabled(preview))
414415
.color(!cfg!(test) && colored::control::SHOULD_COLORIZE.should_colorize())
415416
.with_show_fix_status(show_fix_status(self.fix_mode, fixables.as_ref()))
416417
.with_fix_applicability(self.unsafe_fixes.required_applicability())
417-
.show_fix_diff(preview);
418+
.show_fix_diff(preview.is_enabled());
418419
render_diagnostics(writer, self.format, config, &context, &diagnostics.inner)?;
419420
}
420421
writer.flush()?;

crates/ruff/tests/cli/lint.rs

Lines changed: 56 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -2727,15 +2727,15 @@ fn nested_implicit_namespace_package() -> Result<()> {
27272727
.arg("--select")
27282728
.arg("INP")
27292729
.arg("--preview")
2730-
, @"
2730+
, @r###"
27312731
success: false
27322732
exit_code: 1
27332733
----- stdout -----
2734-
foo/bar/baz/__init__.py:1:1: INP001 File `foo/bar/baz/__init__.py` declares a package, but is nested under an implicit namespace package. Add an `__init__.py` to `foo/bar`.
2734+
foo/bar/baz/__init__.py:1:1: error[INP001] File `foo/bar/baz/__init__.py` declares a package, but is nested under an implicit namespace package. Add an `__init__.py` to `foo/bar`.
27352735
Found 1 error.
27362736
27372737
----- stderr -----
2738-
");
2738+
"###);
27392739

27402740
Ok(())
27412741
}
@@ -3112,7 +3112,7 @@ class Foo[_T, __T]:
31123112
pass
31133113
"#
31143114
),
3115-
@"
3115+
@r###"
31163116
success: false
31173117
exit_code: 1
31183118
----- stdout -----
@@ -3121,9 +3121,9 @@ class Foo[_T, __T]:
31213121
pass
31223122
31233123
----- stderr -----
3124-
test.py:2:14: UP049 Generic class uses private type parameters
3124+
test.py:2:14: error[UP049] Generic class uses private type parameters
31253125
Found 2 errors (1 fixed, 1 remaining).
3126-
"
3126+
"###
31273127
);
31283128
}
31293129

@@ -3263,16 +3263,16 @@ T = TypeVar("T")
32633263
class A(Generic[T]):
32643264
var: T
32653265
"#),
3266-
@"
3266+
@r###"
32673267
success: false
32683268
exit_code: 1
32693269
----- stdout -----
3270-
test.py:6:9: UP046 Generic class `A` uses `Generic` subclass instead of type parameters
3270+
test.py:6:9: error[UP046] Generic class `A` uses `Generic` subclass instead of type parameters
32713271
Found 1 error.
32723272
No fixes available (1 hidden fix can be enabled with the `--unsafe-fixes` option).
32733273
32743274
----- stderr -----
3275-
"
3275+
"###
32763276
);
32773277

32783278
// with per-file-target-version, there should be no errors because the new generic syntax is
@@ -3405,15 +3405,15 @@ match 2:
34053405
print("it's one")
34063406
"#
34073407
),
3408-
@"
3408+
@r###"
34093409
success: false
34103410
exit_code: 1
34113411
----- stdout -----
3412-
test.py:2:1: invalid-syntax: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
3412+
test.py:2:1: error[invalid-syntax] Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
34133413
Found 1 error.
34143414
34153415
----- stderr -----
3416-
"
3416+
"###
34173417
);
34183418
}
34193419

@@ -3431,27 +3431,27 @@ fn cache_syntax_errors() -> Result<()> {
34313431

34323432
assert_cmd_snapshot!(
34333433
cmd,
3434-
@"
3434+
@r###"
34353435
success: false
34363436
exit_code: 1
34373437
----- stdout -----
3438-
main.py:1:1: invalid-syntax: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
3438+
main.py:1:1: error[invalid-syntax] Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
34393439
34403440
----- stderr -----
3441-
"
3441+
"###
34423442
);
34433443

34443444
// this should *not* be cached, like normal parse errors
34453445
assert_cmd_snapshot!(
34463446
cmd,
3447-
@"
3447+
@r###"
34483448
success: false
34493449
exit_code: 1
34503450
----- stdout -----
3451-
main.py:1:1: invalid-syntax: Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
3451+
main.py:1:1: error[invalid-syntax] Cannot use `match` statement on Python 3.9 (syntax was added in Python 3.10)
34523452
34533453
----- stderr -----
3454-
"
3454+
"###
34553455
);
34563456

34573457
Ok(())
@@ -3552,29 +3552,29 @@ fn semantic_syntax_errors() -> Result<()> {
35523552

35533553
assert_cmd_snapshot!(
35543554
cmd,
3555-
@"
3555+
@r###"
35563556
success: false
35573557
exit_code: 1
35583558
----- stdout -----
3559-
main.py:1:3: invalid-syntax: assignment expression cannot rebind comprehension variable
3560-
main.py:1:20: F821 Undefined name `foo`
3559+
main.py:1:3: error[invalid-syntax] assignment expression cannot rebind comprehension variable
3560+
main.py:1:20: error[F821] Undefined name `foo`
35613561
35623562
----- stderr -----
3563-
"
3563+
"###
35643564
);
35653565

35663566
// this should *not* be cached, like normal parse errors
35673567
assert_cmd_snapshot!(
35683568
cmd,
3569-
@"
3569+
@r###"
35703570
success: false
35713571
exit_code: 1
35723572
----- stdout -----
3573-
main.py:1:3: invalid-syntax: assignment expression cannot rebind comprehension variable
3574-
main.py:1:20: F821 Undefined name `foo`
3573+
main.py:1:3: error[invalid-syntax] assignment expression cannot rebind comprehension variable
3574+
main.py:1:20: error[F821] Undefined name `foo`
35753575
35763576
----- stderr -----
3577-
"
3577+
"###
35783578
);
35793579

35803580
// ensure semantic errors are caught even without AST-based rules selected
@@ -3584,15 +3584,15 @@ fn semantic_syntax_errors() -> Result<()> {
35843584
.arg("--preview")
35853585
.arg("-")
35863586
.pass_stdin(contents),
3587-
@"
3587+
@r###"
35883588
success: false
35893589
exit_code: 1
35903590
----- stdout -----
3591-
-:1:3: invalid-syntax: assignment expression cannot rebind comprehension variable
3591+
-:1:3: error[invalid-syntax] assignment expression cannot rebind comprehension variable
35923592
Found 1 error.
35933593
35943594
----- stderr -----
3595-
"
3595+
"###
35963596
);
35973597

35983598
Ok(())
@@ -3741,11 +3741,11 @@ fn show_fixes_in_full_output_with_preview_enabled() {
37413741
.arg("--preview")
37423742
.arg("-")
37433743
.pass_stdin("import math"),
3744-
@"
3744+
@r###"
37453745
success: false
37463746
exit_code: 1
37473747
----- stdout -----
3748-
F401 [*] `math` imported but unused
3748+
error[F401][*]: `math` imported but unused
37493749
--> -:1:8
37503750
|
37513751
1 | import math
@@ -3758,7 +3758,7 @@ fn show_fixes_in_full_output_with_preview_enabled() {
37583758
[*] 1 fixable with the `--fix` option.
37593759
37603760
----- stderr -----
3761-
",
3761+
"###,
37623762
);
37633763
}
37643764

@@ -3772,17 +3772,17 @@ fn rule_panic_mixed_results_concise() -> Result<()> {
37723772
fixture.check_command()
37733773
.args(["--select", "RUF9", "--preview"])
37743774
.args(["normal.py", "panic.py"]),
3775-
@"
3775+
@r###"
37763776
success: false
37773777
exit_code: 2
37783778
----- stdout -----
3779-
normal.py:1:1: RUF900 Hey this is a stable test rule.
3780-
normal.py:1:1: RUF901 [*] Hey this is a stable test rule with a safe fix.
3781-
normal.py:1:1: RUF902 Hey this is a stable test rule with an unsafe fix.
3782-
normal.py:1:1: RUF903 Hey this is a stable test rule with a display only fix.
3783-
normal.py:1:1: RUF911 Hey this is a preview test rule.
3784-
normal.py:1:1: RUF950 Hey this is a test rule that was redirected from another.
3785-
panic.py: panic: Panicked at <location> when checking `[TMP]/panic.py`: `This is a fake panic for testing.`
3779+
normal.py:1:1: error[RUF900] Hey this is a stable test rule.
3780+
normal.py:1:1: error[RUF901] [*] Hey this is a stable test rule with a safe fix.
3781+
normal.py:1:1: error[RUF902] Hey this is a stable test rule with an unsafe fix.
3782+
normal.py:1:1: error[RUF903] Hey this is a stable test rule with a display only fix.
3783+
normal.py:1:1: error[RUF911] Hey this is a preview test rule.
3784+
normal.py:1:1: error[RUF950] Hey this is a test rule that was redirected from another.
3785+
panic.py: fatal[panic] Panicked at <location> when checking `[TMP]/panic.py`: `This is a fake panic for testing.`
37863786
Found 7 errors.
37873787
[*] 1 fixable with the `--fix` option (1 hidden fix can be enabled with the `--unsafe-fixes` option).
37883788
@@ -3792,7 +3792,7 @@ fn rule_panic_mixed_results_concise() -> Result<()> {
37923792
https://github.com/astral-sh/ruff/issues/new?title=%5BLinter%20panic%5D
37933793
37943794
...with the relevant file contents, the `pyproject.toml` settings, and the stack trace above, we'd be very appreciative!
3795-
");
3795+
"###);
37963796

37973797
Ok(())
37983798
}
@@ -3807,31 +3807,31 @@ fn rule_panic_mixed_results_full() -> Result<()> {
38073807
fixture.command()
38083808
.args(["check", "--select", "RUF9", "--preview", "--output-format=full", "--no-cache"])
38093809
.args(["normal.py", "panic.py"]),
3810-
@"
3810+
@r###"
38113811
success: false
38123812
exit_code: 2
38133813
----- stdout -----
3814-
RUF900 Hey this is a stable test rule.
3814+
error[RUF900]: Hey this is a stable test rule.
38153815
--> normal.py:1:1
38163816
3817-
RUF901 [*] Hey this is a stable test rule with a safe fix.
3817+
error[RUF901][*]: Hey this is a stable test rule with a safe fix.
38183818
--> normal.py:1:1
38193819
1 + # fix from stable-test-rule-safe-fix
38203820
2 | import os
38213821
3822-
RUF902 Hey this is a stable test rule with an unsafe fix.
3822+
error[RUF902]: Hey this is a stable test rule with an unsafe fix.
38233823
--> normal.py:1:1
38243824
3825-
RUF903 Hey this is a stable test rule with a display only fix.
3825+
error[RUF903]: Hey this is a stable test rule with a display only fix.
38263826
--> normal.py:1:1
38273827
3828-
RUF911 Hey this is a preview test rule.
3828+
error[RUF911]: Hey this is a preview test rule.
38293829
--> normal.py:1:1
38303830
3831-
RUF950 Hey this is a test rule that was redirected from another.
3831+
error[RUF950]: Hey this is a test rule that was redirected from another.
38323832
--> normal.py:1:1
38333833
3834-
panic: Panicked at <location> when checking `[TMP]/panic.py`: `This is a fake panic for testing.`
3834+
error[panic]: Panicked at <location> when checking `[TMP]/panic.py`: `This is a fake panic for testing.`
38353835
--> panic.py:1:1
38363836
info: This indicates a bug in Ruff.
38373837
info: If you could open an issue at https://github.com/astral-sh/ruff/issues/new?title=%5Bpanic%5D, we'd be very appreciative!
@@ -3846,7 +3846,7 @@ fn rule_panic_mixed_results_full() -> Result<()> {
38463846
https://github.com/astral-sh/ruff/issues/new?title=%5BLinter%20panic%5D
38473847
38483848
...with the relevant file contents, the `pyproject.toml` settings, and the stack trace above, we'd be very appreciative!
3849-
");
3849+
"###);
38503850

38513851
Ok(())
38523852
}
@@ -3996,19 +3996,19 @@ fn supported_file_extensions_preview_enabled() -> Result<()> {
39963996
fixture.check_command()
39973997
.args(["--select", "F401", "--preview"])
39983998
.arg("src"),
3999-
@"
3999+
@r###"
40004000
success: false
40014001
exit_code: 1
40024002
----- stdout -----
4003-
src/thing.ipynb:cell 1:1:8: F401 [*] `os` imported but unused
4004-
src/thing.py:1:8: F401 [*] `os` imported but unused
4005-
src/thing.pyi:1:8: F401 [*] `os` imported but unused
4006-
src/thing.pyw:1:8: F401 [*] `os` imported but unused
4003+
src/thing.ipynb:cell 1:1:8: error[F401] [*] `os` imported but unused
4004+
src/thing.py:1:8: error[F401] [*] `os` imported but unused
4005+
src/thing.pyi:1:8: error[F401] [*] `os` imported but unused
4006+
src/thing.pyw:1:8: error[F401] [*] `os` imported but unused
40074007
Found 4 errors.
40084008
[*] 4 fixable with the `--fix` option.
40094009
40104010
----- stderr -----
4011-
");
4011+
"###);
40124012
Ok(())
40134013
}
40144014

crates/ruff/tests/cli/snapshots/cli__format__output_format_json-lines.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ info:
1414
success: false
1515
exit_code: 1
1616
----- stdout -----
17-
{"cell":null,"code":"unformatted","end_location":{"column":1,"row":2},"filename":"[TMP]/input.py","fix":{"applicability":"safe","edits":[{"content":"","end_location":{"column":1,"row":2},"location":{"column":1,"row":1}}],"message":null},"location":{"column":1,"row":1},"message":"File would be reformatted","noqa_row":null,"url":null}
17+
{"cell":null,"code":"unformatted","end_location":{"column":1,"row":2},"filename":"[TMP]/input.py","fix":{"applicability":"safe","edits":[{"content":"","end_location":{"column":1,"row":2},"location":{"column":1,"row":1}}],"message":null},"location":{"column":1,"row":1},"message":"File would be reformatted","noqa_row":null,"severity":"error","url":null}
1818

1919
----- stderr -----

crates/ruff/tests/cli/snapshots/cli__format__output_format_json.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ exit_code: 1
4646
},
4747
"message": "File would be reformatted",
4848
"noqa_row": null,
49+
"severity": "error",
4950
"url": null
5051
}
5152
]

0 commit comments

Comments
 (0)