[flake8-pyi] Extend fix to Python <= 3.9 for redundant-none-literal (PYI061) (#16044)

This PR extends the fix offered for [redundant-none-literal
(PYI061)](https://docs.astral.sh/ruff/rules/redundant-none-literal/#redundant-none-literal-pyi061)
to include Python versions <= 3.9 by using `typing.Optional` instead of
the operator `|`. We also offer the fix with `|` for any target version
on stub files.

Closes #15795
This commit is contained in:
Dylan 2025-02-09 09:58:53 -06:00 committed by GitHub
parent a46fbda948
commit f178ecc2d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 977 additions and 120 deletions

View file

@ -181,6 +181,8 @@ mod tests {
#[test_case(Rule::TypeAliasWithoutAnnotation, Path::new("PYI026.py"))]
#[test_case(Rule::TypeAliasWithoutAnnotation, Path::new("PYI026.pyi"))]
#[test_case(Rule::RedundantNoneLiteral, Path::new("PYI061.py"))]
#[test_case(Rule::RedundantNoneLiteral, Path::new("PYI061.pyi"))]
fn py38(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("py38_{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(

View file

@ -1,14 +1,18 @@
use anyhow::Result;
use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{
self as ast, Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator,
self as ast,
helpers::{pep_604_union, typing_optional},
name::Name,
Expr, ExprBinOp, ExprContext, ExprNoneLiteral, ExprSubscript, Operator,
};
use ruff_python_semantic::analyze::typing::{traverse_literal, traverse_union};
use ruff_text_size::{Ranged, TextRange};
use smallvec::SmallVec;
use crate::{checkers::ast::Checker, settings::types::PythonVersion};
use crate::{checkers::ast::Checker, importer::ImportRequest, settings::types::PythonVersion};
/// ## What it does
/// Checks for redundant `Literal[None]` annotations.
@ -36,16 +40,16 @@ use crate::{checkers::ast::Checker, settings::types::PythonVersion};
/// ## Fix safety and availability
/// This rule's fix is marked as safe unless the literal contains comments.
///
/// There is currently no fix available if there are other elements in the `Literal` slice aside
/// from `None` and [`target-version`] is set to Python 3.9 or lower, as the fix always uses the
/// `|` syntax to create unions rather than `typing.Union`, and the `|` syntax for unions was added
/// in Python 3.10.
/// There is currently no fix available when applying the fix would lead to
/// a `TypeError` from an expression of the form `None | None` or when we
/// are unable to import the symbol `typing.Optional` and the Python version
/// is 3.9 or below.
///
/// ## References
/// - [Typing documentation: Legal parameters for `Literal` at type check time](https://typing.readthedocs.io/en/latest/spec/literal.html#legal-parameters-for-literal-at-type-check-time)
#[derive(ViolationMetadata)]
pub(crate) struct RedundantNoneLiteral {
other_literal_elements_seen: bool,
union_kind: UnionKind,
}
impl Violation for RedundantNoneLiteral {
@ -53,18 +57,22 @@ impl Violation for RedundantNoneLiteral {
#[derive_message_formats]
fn message(&self) -> String {
if self.other_literal_elements_seen {
"`Literal[None, ...]` can be replaced with `Literal[...] | None`".to_string()
} else {
"`Literal[None]` can be replaced with `None`".to_string()
match self.union_kind {
UnionKind::NoUnion => "Use `None` rather than `Literal[None]`".to_string(),
UnionKind::TypingOptional => {
"Use `Optional[Literal[...]]` rather than `Literal[None, ...]` ".to_string()
}
UnionKind::BitOr => {
"Use `Literal[...] | None` rather than `Literal[None, ...]` ".to_string()
}
}
}
fn fix_title(&self) -> Option<String> {
Some(if self.other_literal_elements_seen {
"Replace with `Literal[...] | None`".to_string()
} else {
"Replace with `None`".to_string()
Some(match self.union_kind {
UnionKind::NoUnion => "Replace with `None`".to_string(),
UnionKind::TypingOptional => "Replace with `Optional[Literal[...]]`".to_string(),
UnionKind::BitOr => "Replace with `Literal[...] | None`".to_string(),
})
}
}
@ -102,31 +110,29 @@ pub(crate) fn redundant_none_literal<'a>(checker: &Checker, literal_expr: &'a Ex
return;
}
let other_literal_elements_seen = !literal_elements.is_empty();
let union_kind = if literal_elements.is_empty() {
UnionKind::NoUnion
} else if (checker.settings.target_version >= PythonVersion::Py310)
|| checker.source_type.is_stub()
{
UnionKind::BitOr
} else {
UnionKind::TypingOptional
};
// N.B. Applying the fix can leave an unused import to be fixed by the `unused-import` rule.
let fix =
create_fix_edit(checker, literal_expr, literal_subscript, literal_elements).map(|edit| {
Fix::applicable_edit(
edit,
if checker.comment_ranges().intersects(literal_expr.range()) {
Applicability::Unsafe
} else {
Applicability::Safe
},
for none_expr in none_exprs {
let mut diagnostic =
Diagnostic::new(RedundantNoneLiteral { union_kind }, none_expr.range());
diagnostic.try_set_optional_fix(|| {
create_fix(
checker,
literal_expr,
literal_subscript,
literal_elements.clone(),
union_kind,
)
});
for none_expr in none_exprs {
let mut diagnostic = Diagnostic::new(
RedundantNoneLiteral {
other_literal_elements_seen,
},
none_expr.range(),
);
if let Some(ref fix) = fix {
diagnostic.set_fix(fix.clone());
}
checker.report_diagnostic(diagnostic);
}
}
@ -140,12 +146,13 @@ pub(crate) fn redundant_none_literal<'a>(checker: &Checker, literal_expr: &'a Ex
/// See <https://github.com/astral-sh/ruff/issues/14567>.
///
/// [`typing.Union`]: https://docs.python.org/3/library/typing.html#typing.Union
fn create_fix_edit(
fn create_fix(
checker: &Checker,
literal_expr: &Expr,
literal_subscript: &Expr,
literal_elements: Vec<&Expr>,
) -> Option<Edit> {
union_kind: UnionKind,
) -> Result<Option<Fix>> {
let semantic = checker.semantic();
let enclosing_pep604_union = semantic
@ -185,44 +192,71 @@ fn create_fix_edit(
);
if !is_fixable {
return None;
return Ok(None);
}
}
if literal_elements.is_empty() {
return Some(Edit::range_replacement(
"None".to_string(),
literal_expr.range(),
));
let applicability = if checker.comment_ranges().intersects(literal_expr.range()) {
Applicability::Unsafe
} else {
Applicability::Safe
};
if matches!(union_kind, UnionKind::NoUnion) {
return Ok(Some(Fix::applicable_edit(
Edit::range_replacement("None".to_string(), literal_expr.range()),
applicability,
)));
}
if checker.settings.target_version < PythonVersion::Py310 {
return None;
}
let bin_or = Expr::BinOp(ExprBinOp {
let new_literal_expr = Expr::Subscript(ast::ExprSubscript {
value: Box::new(literal_subscript.clone()),
range: TextRange::default(),
left: Box::new(Expr::Subscript(ast::ExprSubscript {
value: Box::new(literal_subscript.clone()),
range: TextRange::default(),
ctx: ExprContext::Load,
slice: Box::new(if literal_elements.len() > 1 {
Expr::Tuple(ast::ExprTuple {
elts: literal_elements.into_iter().cloned().collect(),
range: TextRange::default(),
ctx: ExprContext::Load,
parenthesized: true,
})
} else {
literal_elements[0].clone()
}),
})),
op: ruff_python_ast::Operator::BitOr,
right: Box::new(Expr::NoneLiteral(ExprNoneLiteral {
range: TextRange::default(),
})),
ctx: ExprContext::Load,
slice: Box::new(if literal_elements.len() > 1 {
Expr::Tuple(ast::ExprTuple {
elts: literal_elements.into_iter().cloned().collect(),
range: TextRange::default(),
ctx: ExprContext::Load,
parenthesized: true,
})
} else {
literal_elements[0].clone()
}),
});
let content = checker.generator().expr(&bin_or);
Some(Edit::range_replacement(content, literal_expr.range()))
let fix = match union_kind {
UnionKind::TypingOptional => {
let (import_edit, bound_name) = checker.importer().get_or_import_symbol(
&ImportRequest::import_from("typing", "Optional"),
literal_expr.start(),
checker.semantic(),
)?;
let optional_expr = typing_optional(new_literal_expr, Name::from(bound_name));
let content = checker.generator().expr(&optional_expr);
let optional_edit = Edit::range_replacement(content, literal_expr.range());
Fix::applicable_edits(import_edit, [optional_edit], applicability)
}
UnionKind::BitOr => {
let none_expr = Expr::NoneLiteral(ExprNoneLiteral {
range: TextRange::default(),
});
let union_expr = pep_604_union(&[new_literal_expr, none_expr]);
let content = checker.generator().expr(&union_expr);
let union_edit = Edit::range_replacement(content, literal_expr.range());
Fix::applicable_edit(union_edit, applicability)
}
// We dealt with this case earlier to avoid allocating `lhs` and `rhs`
UnionKind::NoUnion => {
unreachable!()
}
};
Ok(Some(fix))
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum UnionKind {
NoUnion,
TypingOptional,
BitOr,
}

View file

@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
---
PYI061.py:4:25: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:4:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
4 | def func1(arg1: Literal[None]):
| ^^^^ PYI061
@ -19,7 +19,7 @@ PYI061.py:4:25: PYI061 [*] `Literal[None]` can be replaced with `None`
6 6 |
7 7 |
PYI061.py:8:25: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:8:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
8 | def func2(arg1: Literal[None] | int):
| ^^^^ PYI061
@ -37,7 +37,7 @@ PYI061.py:8:25: PYI061 [*] `Literal[None]` can be replaced with `None`
10 10 |
11 11 |
PYI061.py:12:24: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:12:24: PYI061 [*] Use `None` rather than `Literal[None]`
|
12 | def func3() -> Literal[None]:
| ^^^^ PYI061
@ -55,7 +55,7 @@ PYI061.py:12:24: PYI061 [*] `Literal[None]` can be replaced with `None`
14 14 |
15 15 |
PYI061.py:16:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.py:16:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
16 | def func4(arg1: Literal[int, None, float]):
| ^^^^ PYI061
@ -73,7 +73,7 @@ PYI061.py:16:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.
18 18 |
19 19 |
PYI061.py:20:25: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:20:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
20 | def func5(arg1: Literal[None, None]):
| ^^^^ PYI061
@ -91,7 +91,7 @@ PYI061.py:20:25: PYI061 [*] `Literal[None]` can be replaced with `None`
22 22 |
23 23 |
PYI061.py:20:31: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:20:31: PYI061 [*] Use `None` rather than `Literal[None]`
|
20 | def func5(arg1: Literal[None, None]):
| ^^^^ PYI061
@ -109,7 +109,7 @@ PYI061.py:20:31: PYI061 [*] `Literal[None]` can be replaced with `None`
22 22 |
23 23 |
PYI061.py:26:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.py:26:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
24 | def func6(arg1: Literal[
25 | "hello",
@ -134,7 +134,7 @@ PYI061.py:26:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[..
30 26 |
31 27 |
PYI061.py:33:5: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:33:5: PYI061 [*] Use `None` rather than `Literal[None]`
|
32 | def func7(arg1: Literal[
33 | None # Comment 1
@ -156,7 +156,7 @@ PYI061.py:33:5: PYI061 [*] `Literal[None]` can be replaced with `None`
36 34 |
37 35 |
PYI061.py:38:25: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:38:25: PYI061 Use `None` rather than `Literal[None]`
|
38 | def func8(arg1: Literal[None] | None):
| ^^^^ PYI061
@ -164,7 +164,7 @@ PYI061.py:38:25: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:42:31: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:42:31: PYI061 [*] Use `None` rather than `Literal[None]`
|
42 | def func9(arg1: Union[Literal[None], None]):
| ^^^^ PYI061
@ -182,7 +182,7 @@ PYI061.py:42:31: PYI061 [*] `Literal[None]` can be replaced with `None`
44 44 |
45 45 |
PYI061.py:52:9: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:52:9: PYI061 [*] Use `None` rather than `Literal[None]`
|
51 | # From flake8-pyi
52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None"
@ -201,7 +201,7 @@ PYI061.py:52:9: PYI061 [*] `Literal[None]` can be replaced with `None`
54 54 |
55 55 | ###
PYI061.py:53:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.py:53:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
51 | # From flake8-pyi
52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None"
@ -222,7 +222,7 @@ PYI061.py:53:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.
55 55 | ###
56 56 | # The following rules here are slightly subtle,
PYI061.py:62:9: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:62:9: PYI061 [*] Use `None` rather than `Literal[None]`
|
60 | # If Y061 and Y062 both apply, but all the duplicate members are None,
61 | # only emit Y061...
@ -242,7 +242,7 @@ PYI061.py:62:9: PYI061 [*] `Literal[None]` can be replaced with `None`
64 64 |
65 65 | # ... but if Y061 and Y062 both apply
PYI061.py:62:15: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:62:15: PYI061 [*] Use `None` rather than `Literal[None]`
|
60 | # If Y061 and Y062 both apply, but all the duplicate members are None,
61 | # only emit Y061...
@ -262,7 +262,7 @@ PYI061.py:62:15: PYI061 [*] `Literal[None]` can be replaced with `None`
64 64 |
65 65 | # ... but if Y061 and Y062 both apply
PYI061.py:63:12: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.py:63:12: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
61 | # only emit Y061...
62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
@ -283,7 +283,7 @@ PYI061.py:63:12: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.
65 65 | # ... but if Y061 and Y062 both apply
66 66 | # and there are no None members in the Literal[] slice,
PYI061.py:63:25: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.py:63:25: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
61 | # only emit Y061...
62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
@ -304,7 +304,7 @@ PYI061.py:63:25: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.
65 65 | # ... but if Y061 and Y062 both apply
66 66 | # and there are no None members in the Literal[] slice,
PYI061.py:68:9: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.py:68:9: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
66 | # and there are no None members in the Literal[] slice,
67 | # only emit Y062:
@ -323,7 +323,7 @@ PYI061.py:68:9: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[..
70 70 |
71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
PYI061.py:68:21: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.py:68:21: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
66 | # and there are no None members in the Literal[] slice,
67 | # only emit Y062:
@ -342,7 +342,7 @@ PYI061.py:68:21: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.
70 70 |
71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
PYI061.py:72:12: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:72:12: PYI061 Use `None` rather than `Literal[None]`
|
71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
72 | x: Literal[None] | None
@ -352,7 +352,7 @@ PYI061.py:72:12: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:73:19: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:73:19: PYI061 Use `None` rather than `Literal[None]`
|
71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
72 | x: Literal[None] | None
@ -362,7 +362,7 @@ PYI061.py:73:19: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:74:18: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.py:74:18: PYI061 [*] Use `None` rather than `Literal[None]`
|
72 | x: Literal[None] | None
73 | y: None | Literal[None]
@ -383,7 +383,7 @@ PYI061.py:74:18: PYI061 [*] `Literal[None]` can be replaced with `None`
76 76 | a: int | Literal[None] | None
77 77 | b: None | Literal[None] | None
PYI061.py:76:18: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:76:18: PYI061 Use `None` rather than `Literal[None]`
|
74 | z: Union[Literal[None], None]
75 |
@ -394,7 +394,7 @@ PYI061.py:76:18: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:77:19: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:77:19: PYI061 Use `None` rather than `Literal[None]`
|
76 | a: int | Literal[None] | None
77 | b: None | Literal[None] | None
@ -404,7 +404,7 @@ PYI061.py:77:19: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:78:20: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:78:20: PYI061 Use `None` rather than `Literal[None]`
|
76 | a: int | Literal[None] | None
77 | b: None | Literal[None] | None
@ -415,7 +415,7 @@ PYI061.py:78:20: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:79:20: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:79:20: PYI061 Use `None` rather than `Literal[None]`
|
77 | b: None | Literal[None] | None
78 | c: (None | Literal[None]) | None
@ -426,7 +426,7 @@ PYI061.py:79:20: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:80:28: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:80:28: PYI061 Use `None` rather than `Literal[None]`
|
78 | c: (None | Literal[None]) | None
79 | d: None | (Literal[None] | None)
@ -436,7 +436,7 @@ PYI061.py:80:28: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:81:12: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:81:12: PYI061 Use `None` rather than `Literal[None]`
|
79 | d: None | (Literal[None] | None)
80 | e: None | ((None | Literal[None]) | None) | None
@ -445,7 +445,7 @@ PYI061.py:81:12: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.py:81:28: PYI061 `Literal[None]` can be replaced with `None`
PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]`
|
79 | d: None | (Literal[None] | None)
80 | e: None | ((None | Literal[None]) | None) | None

View file

@ -1,7 +1,7 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
---
PYI061.pyi:4:25: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
4 | def func1(arg1: Literal[None]): ...
| ^^^^ PYI061
@ -18,7 +18,7 @@ PYI061.pyi:4:25: PYI061 [*] `Literal[None]` can be replaced with `None`
6 6 |
7 7 | def func2(arg1: Literal[None] | int): ...
PYI061.pyi:7:25: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:7:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
7 | def func2(arg1: Literal[None] | int): ...
| ^^^^ PYI061
@ -35,7 +35,7 @@ PYI061.pyi:7:25: PYI061 [*] `Literal[None]` can be replaced with `None`
9 9 |
10 10 | def func3() -> Literal[None]: ...
PYI061.pyi:10:24: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:10:24: PYI061 [*] Use `None` rather than `Literal[None]`
|
10 | def func3() -> Literal[None]: ...
| ^^^^ PYI061
@ -52,7 +52,7 @@ PYI061.pyi:10:24: PYI061 [*] `Literal[None]` can be replaced with `None`
12 12 |
13 13 | def func4(arg1: Literal[int, None, float]): ...
PYI061.pyi:13:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.pyi:13:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
13 | def func4(arg1: Literal[int, None, float]): ...
| ^^^^ PYI061
@ -69,7 +69,7 @@ PYI061.pyi:13:30: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[
15 15 |
16 16 | def func5(arg1: Literal[None, None]): ...
PYI061.pyi:16:25: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:16:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
16 | def func5(arg1: Literal[None, None]): ...
| ^^^^ PYI061
@ -86,7 +86,7 @@ PYI061.pyi:16:25: PYI061 [*] `Literal[None]` can be replaced with `None`
18 18 |
19 19 | def func6(arg1: Literal[
PYI061.pyi:16:31: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:16:31: PYI061 [*] Use `None` rather than `Literal[None]`
|
16 | def func5(arg1: Literal[None, None]): ...
| ^^^^ PYI061
@ -103,7 +103,7 @@ PYI061.pyi:16:31: PYI061 [*] `Literal[None]` can be replaced with `None`
18 18 |
19 19 | def func6(arg1: Literal[
PYI061.pyi:21:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.pyi:21:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
19 | def func6(arg1: Literal[
20 | "hello",
@ -128,7 +128,7 @@ PYI061.pyi:21:5: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[.
25 21 |
26 22 | def func7(arg1: Literal[
PYI061.pyi:27:5: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:27:5: PYI061 [*] Use `None` rather than `Literal[None]`
|
26 | def func7(arg1: Literal[
27 | None # Comment 1
@ -149,14 +149,14 @@ PYI061.pyi:27:5: PYI061 [*] `Literal[None]` can be replaced with `None`
30 28 |
31 29 | def func8(arg1: Literal[None] | None):...
PYI061.pyi:31:25: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:31:25: PYI061 Use `None` rather than `Literal[None]`
|
31 | def func8(arg1: Literal[None] | None):...
| ^^^^ PYI061
|
= help: Replace with `None`
PYI061.pyi:34:31: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:34:31: PYI061 [*] Use `None` rather than `Literal[None]`
|
34 | def func9(arg1: Union[Literal[None], None]): ...
| ^^^^ PYI061
@ -173,7 +173,7 @@ PYI061.pyi:34:31: PYI061 [*] `Literal[None]` can be replaced with `None`
36 36 |
37 37 | # OK
PYI061.pyi:42:9: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:42:9: PYI061 [*] Use `None` rather than `Literal[None]`
|
41 | # From flake8-pyi
42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None"
@ -192,7 +192,7 @@ PYI061.pyi:42:9: PYI061 [*] `Literal[None]` can be replaced with `None`
44 44 |
45 45 |
PYI061.pyi:43:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[...] | None`
PYI061.pyi:43:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
41 | # From flake8-pyi
42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None"
@ -211,7 +211,7 @@ PYI061.pyi:43:15: PYI061 [*] `Literal[None, ...]` can be replaced with `Literal[
45 45 |
46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
PYI061.pyi:47:12: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:47:12: PYI061 Use `None` rather than `Literal[None]`
|
46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
47 | x: Literal[None] | None
@ -221,7 +221,7 @@ PYI061.pyi:47:12: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.pyi:48:19: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:48:19: PYI061 Use `None` rather than `Literal[None]`
|
46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
47 | x: Literal[None] | None
@ -231,7 +231,7 @@ PYI061.pyi:48:19: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.pyi:49:18: PYI061 [*] `Literal[None]` can be replaced with `None`
PYI061.pyi:49:18: PYI061 [*] Use `None` rather than `Literal[None]`
|
47 | x: Literal[None] | None
48 | y: None | Literal[None]
@ -252,7 +252,7 @@ PYI061.pyi:49:18: PYI061 [*] `Literal[None]` can be replaced with `None`
51 51 | a: int | Literal[None] | None
52 52 | b: None | Literal[None] | None
PYI061.pyi:51:18: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:51:18: PYI061 Use `None` rather than `Literal[None]`
|
49 | z: Union[Literal[None], None]
50 |
@ -263,7 +263,7 @@ PYI061.pyi:51:18: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.pyi:52:19: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:52:19: PYI061 Use `None` rather than `Literal[None]`
|
51 | a: int | Literal[None] | None
52 | b: None | Literal[None] | None
@ -273,7 +273,7 @@ PYI061.pyi:52:19: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.pyi:53:20: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]`
|
51 | a: int | Literal[None] | None
52 | b: None | Literal[None] | None
@ -284,7 +284,7 @@ PYI061.pyi:53:20: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.pyi:54:20: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]`
|
52 | b: None | Literal[None] | None
53 | c: (None | Literal[None]) | None
@ -295,7 +295,7 @@ PYI061.pyi:54:20: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.pyi:55:28: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]`
|
53 | c: (None | Literal[None]) | None
54 | d: None | (Literal[None] | None)
@ -305,7 +305,7 @@ PYI061.pyi:55:28: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.pyi:56:12: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:56:12: PYI061 Use `None` rather than `Literal[None]`
|
54 | d: None | (Literal[None] | None)
55 | e: None | ((None | Literal[None]) | None) | None
@ -314,7 +314,7 @@ PYI061.pyi:56:12: PYI061 `Literal[None]` can be replaced with `None`
|
= help: Replace with `None`
PYI061.pyi:56:28: PYI061 `Literal[None]` can be replaced with `None`
PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]`
|
54 | d: None | (Literal[None] | None)
55 | e: None | ((None | Literal[None]) | None) | None

View file

@ -0,0 +1,497 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
---
PYI061.py:4:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
4 | def func1(arg1: Literal[None]):
| ^^^^ PYI061
5 | ...
|
= help: Replace with `None`
Safe fix
1 1 | from typing import Literal, Union
2 2 |
3 3 |
4 |-def func1(arg1: Literal[None]):
4 |+def func1(arg1: None):
5 5 | ...
6 6 |
7 7 |
PYI061.py:8:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
8 | def func2(arg1: Literal[None] | int):
| ^^^^ PYI061
9 | ...
|
= help: Replace with `None`
Safe fix
5 5 | ...
6 6 |
7 7 |
8 |-def func2(arg1: Literal[None] | int):
8 |+def func2(arg1: None | int):
9 9 | ...
10 10 |
11 11 |
PYI061.py:12:24: PYI061 [*] Use `None` rather than `Literal[None]`
|
12 | def func3() -> Literal[None]:
| ^^^^ PYI061
13 | ...
|
= help: Replace with `None`
Safe fix
9 9 | ...
10 10 |
11 11 |
12 |-def func3() -> Literal[None]:
12 |+def func3() -> None:
13 13 | ...
14 14 |
15 15 |
PYI061.py:16:30: PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
16 | def func4(arg1: Literal[int, None, float]):
| ^^^^ PYI061
17 | ...
|
= help: Replace with `Optional[Literal[...]]`
Safe fix
1 |-from typing import Literal, Union
1 |+from typing import Literal, Union, Optional
2 2 |
3 3 |
4 4 | def func1(arg1: Literal[None]):
--------------------------------------------------------------------------------
13 13 | ...
14 14 |
15 15 |
16 |-def func4(arg1: Literal[int, None, float]):
16 |+def func4(arg1: Optional[Literal[int, float]]):
17 17 | ...
18 18 |
19 19 |
PYI061.py:20:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
20 | def func5(arg1: Literal[None, None]):
| ^^^^ PYI061
21 | ...
|
= help: Replace with `None`
Safe fix
17 17 | ...
18 18 |
19 19 |
20 |-def func5(arg1: Literal[None, None]):
20 |+def func5(arg1: None):
21 21 | ...
22 22 |
23 23 |
PYI061.py:20:31: PYI061 [*] Use `None` rather than `Literal[None]`
|
20 | def func5(arg1: Literal[None, None]):
| ^^^^ PYI061
21 | ...
|
= help: Replace with `None`
Safe fix
17 17 | ...
18 18 |
19 19 |
20 |-def func5(arg1: Literal[None, None]):
20 |+def func5(arg1: None):
21 21 | ...
22 22 |
23 23 |
PYI061.py:26:5: PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
24 | def func6(arg1: Literal[
25 | "hello",
26 | None # Comment 1
| ^^^^ PYI061
27 | , "world"
28 | ]):
|
= help: Replace with `Optional[Literal[...]]`
Unsafe fix
1 |-from typing import Literal, Union
1 |+from typing import Literal, Union, Optional
2 2 |
3 3 |
4 4 | def func1(arg1: Literal[None]):
--------------------------------------------------------------------------------
21 21 | ...
22 22 |
23 23 |
24 |-def func6(arg1: Literal[
25 |- "hello",
26 |- None # Comment 1
27 |- , "world"
28 |- ]):
24 |+def func6(arg1: Optional[Literal["hello", "world"]]):
29 25 | ...
30 26 |
31 27 |
PYI061.py:33:5: PYI061 [*] Use `None` rather than `Literal[None]`
|
32 | def func7(arg1: Literal[
33 | None # Comment 1
| ^^^^ PYI061
34 | ]):
35 | ...
|
= help: Replace with `None`
Unsafe fix
29 29 | ...
30 30 |
31 31 |
32 |-def func7(arg1: Literal[
33 |- None # Comment 1
34 |- ]):
32 |+def func7(arg1: None):
35 33 | ...
36 34 |
37 35 |
PYI061.py:38:25: PYI061 Use `None` rather than `Literal[None]`
|
38 | def func8(arg1: Literal[None] | None):
| ^^^^ PYI061
39 | ...
|
= help: Replace with `None`
PYI061.py:42:31: PYI061 [*] Use `None` rather than `Literal[None]`
|
42 | def func9(arg1: Union[Literal[None], None]):
| ^^^^ PYI061
43 | ...
|
= help: Replace with `None`
Safe fix
39 39 | ...
40 40 |
41 41 |
42 |-def func9(arg1: Union[Literal[None], None]):
42 |+def func9(arg1: Union[None, None]):
43 43 | ...
44 44 |
45 45 |
PYI061.py:52:9: PYI061 [*] Use `None` rather than `Literal[None]`
|
51 | # From flake8-pyi
52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None"
| ^^^^ PYI061
53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
|
= help: Replace with `None`
Safe fix
49 49 |
50 50 |
51 51 | # From flake8-pyi
52 |-Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None"
52 |+None # Y061 None inside "Literal[]" expression. Replace with "None"
53 53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
54 54 |
55 55 | ###
PYI061.py:53:15: PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
51 | # From flake8-pyi
52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None"
53 | Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
| ^^^^ PYI061
54 |
55 | ###
|
= help: Replace with `Optional[Literal[...]]`
Safe fix
1 |-from typing import Literal, Union
1 |+from typing import Literal, Union, Optional
2 2 |
3 3 |
4 4 | def func1(arg1: Literal[None]):
--------------------------------------------------------------------------------
50 50 |
51 51 | # From flake8-pyi
52 52 | Literal[None] # Y061 None inside "Literal[]" expression. Replace with "None"
53 |-Literal[True, None] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
53 |+Optional[Literal[True]] # Y061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
54 54 |
55 55 | ###
56 56 | # The following rules here are slightly subtle,
PYI061.py:62:9: PYI061 [*] Use `None` rather than `Literal[None]`
|
60 | # If Y061 and Y062 both apply, but all the duplicate members are None,
61 | # only emit Y061...
62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
| ^^^^ PYI061
63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
|
= help: Replace with `None`
Safe fix
59 59 |
60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None,
61 61 | # only emit Y061...
62 |-Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
62 |+None # Y061 None inside "Literal[]" expression. Replace with "None"
63 63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
64 64 |
65 65 | # ... but if Y061 and Y062 both apply
PYI061.py:62:15: PYI061 [*] Use `None` rather than `Literal[None]`
|
60 | # If Y061 and Y062 both apply, but all the duplicate members are None,
61 | # only emit Y061...
62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
| ^^^^ PYI061
63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
|
= help: Replace with `None`
Safe fix
59 59 |
60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None,
61 61 | # only emit Y061...
62 |-Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
62 |+None # Y061 None inside "Literal[]" expression. Replace with "None"
63 63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
64 64 |
65 65 | # ... but if Y061 and Y062 both apply
PYI061.py:63:12: PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
61 | # only emit Y061...
62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
| ^^^^ PYI061
64 |
65 | # ... but if Y061 and Y062 both apply
|
= help: Replace with `Optional[Literal[...]]`
Safe fix
1 |-from typing import Literal, Union
1 |+from typing import Literal, Union, Optional
2 2 |
3 3 |
4 4 | def func1(arg1: Literal[None]):
--------------------------------------------------------------------------------
60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None,
61 61 | # only emit Y061...
62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
63 |+Optional[Literal[1, "foo"]] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
64 64 |
65 65 | # ... but if Y061 and Y062 both apply
66 66 | # and there are no None members in the Literal[] slice,
PYI061.py:63:25: PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
61 | # only emit Y061...
62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
63 | Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
| ^^^^ PYI061
64 |
65 | # ... but if Y061 and Y062 both apply
|
= help: Replace with `Optional[Literal[...]]`
Safe fix
1 |-from typing import Literal, Union
1 |+from typing import Literal, Union, Optional
2 2 |
3 3 |
4 4 | def func1(arg1: Literal[None]):
--------------------------------------------------------------------------------
60 60 | # If Y061 and Y062 both apply, but all the duplicate members are None,
61 61 | # only emit Y061...
62 62 | Literal[None, None] # Y061 None inside "Literal[]" expression. Replace with "None"
63 |-Literal[1, None, "foo", None] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
63 |+Optional[Literal[1, "foo"]] # Y061 None inside "Literal[]" expression. Replace with "Literal[1, 'foo'] | None"
64 64 |
65 65 | # ... but if Y061 and Y062 both apply
66 66 | # and there are no None members in the Literal[] slice,
PYI061.py:68:9: PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
66 | # and there are no None members in the Literal[] slice,
67 | # only emit Y062:
68 | Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True"
| ^^^^ PYI061
|
= help: Replace with `Optional[Literal[...]]`
Safe fix
1 |-from typing import Literal, Union
1 |+from typing import Literal, Union, Optional
2 2 |
3 3 |
4 4 | def func1(arg1: Literal[None]):
--------------------------------------------------------------------------------
65 65 | # ... but if Y061 and Y062 both apply
66 66 | # and there are no None members in the Literal[] slice,
67 67 | # only emit Y062:
68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True"
68 |+Optional[Literal[True, True]] # Y062 Duplicate "Literal[]" member "True"
69 69 |
70 70 |
71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
PYI061.py:68:21: PYI061 [*] Use `Optional[Literal[...]]` rather than `Literal[None, ...]`
|
66 | # and there are no None members in the Literal[] slice,
67 | # only emit Y062:
68 | Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True"
| ^^^^ PYI061
|
= help: Replace with `Optional[Literal[...]]`
Safe fix
1 |-from typing import Literal, Union
1 |+from typing import Literal, Union, Optional
2 2 |
3 3 |
4 4 | def func1(arg1: Literal[None]):
--------------------------------------------------------------------------------
65 65 | # ... but if Y061 and Y062 both apply
66 66 | # and there are no None members in the Literal[] slice,
67 67 | # only emit Y062:
68 |-Literal[None, True, None, True] # Y062 Duplicate "Literal[]" member "True"
68 |+Optional[Literal[True, True]] # Y062 Duplicate "Literal[]" member "True"
69 69 |
70 70 |
71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
PYI061.py:72:12: PYI061 Use `None` rather than `Literal[None]`
|
71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
72 | x: Literal[None] | None
| ^^^^ PYI061
73 | y: None | Literal[None]
74 | z: Union[Literal[None], None]
|
= help: Replace with `None`
PYI061.py:73:19: PYI061 Use `None` rather than `Literal[None]`
|
71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
72 | x: Literal[None] | None
73 | y: None | Literal[None]
| ^^^^ PYI061
74 | z: Union[Literal[None], None]
|
= help: Replace with `None`
PYI061.py:74:18: PYI061 [*] Use `None` rather than `Literal[None]`
|
72 | x: Literal[None] | None
73 | y: None | Literal[None]
74 | z: Union[Literal[None], None]
| ^^^^ PYI061
75 |
76 | a: int | Literal[None] | None
|
= help: Replace with `None`
Safe fix
71 71 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
72 72 | x: Literal[None] | None
73 73 | y: None | Literal[None]
74 |-z: Union[Literal[None], None]
74 |+z: Union[None, None]
75 75 |
76 76 | a: int | Literal[None] | None
77 77 | b: None | Literal[None] | None
PYI061.py:76:18: PYI061 Use `None` rather than `Literal[None]`
|
74 | z: Union[Literal[None], None]
75 |
76 | a: int | Literal[None] | None
| ^^^^ PYI061
77 | b: None | Literal[None] | None
78 | c: (None | Literal[None]) | None
|
= help: Replace with `None`
PYI061.py:77:19: PYI061 Use `None` rather than `Literal[None]`
|
76 | a: int | Literal[None] | None
77 | b: None | Literal[None] | None
| ^^^^ PYI061
78 | c: (None | Literal[None]) | None
79 | d: None | (Literal[None] | None)
|
= help: Replace with `None`
PYI061.py:78:20: PYI061 Use `None` rather than `Literal[None]`
|
76 | a: int | Literal[None] | None
77 | b: None | Literal[None] | None
78 | c: (None | Literal[None]) | None
| ^^^^ PYI061
79 | d: None | (Literal[None] | None)
80 | e: None | ((None | Literal[None]) | None) | None
|
= help: Replace with `None`
PYI061.py:79:20: PYI061 Use `None` rather than `Literal[None]`
|
77 | b: None | Literal[None] | None
78 | c: (None | Literal[None]) | None
79 | d: None | (Literal[None] | None)
| ^^^^ PYI061
80 | e: None | ((None | Literal[None]) | None) | None
81 | f: Literal[None] | Literal[None]
|
= help: Replace with `None`
PYI061.py:80:28: PYI061 Use `None` rather than `Literal[None]`
|
78 | c: (None | Literal[None]) | None
79 | d: None | (Literal[None] | None)
80 | e: None | ((None | Literal[None]) | None) | None
| ^^^^ PYI061
81 | f: Literal[None] | Literal[None]
|
= help: Replace with `None`
PYI061.py:81:12: PYI061 Use `None` rather than `Literal[None]`
|
79 | d: None | (Literal[None] | None)
80 | e: None | ((None | Literal[None]) | None) | None
81 | f: Literal[None] | Literal[None]
| ^^^^ PYI061
|
= help: Replace with `None`
PYI061.py:81:28: PYI061 Use `None` rather than `Literal[None]`
|
79 | d: None | (Literal[None] | None)
80 | e: None | ((None | Literal[None]) | None) | None
81 | f: Literal[None] | Literal[None]
| ^^^^ PYI061
|
= help: Replace with `None`

View file

@ -0,0 +1,324 @@
---
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
---
PYI061.pyi:4:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
4 | def func1(arg1: Literal[None]): ...
| ^^^^ PYI061
|
= help: Replace with `None`
Safe fix
1 1 | from typing import Literal, Union
2 2 |
3 3 |
4 |-def func1(arg1: Literal[None]): ...
4 |+def func1(arg1: None): ...
5 5 |
6 6 |
7 7 | def func2(arg1: Literal[None] | int): ...
PYI061.pyi:7:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
7 | def func2(arg1: Literal[None] | int): ...
| ^^^^ PYI061
|
= help: Replace with `None`
Safe fix
4 4 | def func1(arg1: Literal[None]): ...
5 5 |
6 6 |
7 |-def func2(arg1: Literal[None] | int): ...
7 |+def func2(arg1: None | int): ...
8 8 |
9 9 |
10 10 | def func3() -> Literal[None]: ...
PYI061.pyi:10:24: PYI061 [*] Use `None` rather than `Literal[None]`
|
10 | def func3() -> Literal[None]: ...
| ^^^^ PYI061
|
= help: Replace with `None`
Safe fix
7 7 | def func2(arg1: Literal[None] | int): ...
8 8 |
9 9 |
10 |-def func3() -> Literal[None]: ...
10 |+def func3() -> None: ...
11 11 |
12 12 |
13 13 | def func4(arg1: Literal[int, None, float]): ...
PYI061.pyi:13:30: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
13 | def func4(arg1: Literal[int, None, float]): ...
| ^^^^ PYI061
|
= help: Replace with `Literal[...] | None`
Safe fix
10 10 | def func3() -> Literal[None]: ...
11 11 |
12 12 |
13 |-def func4(arg1: Literal[int, None, float]): ...
13 |+def func4(arg1: Literal[int, float] | None): ...
14 14 |
15 15 |
16 16 | def func5(arg1: Literal[None, None]): ...
PYI061.pyi:16:25: PYI061 [*] Use `None` rather than `Literal[None]`
|
16 | def func5(arg1: Literal[None, None]): ...
| ^^^^ PYI061
|
= help: Replace with `None`
Safe fix
13 13 | def func4(arg1: Literal[int, None, float]): ...
14 14 |
15 15 |
16 |-def func5(arg1: Literal[None, None]): ...
16 |+def func5(arg1: None): ...
17 17 |
18 18 |
19 19 | def func6(arg1: Literal[
PYI061.pyi:16:31: PYI061 [*] Use `None` rather than `Literal[None]`
|
16 | def func5(arg1: Literal[None, None]): ...
| ^^^^ PYI061
|
= help: Replace with `None`
Safe fix
13 13 | def func4(arg1: Literal[int, None, float]): ...
14 14 |
15 15 |
16 |-def func5(arg1: Literal[None, None]): ...
16 |+def func5(arg1: None): ...
17 17 |
18 18 |
19 19 | def func6(arg1: Literal[
PYI061.pyi:21:5: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
19 | def func6(arg1: Literal[
20 | "hello",
21 | None # Comment 1
| ^^^^ PYI061
22 | , "world"
23 | ]): ...
|
= help: Replace with `Literal[...] | None`
Unsafe fix
16 16 | def func5(arg1: Literal[None, None]): ...
17 17 |
18 18 |
19 |-def func6(arg1: Literal[
20 |- "hello",
21 |- None # Comment 1
22 |- , "world"
23 |-]): ...
19 |+def func6(arg1: Literal["hello", "world"] | None): ...
24 20 |
25 21 |
26 22 | def func7(arg1: Literal[
PYI061.pyi:27:5: PYI061 [*] Use `None` rather than `Literal[None]`
|
26 | def func7(arg1: Literal[
27 | None # Comment 1
| ^^^^ PYI061
28 | ]): ...
|
= help: Replace with `None`
Unsafe fix
23 23 | ]): ...
24 24 |
25 25 |
26 |-def func7(arg1: Literal[
27 |- None # Comment 1
28 |-]): ...
26 |+def func7(arg1: None): ...
29 27 |
30 28 |
31 29 | def func8(arg1: Literal[None] | None):...
PYI061.pyi:31:25: PYI061 Use `None` rather than `Literal[None]`
|
31 | def func8(arg1: Literal[None] | None):...
| ^^^^ PYI061
|
= help: Replace with `None`
PYI061.pyi:34:31: PYI061 [*] Use `None` rather than `Literal[None]`
|
34 | def func9(arg1: Union[Literal[None], None]): ...
| ^^^^ PYI061
|
= help: Replace with `None`
Safe fix
31 31 | def func8(arg1: Literal[None] | None):...
32 32 |
33 33 |
34 |-def func9(arg1: Union[Literal[None], None]): ...
34 |+def func9(arg1: Union[None, None]): ...
35 35 |
36 36 |
37 37 | # OK
PYI061.pyi:42:9: PYI061 [*] Use `None` rather than `Literal[None]`
|
41 | # From flake8-pyi
42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None"
| ^^^^ PYI061
43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
|
= help: Replace with `None`
Safe fix
39 39 |
40 40 |
41 41 | # From flake8-pyi
42 |-Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None"
42 |+None # PYI061 None inside "Literal[]" expression. Replace with "None"
43 43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
44 44 |
45 45 |
PYI061.pyi:43:15: PYI061 [*] Use `Literal[...] | None` rather than `Literal[None, ...]`
|
41 | # From flake8-pyi
42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None"
43 | Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
| ^^^^ PYI061
|
= help: Replace with `Literal[...] | None`
Safe fix
40 40 |
41 41 | # From flake8-pyi
42 42 | Literal[None] # PYI061 None inside "Literal[]" expression. Replace with "None"
43 |-Literal[True, None] # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
43 |+Literal[True] | None # PYI061 None inside "Literal[]" expression. Replace with "Literal[True] | None"
44 44 |
45 45 |
46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
PYI061.pyi:47:12: PYI061 Use `None` rather than `Literal[None]`
|
46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
47 | x: Literal[None] | None
| ^^^^ PYI061
48 | y: None | Literal[None]
49 | z: Union[Literal[None], None]
|
= help: Replace with `None`
PYI061.pyi:48:19: PYI061 Use `None` rather than `Literal[None]`
|
46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
47 | x: Literal[None] | None
48 | y: None | Literal[None]
| ^^^^ PYI061
49 | z: Union[Literal[None], None]
|
= help: Replace with `None`
PYI061.pyi:49:18: PYI061 [*] Use `None` rather than `Literal[None]`
|
47 | x: Literal[None] | None
48 | y: None | Literal[None]
49 | z: Union[Literal[None], None]
| ^^^^ PYI061
50 |
51 | a: int | Literal[None] | None
|
= help: Replace with `None`
Safe fix
46 46 | # Regression tests for https://github.com/astral-sh/ruff/issues/14567
47 47 | x: Literal[None] | None
48 48 | y: None | Literal[None]
49 |-z: Union[Literal[None], None]
49 |+z: Union[None, None]
50 50 |
51 51 | a: int | Literal[None] | None
52 52 | b: None | Literal[None] | None
PYI061.pyi:51:18: PYI061 Use `None` rather than `Literal[None]`
|
49 | z: Union[Literal[None], None]
50 |
51 | a: int | Literal[None] | None
| ^^^^ PYI061
52 | b: None | Literal[None] | None
53 | c: (None | Literal[None]) | None
|
= help: Replace with `None`
PYI061.pyi:52:19: PYI061 Use `None` rather than `Literal[None]`
|
51 | a: int | Literal[None] | None
52 | b: None | Literal[None] | None
| ^^^^ PYI061
53 | c: (None | Literal[None]) | None
54 | d: None | (Literal[None] | None)
|
= help: Replace with `None`
PYI061.pyi:53:20: PYI061 Use `None` rather than `Literal[None]`
|
51 | a: int | Literal[None] | None
52 | b: None | Literal[None] | None
53 | c: (None | Literal[None]) | None
| ^^^^ PYI061
54 | d: None | (Literal[None] | None)
55 | e: None | ((None | Literal[None]) | None) | None
|
= help: Replace with `None`
PYI061.pyi:54:20: PYI061 Use `None` rather than `Literal[None]`
|
52 | b: None | Literal[None] | None
53 | c: (None | Literal[None]) | None
54 | d: None | (Literal[None] | None)
| ^^^^ PYI061
55 | e: None | ((None | Literal[None]) | None) | None
56 | f: Literal[None] | Literal[None]
|
= help: Replace with `None`
PYI061.pyi:55:28: PYI061 Use `None` rather than `Literal[None]`
|
53 | c: (None | Literal[None]) | None
54 | d: None | (Literal[None] | None)
55 | e: None | ((None | Literal[None]) | None) | None
| ^^^^ PYI061
56 | f: Literal[None] | Literal[None]
|
= help: Replace with `None`
PYI061.pyi:56:12: PYI061 Use `None` rather than `Literal[None]`
|
54 | d: None | (Literal[None] | None)
55 | e: None | ((None | Literal[None]) | None) | None
56 | f: Literal[None] | Literal[None]
| ^^^^ PYI061
|
= help: Replace with `None`
PYI061.pyi:56:28: PYI061 Use `None` rather than `Literal[None]`
|
54 | d: None | (Literal[None] | None)
55 | e: None | ((None | Literal[None]) | None) | None
56 | f: Literal[None] | Literal[None]
| ^^^^ PYI061
|
= help: Replace with `None`