mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 14:52:01 +00:00
[flake8-pyi] Add autofix for PYI058 (#9355)
## Summary This PR adds an autofix for the newly added PYI058 rule (added in #9313). ~~The PR's current implementation is that the fix is only available if the fully qualified name of `Generator` or `AsyncGenerator` is being used:~~ - ~~`-> typing.Generator` is converted to `-> typing.Iterator`;~~ - ~~`-> collections.abc.AsyncGenerator[str, Any]` is converted to `-> collections.abc.AsyncIterator[str]`;~~ - ~~but `-> Generator` is _not_ converted to `-> Iterator`. (It would require more work to figure out if `Iterator` was already imported or not. And if it wasn't, where should we import it from? `typing`, `typing_extensions`, or `collections.abc`? It seems much more complicated.)~~ The fix is marked as always safe for `__iter__` or `__aiter__` methods in `.pyi` files, but unsafe for all such methods in `.py` files that have more than one statement in the method body. This felt slightly fiddly to accomplish, but I couldn't _see_ any utilities in https://github.com/astral-sh/ruff/tree/main/crates/ruff_linter/src/fix that would have made it simpler to implement. Lmk if I'm missing something, though -- my first time implementing an autofix! :) ## Test Plan `cargo test` / `cargo insta review`.
This commit is contained in:
parent
dc5094d42a
commit
1ffc738c84
5 changed files with 853 additions and 234 deletions
|
@ -1,11 +1,13 @@
|
|||
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::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::importer::ImportRequest;
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for simple `__iter__` methods that return `Generator`, and for
|
||||
|
@ -48,20 +50,40 @@ 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,
|
||||
method_name: String,
|
||||
return_type: Iterator,
|
||||
method: Method,
|
||||
}
|
||||
|
||||
impl Violation for GeneratorReturnFromIterMethod {
|
||||
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||
|
||||
#[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<String> {
|
||||
let GeneratorReturnFromIterMethod {
|
||||
return_type,
|
||||
method,
|
||||
} = self;
|
||||
Some(format!(
|
||||
"Convert the return annotation of your `{method}` method to `{return_type}`"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,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() {
|
||||
|
@ -106,44 +122,68 @@ 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;
|
||||
// Determine the module from which the existing annotation is imported (e.g., `typing` or
|
||||
// `collections.abc`)
|
||||
let (method, module, member) = {
|
||||
let Some(call_path) = semantic.resolve_call_path(map_subscript(returns)) else {
|
||||
return;
|
||||
};
|
||||
match (name, call_path.as_slice()) {
|
||||
("__iter__", ["typing", "Generator"]) => {
|
||||
(Method::Iter, Module::Typing, Generator::Generator)
|
||||
}
|
||||
("__aiter__", ["typing", "AsyncGenerator"]) => {
|
||||
(Method::AIter, Module::Typing, Generator::AsyncGenerator)
|
||||
}
|
||||
("__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,
|
||||
}
|
||||
};
|
||||
|
||||
// `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.as_slice()) {
|
||||
("__iter__", [yield_type, _, _]) => yield_type,
|
||||
("__aiter__", [yield_type, _]) => yield_type,
|
||||
_ => return,
|
||||
};
|
||||
Some(YieldTypeInfo {
|
||||
expr: yield_type,
|
||||
range: slice_tuple.range,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
_ => return,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// For .py files (runtime Python!),
|
||||
|
@ -157,9 +197,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;
|
||||
}
|
||||
}
|
||||
|
@ -176,16 +214,151 @@ 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(),
|
||||
return_type: member.to_iter(),
|
||||
method,
|
||||
},
|
||||
function_def.identifier(),
|
||||
));
|
||||
);
|
||||
if let Some(fix) = generate_fix(
|
||||
function_def,
|
||||
returns,
|
||||
yield_type_info,
|
||||
module,
|
||||
member,
|
||||
checker,
|
||||
) {
|
||||
diagnostic.set_fix(fix);
|
||||
};
|
||||
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")
|
||||
}
|
||||
|
||||
/// 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<YieldTypeInfo>,
|
||||
module: Module,
|
||||
member: Generator,
|
||||
checker: &Checker,
|
||||
) -> Option<Fix> {
|
||||
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,
|
||||
range: TextRange,
|
||||
}
|
||||
|
||||
impl Ranged for YieldTypeInfo<'_> {
|
||||
fn range(&self) -> TextRange {
|
||||
self.range
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum Module {
|
||||
Typing,
|
||||
TypingExtensions,
|
||||
CollectionsAbc,
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum Method {
|
||||
Iter,
|
||||
AIter,
|
||||
}
|
||||
|
||||
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__"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum Generator {
|
||||
Generator,
|
||||
AsyncGenerator,
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Generator {
|
||||
fn to_iter(self) -> Iterator {
|
||||
match self {
|
||||
Generator::Generator => Iterator::Iterator,
|
||||
Generator::AsyncGenerator => Iterator::AsyncIterator,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
enum Iterator {
|
||||
Iterator,
|
||||
AsyncIterator,
|
||||
}
|
||||
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,57 +1,184 @@
|
|||
---
|
||||
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:5:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods
|
||||
|
|
||||
6 | class IteratorReturningSimpleGenerator1:
|
||||
7 | def __iter__(self) -> Generator: # PYI058 (use `Iterator`)
|
||||
| ^^^^^^^^ PYI058
|
||||
8 | 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`
|
||||
|
||||
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`)
|
||||
| ^^^^^^^^ PYI058
|
||||
12 | """Fully documented, because I'm a runtime function!"""
|
||||
13 | yield from "abcdefg"
|
||||
|
|
||||
ℹ Safe fix
|
||||
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:17:9: PYI058 Use `Iterator` as the return value for simple `__iter__` methods
|
||||
PYI058.py:13:13: 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`)
|
||||
| ^^^^^^^^ PYI058
|
||||
18 | yield "a"
|
||||
19 | yield "b"
|
||||
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`
|
||||
|
||||
PYI058.py:24:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods
|
||||
|
|
||||
23 | class AsyncIteratorReturningSimpleAsyncGenerator1:
|
||||
24 | def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`)
|
||||
| ^^^^^^^^^ PYI058
|
||||
25 |
|
||||
26 | class AsyncIteratorReturningSimpleAsyncGenerator2:
|
||||
|
|
||||
ℹ Safe fix
|
||||
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:27:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods
|
||||
PYI058.py:21:13: PYI058 [*] Use `Iterator` as the return value for simple `__iter__` methods
|
||||
|
|
||||
26 | class AsyncIteratorReturningSimpleAsyncGenerator2:
|
||||
27 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`)
|
||||
| ^^^^^^^^^ PYI058
|
||||
28 |
|
||||
29 | class AsyncIteratorReturningSimpleAsyncGenerator3:
|
||||
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`
|
||||
|
||||
PYI058.py:30:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods
|
||||
ℹ Safe fix
|
||||
1 |+from collections.abc import Iterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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 AsyncIteratorReturningSimpleAsyncGenerator3:
|
||||
30 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`)
|
||||
| ^^^^^^^^^ PYI058
|
||||
31 |
|
||||
32 | class CorrectIterator:
|
||||
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`
|
||||
|
||||
ℹ Safe fix
|
||||
1 |+from collections.abc import Iterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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`
|
||||
|
||||
ℹ Safe fix
|
||||
1 |+from collections.abc import Iterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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 |+from collections.abc import Iterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
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 |+from collections.abc import AsyncIterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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 |
|
||||
|
||||
|
||||
|
|
|
@ -1,58 +1,215 @@
|
|||
---
|
||||
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
|
||||
PYI058.pyi:5:13: 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:
|
||||
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`
|
||||
|
||||
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`)
|
||||
| ^^^^^^^^ PYI058
|
||||
11 |
|
||||
12 | class IteratorReturningSimpleGenerator3:
|
||||
|
|
||||
ℹ Safe fix
|
||||
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:13:9: PYI058 Use `Iterator` as the return value for simple `__iter__` methods
|
||||
PYI058.pyi:11:13: 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`)
|
||||
| ^^^^^^^^ PYI058
|
||||
14 |
|
||||
15 | class AsyncIteratorReturningSimpleAsyncGenerator1:
|
||||
10 | class IteratorReturningSimpleGenerator2:
|
||||
11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`)
|
||||
| ^^^^^^^^ PYI058
|
||||
12 |
|
||||
13 | def scope():
|
||||
|
|
||||
= help: Convert the return annotation of your `__iter__` method to `Iterator`
|
||||
|
||||
PYI058.pyi:16:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods
|
||||
|
|
||||
15 | class AsyncIteratorReturningSimpleAsyncGenerator1:
|
||||
16 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`)
|
||||
| ^^^^^^^^^ PYI058
|
||||
17 |
|
||||
18 | class AsyncIteratorReturningSimpleAsyncGenerator2:
|
||||
|
|
||||
ℹ Safe fix
|
||||
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`)
|
||||
12 12 |
|
||||
13 13 | def scope():
|
||||
14 14 | import collections.abc
|
||||
|
||||
PYI058.pyi:19:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods
|
||||
PYI058.pyi:17:13: 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
|
||||
20 |
|
||||
21 | class AsyncIteratorReturningSimpleAsyncGenerator3:
|
||||
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`
|
||||
|
||||
PYI058.pyi:22:9: PYI058 Use `AsyncIterator` as the return value for simple `__aiter__` methods
|
||||
ℹ Safe fix
|
||||
1 |+from collections.abc import Iterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
|
|
||||
21 | class AsyncIteratorReturningSimpleAsyncGenerator3:
|
||||
22 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`)
|
||||
| ^^^^^^^^^ PYI058
|
||||
23 |
|
||||
24 | class CorrectIterator:
|
||||
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 |+from collections.abc import Iterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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 |+from collections.abc import Iterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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 |+from collections.abc import Iterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
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 |+from collections.abc import AsyncIterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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 |+from collections.abc import AsyncIterator
|
||||
1 2 | def scope():
|
||||
2 3 | from collections.abc import Generator
|
||||
3 4 |
|
||||
--------------------------------------------------------------------------------
|
||||
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
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue