[pyupgrade] Don't offer fix for Optional[None] in non-pep604-annotation-optional (UP045) or non-pep604-annotation-union (UP007) (#18545)

This commit is contained in:
Robsdedude 2025-06-11 06:19:00 +00:00 committed by GitHub
parent 2213698a5d
commit 0724bee59c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 26 additions and 4 deletions

View file

@ -42,3 +42,8 @@ class ServiceRefOrValue:
# Regression test for: https://github.com/astral-sh/ruff/issues/7201 # Regression test for: https://github.com/astral-sh/ruff/issues/7201
class ServiceRefOrValue: class ServiceRefOrValue:
service_specification: Optional[str]is not True = None service_specification: Optional[str]is not True = None
# Test for: https://github.com/astral-sh/ruff/issues/18508
# Optional[None] should not be offered a fix
foo: Optional[None] = None

View file

@ -100,8 +100,8 @@ impl Violation for NonPEP604AnnotationUnion {
/// ``` /// ```
/// ///
/// ## Fix safety /// ## Fix safety
/// This rule's fix is marked as unsafe, as it may lead to runtime errors when /// This rule's fix is marked as unsafe, as it may lead to runtime errors
/// alongside libraries that rely on runtime type annotations, like Pydantic, /// using libraries that rely on runtime type annotations, like Pydantic,
/// on Python versions prior to Python 3.10. It may also lead to runtime errors /// on Python versions prior to Python 3.10. It may also lead to runtime errors
/// in unusual and likely incorrect type annotations where the type does not /// in unusual and likely incorrect type annotations where the type does not
/// support the `|` operator. /// support the `|` operator.
@ -138,7 +138,8 @@ pub(crate) fn non_pep604_annotation(
// lead to invalid syntax. // lead to invalid syntax.
let fixable = checker.semantic().in_type_definition() let fixable = checker.semantic().in_type_definition()
&& !checker.semantic().in_complex_string_type_definition() && !checker.semantic().in_complex_string_type_definition()
&& is_allowed_value(slice); && is_allowed_value(slice)
&& !is_optional_none(operator, slice);
let applicability = if checker.target_version() >= PythonVersion::PY310 { let applicability = if checker.target_version() >= PythonVersion::PY310 {
Applicability::Safe Applicability::Safe
@ -276,3 +277,8 @@ fn is_allowed_value(expr: &Expr) -> bool {
| Expr::IpyEscapeCommand(_) => false, | Expr::IpyEscapeCommand(_) => false,
} }
} }
/// Return `true` if this is an `Optional[None]` annotation.
fn is_optional_none(operator: Pep604Operator, slice: &Expr) -> bool {
matches!(operator, Pep604Operator::Optional) && matches!(slice, Expr::NoneLiteral(_))
}

View file

@ -1,6 +1,5 @@
--- ---
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
snapshot_kind: text
--- ---
UP045.py:5:10: UP007 [*] Use `X | Y` for type annotations UP045.py:5:10: UP007 [*] Use `X | Y` for type annotations
| |
@ -149,3 +148,15 @@ UP045.py:44:28: UP007 [*] Use `X | Y` for type annotations
43 43 | class ServiceRefOrValue: 43 43 | class ServiceRefOrValue:
44 |- service_specification: Optional[str]is not True = None 44 |- service_specification: Optional[str]is not True = None
44 |+ service_specification: str | None is not True = None 44 |+ service_specification: str | None is not True = None
45 45 |
46 46 |
47 47 | # Test for: https://github.com/astral-sh/ruff/issues/18508
UP045.py:49:6: UP007 Use `X | Y` for type annotations
|
47 | # Test for: https://github.com/astral-sh/ruff/issues/18508
48 | # Optional[None] should not be offered a fix
49 | foo: Optional[None] = None
| ^^^^^^^^^^^^^^ UP007
|
= help: Convert to `X | Y`