From de8f9cdb39985b263d9e53fc8599c2fa5290881f Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Tue, 2 Jan 2024 11:54:19 +0000 Subject: [PATCH 1/5] [flake8-pyi] Add autofix for PYI058 --- .../test/fixtures/flake8_pyi/PYI058.py | 10 +- .../test/fixtures/flake8_pyi/PYI058.pyi | 6 + .../rules/bad_generator_return_type.rs | 148 +++++++++++++++--- ...__flake8_pyi__tests__PYI058_PYI058.py.snap | 142 ++++++++++++++--- ..._flake8_pyi__tests__PYI058_PYI058.pyi.snap | 134 +++++++++++++--- 5 files changed, 376 insertions(+), 64 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py index 810a084c62d00..1fd9f1db4840d 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py @@ -8,12 +8,20 @@ def __iter__(self) -> Generator: # PYI058 (use `Iterator`) return (x for x in range(42)) class IteratorReturningSimpleGenerator2: + def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) + return (x for x in range(42)) + +class IteratorReturningSimpleGenerator3: + def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) + return (x for x in range(42)) + +class IteratorReturningSimpleGenerator4: def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) """Fully documented, because I'm a runtime function!""" yield from "abcdefg" return None -class IteratorReturningSimpleGenerator3: +class IteratorReturningSimpleGenerator5: def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) yield "a" yield "b" diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi index ce6e78a68b311..74d8fadfd5b01 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi @@ -6,6 +6,12 @@ from typing import Any class IteratorReturningSimpleGenerator1: def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) +class IteratorReturningSimpleGenerator2: + def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) + +class IteratorReturningSimpleGenerator3: + def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) + class IteratorReturningSimpleGenerator2: def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs index 5dc0de179227f..4727638ee9417 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs @@ -1,9 +1,10 @@ -use ruff_diagnostics::{Diagnostic, Violation}; +use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast as ast; use ruff_python_ast::helpers::map_subscript; use ruff_python_ast::identifier::Identifier; use ruff_python_semantic::SemanticModel; +use ruff_text_size::TextRange; use crate::checkers::ast::Checker; @@ -48,6 +49,14 @@ use crate::checkers::ast::Checker; /// def __iter__(self) -> Iterator[str]: /// yield from "abdefg" /// ``` +/// +/// ## Fix safety +/// This rule tries hard to avoid false-positive errors, and the rule's fix +/// should always be safe for `.pyi` stub files. However, there is a slightly +/// higher chance that a false positive might be emitted by this rule when +/// applied to runtime Python (`.py` files). As such, the fix is marked as +/// unsafe for any `__iter__` or `__aiter__` method in a `.py` file that has +/// more than two statements (including docstrings) in its body. #[violation] pub struct GeneratorReturnFromIterMethod { better_return_type: String, @@ -55,6 +64,10 @@ pub struct GeneratorReturnFromIterMethod { } impl Violation for GeneratorReturnFromIterMethod { + // Fixable iff the fully qualified name is being used: + // one of {typing.Generator, typing_extensions.Generator, collections.abc.Generator} + const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; + #[derive_message_formats] fn message(&self) -> String { let GeneratorReturnFromIterMethod { @@ -63,6 +76,21 @@ impl Violation for GeneratorReturnFromIterMethod { } = self; format!("Use `{better_return_type}` as the return value for simple `{method_name}` methods") } + + fn fix_title(&self) -> Option { + let GeneratorReturnFromIterMethod { + better_return_type, + method_name, + } = self; + Some(format!( + "Convert the return annotation of your `{method_name}` method to `{better_return_type}`" + )) + } +} + +struct YieldTypeInfo { + expr: ast::Expr, + range: TextRange, } /// PYI058 @@ -128,29 +156,41 @@ pub(crate) fn bad_generator_return_type( // `Generator` allows three type parameters; `AsyncGenerator` allows two. // If type parameters are present, - // Check that all parameters except the first one are either `typing.Any` or `None`; - // if not, don't emit the diagnostic - if let ast::Expr::Subscript(ast::ExprSubscript { slice, .. }) = returns { - let ast::Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() else { - return; - }; - if matches!( - (name, &elts[..]), - ("__iter__", [_, _, _]) | ("__aiter__", [_, _]) - ) { - if !&elts.iter().skip(1).all(|elt| is_any_or_none(elt, semantic)) { - return; + // check that all parameters except the first one are either `typing.Any` or `None`: + // - if so, collect information on the first parameter for use in the rule's autofix; + // - if not, don't emit the diagnostic + let yield_type_info = match returns { + ast::Expr::Subscript(ast::ExprSubscript { slice, .. }) => match slice.as_ref() { + ast::Expr::Tuple(slice_tuple @ ast::ExprTuple { .. }) => { + if !&slice_tuple + .elts + .iter() + .skip(1) + .all(|elt| is_any_or_none(elt, semantic)) + { + return; + } + let yield_type = match (name, &slice_tuple.elts[..]) { + ("__iter__", [yield_type, _, _]) => yield_type, + ("__aiter__", [yield_type, _]) => yield_type, + _ => return, + }; + Some(YieldTypeInfo { + expr: yield_type.to_owned(), + range: slice_tuple.range, + }) } - } else { - return; - } + _ => return, + }, + _ => None, }; // For .py files (runtime Python!), // only emit the lint if it's a simple __(a)iter__ implementation // -- for more complex function bodies, // it's more likely we'll be emitting a false positive here - if !checker.source_type.is_stub() { + let is_stub = checker.source_type.is_stub(); + if !is_stub { let mut yield_encountered = false; for stmt in &function_def.body { match stmt { @@ -176,16 +216,84 @@ pub(crate) fn bad_generator_return_type( } } }; - - checker.diagnostics.push(Diagnostic::new( + let mut diagnostic = Diagnostic::new( GeneratorReturnFromIterMethod { better_return_type: better_return_type.to_string(), method_name: name.to_string(), }, function_def.identifier(), - )); + ); + if let Some(fix) = get_fix(function_def, checker, returns, is_stub, yield_type_info) { + diagnostic.set_fix(fix); + }; + checker.diagnostics.push(diagnostic); } fn is_any_or_none(expr: &ast::Expr, semantic: &SemanticModel) -> bool { semantic.match_typing_expr(expr, "Any") || matches!(expr, ast::Expr::NoneLiteral(_)) } + +fn get_fix( + function_def: &ast::StmtFunctionDef, + checker: &Checker, + returns: &ast::Expr, + is_stub: bool, + yield_type_info: Option, +) -> Option { + let edit = match returns { + ast::Expr::Attribute(_) => get_edit(returns), + ast::Expr::Subscript(ast::ExprSubscript { value, .. }) => get_edit(value.as_ref()), + _ => None, + }; + + let Some(edit) = edit else { + return None; + }; + let mut rest = vec![]; + if let Some(yield_type_info) = yield_type_info { + rest.push(Edit::range_replacement( + checker.generator().expr(&yield_type_info.expr), + yield_type_info.range, + )); + } + + // Mark as unsafe if it's a runtime Python file + // and the body has more than one statement in it. + let applicability = if is_stub || function_def.body.len() == 1 { + Applicability::Safe + } else { + Applicability::Unsafe + }; + Some(Fix::applicable_edits(edit, rest, applicability)) +} + +fn get_edit(expr: &ast::Expr) -> Option { + let ast::Expr::Attribute(ast::ExprAttribute { + value, attr, range, .. + }) = expr + else { + return None; + }; + + let new_return = match attr.as_str() { + "Generator" => "Iterator", + "AsyncGenerator" => "AsyncIterator", + _ => return None, + }; + + let module = match value.as_ref() { + ast::Expr::Name(ast::ExprName { id, .. }) => id.to_owned(), + ast::Expr::Attribute(ast::ExprAttribute { attr, value, .. }) => match value.as_ref() { + ast::Expr::Name(ast::ExprName { id, .. }) => format!("{id}.{attr}"), + _ => return None, + }, + _ => return None, + }; + + if !["typing", "typing_extensions", "collections.abc"].contains(&module.as_str()) { + return None; + } + + let repl = format!("{module}.{new_return}"); + Some(Edit::range_replacement(repl, range.to_owned())) +} diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap index 41b5c92fca6da..b3d9a9624584d 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap @@ -8,50 +8,144 @@ PYI058.py:7:9: PYI058 Use `Iterator` as the return value for simple `__iter__` m | ^^^^^^^^ PYI058 8 | return (x for x in range(42)) | + = help: Convert the return annotation of your `__iter__` method to `Iterator` -PYI058.py:11:9: PYI058 Use `Iterator` as the return value for simple `__iter__` methods +PYI058.py:11:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | 10 | class IteratorReturningSimpleGenerator2: -11 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) +11 | def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -12 | """Fully documented, because I'm a runtime function!""" -13 | yield from "abcdefg" +12 | return (x for x in range(42)) | + = help: Convert the return annotation of your `__iter__` method to `Iterator` -PYI058.py:17:9: PYI058 Use `Iterator` as the return value for simple `__iter__` methods +ℹ Safe fix +8 8 | return (x for x in range(42)) +9 9 | +10 10 | class IteratorReturningSimpleGenerator2: +11 |- def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) + 11 |+ def __iter__(self) -> typing.Iterator: # PYI058 (use `Iterator`) +12 12 | return (x for x in range(42)) +13 13 | +14 14 | class IteratorReturningSimpleGenerator3: + +PYI058.py:15:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +14 | class IteratorReturningSimpleGenerator3: +15 | def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +16 | return (x for x in range(42)) + | + = help: Convert the return annotation of your `__iter__` method to `Iterator` + +ℹ Safe fix +12 12 | return (x for x in range(42)) +13 13 | +14 14 | class IteratorReturningSimpleGenerator3: +15 |- def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) + 15 |+ def __iter__(self) -> collections.abc.Iterator: # PYI058 (use `Iterator`) +16 16 | return (x for x in range(42)) +17 17 | +18 18 | class IteratorReturningSimpleGenerator4: + +PYI058.py:19:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | -16 | class IteratorReturningSimpleGenerator3: -17 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) +18 | class IteratorReturningSimpleGenerator4: +19 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -18 | yield "a" -19 | yield "b" +20 | """Fully documented, because I'm a runtime function!""" +21 | yield from "abcdefg" | + = help: Convert the return annotation of your `__iter__` method to `Iterator` -PYI058.py:24:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods +ℹ Unsafe fix +16 16 | return (x for x in range(42)) +17 17 | +18 18 | class IteratorReturningSimpleGenerator4: +19 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) + 19 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: # PYI058 (use `Iterator`) +20 20 | """Fully documented, because I'm a runtime function!""" +21 21 | yield from "abcdefg" +22 22 | return None + +PYI058.py:25:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | -23 | class AsyncIteratorReturningSimpleAsyncGenerator1: -24 | def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) +24 | class IteratorReturningSimpleGenerator5: +25 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +26 | yield "a" +27 | yield "b" + | + = help: Convert the return annotation of your `__iter__` method to `Iterator` + +ℹ Unsafe fix +22 22 | return None +23 23 | +24 24 | class IteratorReturningSimpleGenerator5: +25 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) + 25 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: # PYI058 (use `Iterator`) +26 26 | yield "a" +27 27 | yield "b" +28 28 | yield "c" + +PYI058.py:32:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +31 | class AsyncIteratorReturningSimpleAsyncGenerator1: +32 | def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -25 | -26 | class AsyncIteratorReturningSimpleAsyncGenerator2: +33 | +34 | class AsyncIteratorReturningSimpleAsyncGenerator2: | + = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` -PYI058.py:27:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods +ℹ Safe fix +29 29 | return +30 30 | +31 31 | class AsyncIteratorReturningSimpleAsyncGenerator1: +32 |- def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) + 32 |+ def __aiter__(self) -> typing.AsyncIterator: pass # PYI058 (Use `AsyncIterator`) +33 33 | +34 34 | class AsyncIteratorReturningSimpleAsyncGenerator2: +35 35 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + +PYI058.py:35:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods | -26 | class AsyncIteratorReturningSimpleAsyncGenerator2: -27 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) +34 | class AsyncIteratorReturningSimpleAsyncGenerator2: +35 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -28 | -29 | class AsyncIteratorReturningSimpleAsyncGenerator3: +36 | +37 | class AsyncIteratorReturningSimpleAsyncGenerator3: | + = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` + +ℹ Safe fix +32 32 | def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) +33 33 | +34 34 | class AsyncIteratorReturningSimpleAsyncGenerator2: +35 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + 35 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) +36 36 | +37 37 | class AsyncIteratorReturningSimpleAsyncGenerator3: +38 38 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) -PYI058.py:30:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods +PYI058.py:38:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods | -29 | class AsyncIteratorReturningSimpleAsyncGenerator3: -30 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) +37 | class AsyncIteratorReturningSimpleAsyncGenerator3: +38 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -31 | -32 | class CorrectIterator: +39 | +40 | class CorrectIterator: | + = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` + +ℹ Safe fix +35 35 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) +36 36 | +37 37 | class AsyncIteratorReturningSimpleAsyncGenerator3: +38 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) + 38 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: pass # PYI058 (Use `AsyncIterator`) +39 39 | +40 40 | class CorrectIterator: +41 41 | def __iter__(self) -> Iterator[str]: ... # OK diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap index 444916e1af727..a2b9db9f1e1f9 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap @@ -9,50 +9,146 @@ PYI058.pyi:7:9: PYI058 Use `Iterator` as the return value for simple `__iter__` 8 | 9 | class IteratorReturningSimpleGenerator2: | + = help: Convert the return annotation of your `__iter__` method to `Iterator` -PYI058.pyi:10:9: PYI058 Use `Iterator` as the return value for simple `__iter__` methods +PYI058.pyi:10:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | 9 | class IteratorReturningSimpleGenerator2: -10 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) +10 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 11 | 12 | class IteratorReturningSimpleGenerator3: | + = help: Convert the return annotation of your `__iter__` method to `Iterator` -PYI058.pyi:13:9: PYI058 Use `Iterator` as the return value for simple `__iter__` methods +ℹ Safe fix +7 7 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) +8 8 | +9 9 | class IteratorReturningSimpleGenerator2: +10 |- def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) + 10 |+ def __iter__(self) -> typing.Iterator: ... # PYI058 (use `Iterator`) +11 11 | +12 12 | class IteratorReturningSimpleGenerator3: +13 13 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) + +PYI058.pyi:13:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | 12 | class IteratorReturningSimpleGenerator3: -13 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) +13 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 14 | -15 | class AsyncIteratorReturningSimpleAsyncGenerator1: +15 | class IteratorReturningSimpleGenerator2: | + = help: Convert the return annotation of your `__iter__` method to `Iterator` + +ℹ Safe fix +10 10 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) +11 11 | +12 12 | class IteratorReturningSimpleGenerator3: +13 |- def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) + 13 |+ def __iter__(self) -> collections.abc.Iterator: ... # PYI058 (use `Iterator`) +14 14 | +15 15 | class IteratorReturningSimpleGenerator2: +16 16 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) -PYI058.pyi:16:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods +PYI058.pyi:16:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | -15 | class AsyncIteratorReturningSimpleAsyncGenerator1: -16 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) - | ^^^^^^^^^ PYI058 +15 | class IteratorReturningSimpleGenerator2: +16 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 17 | -18 | class AsyncIteratorReturningSimpleAsyncGenerator2: +18 | class IteratorReturningSimpleGenerator3: | + = help: Convert the return annotation of your `__iter__` method to `Iterator` + +ℹ Safe fix +13 13 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) +14 14 | +15 15 | class IteratorReturningSimpleGenerator2: +16 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) + 16 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`) +17 17 | +18 18 | class IteratorReturningSimpleGenerator3: +19 19 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) -PYI058.pyi:19:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods +PYI058.pyi:19:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | -18 | class AsyncIteratorReturningSimpleAsyncGenerator2: -19 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - | ^^^^^^^^^ PYI058 +18 | class IteratorReturningSimpleGenerator3: +19 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 20 | -21 | class AsyncIteratorReturningSimpleAsyncGenerator3: +21 | class AsyncIteratorReturningSimpleAsyncGenerator1: | + = help: Convert the return annotation of your `__iter__` method to `Iterator` + +ℹ Safe fix +16 16 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) +17 17 | +18 18 | class IteratorReturningSimpleGenerator3: +19 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) + 19 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`) +20 20 | +21 21 | class AsyncIteratorReturningSimpleAsyncGenerator1: +22 22 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) -PYI058.pyi:22:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods +PYI058.pyi:22:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods | -21 | class AsyncIteratorReturningSimpleAsyncGenerator3: -22 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) +21 | class AsyncIteratorReturningSimpleAsyncGenerator1: +22 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 23 | -24 | class CorrectIterator: +24 | class AsyncIteratorReturningSimpleAsyncGenerator2: | + = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` + +ℹ Safe fix +19 19 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) +20 20 | +21 21 | class AsyncIteratorReturningSimpleAsyncGenerator1: +22 |- def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) + 22 |+ def __aiter__(self) -> typing.AsyncIterator: ... # PYI058 (Use `AsyncIterator`) +23 23 | +24 24 | class AsyncIteratorReturningSimpleAsyncGenerator2: +25 25 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + +PYI058.pyi:25:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +24 | class AsyncIteratorReturningSimpleAsyncGenerator2: +25 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + | ^^^^^^^^^ PYI058 +26 | +27 | class AsyncIteratorReturningSimpleAsyncGenerator3: + | + = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` + +ℹ Safe fix +22 22 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) +23 23 | +24 24 | class AsyncIteratorReturningSimpleAsyncGenerator2: +25 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + 25 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) +26 26 | +27 27 | class AsyncIteratorReturningSimpleAsyncGenerator3: +28 28 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) + +PYI058.pyi:28:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +27 | class AsyncIteratorReturningSimpleAsyncGenerator3: +28 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) + | ^^^^^^^^^ PYI058 +29 | +30 | class CorrectIterator: + | + = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` + +ℹ Safe fix +25 25 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) +26 26 | +27 27 | class AsyncIteratorReturningSimpleAsyncGenerator3: +28 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) + 28 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) +29 29 | +30 30 | class CorrectIterator: +31 31 | def __iter__(self) -> Iterator[str]: ... # OK From adef1d7db328fa4758da7f5ec528086f338ea507 Mon Sep 17 00:00:00 2001 From: AlexWaygood Date: Wed, 3 Jan 2024 14:53:05 +0000 Subject: [PATCH 2/5] Address reviews --- .../test/fixtures/flake8_pyi/PYI058.py | 7 +- .../test/fixtures/flake8_pyi/PYI058.pyi | 10 +- .../rules/bad_generator_return_type.rs | 104 ++++--- ...__flake8_pyi__tests__PYI058_PYI058.py.snap | 253 +++++++++------- ..._flake8_pyi__tests__PYI058_PYI058.pyi.snap | 272 ++++++++++-------- 5 files changed, 391 insertions(+), 255 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py index 1fd9f1db4840d..02fe101e10839 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py @@ -1,5 +1,6 @@ import collections.abc import typing +import typing_extensions from collections.abc import AsyncGenerator, Generator from typing import Any @@ -28,8 +29,12 @@ def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PY yield "c" return +class IteratorReturningSimpleGenerator6: + def __iter__(self) -> Generator[int, None, None]: # PYI058 (use `Iterator`) + yield from range(42) + class AsyncIteratorReturningSimpleAsyncGenerator1: - def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) + def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) class AsyncIteratorReturningSimpleAsyncGenerator2: def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi index 74d8fadfd5b01..a1d747e604ee0 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi @@ -1,5 +1,6 @@ import collections.abc import typing +import typing_extensions from collections.abc import AsyncGenerator, Generator from typing import Any @@ -12,14 +13,17 @@ class IteratorReturningSimpleGenerator2: class IteratorReturningSimpleGenerator3: def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) -class IteratorReturningSimpleGenerator2: +class IteratorReturningSimpleGenerator4: def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) -class IteratorReturningSimpleGenerator3: +class IteratorReturningSimpleGenerator5: def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) +class IteratorReturningSimpleGenerator6: + def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) + class AsyncIteratorReturningSimpleAsyncGenerator1: - def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) + def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) class AsyncIteratorReturningSimpleAsyncGenerator2: def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs index 4727638ee9417..6ef44736ea4ea 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs @@ -1,3 +1,5 @@ +use std::borrow::ToOwned; + use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast as ast; @@ -7,6 +9,9 @@ use ruff_python_semantic::SemanticModel; use ruff_text_size::TextRange; use crate::checkers::ast::Checker; +use crate::importer::ImportRequest; + +use itertools::Itertools; /// ## What it does /// Checks for simple `__iter__` methods that return `Generator`, and for @@ -134,24 +139,17 @@ pub(crate) fn bad_generator_return_type( _ => return, }; - if !semantic - .resolve_call_path(map_subscript(returns)) - .is_some_and(|call_path| { - matches!( - (name, call_path.as_slice()), - ( - "__iter__", - ["typing" | "typing_extensions", "Generator"] - | ["collections", "abc", "Generator"] - ) | ( - "__aiter__", - ["typing" | "typing_extensions", "AsyncGenerator"] - | ["collections", "abc", "AsyncGenerator"] - ) - ) - }) - { - return; + let module_name = { + let Some(call_path) = semantic.resolve_call_path(map_subscript(returns)) else { + return; + }; + match (name, call_path.as_slice()) { + ("__iter__", ["typing" | "typing_extensions", "Generator"]) + | ("__aiter__", ["typing" | "typing_extensions", "AsyncGenerator"]) => call_path[0], + ("__iter__", ["collections", "abc", "Generator"]) + | ("__aiter__", ["collections", "abc", "AsyncGenerator"]) => "collections.abc", + _ => return, + } }; // `Generator` allows three type parameters; `AsyncGenerator` allows two. @@ -223,7 +221,14 @@ pub(crate) fn bad_generator_return_type( }, function_def.identifier(), ); - if let Some(fix) = get_fix(function_def, checker, returns, is_stub, yield_type_info) { + if let Some(fix) = get_fix( + function_def, + checker, + returns, + is_stub, + yield_type_info, + module_name, + ) { diagnostic.set_fix(fix); }; checker.diagnostics.push(diagnostic); @@ -239,17 +244,32 @@ fn get_fix( returns: &ast::Expr, is_stub: bool, yield_type_info: Option, + module_name: &str, ) -> Option { - let edit = match returns { - ast::Expr::Attribute(_) => get_edit(returns), - ast::Expr::Subscript(ast::ExprSubscript { value, .. }) => get_edit(value.as_ref()), + let edits = match returns { + ast::Expr::Attribute(attribute @ ast::ExprAttribute { .. }) => { + get_attribute_edits(attribute) + } + ast::Expr::Name(name @ ast::ExprName { .. }) => get_name_edits(name, module_name, checker), + ast::Expr::Subscript(ast::ExprSubscript { value, .. }) => match value.as_ref() { + ast::Expr::Attribute(attribute @ ast::ExprAttribute { .. }) => { + get_attribute_edits(attribute) + } + ast::Expr::Name(name @ ast::ExprName { .. }) => { + get_name_edits(name, module_name, checker) + } + _ => None, + }, _ => None, }; - let Some(edit) = edit else { + let Some(edits) = edits else { return None; }; - let mut rest = vec![]; + let [first_edit, ..] = &edits[..] else { + return None; + }; + let mut rest = edits.iter().skip(1).map(ToOwned::to_owned).collect_vec(); if let Some(yield_type_info) = yield_type_info { rest.push(Edit::range_replacement( checker.generator().expr(&yield_type_info.expr), @@ -264,16 +284,17 @@ fn get_fix( } else { Applicability::Unsafe }; - Some(Fix::applicable_edits(edit, rest, applicability)) + Some(Fix::applicable_edits( + first_edit.to_owned(), + rest, + applicability, + )) } -fn get_edit(expr: &ast::Expr) -> Option { - let ast::Expr::Attribute(ast::ExprAttribute { +fn get_attribute_edits(expr: &ast::ExprAttribute) -> Option> { + let ast::ExprAttribute { value, attr, range, .. - }) = expr - else { - return None; - }; + } = expr; let new_return = match attr.as_str() { "Generator" => "Iterator", @@ -295,5 +316,24 @@ fn get_edit(expr: &ast::Expr) -> Option { } let repl = format!("{module}.{new_return}"); - Some(Edit::range_replacement(repl, range.to_owned())) + Some(vec![Edit::range_replacement(repl, range.to_owned())]) +} + +fn get_name_edits(expr: &ast::ExprName, module_name: &str, checker: &Checker) -> Option> { + let ast::ExprName { id, range, .. } = expr; + let new_return = match id.as_str() { + "Generator" => "Iterator", + "AsyncGenerator" => "AsyncIterator", + _ => return None, + }; + let (edit1, binding) = checker + .importer() + .get_or_import_symbol( + &ImportRequest::import_from(module_name, new_return), + range.start(), + checker.semantic(), + ) + .ok()?; + let edit2 = Edit::range_replacement(binding, *range); + Some(vec![edit1, edit2]) } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap index b3d9a9624584d..4c07eb9835fd6 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap @@ -1,151 +1,194 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI058.py:7:9: PYI058 Use `Iterator` as the return value for simple `__iter__` methods +PYI058.py:8:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | -6 | class IteratorReturningSimpleGenerator1: -7 | def __iter__(self) -> Generator: # PYI058 (use `Iterator`) +7 | class IteratorReturningSimpleGenerator1: +8 | def __iter__(self) -> Generator: # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -8 | return (x for x in range(42)) +9 | return (x for x in range(42)) | = help: Convert the return annotation of your `__iter__` method to `Iterator` -PYI058.py:11:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods +ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +8 |- def __iter__(self) -> Generator: # PYI058 (use `Iterator`) + 8 |+ def __iter__(self) -> Iterator: # PYI058 (use `Iterator`) +9 9 | return (x for x in range(42)) +10 10 | +11 11 | class IteratorReturningSimpleGenerator2: + +PYI058.py:12:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | -10 | class IteratorReturningSimpleGenerator2: -11 | def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) +11 | class IteratorReturningSimpleGenerator2: +12 | def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -12 | return (x for x in range(42)) +13 | return (x for x in range(42)) | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -8 8 | return (x for x in range(42)) -9 9 | -10 10 | class IteratorReturningSimpleGenerator2: -11 |- def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) - 11 |+ def __iter__(self) -> typing.Iterator: # PYI058 (use `Iterator`) -12 12 | return (x for x in range(42)) -13 13 | -14 14 | class IteratorReturningSimpleGenerator3: - -PYI058.py:15:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -14 | class IteratorReturningSimpleGenerator3: -15 | def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) +9 9 | return (x for x in range(42)) +10 10 | +11 11 | class IteratorReturningSimpleGenerator2: +12 |- def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) + 12 |+ def __iter__(self) -> typing.Iterator: # PYI058 (use `Iterator`) +13 13 | return (x for x in range(42)) +14 14 | +15 15 | class IteratorReturningSimpleGenerator3: + +PYI058.py:16:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +15 | class IteratorReturningSimpleGenerator3: +16 | def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -16 | return (x for x in range(42)) +17 | return (x for x in range(42)) | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -12 12 | return (x for x in range(42)) -13 13 | -14 14 | class IteratorReturningSimpleGenerator3: -15 |- def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) - 15 |+ def __iter__(self) -> collections.abc.Iterator: # PYI058 (use `Iterator`) -16 16 | return (x for x in range(42)) -17 17 | -18 18 | class IteratorReturningSimpleGenerator4: - -PYI058.py:19:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -18 | class IteratorReturningSimpleGenerator4: -19 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) +13 13 | return (x for x in range(42)) +14 14 | +15 15 | class IteratorReturningSimpleGenerator3: +16 |- def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) + 16 |+ def __iter__(self) -> collections.abc.Iterator: # PYI058 (use `Iterator`) +17 17 | return (x for x in range(42)) +18 18 | +19 19 | class IteratorReturningSimpleGenerator4: + +PYI058.py:20:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +19 | class IteratorReturningSimpleGenerator4: +20 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -20 | """Fully documented, because I'm a runtime function!""" -21 | yield from "abcdefg" +21 | """Fully documented, because I'm a runtime function!""" +22 | yield from "abcdefg" | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Unsafe fix -16 16 | return (x for x in range(42)) -17 17 | -18 18 | class IteratorReturningSimpleGenerator4: -19 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) - 19 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: # PYI058 (use `Iterator`) -20 20 | """Fully documented, because I'm a runtime function!""" -21 21 | yield from "abcdefg" -22 22 | return None - -PYI058.py:25:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -24 | class IteratorReturningSimpleGenerator5: -25 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) +17 17 | return (x for x in range(42)) +18 18 | +19 19 | class IteratorReturningSimpleGenerator4: +20 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) + 20 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: # PYI058 (use `Iterator`) +21 21 | """Fully documented, because I'm a runtime function!""" +22 22 | yield from "abcdefg" +23 23 | return None + +PYI058.py:26:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +25 | class IteratorReturningSimpleGenerator5: +26 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -26 | yield "a" -27 | yield "b" +27 | yield "a" +28 | yield "b" | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Unsafe fix -22 22 | return None -23 23 | -24 24 | class IteratorReturningSimpleGenerator5: -25 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) - 25 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: # PYI058 (use `Iterator`) -26 26 | yield "a" -27 27 | yield "b" -28 28 | yield "c" - -PYI058.py:32:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -31 | class AsyncIteratorReturningSimpleAsyncGenerator1: -32 | def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) +23 23 | return None +24 24 | +25 25 | class IteratorReturningSimpleGenerator5: +26 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) + 26 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: # PYI058 (use `Iterator`) +27 27 | yield "a" +28 28 | yield "b" +29 29 | yield "c" + +PYI058.py:33:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +32 | class IteratorReturningSimpleGenerator6: +33 | def __iter__(self) -> Generator[int, None, None]: # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +34 | yield from range(42) + | + = help: Convert the return annotation of your `__iter__` method to `Iterator` + +ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- +30 30 | return +31 31 | +32 32 | class IteratorReturningSimpleGenerator6: +33 |- def __iter__(self) -> Generator[int, None, None]: # PYI058 (use `Iterator`) + 33 |+ def __iter__(self) -> Iterator[int]: # PYI058 (use `Iterator`) +34 34 | yield from range(42) +35 35 | +36 36 | class AsyncIteratorReturningSimpleAsyncGenerator1: + +PYI058.py:37:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +36 | class AsyncIteratorReturningSimpleAsyncGenerator1: +37 | def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -33 | -34 | class AsyncIteratorReturningSimpleAsyncGenerator2: +38 | +39 | class AsyncIteratorReturningSimpleAsyncGenerator2: | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -29 29 | return -30 30 | -31 31 | class AsyncIteratorReturningSimpleAsyncGenerator1: -32 |- def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) - 32 |+ def __aiter__(self) -> typing.AsyncIterator: pass # PYI058 (Use `AsyncIterator`) -33 33 | -34 34 | class AsyncIteratorReturningSimpleAsyncGenerator2: -35 35 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - -PYI058.py:35:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -34 | class AsyncIteratorReturningSimpleAsyncGenerator2: -35 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) +34 34 | yield from range(42) +35 35 | +36 36 | class AsyncIteratorReturningSimpleAsyncGenerator1: +37 |- def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) + 37 |+ def __aiter__(self) -> typing_extensions.AsyncIterator: pass # PYI058 (Use `AsyncIterator`) +38 38 | +39 39 | class AsyncIteratorReturningSimpleAsyncGenerator2: +40 40 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + +PYI058.py:40:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +39 | class AsyncIteratorReturningSimpleAsyncGenerator2: +40 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -36 | -37 | class AsyncIteratorReturningSimpleAsyncGenerator3: +41 | +42 | class AsyncIteratorReturningSimpleAsyncGenerator3: | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -32 32 | def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) -33 33 | -34 34 | class AsyncIteratorReturningSimpleAsyncGenerator2: -35 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - 35 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) -36 36 | -37 37 | class AsyncIteratorReturningSimpleAsyncGenerator3: -38 38 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) - -PYI058.py:38:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -37 | class AsyncIteratorReturningSimpleAsyncGenerator3: -38 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) +37 37 | def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) +38 38 | +39 39 | class AsyncIteratorReturningSimpleAsyncGenerator2: +40 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + 40 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) +41 41 | +42 42 | class AsyncIteratorReturningSimpleAsyncGenerator3: +43 43 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) + +PYI058.py:43:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +42 | class AsyncIteratorReturningSimpleAsyncGenerator3: +43 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -39 | -40 | class CorrectIterator: +44 | +45 | class CorrectIterator: | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -35 35 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) -36 36 | -37 37 | class AsyncIteratorReturningSimpleAsyncGenerator3: -38 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) - 38 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: pass # PYI058 (Use `AsyncIterator`) -39 39 | -40 40 | class CorrectIterator: -41 41 | def __iter__(self) -> Iterator[str]: ... # OK +40 40 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) +41 41 | +42 42 | class AsyncIteratorReturningSimpleAsyncGenerator3: +43 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) + 43 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: pass # PYI058 (Use `AsyncIterator`) +44 44 | +45 45 | class CorrectIterator: +46 46 | def __iter__(self) -> Iterator[str]: ... # OK diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap index a2b9db9f1e1f9..62df041cc4ce1 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap @@ -1,154 +1,198 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI058.pyi:7:9: PYI058 Use `Iterator` as the return value for simple `__iter__` methods - | -6 | class IteratorReturningSimpleGenerator1: -7 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -8 | -9 | class IteratorReturningSimpleGenerator2: - | - = help: Convert the return annotation of your `__iter__` method to `Iterator` - -PYI058.pyi:10:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | - 9 | class IteratorReturningSimpleGenerator2: -10 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) +PYI058.pyi:8:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | + 7 | class IteratorReturningSimpleGenerator1: + 8 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 + 9 | +10 | class IteratorReturningSimpleGenerator2: + | + = help: Convert the return annotation of your `__iter__` method to `Iterator` + +ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +8 |- def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) + 8 |+ def __iter__(self) -> Iterator: ... # PYI058 (use `Iterator`) +9 9 | +10 10 | class IteratorReturningSimpleGenerator2: +11 11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) + +PYI058.pyi:11:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +10 | class IteratorReturningSimpleGenerator2: +11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -11 | -12 | class IteratorReturningSimpleGenerator3: +12 | +13 | class IteratorReturningSimpleGenerator3: | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -7 7 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) -8 8 | -9 9 | class IteratorReturningSimpleGenerator2: -10 |- def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) - 10 |+ def __iter__(self) -> typing.Iterator: ... # PYI058 (use `Iterator`) -11 11 | -12 12 | class IteratorReturningSimpleGenerator3: -13 13 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) - -PYI058.pyi:13:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -12 | class IteratorReturningSimpleGenerator3: -13 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) +8 8 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) +9 9 | +10 10 | class IteratorReturningSimpleGenerator2: +11 |- def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) + 11 |+ def __iter__(self) -> typing.Iterator: ... # PYI058 (use `Iterator`) +12 12 | +13 13 | class IteratorReturningSimpleGenerator3: +14 14 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) + +PYI058.pyi:14:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +13 | class IteratorReturningSimpleGenerator3: +14 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -14 | -15 | class IteratorReturningSimpleGenerator2: +15 | +16 | class IteratorReturningSimpleGenerator4: | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -10 10 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) -11 11 | -12 12 | class IteratorReturningSimpleGenerator3: -13 |- def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) - 13 |+ def __iter__(self) -> collections.abc.Iterator: ... # PYI058 (use `Iterator`) -14 14 | -15 15 | class IteratorReturningSimpleGenerator2: -16 16 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) - -PYI058.pyi:16:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -15 | class IteratorReturningSimpleGenerator2: -16 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) +11 11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) +12 12 | +13 13 | class IteratorReturningSimpleGenerator3: +14 |- def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) + 14 |+ def __iter__(self) -> collections.abc.Iterator: ... # PYI058 (use `Iterator`) +15 15 | +16 16 | class IteratorReturningSimpleGenerator4: +17 17 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) + +PYI058.pyi:17:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +16 | class IteratorReturningSimpleGenerator4: +17 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -17 | -18 | class IteratorReturningSimpleGenerator3: +18 | +19 | class IteratorReturningSimpleGenerator5: | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -13 13 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) -14 14 | -15 15 | class IteratorReturningSimpleGenerator2: -16 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) - 16 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`) -17 17 | -18 18 | class IteratorReturningSimpleGenerator3: -19 19 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) - -PYI058.pyi:19:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -18 | class IteratorReturningSimpleGenerator3: -19 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) +14 14 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) +15 15 | +16 16 | class IteratorReturningSimpleGenerator4: +17 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) + 17 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`) +18 18 | +19 19 | class IteratorReturningSimpleGenerator5: +20 20 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) + +PYI058.pyi:20:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +19 | class IteratorReturningSimpleGenerator5: +20 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) | ^^^^^^^^ PYI058 -20 | -21 | class AsyncIteratorReturningSimpleAsyncGenerator1: +21 | +22 | class IteratorReturningSimpleGenerator6: | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -16 16 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) -17 17 | -18 18 | class IteratorReturningSimpleGenerator3: -19 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) - 19 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`) -20 20 | -21 21 | class AsyncIteratorReturningSimpleAsyncGenerator1: -22 22 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) - -PYI058.pyi:22:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -21 | class AsyncIteratorReturningSimpleAsyncGenerator1: -22 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) +17 17 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) +18 18 | +19 19 | class IteratorReturningSimpleGenerator5: +20 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) + 20 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`) +21 21 | +22 22 | class IteratorReturningSimpleGenerator6: +23 23 | def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) + +PYI058.pyi:23:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +22 | class IteratorReturningSimpleGenerator6: +23 | def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +24 | +25 | class AsyncIteratorReturningSimpleAsyncGenerator1: + | + = help: Convert the return annotation of your `__iter__` method to `Iterator` + +ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- +20 20 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) +21 21 | +22 22 | class IteratorReturningSimpleGenerator6: +23 |- def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) + 23 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) +24 24 | +25 25 | class AsyncIteratorReturningSimpleAsyncGenerator1: +26 26 | def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) + +PYI058.pyi:26:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +25 | class AsyncIteratorReturningSimpleAsyncGenerator1: +26 | def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -23 | -24 | class AsyncIteratorReturningSimpleAsyncGenerator2: +27 | +28 | class AsyncIteratorReturningSimpleAsyncGenerator2: | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -19 19 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) -20 20 | -21 21 | class AsyncIteratorReturningSimpleAsyncGenerator1: -22 |- def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) - 22 |+ def __aiter__(self) -> typing.AsyncIterator: ... # PYI058 (Use `AsyncIterator`) -23 23 | -24 24 | class AsyncIteratorReturningSimpleAsyncGenerator2: -25 25 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - -PYI058.pyi:25:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -24 | class AsyncIteratorReturningSimpleAsyncGenerator2: -25 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) +23 23 | def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) +24 24 | +25 25 | class AsyncIteratorReturningSimpleAsyncGenerator1: +26 |- def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) + 26 |+ def __aiter__(self) -> typing_extensions.AsyncIterator: ... # PYI058 (Use `AsyncIterator`) +27 27 | +28 28 | class AsyncIteratorReturningSimpleAsyncGenerator2: +29 29 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + +PYI058.pyi:29:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +28 | class AsyncIteratorReturningSimpleAsyncGenerator2: +29 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -26 | -27 | class AsyncIteratorReturningSimpleAsyncGenerator3: +30 | +31 | class AsyncIteratorReturningSimpleAsyncGenerator3: | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -22 22 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) -23 23 | -24 24 | class AsyncIteratorReturningSimpleAsyncGenerator2: -25 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - 25 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) -26 26 | -27 27 | class AsyncIteratorReturningSimpleAsyncGenerator3: -28 28 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) - -PYI058.pyi:28:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -27 | class AsyncIteratorReturningSimpleAsyncGenerator3: -28 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) +26 26 | def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) +27 27 | +28 28 | class AsyncIteratorReturningSimpleAsyncGenerator2: +29 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) + 29 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) +30 30 | +31 31 | class AsyncIteratorReturningSimpleAsyncGenerator3: +32 32 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) + +PYI058.pyi:32:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +31 | class AsyncIteratorReturningSimpleAsyncGenerator3: +32 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) | ^^^^^^^^^ PYI058 -29 | -30 | class CorrectIterator: +33 | +34 | class CorrectIterator: | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -25 25 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) -26 26 | -27 27 | class AsyncIteratorReturningSimpleAsyncGenerator3: -28 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) - 28 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) -29 29 | -30 30 | class CorrectIterator: -31 31 | def __iter__(self) -> Iterator[str]: ... # OK +29 29 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) +30 30 | +31 31 | class AsyncIteratorReturningSimpleAsyncGenerator3: +32 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) + 32 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) +33 33 | +34 34 | class CorrectIterator: +35 35 | def __iter__(self) -> Iterator[str]: ... # OK From 79746d5a1fdbb48597722ba5dd0df8d2c7e3f009 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 3 Jan 2024 10:32:21 -0500 Subject: [PATCH 3/5] Misc. minor tweaks --- .../rules/bad_generator_return_type.rs | 71 +++++++++---------- 1 file changed, 34 insertions(+), 37 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs index 6ef44736ea4ea..f11b19b6b6830 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs @@ -6,13 +6,11 @@ use ruff_python_ast as ast; use ruff_python_ast::helpers::map_subscript; use ruff_python_ast::identifier::Identifier; use ruff_python_semantic::SemanticModel; -use ruff_text_size::TextRange; +use ruff_text_size::{Ranged, TextRange}; use crate::checkers::ast::Checker; use crate::importer::ImportRequest; -use itertools::Itertools; - /// ## What it does /// Checks for simple `__iter__` methods that return `Generator`, and for /// simple `__aiter__` methods that return `AsyncGenerator`. @@ -69,8 +67,6 @@ pub struct GeneratorReturnFromIterMethod { } impl Violation for GeneratorReturnFromIterMethod { - // Fixable iff the fully qualified name is being used: - // one of {typing.Generator, typing_extensions.Generator, collections.abc.Generator} const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes; #[derive_message_formats] @@ -93,11 +89,6 @@ impl Violation for GeneratorReturnFromIterMethod { } } -struct YieldTypeInfo { - expr: ast::Expr, - range: TextRange, -} - /// PYI058 pub(crate) fn bad_generator_return_type( function_def: &ast::StmtFunctionDef, @@ -139,13 +130,17 @@ pub(crate) fn bad_generator_return_type( _ => return, }; + // Determine the module from which the existing annotation is imported (e.g., `typing` or + // `collections.abc`) let module_name = { let Some(call_path) = semantic.resolve_call_path(map_subscript(returns)) else { return; }; match (name, call_path.as_slice()) { - ("__iter__", ["typing" | "typing_extensions", "Generator"]) - | ("__aiter__", ["typing" | "typing_extensions", "AsyncGenerator"]) => call_path[0], + ("__iter__", [module_name @ ("typing" | "typing_extensions"), "Generator"]) + | ("__aiter__", [module_name @ ("typing" | "typing_extensions"), "AsyncGenerator"]) => { + module_name + } ("__iter__", ["collections", "abc", "Generator"]) | ("__aiter__", ["collections", "abc", "AsyncGenerator"]) => "collections.abc", _ => return, @@ -160,7 +155,7 @@ pub(crate) fn bad_generator_return_type( let yield_type_info = match returns { ast::Expr::Subscript(ast::ExprSubscript { slice, .. }) => match slice.as_ref() { ast::Expr::Tuple(slice_tuple @ ast::ExprTuple { .. }) => { - if !&slice_tuple + if !slice_tuple .elts .iter() .skip(1) @@ -168,13 +163,13 @@ pub(crate) fn bad_generator_return_type( { return; } - let yield_type = match (name, &slice_tuple.elts[..]) { + let yield_type = match (name, slice_tuple.elts.as_slice()) { ("__iter__", [yield_type, _, _]) => yield_type, ("__aiter__", [yield_type, _]) => yield_type, _ => return, }; Some(YieldTypeInfo { - expr: yield_type.to_owned(), + expr: yield_type, range: slice_tuple.range, }) } @@ -195,9 +190,7 @@ pub(crate) fn bad_generator_return_type( ast::Stmt::Pass(_) => continue, ast::Stmt::Return(ast::StmtReturn { value, .. }) => { if let Some(ret_val) = value { - if yield_encountered - && !matches!(ret_val.as_ref(), ast::Expr::NoneLiteral(_)) - { + if yield_encountered && !ret_val.is_none_literal_expr() { return; } } @@ -234,8 +227,21 @@ pub(crate) fn bad_generator_return_type( checker.diagnostics.push(diagnostic); } +/// Returns `true` if the [`ast::Expr`] is a `None` literal or a `typing.Any` expression. fn is_any_or_none(expr: &ast::Expr, semantic: &SemanticModel) -> bool { - semantic.match_typing_expr(expr, "Any") || matches!(expr, ast::Expr::NoneLiteral(_)) + expr.is_none_literal_expr() || semantic.match_typing_expr(expr, "Any") +} + +#[derive(Debug)] +struct YieldTypeInfo<'a> { + expr: &'a ast::Expr, + range: TextRange, +} + +impl Ranged for YieldTypeInfo<'_> { + fn range(&self) -> TextRange { + self.range + } } fn get_fix( @@ -246,7 +252,7 @@ fn get_fix( yield_type_info: Option, module_name: &str, ) -> Option { - let edits = match returns { + let mut edits = match returns { ast::Expr::Attribute(attribute @ ast::ExprAttribute { .. }) => { get_attribute_edits(attribute) } @@ -261,34 +267,25 @@ fn get_fix( _ => None, }, _ => None, - }; + }?; - let Some(edits) = edits else { - return None; - }; - let [first_edit, ..] = &edits[..] else { - return None; - }; - let mut rest = edits.iter().skip(1).map(ToOwned::to_owned).collect_vec(); if let Some(yield_type_info) = yield_type_info { - rest.push(Edit::range_replacement( + edits.push(Edit::range_replacement( checker.generator().expr(&yield_type_info.expr), - yield_type_info.range, + yield_type_info.range(), )); } - // Mark as unsafe if it's a runtime Python file - // and the body has more than one statement in it. + // Mark as unsafe if it's a runtime Python file and the body has more than one statement in it. let applicability = if is_stub || function_def.body.len() == 1 { Applicability::Safe } else { Applicability::Unsafe }; - Some(Fix::applicable_edits( - first_edit.to_owned(), - rest, - applicability, - )) + + let head = edits.pop()?; + let rest = edits; + Some(Fix::applicable_edits(head, rest, applicability)) } fn get_attribute_edits(expr: &ast::ExprAttribute) -> Option> { From c2fabf352a27470b1b12801d492d8dcd4cb3b90c Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 3 Jan 2024 10:49:46 -0500 Subject: [PATCH 4/5] Use enum types --- .../rules/bad_generator_return_type.rs | 242 ++++++++++-------- ...__flake8_pyi__tests__PYI058_PYI058.py.snap | 55 +++- ..._flake8_pyi__tests__PYI058_PYI058.pyi.snap | 55 +++- 3 files changed, 235 insertions(+), 117 deletions(-) diff --git a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs index f11b19b6b6830..29a064473fe05 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs +++ b/crates/ruff_linter/src/rules/flake8_pyi/rules/bad_generator_return_type.rs @@ -1,5 +1,3 @@ -use std::borrow::ToOwned; - use ruff_diagnostics::{Applicability, Diagnostic, Edit, Fix, FixAvailability, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast as ast; @@ -62,8 +60,8 @@ use crate::importer::ImportRequest; /// more than two statements (including docstrings) in its body. #[violation] pub struct GeneratorReturnFromIterMethod { - better_return_type: String, - method_name: String, + return_type: Iterator, + method: Method, } impl Violation for GeneratorReturnFromIterMethod { @@ -72,19 +70,19 @@ impl Violation for GeneratorReturnFromIterMethod { #[derive_message_formats] fn message(&self) -> String { let GeneratorReturnFromIterMethod { - better_return_type, - method_name, + return_type, + method, } = self; - format!("Use `{better_return_type}` as the return value for simple `{method_name}` methods") + format!("Use `{return_type}` as the return value for simple `{method}` methods") } fn fix_title(&self) -> Option { let GeneratorReturnFromIterMethod { - better_return_type, - method_name, + return_type, + method, } = self; Some(format!( - "Convert the return annotation of your `{method_name}` method to `{better_return_type}`" + "Convert the return annotation of your `{method}` method to `{return_type}`" )) } } @@ -100,12 +98,6 @@ pub(crate) fn bad_generator_return_type( let name = function_def.name.as_str(); - let better_return_type = match name { - "__iter__" => "Iterator", - "__aiter__" => "AsyncIterator", - _ => return, - }; - let semantic = checker.semantic(); if !semantic.current_scope().kind.is_class() { @@ -132,17 +124,33 @@ pub(crate) fn bad_generator_return_type( // Determine the module from which the existing annotation is imported (e.g., `typing` or // `collections.abc`) - let module_name = { + let (method, module, member) = { let Some(call_path) = semantic.resolve_call_path(map_subscript(returns)) else { return; }; match (name, call_path.as_slice()) { - ("__iter__", [module_name @ ("typing" | "typing_extensions"), "Generator"]) - | ("__aiter__", [module_name @ ("typing" | "typing_extensions"), "AsyncGenerator"]) => { - module_name + ("__iter__", ["typing", "Generator"]) => { + (Method::Iter, Module::Typing, Generator::Generator) + } + ("__aiter__", ["typing", "AsyncGenerator"]) => { + (Method::AIter, Module::Typing, Generator::AsyncGenerator) } - ("__iter__", ["collections", "abc", "Generator"]) - | ("__aiter__", ["collections", "abc", "AsyncGenerator"]) => "collections.abc", + ("__iter__", ["typing_extensions", "Generator"]) => { + (Method::Iter, Module::TypingExtensions, Generator::Generator) + } + ("__aiter__", ["typing_extensions", "AsyncGenerator"]) => ( + Method::AIter, + Module::TypingExtensions, + Generator::AsyncGenerator, + ), + ("__iter__", ["collections", "abc", "Generator"]) => { + (Method::Iter, Module::CollectionsAbc, Generator::Generator) + } + ("__aiter__", ["collections", "abc", "AsyncGenerator"]) => ( + Method::AIter, + Module::CollectionsAbc, + Generator::AsyncGenerator, + ), _ => return, } }; @@ -182,8 +190,7 @@ pub(crate) fn bad_generator_return_type( // only emit the lint if it's a simple __(a)iter__ implementation // -- for more complex function bodies, // it's more likely we'll be emitting a false positive here - let is_stub = checker.source_type.is_stub(); - if !is_stub { + if !checker.source_type.is_stub() { let mut yield_encountered = false; for stmt in &function_def.body { match stmt { @@ -209,18 +216,18 @@ pub(crate) fn bad_generator_return_type( }; let mut diagnostic = Diagnostic::new( GeneratorReturnFromIterMethod { - better_return_type: better_return_type.to_string(), - method_name: name.to_string(), + return_type: member.to_iter(), + method, }, function_def.identifier(), ); - if let Some(fix) = get_fix( + if let Some(fix) = generate_fix( function_def, - checker, returns, - is_stub, yield_type_info, - module_name, + module, + member, + checker, ) { diagnostic.set_fix(fix); }; @@ -232,6 +239,47 @@ fn is_any_or_none(expr: &ast::Expr, semantic: &SemanticModel) -> bool { expr.is_none_literal_expr() || semantic.match_typing_expr(expr, "Any") } +/// Generate a [`Fix`] to convert the return type annotation to `Iterator` or `AsyncIterator`. +fn generate_fix( + function_def: &ast::StmtFunctionDef, + returns: &ast::Expr, + yield_type_info: Option, + module: Module, + member: Generator, + checker: &Checker, +) -> Option { + let expr = map_subscript(returns); + + let (import_edit, binding) = checker + .importer() + .get_or_import_symbol( + &ImportRequest::import_from(&module.to_string(), &member.to_iter().to_string()), + expr.start(), + checker.semantic(), + ) + .ok()?; + let binding_edit = Edit::range_replacement(binding, expr.range()); + let yield_edit = yield_type_info.map(|yield_type_info| { + Edit::range_replacement( + checker.generator().expr(yield_type_info.expr), + yield_type_info.range(), + ) + }); + + // Mark as unsafe if it's a runtime Python file and the body has more than one statement in it. + let applicability = if checker.source_type.is_stub() || function_def.body.len() == 1 { + Applicability::Safe + } else { + Applicability::Unsafe + }; + + Some(Fix::applicable_edits( + import_edit, + std::iter::once(binding_edit).chain(yield_edit), + applicability, + )) +} + #[derive(Debug)] struct YieldTypeInfo<'a> { expr: &'a ast::Expr, @@ -244,93 +292,73 @@ impl Ranged for YieldTypeInfo<'_> { } } -fn get_fix( - function_def: &ast::StmtFunctionDef, - checker: &Checker, - returns: &ast::Expr, - is_stub: bool, - yield_type_info: Option, - module_name: &str, -) -> Option { - let mut edits = match returns { - ast::Expr::Attribute(attribute @ ast::ExprAttribute { .. }) => { - get_attribute_edits(attribute) - } - ast::Expr::Name(name @ ast::ExprName { .. }) => get_name_edits(name, module_name, checker), - ast::Expr::Subscript(ast::ExprSubscript { value, .. }) => match value.as_ref() { - ast::Expr::Attribute(attribute @ ast::ExprAttribute { .. }) => { - get_attribute_edits(attribute) - } - ast::Expr::Name(name @ ast::ExprName { .. }) => { - get_name_edits(name, module_name, checker) - } - _ => None, - }, - _ => None, - }?; +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum Module { + Typing, + TypingExtensions, + CollectionsAbc, +} - if let Some(yield_type_info) = yield_type_info { - edits.push(Edit::range_replacement( - checker.generator().expr(&yield_type_info.expr), - yield_type_info.range(), - )); +impl std::fmt::Display for Module { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Module::Typing => write!(f, "typing"), + Module::TypingExtensions => write!(f, "typing_extensions"), + Module::CollectionsAbc => write!(f, "collections.abc"), + } } +} - // Mark as unsafe if it's a runtime Python file and the body has more than one statement in it. - let applicability = if is_stub || function_def.body.len() == 1 { - Applicability::Safe - } else { - Applicability::Unsafe - }; - - let head = edits.pop()?; - let rest = edits; - Some(Fix::applicable_edits(head, rest, applicability)) +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum Method { + Iter, + AIter, } -fn get_attribute_edits(expr: &ast::ExprAttribute) -> Option> { - let ast::ExprAttribute { - value, attr, range, .. - } = expr; +impl std::fmt::Display for Method { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Method::Iter => write!(f, "__iter__"), + Method::AIter => write!(f, "__aiter__"), + } + } +} - let new_return = match attr.as_str() { - "Generator" => "Iterator", - "AsyncGenerator" => "AsyncIterator", - _ => return None, - }; +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum Generator { + Generator, + AsyncGenerator, +} - let module = match value.as_ref() { - ast::Expr::Name(ast::ExprName { id, .. }) => id.to_owned(), - ast::Expr::Attribute(ast::ExprAttribute { attr, value, .. }) => match value.as_ref() { - ast::Expr::Name(ast::ExprName { id, .. }) => format!("{id}.{attr}"), - _ => return None, - }, - _ => return None, - }; +impl std::fmt::Display for Generator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Generator::Generator => write!(f, "Generator"), + Generator::AsyncGenerator => write!(f, "AsyncGenerator"), + } + } +} - if !["typing", "typing_extensions", "collections.abc"].contains(&module.as_str()) { - return None; +impl Generator { + fn to_iter(self) -> Iterator { + match self { + Generator::Generator => Iterator::Iterator, + Generator::AsyncGenerator => Iterator::AsyncIterator, + } } +} - let repl = format!("{module}.{new_return}"); - Some(vec![Edit::range_replacement(repl, range.to_owned())]) +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum Iterator { + Iterator, + AsyncIterator, } -fn get_name_edits(expr: &ast::ExprName, module_name: &str, checker: &Checker) -> Option> { - let ast::ExprName { id, range, .. } = expr; - let new_return = match id.as_str() { - "Generator" => "Iterator", - "AsyncGenerator" => "AsyncIterator", - _ => return None, - }; - let (edit1, binding) = checker - .importer() - .get_or_import_symbol( - &ImportRequest::import_from(module_name, new_return), - range.start(), - checker.semantic(), - ) - .ok()?; - let edit2 = Edit::range_replacement(binding, *range); - Some(vec![edit1, edit2]) +impl std::fmt::Display for Iterator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Iterator::Iterator => write!(f, "Iterator"), + Iterator::AsyncIterator => write!(f, "AsyncIterator"), + } + } } diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap index 4c07eb9835fd6..34cd3db3d520f 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap @@ -54,11 +54,20 @@ PYI058.py:16:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 13 13 | return (x for x in range(42)) 14 14 | 15 15 | class IteratorReturningSimpleGenerator3: 16 |- def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) - 16 |+ def __iter__(self) -> collections.abc.Iterator: # PYI058 (use `Iterator`) + 16 |+ def __iter__(self) -> Iterator: # PYI058 (use `Iterator`) 17 17 | return (x for x in range(42)) 18 18 | 19 19 | class IteratorReturningSimpleGenerator4: @@ -74,11 +83,20 @@ PYI058.py:20:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Unsafe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 17 17 | return (x for x in range(42)) 18 18 | 19 19 | class IteratorReturningSimpleGenerator4: 20 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) - 20 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: # PYI058 (use `Iterator`) + 20 |+ def __iter__(self, /) -> Iterator[str]: # PYI058 (use `Iterator`) 21 21 | """Fully documented, because I'm a runtime function!""" 22 22 | yield from "abcdefg" 23 23 | return None @@ -94,11 +112,20 @@ PYI058.py:26:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Unsafe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 23 23 | return None 24 24 | 25 25 | class IteratorReturningSimpleGenerator5: 26 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) - 26 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: # PYI058 (use `Iterator`) + 26 |+ def __iter__(self, /) -> Iterator[str]: # PYI058 (use `Iterator`) 27 27 | yield "a" 28 28 | yield "b" 29 29 | yield "c" @@ -162,11 +189,20 @@ PYI058.py:40:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `_ = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, AsyncIterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 37 37 | def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) 38 38 | 39 39 | class AsyncIteratorReturningSimpleAsyncGenerator2: 40 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - 40 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) + 40 |+ def __aiter__(self, /) -> AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) 41 41 | 42 42 | class AsyncIteratorReturningSimpleAsyncGenerator3: 43 43 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) @@ -182,11 +218,20 @@ PYI058.py:43:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `_ = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, AsyncIterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 40 40 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) 41 41 | 42 42 | class AsyncIteratorReturningSimpleAsyncGenerator3: 43 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) - 43 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: pass # PYI058 (Use `AsyncIterator`) + 43 |+ def __aiter__(self, /) -> AsyncIterator[str]: pass # PYI058 (Use `AsyncIterator`) 44 44 | 45 45 | class CorrectIterator: 46 46 | def __iter__(self) -> Iterator[str]: ... # OK diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap index 62df041cc4ce1..e761165bc1619 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap @@ -57,11 +57,20 @@ PYI058.pyi:14:9: PYI058 [*] Use `Iterator` as the return value for simple `__ite = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 11 11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) 12 12 | 13 13 | class IteratorReturningSimpleGenerator3: 14 |- def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) - 14 |+ def __iter__(self) -> collections.abc.Iterator: ... # PYI058 (use `Iterator`) + 14 |+ def __iter__(self) -> Iterator: ... # PYI058 (use `Iterator`) 15 15 | 16 16 | class IteratorReturningSimpleGenerator4: 17 17 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) @@ -77,11 +86,20 @@ PYI058.pyi:17:9: PYI058 [*] Use `Iterator` as the return value for simple `__ite = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 14 14 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) 15 15 | 16 16 | class IteratorReturningSimpleGenerator4: 17 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) - 17 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`) + 17 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) 18 18 | 19 19 | class IteratorReturningSimpleGenerator5: 20 20 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) @@ -97,11 +115,20 @@ PYI058.pyi:20:9: PYI058 [*] Use `Iterator` as the return value for simple `__ite = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, Iterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 17 17 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) 18 18 | 19 19 | class IteratorReturningSimpleGenerator5: 20 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) - 20 |+ def __iter__(self, /) -> collections.abc.Iterator[str]: ... # PYI058 (use `Iterator`) + 20 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) 21 21 | 22 22 | class IteratorReturningSimpleGenerator6: 23 23 | def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) @@ -166,11 +193,20 @@ PYI058.pyi:29:9: PYI058 [*] Use `AsyncIterator` as the return value for simple ` = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, AsyncIterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 26 26 | def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) 27 27 | 28 28 | class AsyncIteratorReturningSimpleAsyncGenerator2: 29 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - 29 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) + 29 |+ def __aiter__(self, /) -> AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) 30 30 | 31 31 | class AsyncIteratorReturningSimpleAsyncGenerator3: 32 32 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) @@ -186,11 +222,20 @@ PYI058.pyi:32:9: PYI058 [*] Use `AsyncIterator` as the return value for simple ` = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix +1 1 | import collections.abc +2 2 | import typing +3 3 | import typing_extensions +4 |-from collections.abc import AsyncGenerator, Generator + 4 |+from collections.abc import AsyncGenerator, Generator, AsyncIterator +5 5 | from typing import Any +6 6 | +7 7 | class IteratorReturningSimpleGenerator1: +-------------------------------------------------------------------------------- 29 29 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) 30 30 | 31 31 | class AsyncIteratorReturningSimpleAsyncGenerator3: 32 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) - 32 |+ def __aiter__(self, /) -> collections.abc.AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) + 32 |+ def __aiter__(self, /) -> AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) 33 33 | 34 34 | class CorrectIterator: 35 35 | def __iter__(self) -> Iterator[str]: ... # OK From 2745f771006eee226a43d8885dce753df76c4ae4 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 3 Jan 2024 10:57:44 -0500 Subject: [PATCH 5/5] Update fixtures --- .../test/fixtures/flake8_pyi/PYI058.py | 221 +++++++---- .../test/fixtures/flake8_pyi/PYI058.pyi | 154 +++++--- ...__flake8_pyi__tests__PYI058_PYI058.py.snap | 341 +++++++---------- ..._flake8_pyi__tests__PYI058_PYI058.pyi.snap | 358 ++++++++---------- 4 files changed, 565 insertions(+), 509 deletions(-) diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py index 02fe101e10839..2add612f4b857 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.py @@ -1,95 +1,174 @@ -import collections.abc -import typing -import typing_extensions -from collections.abc import AsyncGenerator, Generator -from typing import Any +def scope(): + from collections.abc import Generator -class IteratorReturningSimpleGenerator1: - def __iter__(self) -> Generator: # PYI058 (use `Iterator`) - return (x for x in range(42)) + class IteratorReturningSimpleGenerator1: + def __iter__(self) -> Generator: + ... # PYI058 (use `Iterator`) -class IteratorReturningSimpleGenerator2: - def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) - return (x for x in range(42)) -class IteratorReturningSimpleGenerator3: - def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) - return (x for x in range(42)) +def scope(): + import typing -class IteratorReturningSimpleGenerator4: - def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) - """Fully documented, because I'm a runtime function!""" - yield from "abcdefg" - return None + class IteratorReturningSimpleGenerator2: + def __iter__(self) -> typing.Generator: + ... # PYI058 (use `Iterator`) -class IteratorReturningSimpleGenerator5: - def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) - yield "a" - yield "b" - yield "c" - return -class IteratorReturningSimpleGenerator6: - def __iter__(self) -> Generator[int, None, None]: # PYI058 (use `Iterator`) - yield from range(42) +def scope(): + import collections.abc -class AsyncIteratorReturningSimpleAsyncGenerator1: - def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) + class IteratorReturningSimpleGenerator3: + def __iter__(self) -> collections.abc.Generator: + ... # PYI058 (use `Iterator`) -class AsyncIteratorReturningSimpleAsyncGenerator2: - def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) -class AsyncIteratorReturningSimpleAsyncGenerator3: - def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) +def scope(): + import collections.abc + from typing import Any -class CorrectIterator: - def __iter__(self) -> Iterator[str]: ... # OK + class IteratorReturningSimpleGenerator4: + def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: + ... # PYI058 (use `Iterator`) -class CorrectAsyncIterator: - def __aiter__(self) -> collections.abc.AsyncIterator[int]: ... # OK -class Fine: - def __iter__(self) -> typing.Self: ... # OK +def scope(): + import collections.abc + import typing -class StrangeButWeWontComplainHere: - def __aiter__(self) -> list[bytes]: ... # OK + class IteratorReturningSimpleGenerator5: + def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: + ... # PYI058 (use `Iterator`) -def __iter__(self) -> Generator: ... # OK (not in class scope) -def __aiter__(self) -> AsyncGenerator: ... # OK (not in class scope) -class IteratorReturningComplexGenerator: - def __iter__(self) -> Generator[str, int, bytes]: ... # OK +def scope(): + from collections.abc import Generator -class AsyncIteratorReturningComplexAsyncGenerator: - def __aiter__(self) -> AsyncGenerator[str, int]: ... # OK + class IteratorReturningSimpleGenerator6: + def __iter__(self, /) -> Generator[str, None, None]: + ... # PYI058 (use `Iterator`) -class ClassWithInvalidAsyncAiterMethod: - async def __aiter__(self) -> AsyncGenerator: ... # OK -class IteratorWithUnusualParameters1: - def __iter__(self, foo) -> Generator: ... # OK +def scope(): + import typing_extensions -class IteratorWithUnusualParameters2: - def __iter__(self, *, bar) -> Generator: ... # OK + class AsyncIteratorReturningSimpleAsyncGenerator1: + def __aiter__( + self, + ) -> typing_extensions.AsyncGenerator: + ... # PYI058 (Use `AsyncIterator`) -class IteratorWithUnusualParameters3: - def __iter__(self, *args) -> Generator: ... # OK -class IteratorWithUnusualParameters4: - def __iter__(self, **kwargs) -> Generator: ... # OK +def scope(): + import collections.abc -class IteratorWithIterMethodThatReturnsThings: - def __iter__(self) -> Generator: # OK - yield - return 42 + class AsyncIteratorReturningSimpleAsyncGenerator2: + def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: + ... # PYI058 (Use `AsyncIterator`) -class IteratorWithIterMethodThatReceivesThingsFromSend: - def __iter__(self) -> Generator: # OK - x = yield 42 -class IteratorWithNonTrivialIterBody: - def __iter__(self) -> Generator: # OK - foo, bar, baz = (1, 2, 3) - yield foo - yield bar - yield baz +def scope(): + import collections.abc + + class AsyncIteratorReturningSimpleAsyncGenerator3: + def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: + ... # PYI058 (Use `AsyncIterator`) + + +def scope(): + from typing import Iterator + + class CorrectIterator: + def __iter__(self) -> Iterator[str]: + ... # OK + + +def scope(): + import collections.abc + + class CorrectAsyncIterator: + def __aiter__(self) -> collections.abc.AsyncIterator[int]: + ... # OK + + +def scope(): + import typing + + class Fine: + def __iter__(self) -> typing.Self: + ... # OK + + +def scope(): + class StrangeButWeWontComplainHere: + def __aiter__(self) -> list[bytes]: + ... # OK + + +def scope(): + from collections.abc import Generator + + def __iter__(self) -> Generator: + ... # OK (not in class scope) + + +def scope(): + from collections.abc import AsyncGenerator + + def __aiter__(self) -> AsyncGenerator: + ... # OK (not in class scope) + + +def scope(): + from collections.abc import Generator + + class IteratorReturningComplexGenerator: + def __iter__(self) -> Generator[str, int, bytes]: + ... # OK + + +def scope(): + from collections.abc import AsyncGenerator + + class AsyncIteratorReturningComplexAsyncGenerator: + def __aiter__(self) -> AsyncGenerator[str, int]: + ... # OK + + +def scope(): + from collections.abc import AsyncGenerator + + class ClassWithInvalidAsyncAiterMethod: + async def __aiter__(self) -> AsyncGenerator: + ... # OK + + +def scope(): + from collections.abc import Generator + + class IteratorWithUnusualParameters1: + def __iter__(self, foo) -> Generator: + ... # OK + + +def scope(): + from collections.abc import Generator + + class IteratorWithUnusualParameters2: + def __iter__(self, *, bar) -> Generator: + ... # OK + + +def scope(): + from collections.abc import Generator + + class IteratorWithUnusualParameters3: + def __iter__(self, *args) -> Generator: + ... # OK + + +def scope(): + from collections.abc import Generator + + class IteratorWithUnusualParameters4: + def __iter__(self, **kwargs) -> Generator: + ... # OK diff --git a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi index a1d747e604ee0..14a904a4e9839 100644 --- a/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi +++ b/crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI058.pyi @@ -1,68 +1,128 @@ -import collections.abc -import typing -import typing_extensions -from collections.abc import AsyncGenerator, Generator -from typing import Any +def scope(): + from collections.abc import Generator -class IteratorReturningSimpleGenerator1: - def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) + class IteratorReturningSimpleGenerator1: + def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) -class IteratorReturningSimpleGenerator2: - def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) +def scope(): + import typing -class IteratorReturningSimpleGenerator3: - def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) + class IteratorReturningSimpleGenerator2: + def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) -class IteratorReturningSimpleGenerator4: - def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) +def scope(): + import collections.abc -class IteratorReturningSimpleGenerator5: - def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) + class IteratorReturningSimpleGenerator3: + def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) -class IteratorReturningSimpleGenerator6: - def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) +def scope(): + import collections.abc + from typing import Any -class AsyncIteratorReturningSimpleAsyncGenerator1: - def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) + class IteratorReturningSimpleGenerator4: + def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) -class AsyncIteratorReturningSimpleAsyncGenerator2: - def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) +def scope(): + import collections.abc + import typing -class AsyncIteratorReturningSimpleAsyncGenerator3: - def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) + class IteratorReturningSimpleGenerator5: + def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) -class CorrectIterator: - def __iter__(self) -> Iterator[str]: ... # OK +def scope(): + from collections.abc import Generator -class CorrectAsyncIterator: - def __aiter__(self) -> collections.abc.AsyncIterator[int]: ... # OK + class IteratorReturningSimpleGenerator6: + def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) -class Fine: - def __iter__(self) -> typing.Self: ... # OK +def scope(): + import typing_extensions -class StrangeButWeWontComplainHere: - def __aiter__(self) -> list[bytes]: ... # OK + class AsyncIteratorReturningSimpleAsyncGenerator1: + def __aiter__(self,) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) -def __iter__(self) -> Generator: ... # OK (not in class scope) -def __aiter__(self) -> AsyncGenerator: ... # OK (not in class scope) +def scope(): + import collections.abc -class IteratorReturningComplexGenerator: - def __iter__(self) -> Generator[str, int, bytes]: ... # OK + class AsyncIteratorReturningSimpleAsyncGenerator3: + def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: + ... # PYI058 (Use `AsyncIterator`) -class AsyncIteratorReturningComplexAsyncGenerator: - def __aiter__(self) -> AsyncGenerator[str, int]: ... # OK +def scope(): + import collections.abc -class ClassWithInvalidAsyncAiterMethod: - async def __aiter__(self) -> AsyncGenerator: ... # OK + class AsyncIteratorReturningSimpleAsyncGenerator3: + def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) -class IteratorWithUnusualParameters1: - def __iter__(self, foo) -> Generator: ... # OK +def scope(): + from typing import Iterator -class IteratorWithUnusualParameters2: - def __iter__(self, *, bar) -> Generator: ... # OK + class CorrectIterator: + def __iter__(self) -> Iterator[str]: ... # OK -class IteratorWithUnusualParameters3: - def __iter__(self, *args) -> Generator: ... # OK +def scope(): + import collections.abc -class IteratorWithUnusualParameters4: - def __iter__(self, **kwargs) -> Generator: ... # OK + class CorrectAsyncIterator: + def __aiter__(self) -> collections.abc.AsyncIterator[int]: ... # OK + +def scope(): + import typing + + class Fine: + def __iter__(self) -> typing.Self: ... # OK + +def scope(): + class StrangeButWeWontComplainHere: + def __aiter__(self) -> list[bytes]: ... # OK + +def scope(): + from collections.abc import Generator + def __iter__(self) -> Generator: ... # OK (not in class scope) + +def scope(): + from collections.abc import AsyncGenerator + def __aiter__(self) -> AsyncGenerator: ... # OK (not in class scope) + +def scope(): + from collections.abc import Generator + + class IteratorReturningComplexGenerator: + def __iter__(self) -> Generator[str, int, bytes]: ... # OK + +def scope(): + from collections.abc import AsyncGenerator + + class AsyncIteratorReturningComplexAsyncGenerator: + def __aiter__(self) -> AsyncGenerator[str, int]: ... # OK + +def scope(): + from collections.abc import AsyncGenerator + + class ClassWithInvalidAsyncAiterMethod: + async def __aiter__(self) -> AsyncGenerator: ... # OK + +def scope(): + from collections.abc import Generator + + class IteratorWithUnusualParameters1: + def __iter__(self, foo) -> Generator: ... # OK + +def scope(): + from collections.abc import Generator + + class IteratorWithUnusualParameters2: + def __iter__(self, *, bar) -> Generator: ... # OK + +def scope(): + from collections.abc import Generator + + class IteratorWithUnusualParameters3: + def __iter__(self, *args) -> Generator: ... # OK + +def scope(): + from collections.abc import Generator + + class IteratorWithUnusualParameters4: + def __iter__(self, **kwargs) -> Generator: ... # OK diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap index 34cd3db3d520f..e5e20116dcad9 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.py.snap @@ -1,239 +1,184 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI058.py:8:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods +PYI058.py:5:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | -7 | class IteratorReturningSimpleGenerator1: -8 | def __iter__(self) -> Generator: # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -9 | return (x for x in range(42)) +4 | class IteratorReturningSimpleGenerator1: +5 | def __iter__(self) -> Generator: + | ^^^^^^^^ PYI058 +6 | ... # PYI058 (use `Iterator`) | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: -8 |- def __iter__(self) -> Generator: # PYI058 (use `Iterator`) - 8 |+ def __iter__(self) -> Iterator: # PYI058 (use `Iterator`) -9 9 | return (x for x in range(42)) -10 10 | -11 11 | class IteratorReturningSimpleGenerator2: - -PYI058.py:12:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -11 | class IteratorReturningSimpleGenerator2: -12 | def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -13 | return (x for x in range(42)) + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | +4 5 | class IteratorReturningSimpleGenerator1: +5 |- def __iter__(self) -> Generator: + 6 |+ def __iter__(self) -> Iterator: +6 7 | ... # PYI058 (use `Iterator`) +7 8 | +8 9 | + +PYI058.py:13:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +12 | class IteratorReturningSimpleGenerator2: +13 | def __iter__(self) -> typing.Generator: + | ^^^^^^^^ PYI058 +14 | ... # PYI058 (use `Iterator`) | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -9 9 | return (x for x in range(42)) -10 10 | -11 11 | class IteratorReturningSimpleGenerator2: -12 |- def __iter__(self) -> typing.Generator: # PYI058 (use `Iterator`) - 12 |+ def __iter__(self) -> typing.Iterator: # PYI058 (use `Iterator`) -13 13 | return (x for x in range(42)) -14 14 | -15 15 | class IteratorReturningSimpleGenerator3: - -PYI058.py:16:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -15 | class IteratorReturningSimpleGenerator3: -16 | def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -17 | return (x for x in range(42)) +10 10 | import typing +11 11 | +12 12 | class IteratorReturningSimpleGenerator2: +13 |- def __iter__(self) -> typing.Generator: + 13 |+ def __iter__(self) -> typing.Iterator: +14 14 | ... # PYI058 (use `Iterator`) +15 15 | +16 16 | + +PYI058.py:21:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +20 | class IteratorReturningSimpleGenerator3: +21 | def __iter__(self) -> collections.abc.Generator: + | ^^^^^^^^ PYI058 +22 | ... # PYI058 (use `Iterator`) | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -13 13 | return (x for x in range(42)) -14 14 | -15 15 | class IteratorReturningSimpleGenerator3: -16 |- def __iter__(self) -> collections.abc.Generator: # PYI058 (use `Iterator`) - 16 |+ def __iter__(self) -> Iterator: # PYI058 (use `Iterator`) -17 17 | return (x for x in range(42)) -18 18 | -19 19 | class IteratorReturningSimpleGenerator4: - -PYI058.py:20:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -19 | class IteratorReturningSimpleGenerator4: -20 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -21 | """Fully documented, because I'm a runtime function!""" -22 | yield from "abcdefg" +18 19 | import collections.abc +19 20 | +20 21 | class IteratorReturningSimpleGenerator3: +21 |- def __iter__(self) -> collections.abc.Generator: + 22 |+ def __iter__(self) -> Iterator: +22 23 | ... # PYI058 (use `Iterator`) +23 24 | +24 25 | + +PYI058.py:30:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +29 | class IteratorReturningSimpleGenerator4: +30 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: + | ^^^^^^^^ PYI058 +31 | ... # PYI058 (use `Iterator`) | = help: Convert the return annotation of your `__iter__` method to `Iterator` -ℹ Unsafe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: +ℹ Safe fix + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -17 17 | return (x for x in range(42)) -18 18 | -19 19 | class IteratorReturningSimpleGenerator4: -20 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`) - 20 |+ def __iter__(self, /) -> Iterator[str]: # PYI058 (use `Iterator`) -21 21 | """Fully documented, because I'm a runtime function!""" -22 22 | yield from "abcdefg" -23 23 | return None - -PYI058.py:26:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -25 | class IteratorReturningSimpleGenerator5: -26 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -27 | yield "a" -28 | yield "b" +27 28 | from typing import Any +28 29 | +29 30 | class IteratorReturningSimpleGenerator4: +30 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: + 31 |+ def __iter__(self, /) -> Iterator[str]: +31 32 | ... # PYI058 (use `Iterator`) +32 33 | +33 34 | + +PYI058.py:39:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +38 | class IteratorReturningSimpleGenerator5: +39 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: + | ^^^^^^^^ PYI058 +40 | ... # PYI058 (use `Iterator`) | = help: Convert the return annotation of your `__iter__` method to `Iterator` -ℹ Unsafe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: +ℹ Safe fix + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -23 23 | return None -24 24 | -25 25 | class IteratorReturningSimpleGenerator5: -26 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`) - 26 |+ def __iter__(self, /) -> Iterator[str]: # PYI058 (use `Iterator`) -27 27 | yield "a" -28 28 | yield "b" -29 29 | yield "c" - -PYI058.py:33:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -32 | class IteratorReturningSimpleGenerator6: -33 | def __iter__(self) -> Generator[int, None, None]: # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -34 | yield from range(42) +36 37 | import typing +37 38 | +38 39 | class IteratorReturningSimpleGenerator5: +39 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: + 40 |+ def __iter__(self, /) -> Iterator[str]: +40 41 | ... # PYI058 (use `Iterator`) +41 42 | +42 43 | + +PYI058.py:47:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +46 | class IteratorReturningSimpleGenerator6: +47 | def __iter__(self, /) -> Generator[str, None, None]: + | ^^^^^^^^ PYI058 +48 | ... # PYI058 (use `Iterator`) | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -30 30 | return -31 31 | -32 32 | class IteratorReturningSimpleGenerator6: -33 |- def __iter__(self) -> Generator[int, None, None]: # PYI058 (use `Iterator`) - 33 |+ def __iter__(self) -> Iterator[int]: # PYI058 (use `Iterator`) -34 34 | yield from range(42) -35 35 | -36 36 | class AsyncIteratorReturningSimpleAsyncGenerator1: - -PYI058.py:37:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -36 | class AsyncIteratorReturningSimpleAsyncGenerator1: -37 | def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) - | ^^^^^^^^^ PYI058 -38 | -39 | class AsyncIteratorReturningSimpleAsyncGenerator2: +44 45 | from collections.abc import Generator +45 46 | +46 47 | class IteratorReturningSimpleGenerator6: +47 |- def __iter__(self, /) -> Generator[str, None, None]: + 48 |+ def __iter__(self, /) -> Iterator[str]: +48 49 | ... # PYI058 (use `Iterator`) +49 50 | +50 51 | + +PYI058.py:55:13: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +54 | class AsyncIteratorReturningSimpleAsyncGenerator1: +55 | def __aiter__( + | ^^^^^^^^^ PYI058 +56 | self, +57 | ) -> typing_extensions.AsyncGenerator: | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -34 34 | yield from range(42) -35 35 | -36 36 | class AsyncIteratorReturningSimpleAsyncGenerator1: -37 |- def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) - 37 |+ def __aiter__(self) -> typing_extensions.AsyncIterator: pass # PYI058 (Use `AsyncIterator`) -38 38 | -39 39 | class AsyncIteratorReturningSimpleAsyncGenerator2: -40 40 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - -PYI058.py:40:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -39 | class AsyncIteratorReturningSimpleAsyncGenerator2: -40 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - | ^^^^^^^^^ PYI058 -41 | -42 | class AsyncIteratorReturningSimpleAsyncGenerator3: - | - = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` - -ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, AsyncIterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: --------------------------------------------------------------------------------- -37 37 | def __aiter__(self) -> typing_extensions.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`) -38 38 | -39 39 | class AsyncIteratorReturningSimpleAsyncGenerator2: -40 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - 40 |+ def __aiter__(self, /) -> AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) -41 41 | -42 42 | class AsyncIteratorReturningSimpleAsyncGenerator3: -43 43 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) - -PYI058.py:43:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -42 | class AsyncIteratorReturningSimpleAsyncGenerator3: -43 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) - | ^^^^^^^^^ PYI058 -44 | -45 | class CorrectIterator: +54 54 | class AsyncIteratorReturningSimpleAsyncGenerator1: +55 55 | def __aiter__( +56 56 | self, +57 |- ) -> typing_extensions.AsyncGenerator: + 57 |+ ) -> typing_extensions.AsyncIterator: +58 58 | ... # PYI058 (Use `AsyncIterator`) +59 59 | +60 60 | + +PYI058.py:73:13: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +72 | class AsyncIteratorReturningSimpleAsyncGenerator3: +73 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: + | ^^^^^^^^^ PYI058 +74 | ... # PYI058 (Use `AsyncIterator`) | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, AsyncIterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import AsyncIterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -40 40 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) -41 41 | -42 42 | class AsyncIteratorReturningSimpleAsyncGenerator3: -43 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`) - 43 |+ def __aiter__(self, /) -> AsyncIterator[str]: pass # PYI058 (Use `AsyncIterator`) -44 44 | -45 45 | class CorrectIterator: -46 46 | def __iter__(self) -> Iterator[str]: ... # OK +70 71 | import collections.abc +71 72 | +72 73 | class AsyncIteratorReturningSimpleAsyncGenerator3: +73 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: + 74 |+ def __aiter__(self, /) -> AsyncIterator[str]: +74 75 | ... # PYI058 (Use `AsyncIterator`) +75 76 | +76 77 | diff --git a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap index e761165bc1619..88ed75b49511d 100644 --- a/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap +++ b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__PYI058_PYI058.pyi.snap @@ -1,243 +1,215 @@ --- source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs --- -PYI058.pyi:8:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | - 7 | class IteratorReturningSimpleGenerator1: - 8 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 - 9 | -10 | class IteratorReturningSimpleGenerator2: - | - = help: Convert the return annotation of your `__iter__` method to `Iterator` +PYI058.pyi:5:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +4 | class IteratorReturningSimpleGenerator1: +5 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +6 | +7 | def scope(): + | + = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: -8 |- def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) - 8 |+ def __iter__(self) -> Iterator: ... # PYI058 (use `Iterator`) -9 9 | -10 10 | class IteratorReturningSimpleGenerator2: -11 11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) - -PYI058.pyi:11:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -10 | class IteratorReturningSimpleGenerator2: -11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | +4 5 | class IteratorReturningSimpleGenerator1: +5 |- def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) + 6 |+ def __iter__(self) -> Iterator: ... # PYI058 (use `Iterator`) +6 7 | +7 8 | def scope(): +8 9 | import typing + +PYI058.pyi:11:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +10 | class IteratorReturningSimpleGenerator2: +11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 12 | -13 | class IteratorReturningSimpleGenerator3: +13 | def scope(): | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -8 8 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`) +8 8 | import typing 9 9 | -10 10 | class IteratorReturningSimpleGenerator2: -11 |- def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) - 11 |+ def __iter__(self) -> typing.Iterator: ... # PYI058 (use `Iterator`) +10 10 | class IteratorReturningSimpleGenerator2: +11 |- def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) + 11 |+ def __iter__(self) -> typing.Iterator: ... # PYI058 (use `Iterator`) 12 12 | -13 13 | class IteratorReturningSimpleGenerator3: -14 14 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) +13 13 | def scope(): +14 14 | import collections.abc -PYI058.pyi:14:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods +PYI058.pyi:17:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods | -13 | class IteratorReturningSimpleGenerator3: -14 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -15 | -16 | class IteratorReturningSimpleGenerator4: +16 | class IteratorReturningSimpleGenerator3: +17 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +18 | +19 | def scope(): | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -11 11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`) -12 12 | -13 13 | class IteratorReturningSimpleGenerator3: -14 |- def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) - 14 |+ def __iter__(self) -> Iterator: ... # PYI058 (use `Iterator`) -15 15 | -16 16 | class IteratorReturningSimpleGenerator4: -17 17 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) - -PYI058.pyi:17:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -16 | class IteratorReturningSimpleGenerator4: -17 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -18 | -19 | class IteratorReturningSimpleGenerator5: +14 15 | import collections.abc +15 16 | +16 17 | class IteratorReturningSimpleGenerator3: +17 |- def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) + 18 |+ def __iter__(self) -> Iterator: ... # PYI058 (use `Iterator`) +18 19 | +19 20 | def scope(): +20 21 | import collections.abc + +PYI058.pyi:24:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +23 | class IteratorReturningSimpleGenerator4: +24 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +25 | +26 | def scope(): | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -14 14 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`) -15 15 | -16 16 | class IteratorReturningSimpleGenerator4: -17 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) - 17 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) -18 18 | -19 19 | class IteratorReturningSimpleGenerator5: -20 20 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) - -PYI058.pyi:20:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -19 | class IteratorReturningSimpleGenerator5: -20 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -21 | -22 | class IteratorReturningSimpleGenerator6: +21 22 | from typing import Any +22 23 | +23 24 | class IteratorReturningSimpleGenerator4: +24 |- def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) + 25 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) +25 26 | +26 27 | def scope(): +27 28 | import collections.abc + +PYI058.pyi:31:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +30 | class IteratorReturningSimpleGenerator5: +31 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +32 | +33 | def scope(): | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -17 17 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`) -18 18 | -19 19 | class IteratorReturningSimpleGenerator5: -20 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) - 20 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) -21 21 | -22 22 | class IteratorReturningSimpleGenerator6: -23 23 | def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) - -PYI058.pyi:23:9: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods - | -22 | class IteratorReturningSimpleGenerator6: -23 | def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) - | ^^^^^^^^ PYI058 -24 | -25 | class AsyncIteratorReturningSimpleAsyncGenerator1: +28 29 | import typing +29 30 | +30 31 | class IteratorReturningSimpleGenerator5: +31 |- def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) + 32 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) +32 33 | +33 34 | def scope(): +34 35 | from collections.abc import Generator + +PYI058.pyi:37:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods + | +36 | class IteratorReturningSimpleGenerator6: +37 | def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) + | ^^^^^^^^ PYI058 +38 | +39 | def scope(): | = help: Convert the return annotation of your `__iter__` method to `Iterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, Iterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import Iterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -20 20 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`) -21 21 | -22 22 | class IteratorReturningSimpleGenerator6: -23 |- def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) - 23 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) -24 24 | -25 25 | class AsyncIteratorReturningSimpleAsyncGenerator1: -26 26 | def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) - -PYI058.pyi:26:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -25 | class AsyncIteratorReturningSimpleAsyncGenerator1: -26 | def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) - | ^^^^^^^^^ PYI058 -27 | -28 | class AsyncIteratorReturningSimpleAsyncGenerator2: +34 35 | from collections.abc import Generator +35 36 | +36 37 | class IteratorReturningSimpleGenerator6: +37 |- def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) + 38 |+ def __iter__(self, /) -> Iterator[str]: ... # PYI058 (use `Iterator`) +38 39 | +39 40 | def scope(): +40 41 | import typing_extensions + +PYI058.pyi:43:13: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +42 | class AsyncIteratorReturningSimpleAsyncGenerator1: +43 | def __aiter__(self,) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) + | ^^^^^^^^^ PYI058 +44 | +45 | def scope(): | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -23 23 | def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`) -24 24 | -25 25 | class AsyncIteratorReturningSimpleAsyncGenerator1: -26 |- def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) - 26 |+ def __aiter__(self) -> typing_extensions.AsyncIterator: ... # PYI058 (Use `AsyncIterator`) -27 27 | -28 28 | class AsyncIteratorReturningSimpleAsyncGenerator2: -29 29 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - -PYI058.pyi:29:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -28 | class AsyncIteratorReturningSimpleAsyncGenerator2: -29 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - | ^^^^^^^^^ PYI058 -30 | -31 | class AsyncIteratorReturningSimpleAsyncGenerator3: +40 40 | import typing_extensions +41 41 | +42 42 | class AsyncIteratorReturningSimpleAsyncGenerator1: +43 |- def __aiter__(self,) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) + 43 |+ def __aiter__(self,) -> typing_extensions.AsyncIterator: ... # PYI058 (Use `AsyncIterator`) +44 44 | +45 45 | def scope(): +46 46 | import collections.abc + +PYI058.pyi:49:13: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +48 | class AsyncIteratorReturningSimpleAsyncGenerator3: +49 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: + | ^^^^^^^^^ PYI058 +50 | ... # PYI058 (Use `AsyncIterator`) | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, AsyncIterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import AsyncIterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -26 26 | def __aiter__(self) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`) -27 27 | -28 28 | class AsyncIteratorReturningSimpleAsyncGenerator2: -29 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) - 29 |+ def __aiter__(self, /) -> AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) -30 30 | -31 31 | class AsyncIteratorReturningSimpleAsyncGenerator3: -32 32 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) - -PYI058.pyi:32:9: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods - | -31 | class AsyncIteratorReturningSimpleAsyncGenerator3: -32 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) - | ^^^^^^^^^ PYI058 -33 | -34 | class CorrectIterator: +46 47 | import collections.abc +47 48 | +48 49 | class AsyncIteratorReturningSimpleAsyncGenerator3: +49 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: + 50 |+ def __aiter__(self, /) -> AsyncIterator[str]: +50 51 | ... # PYI058 (Use `AsyncIterator`) +51 52 | +52 53 | def scope(): + +PYI058.pyi:56:13: PYI058 [*] Use `AsyncIterator` as the return value for simple `__aiter__` methods + | +55 | class AsyncIteratorReturningSimpleAsyncGenerator3: +56 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) + | ^^^^^^^^^ PYI058 +57 | +58 | def scope(): | = help: Convert the return annotation of your `__aiter__` method to `AsyncIterator` ℹ Safe fix -1 1 | import collections.abc -2 2 | import typing -3 3 | import typing_extensions -4 |-from collections.abc import AsyncGenerator, Generator - 4 |+from collections.abc import AsyncGenerator, Generator, AsyncIterator -5 5 | from typing import Any -6 6 | -7 7 | class IteratorReturningSimpleGenerator1: + 1 |+from collections.abc import AsyncIterator +1 2 | def scope(): +2 3 | from collections.abc import Generator +3 4 | -------------------------------------------------------------------------------- -29 29 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`) -30 30 | -31 31 | class AsyncIteratorReturningSimpleAsyncGenerator3: -32 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) - 32 |+ def __aiter__(self, /) -> AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) -33 33 | -34 34 | class CorrectIterator: -35 35 | def __iter__(self) -> Iterator[str]: ... # OK +53 54 | import collections.abc +54 55 | +55 56 | class AsyncIteratorReturningSimpleAsyncGenerator3: +56 |- def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`) + 57 |+ def __aiter__(self, /) -> AsyncIterator[str]: ... # PYI058 (Use `AsyncIterator`) +57 58 | +58 59 | def scope(): +59 60 | from typing import Iterator