From de4fc5b171d8b4a9dfd44ef9b551f1257661ab60 Mon Sep 17 00:00:00 2001 From: Dylan Date: Mon, 9 Jun 2025 17:25:14 -0500 Subject: [PATCH] [`pyupgrade`] Stabilize `non-pep604-annotation-optional` (`UP045`) and preview behavior for `non-pep604-annotation-union` (`UP007`) (#18505) --- crates/ruff_linter/src/codes.rs | 2 +- crates/ruff_linter/src/preview.rs | 5 -- crates/ruff_linter/src/rules/pyupgrade/mod.rs | 17 +---- .../pyupgrade/rules/use_pep604_annotation.rs | 11 +--- ...er__rules__pyupgrade__tests__UP045.py.snap | 66 +++++++++---------- ...tests__future_annotations_pep_604_p37.snap | 6 +- ...sts__future_annotations_pep_604_py310.snap | 6 +- ...ules__pyupgrade__tests__up007_preview.snap | 5 -- 8 files changed, 45 insertions(+), 73 deletions(-) delete mode 100644 crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__up007_preview.snap diff --git a/crates/ruff_linter/src/codes.rs b/crates/ruff_linter/src/codes.rs index 0581769073..ebe281d0f4 100644 --- a/crates/ruff_linter/src/codes.rs +++ b/crates/ruff_linter/src/codes.rs @@ -549,7 +549,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> { (Pyupgrade, "042") => (RuleGroup::Preview, rules::pyupgrade::rules::ReplaceStrEnum), (Pyupgrade, "043") => (RuleGroup::Stable, rules::pyupgrade::rules::UnnecessaryDefaultTypeArgs), (Pyupgrade, "044") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP646Unpack), - (Pyupgrade, "045") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP604AnnotationOptional), + (Pyupgrade, "045") => (RuleGroup::Stable, rules::pyupgrade::rules::NonPEP604AnnotationOptional), (Pyupgrade, "046") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP695GenericClass), (Pyupgrade, "047") => (RuleGroup::Preview, rules::pyupgrade::rules::NonPEP695GenericFunction), (Pyupgrade, "049") => (RuleGroup::Stable, rules::pyupgrade::rules::PrivateTypeParameter), diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index 32f4b06a5b..023e918c55 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -61,11 +61,6 @@ pub(crate) const fn is_dunder_init_fix_unused_import_enabled(settings: &LinterSe settings.preview.is_enabled() } -// https://github.com/astral-sh/ruff/pull/15313 -pub(crate) const fn is_defer_optional_to_up045_enabled(settings: &LinterSettings) -> bool { - settings.preview.is_enabled() -} - // https://github.com/astral-sh/ruff/pull/8473 pub(crate) const fn is_unicode_to_unicode_confusables_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() diff --git a/crates/ruff_linter/src/rules/pyupgrade/mod.rs b/crates/ruff_linter/src/rules/pyupgrade/mod.rs index 3f0388787b..0ae7cf1b92 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/mod.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/mod.rs @@ -15,7 +15,7 @@ mod tests { use crate::registry::Rule; use crate::rules::pyupgrade; - use crate::settings::types::PreviewMode; + use crate::test::test_path; use crate::{assert_messages, settings}; @@ -43,7 +43,7 @@ mod tests { #[test_case(Rule::NonPEP585Annotation, Path::new("UP006_2.py"))] #[test_case(Rule::NonPEP585Annotation, Path::new("UP006_3.py"))] #[test_case(Rule::NonPEP604AnnotationUnion, Path::new("UP007.py"))] - #[test_case(Rule::NonPEP604AnnotationUnion, Path::new("UP045.py"))] + #[test_case(Rule::NonPEP604AnnotationOptional, Path::new("UP045.py"))] #[test_case(Rule::NonPEP604Isinstance, Path::new("UP038.py"))] #[test_case(Rule::OSErrorAlias, Path::new("UP024_0.py"))] #[test_case(Rule::OSErrorAlias, Path::new("UP024_1.py"))] @@ -122,19 +122,6 @@ mod tests { Ok(()) } - #[test] - fn up007_preview() -> Result<()> { - let diagnostics = test_path( - Path::new("pyupgrade/UP045.py"), - &settings::LinterSettings { - preview: PreviewMode::Enabled, - ..settings::LinterSettings::for_rule(Rule::NonPEP604AnnotationUnion) - }, - )?; - assert_messages!(diagnostics); - Ok(()) - } - #[test] fn async_timeout_error_alias_not_applied_py310() -> Result<()> { let diagnostics = test_path( diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs index bc49d8df08..4cf3979775 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/use_pep604_annotation.rs @@ -8,7 +8,6 @@ use ruff_text_size::Ranged; use crate::checkers::ast::Checker; use crate::codes::Rule; use crate::fix::edits::pad; -use crate::preview::is_defer_optional_to_up045_enabled; use crate::{Applicability, Edit, Fix, FixAvailability, Violation}; /// ## What it does @@ -39,8 +38,7 @@ use crate::{Applicability, Edit, Fix, FixAvailability, Violation}; /// foo: int | str = 1 /// ``` /// -/// ## Preview -/// In preview mode, this rule only checks for usages of `typing.Union`, +/// Note that this rule only checks for usages of `typing.Union`, /// while `UP045` checks for `typing.Optional`. /// /// ## Fix safety @@ -149,11 +147,8 @@ pub(crate) fn non_pep604_annotation( match operator { Pep604Operator::Optional => { - let guard = if is_defer_optional_to_up045_enabled(checker.settings) { - checker.report_diagnostic_if_enabled(NonPEP604AnnotationOptional, expr.range()) - } else { - checker.report_diagnostic_if_enabled(NonPEP604AnnotationUnion, expr.range()) - }; + let guard = + checker.report_diagnostic_if_enabled(NonPEP604AnnotationOptional, expr.range()); let Some(mut diagnostic) = guard else { return; diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP045.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP045.py.snap index 9e78420b08..bc50bc8688 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP045.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP045.py.snap @@ -1,13 +1,13 @@ --- source: crates/ruff_linter/src/rules/pyupgrade/mod.rs --- -UP045.py:5:10: UP007 [*] Use `X | Y` for type annotations +UP045.py:5:10: UP045 [*] Use `X | None` for type annotations | 5 | def f(x: Optional[str]) -> None: - | ^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^ UP045 6 | ... | - = help: Convert to `X | Y` + = help: Convert to `X | None` ℹ Safe fix 2 2 | from typing import Optional @@ -19,13 +19,13 @@ UP045.py:5:10: UP007 [*] Use `X | Y` for type annotations 7 7 | 8 8 | -UP045.py:9:10: UP007 [*] Use `X | Y` for type annotations +UP045.py:9:10: UP045 [*] Use `X | None` for type annotations | 9 | def f(x: typing.Optional[str]) -> None: - | ^^^^^^^^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^^^^^^^^ UP045 10 | ... | - = help: Convert to `X | Y` + = help: Convert to `X | None` ℹ Safe fix 6 6 | ... @@ -37,14 +37,14 @@ UP045.py:9:10: UP007 [*] Use `X | Y` for type annotations 11 11 | 12 12 | -UP045.py:14:8: UP007 [*] Use `X | Y` for type annotations +UP045.py:14:8: UP045 [*] Use `X | None` for type annotations | 13 | def f() -> None: 14 | x: Optional[str] - | ^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^ UP045 15 | x = Optional[str] | - = help: Convert to `X | Y` + = help: Convert to `X | None` ℹ Safe fix 11 11 | @@ -56,22 +56,22 @@ UP045.py:14:8: UP007 [*] Use `X | Y` for type annotations 16 16 | 17 17 | -UP045.py:15:9: UP007 Use `X | Y` for type annotations +UP045.py:15:9: UP045 Use `X | None` for type annotations | 13 | def f() -> None: 14 | x: Optional[str] 15 | x = Optional[str] - | ^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^ UP045 | - = help: Convert to `X | Y` + = help: Convert to `X | None` -UP045.py:18:15: UP007 [*] Use `X | Y` for type annotations +UP045.py:18:15: UP045 [*] Use `X | None` for type annotations | 18 | def f(x: list[Optional[int]]) -> None: - | ^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^ UP045 19 | ... | - = help: Convert to `X | Y` + = help: Convert to `X | None` ℹ Safe fix 15 15 | x = Optional[str] @@ -83,31 +83,31 @@ UP045.py:18:15: UP007 [*] Use `X | Y` for type annotations 20 20 | 21 21 | -UP045.py:22:10: UP007 Use `X | Y` for type annotations +UP045.py:22:10: UP045 Use `X | None` for type annotations | 22 | def f(x: Optional[int : float]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^^^^^^^^^ UP045 23 | ... | - = help: Convert to `X | Y` + = help: Convert to `X | None` -UP045.py:26:10: UP007 Use `X | Y` for type annotations +UP045.py:26:10: UP045 Use `X | None` for type annotations | 26 | def f(x: Optional[str, int : float]) -> None: - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ UP045 27 | ... | - = help: Convert to `X | Y` + = help: Convert to `X | None` -UP045.py:30:10: UP007 Use `X | Y` for type annotations +UP045.py:30:10: UP045 Use `X | None` for type annotations | 30 | def f(x: Optional[int, float]) -> None: - | ^^^^^^^^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^^^^^^^^ UP045 31 | ... | - = help: Convert to `X | Y` + = help: Convert to `X | None` -UP045.py:36:28: UP007 [*] Use `X | Y` for type annotations +UP045.py:36:28: UP045 [*] Use `X | None` for type annotations | 34 | # Regression test for: https://github.com/astral-sh/ruff/issues/7131 35 | class ServiceRefOrValue: @@ -116,9 +116,9 @@ UP045.py:36:28: UP007 [*] Use `X | Y` for type annotations 37 | | list[ServiceSpecificationRef] 38 | | | list[ServiceSpecification] 39 | | ] = None - | |_____^ UP007 + | |_____^ UP045 | - = help: Convert to `X | Y` + = help: Convert to `X | None` ℹ Safe fix 33 33 | @@ -133,14 +133,14 @@ UP045.py:36:28: UP007 [*] Use `X | Y` for type annotations 41 38 | 42 39 | # Regression test for: https://github.com/astral-sh/ruff/issues/7201 -UP045.py:44:28: UP007 [*] Use `X | Y` for type annotations +UP045.py:44:28: UP045 [*] Use `X | None` for type annotations | 42 | # Regression test for: https://github.com/astral-sh/ruff/issues/7201 43 | class ServiceRefOrValue: 44 | service_specification: Optional[str]is not True = None - | ^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^ UP045 | - = help: Convert to `X | Y` + = help: Convert to `X | None` ℹ Safe fix 41 41 | @@ -152,11 +152,11 @@ UP045.py:44:28: UP007 [*] Use `X | Y` for type annotations 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 +UP045.py:49:6: UP045 Use `X | None` 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 + | ^^^^^^^^^^^^^^ UP045 | - = help: Convert to `X | Y` + = help: Convert to `X | None` diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_p37.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_p37.snap index 525b392e7c..29df4b48bb 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_p37.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_p37.snap @@ -1,14 +1,14 @@ --- source: crates/ruff_linter/src/rules/pyupgrade/mod.rs --- -future_annotations.py:40:4: UP007 [*] Use `X | Y` for type annotations +future_annotations.py:40:4: UP045 [*] Use `X | None` for type annotations | 40 | x: Optional[int] = None - | ^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^ UP045 41 | 42 | MyList: TypeAlias = Union[List[int], List[str]] | - = help: Convert to `X | Y` + = help: Convert to `X | None` ℹ Unsafe fix 37 37 | return y diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_py310.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_py310.snap index f88a3b32fb..163b0a8cb5 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_py310.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__future_annotations_pep_604_py310.snap @@ -1,14 +1,14 @@ --- source: crates/ruff_linter/src/rules/pyupgrade/mod.rs --- -future_annotations.py:40:4: UP007 [*] Use `X | Y` for type annotations +future_annotations.py:40:4: UP045 [*] Use `X | None` for type annotations | 40 | x: Optional[int] = None - | ^^^^^^^^^^^^^ UP007 + | ^^^^^^^^^^^^^ UP045 41 | 42 | MyList: TypeAlias = Union[List[int], List[str]] | - = help: Convert to `X | Y` + = help: Convert to `X | None` ℹ Safe fix 37 37 | return y diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__up007_preview.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__up007_preview.snap deleted file mode 100644 index c996a51d38..0000000000 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__up007_preview.snap +++ /dev/null @@ -1,5 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/pyupgrade/mod.rs -snapshot_kind: text ---- -