From 3728d5b3a2ba1d97a1e6cbbe719b5960ca8ca53c Mon Sep 17 00:00:00 2001 From: cake-monotone Date: Thu, 3 Oct 2024 21:06:15 +0900 Subject: [PATCH] [`pyupgrade`] Fix UP043 to apply to `collections.abc.Generator` and `collections.abc.AsyncGenerator` (#13611) ## Summary fix #13602 Currently, `UP043` only applies to typing.Generator, but it should also support collections.abc.Generator. This update ensures `UP043` correctly handles both `collections.abc.Generator` and `collections.abc.AsyncGenerator` ### UP043 > `UP043` > Python 3.13 introduced the ability for type parameters to specify default values. As such, the default type arguments for some types in the standard library (e.g., Generator, AsyncGenerator) are now optional. > Omitting type parameters that match the default values can make the code more concise and easier to read. ```py Generator[int, None, None] -> Generator[int] ``` --- .../test/fixtures/pyupgrade/UP043.py | 13 ++++++- .../rules/unnecessary_default_type_args.rs | 20 ++++++++-- ...er__rules__pyupgrade__tests__UP043.py.snap | 38 ++++++++++++++++++- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.py b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.py index c4ebf662a67cd..3968a16a54a83 100644 --- a/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.py +++ b/crates/ruff_linter/resources/test/fixtures/pyupgrade/UP043.py @@ -1,4 +1,4 @@ -from typing import Generator, AsyncGenerator +from collections.abc import Generator, AsyncGenerator def func() -> Generator[int, None, None]: @@ -39,3 +39,14 @@ async def func() -> AsyncGenerator[int]: async def func() -> AsyncGenerator[int, int]: foo = yield 42 return foo + + +from typing import Generator, AsyncGenerator + + +def func() -> Generator[str, None, None]: + yield "hello" + + +async def func() -> AsyncGenerator[str, None]: + yield "hello" diff --git a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs index 8349eae78fce0..4ef77fbc30f71 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs +++ b/crates/ruff_linter/src/rules/pyupgrade/rules/unnecessary_default_type_args.rs @@ -19,7 +19,7 @@ use crate::checkers::ast::Checker; /// ## Examples /// /// ```python -/// from typing import Generator, AsyncGenerator +/// from collections.abc import Generator, AsyncGenerator /// /// /// def sync_gen() -> Generator[int, None, None]: @@ -33,7 +33,7 @@ use crate::checkers::ast::Checker; /// Use instead: /// /// ```python -/// from typing import Generator, AsyncGenerator +/// from collections.abc import Generator, AsyncGenerator /// /// /// def sync_gen() -> Generator[int]: @@ -47,6 +47,7 @@ use crate::checkers::ast::Checker; /// ## References /// /// - [PEP 696 – Type Defaults for Type Parameters](https://peps.python.org/pep-0696/) +/// - [Annotating generators and coroutines](https://docs.python.org/3.13/library/typing.html#annotating-generators-and-coroutines) /// - [typing.Generator](https://docs.python.org/3.13/library/typing.html#typing.Generator) /// - [typing.AsyncGenerator](https://docs.python.org/3.13/library/typing.html#typing.AsyncGenerator) #[violation] @@ -140,9 +141,20 @@ impl DefaultedTypeAnnotation { /// includes default type arguments. fn from_expr(expr: &Expr, semantic: &ruff_python_semantic::SemanticModel) -> Option { let qualified_name = semantic.resolve_qualified_name(expr)?; - if semantic.match_typing_qualified_name(&qualified_name, "Generator") { + + if semantic.match_typing_qualified_name(&qualified_name, "Generator") + || matches!( + qualified_name.segments(), + ["collections", "abc", "Generator"] + ) + { Some(Self::Generator) - } else if semantic.match_typing_qualified_name(&qualified_name, "AsyncGenerator") { + } else if semantic.match_typing_qualified_name(&qualified_name, "AsyncGenerator") + || matches!( + qualified_name.segments(), + ["collections", "abc", "AsyncGenerator"] + ) + { Some(Self::AsyncGenerator) } else { None diff --git a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap index 4198822ad3363..65243085cce44 100644 --- a/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap +++ b/crates/ruff_linter/src/rules/pyupgrade/snapshots/ruff_linter__rules__pyupgrade__tests__UP043.py.snap @@ -10,7 +10,7 @@ UP043.py:4:15: UP043 [*] Unnecessary default type arguments = help: Remove default type arguments ℹ Safe fix -1 1 | from typing import Generator, AsyncGenerator +1 1 | from collections.abc import Generator, AsyncGenerator 2 2 | 3 3 | 4 |-def func() -> Generator[int, None, None]: @@ -72,4 +72,38 @@ UP043.py:31:21: UP043 [*] Unnecessary default type arguments 31 |+async def func() -> AsyncGenerator[int]: 32 32 | yield 42 33 33 | -34 34 | +34 34 | + +UP043.py:47:15: UP043 [*] Unnecessary default type arguments + | +47 | def func() -> Generator[str, None, None]: + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ UP043 +48 | yield "hello" + | + = help: Remove default type arguments + +ℹ Safe fix +44 44 | from typing import Generator, AsyncGenerator +45 45 | +46 46 | +47 |-def func() -> Generator[str, None, None]: + 47 |+def func() -> Generator[str]: +48 48 | yield "hello" +49 49 | +50 50 | + +UP043.py:51:21: UP043 [*] Unnecessary default type arguments + | +51 | async def func() -> AsyncGenerator[str, None]: + | ^^^^^^^^^^^^^^^^^^^^^^^^^ UP043 +52 | yield "hello" + | + = help: Remove default type arguments + +ℹ Safe fix +48 48 | yield "hello" +49 49 | +50 50 | +51 |-async def func() -> AsyncGenerator[str, None]: + 51 |+async def func() -> AsyncGenerator[str]: +52 52 | yield "hello"