mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-02 06:41:23 +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,82 +1,174 @@
|
||||||
import collections.abc
|
def scope():
|
||||||
import typing
|
from collections.abc import Generator
|
||||||
from collections.abc import AsyncGenerator, Generator
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
class IteratorReturningSimpleGenerator1:
|
class IteratorReturningSimpleGenerator1:
|
||||||
def __iter__(self) -> Generator: # PYI058 (use `Iterator`)
|
def __iter__(self) -> Generator:
|
||||||
return (x for x in range(42))
|
... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class IteratorReturningSimpleGenerator2:
|
|
||||||
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:
|
def scope():
|
||||||
def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`)
|
import typing
|
||||||
yield "a"
|
|
||||||
yield "b"
|
|
||||||
yield "c"
|
|
||||||
return
|
|
||||||
|
|
||||||
class AsyncIteratorReturningSimpleAsyncGenerator1:
|
class IteratorReturningSimpleGenerator2:
|
||||||
def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`)
|
def __iter__(self) -> typing.Generator:
|
||||||
|
... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class AsyncIteratorReturningSimpleAsyncGenerator2:
|
|
||||||
def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`)
|
|
||||||
|
|
||||||
class AsyncIteratorReturningSimpleAsyncGenerator3:
|
def scope():
|
||||||
def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`)
|
import collections.abc
|
||||||
|
|
||||||
class CorrectIterator:
|
class IteratorReturningSimpleGenerator3:
|
||||||
def __iter__(self) -> Iterator[str]: ... # OK
|
def __iter__(self) -> collections.abc.Generator:
|
||||||
|
... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class CorrectAsyncIterator:
|
|
||||||
def __aiter__(self) -> collections.abc.AsyncIterator[int]: ... # OK
|
|
||||||
|
|
||||||
class Fine:
|
def scope():
|
||||||
def __iter__(self) -> typing.Self: ... # OK
|
import collections.abc
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
class StrangeButWeWontComplainHere:
|
class IteratorReturningSimpleGenerator4:
|
||||||
def __aiter__(self) -> list[bytes]: ... # OK
|
def __iter__(self, /) -> collections.abc.Generator[str, Any, None]:
|
||||||
|
... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
def __iter__(self) -> Generator: ... # OK (not in class scope)
|
|
||||||
def __aiter__(self) -> AsyncGenerator: ... # OK (not in class scope)
|
|
||||||
|
|
||||||
class IteratorReturningComplexGenerator:
|
def scope():
|
||||||
def __iter__(self) -> Generator[str, int, bytes]: ... # OK
|
import collections.abc
|
||||||
|
import typing
|
||||||
|
|
||||||
class AsyncIteratorReturningComplexAsyncGenerator:
|
class IteratorReturningSimpleGenerator5:
|
||||||
def __aiter__(self) -> AsyncGenerator[str, int]: ... # OK
|
def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]:
|
||||||
|
... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class ClassWithInvalidAsyncAiterMethod:
|
|
||||||
async def __aiter__(self) -> AsyncGenerator: ... # OK
|
|
||||||
|
|
||||||
class IteratorWithUnusualParameters1:
|
def scope():
|
||||||
def __iter__(self, foo) -> Generator: ... # OK
|
from collections.abc import Generator
|
||||||
|
|
||||||
class IteratorWithUnusualParameters2:
|
class IteratorReturningSimpleGenerator6:
|
||||||
def __iter__(self, *, bar) -> Generator: ... # OK
|
def __iter__(self, /) -> Generator[str, None, None]:
|
||||||
|
... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class IteratorWithUnusualParameters3:
|
|
||||||
def __iter__(self, *args) -> Generator: ... # OK
|
|
||||||
|
|
||||||
class IteratorWithUnusualParameters4:
|
def scope():
|
||||||
def __iter__(self, **kwargs) -> Generator: ... # OK
|
import typing_extensions
|
||||||
|
|
||||||
class IteratorWithIterMethodThatReturnsThings:
|
class AsyncIteratorReturningSimpleAsyncGenerator1:
|
||||||
def __iter__(self) -> Generator: # OK
|
def __aiter__(
|
||||||
yield
|
self,
|
||||||
return 42
|
) -> typing_extensions.AsyncGenerator:
|
||||||
|
... # PYI058 (Use `AsyncIterator`)
|
||||||
|
|
||||||
class IteratorWithIterMethodThatReceivesThingsFromSend:
|
|
||||||
def __iter__(self) -> Generator: # OK
|
|
||||||
x = yield 42
|
|
||||||
|
|
||||||
class IteratorWithNonTrivialIterBody:
|
def scope():
|
||||||
def __iter__(self) -> Generator: # OK
|
import collections.abc
|
||||||
foo, bar, baz = (1, 2, 3)
|
|
||||||
yield foo
|
class AsyncIteratorReturningSimpleAsyncGenerator2:
|
||||||
yield bar
|
def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]:
|
||||||
yield baz
|
... # PYI058 (Use `AsyncIterator`)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
|
@ -1,58 +1,128 @@
|
||||||
import collections.abc
|
def scope():
|
||||||
import typing
|
from collections.abc import Generator
|
||||||
from collections.abc import AsyncGenerator, Generator
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
class IteratorReturningSimpleGenerator1:
|
class IteratorReturningSimpleGenerator1:
|
||||||
def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`)
|
def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class IteratorReturningSimpleGenerator2:
|
def scope():
|
||||||
def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`)
|
import typing
|
||||||
|
|
||||||
class IteratorReturningSimpleGenerator3:
|
class IteratorReturningSimpleGenerator2:
|
||||||
def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`)
|
def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class AsyncIteratorReturningSimpleAsyncGenerator1:
|
def scope():
|
||||||
def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`)
|
import collections.abc
|
||||||
|
|
||||||
class AsyncIteratorReturningSimpleAsyncGenerator2:
|
class IteratorReturningSimpleGenerator3:
|
||||||
def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`)
|
def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class AsyncIteratorReturningSimpleAsyncGenerator3:
|
def scope():
|
||||||
def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`)
|
import collections.abc
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
class CorrectIterator:
|
class IteratorReturningSimpleGenerator4:
|
||||||
def __iter__(self) -> Iterator[str]: ... # OK
|
def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class CorrectAsyncIterator:
|
def scope():
|
||||||
def __aiter__(self) -> collections.abc.AsyncIterator[int]: ... # OK
|
import collections.abc
|
||||||
|
import typing
|
||||||
|
|
||||||
class Fine:
|
class IteratorReturningSimpleGenerator5:
|
||||||
def __iter__(self) -> typing.Self: ... # OK
|
def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class StrangeButWeWontComplainHere:
|
def scope():
|
||||||
def __aiter__(self) -> list[bytes]: ... # OK
|
from collections.abc import Generator
|
||||||
|
|
||||||
def __iter__(self) -> Generator: ... # OK (not in class scope)
|
class IteratorReturningSimpleGenerator6:
|
||||||
def __aiter__(self) -> AsyncGenerator: ... # OK (not in class scope)
|
def __iter__(self, /) -> Generator[str, None, None]: ... # PYI058 (use `Iterator`)
|
||||||
|
|
||||||
class IteratorReturningComplexGenerator:
|
def scope():
|
||||||
def __iter__(self) -> Generator[str, int, bytes]: ... # OK
|
import typing_extensions
|
||||||
|
|
||||||
class AsyncIteratorReturningComplexAsyncGenerator:
|
class AsyncIteratorReturningSimpleAsyncGenerator1:
|
||||||
def __aiter__(self) -> AsyncGenerator[str, int]: ... # OK
|
def __aiter__(self,) -> typing_extensions.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`)
|
||||||
|
|
||||||
class ClassWithInvalidAsyncAiterMethod:
|
def scope():
|
||||||
async def __aiter__(self) -> AsyncGenerator: ... # OK
|
import collections.abc
|
||||||
|
|
||||||
class IteratorWithUnusualParameters1:
|
class AsyncIteratorReturningSimpleAsyncGenerator3:
|
||||||
def __iter__(self, foo) -> Generator: ... # OK
|
def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]:
|
||||||
|
... # PYI058 (Use `AsyncIterator`)
|
||||||
|
|
||||||
class IteratorWithUnusualParameters2:
|
def scope():
|
||||||
def __iter__(self, *, bar) -> Generator: ... # OK
|
import collections.abc
|
||||||
|
|
||||||
class IteratorWithUnusualParameters3:
|
class AsyncIteratorReturningSimpleAsyncGenerator3:
|
||||||
def __iter__(self, *args) -> Generator: ... # OK
|
def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`)
|
||||||
|
|
||||||
class IteratorWithUnusualParameters4:
|
def scope():
|
||||||
def __iter__(self, **kwargs) -> Generator: ... # OK
|
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
|
||||||
|
|
|
@ -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_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
use ruff_python_ast::helpers::map_subscript;
|
use ruff_python_ast::helpers::map_subscript;
|
||||||
use ruff_python_ast::identifier::Identifier;
|
use ruff_python_ast::identifier::Identifier;
|
||||||
use ruff_python_semantic::SemanticModel;
|
use ruff_python_semantic::SemanticModel;
|
||||||
|
use ruff_text_size::{Ranged, TextRange};
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::importer::ImportRequest;
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for simple `__iter__` methods that return `Generator`, and for
|
/// Checks for simple `__iter__` methods that return `Generator`, and for
|
||||||
|
@ -48,20 +50,40 @@ use crate::checkers::ast::Checker;
|
||||||
/// def __iter__(self) -> Iterator[str]:
|
/// def __iter__(self) -> Iterator[str]:
|
||||||
/// yield from "abdefg"
|
/// 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]
|
#[violation]
|
||||||
pub struct GeneratorReturnFromIterMethod {
|
pub struct GeneratorReturnFromIterMethod {
|
||||||
better_return_type: String,
|
return_type: Iterator,
|
||||||
method_name: String,
|
method: Method,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Violation for GeneratorReturnFromIterMethod {
|
impl Violation for GeneratorReturnFromIterMethod {
|
||||||
|
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||||
|
|
||||||
#[derive_message_formats]
|
#[derive_message_formats]
|
||||||
fn message(&self) -> String {
|
fn message(&self) -> String {
|
||||||
let GeneratorReturnFromIterMethod {
|
let GeneratorReturnFromIterMethod {
|
||||||
better_return_type,
|
return_type,
|
||||||
method_name,
|
method,
|
||||||
} = self;
|
} = 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 name = function_def.name.as_str();
|
||||||
|
|
||||||
let better_return_type = match name {
|
|
||||||
"__iter__" => "Iterator",
|
|
||||||
"__aiter__" => "AsyncIterator",
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
let semantic = checker.semantic();
|
let semantic = checker.semantic();
|
||||||
|
|
||||||
if !semantic.current_scope().kind.is_class() {
|
if !semantic.current_scope().kind.is_class() {
|
||||||
|
@ -106,44 +122,68 @@ pub(crate) fn bad_generator_return_type(
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
if !semantic
|
// Determine the module from which the existing annotation is imported (e.g., `typing` or
|
||||||
.resolve_call_path(map_subscript(returns))
|
// `collections.abc`)
|
||||||
.is_some_and(|call_path| {
|
let (method, module, member) = {
|
||||||
matches!(
|
let Some(call_path) = semantic.resolve_call_path(map_subscript(returns)) else {
|
||||||
(name, call_path.as_slice()),
|
return;
|
||||||
(
|
};
|
||||||
"__iter__",
|
match (name, call_path.as_slice()) {
|
||||||
["typing" | "typing_extensions", "Generator"]
|
("__iter__", ["typing", "Generator"]) => {
|
||||||
| ["collections", "abc", "Generator"]
|
(Method::Iter, Module::Typing, Generator::Generator)
|
||||||
) | (
|
}
|
||||||
"__aiter__",
|
("__aiter__", ["typing", "AsyncGenerator"]) => {
|
||||||
["typing" | "typing_extensions", "AsyncGenerator"]
|
(Method::AIter, Module::Typing, Generator::AsyncGenerator)
|
||||||
| ["collections", "abc", "AsyncGenerator"]
|
}
|
||||||
)
|
("__iter__", ["typing_extensions", "Generator"]) => {
|
||||||
)
|
(Method::Iter, Module::TypingExtensions, Generator::Generator)
|
||||||
})
|
}
|
||||||
{
|
("__aiter__", ["typing_extensions", "AsyncGenerator"]) => (
|
||||||
return;
|
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.
|
// `Generator` allows three type parameters; `AsyncGenerator` allows two.
|
||||||
// If type parameters are present,
|
// If type parameters are present,
|
||||||
// Check that all parameters except the first one are either `typing.Any` or `None`;
|
// check that all parameters except the first one are either `typing.Any` or `None`:
|
||||||
// if not, don't emit the diagnostic
|
// - if so, collect information on the first parameter for use in the rule's autofix;
|
||||||
if let ast::Expr::Subscript(ast::ExprSubscript { slice, .. }) = returns {
|
// - if not, don't emit the diagnostic
|
||||||
let ast::Expr::Tuple(ast::ExprTuple { elts, .. }) = slice.as_ref() else {
|
let yield_type_info = match returns {
|
||||||
return;
|
ast::Expr::Subscript(ast::ExprSubscript { slice, .. }) => match slice.as_ref() {
|
||||||
};
|
ast::Expr::Tuple(slice_tuple @ ast::ExprTuple { .. }) => {
|
||||||
if matches!(
|
if !slice_tuple
|
||||||
(name, &elts[..]),
|
.elts
|
||||||
("__iter__", [_, _, _]) | ("__aiter__", [_, _])
|
.iter()
|
||||||
) {
|
.skip(1)
|
||||||
if !&elts.iter().skip(1).all(|elt| is_any_or_none(elt, semantic)) {
|
.all(|elt| is_any_or_none(elt, semantic))
|
||||||
return;
|
{
|
||||||
|
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!),
|
// For .py files (runtime Python!),
|
||||||
|
@ -157,9 +197,7 @@ pub(crate) fn bad_generator_return_type(
|
||||||
ast::Stmt::Pass(_) => continue,
|
ast::Stmt::Pass(_) => continue,
|
||||||
ast::Stmt::Return(ast::StmtReturn { value, .. }) => {
|
ast::Stmt::Return(ast::StmtReturn { value, .. }) => {
|
||||||
if let Some(ret_val) = value {
|
if let Some(ret_val) = value {
|
||||||
if yield_encountered
|
if yield_encountered && !ret_val.is_none_literal_expr() {
|
||||||
&& !matches!(ret_val.as_ref(), ast::Expr::NoneLiteral(_))
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,16 +214,151 @@ pub(crate) fn bad_generator_return_type(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
let mut diagnostic = Diagnostic::new(
|
||||||
checker.diagnostics.push(Diagnostic::new(
|
|
||||||
GeneratorReturnFromIterMethod {
|
GeneratorReturnFromIterMethod {
|
||||||
better_return_type: better_return_type.to_string(),
|
return_type: member.to_iter(),
|
||||||
method_name: name.to_string(),
|
method,
|
||||||
},
|
},
|
||||||
function_def.identifier(),
|
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 {
|
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
|
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:
|
4 | class IteratorReturningSimpleGenerator1:
|
||||||
7 | def __iter__(self) -> Generator: # PYI058 (use `Iterator`)
|
5 | def __iter__(self) -> Generator:
|
||||||
| ^^^^^^^^ PYI058
|
| ^^^^^^^^ PYI058
|
||||||
8 | return (x for x in range(42))
|
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
|
ℹ Safe fix
|
||||||
|
|
1 |+from collections.abc import Iterator
|
||||||
10 | class IteratorReturningSimpleGenerator2:
|
1 2 | def scope():
|
||||||
11 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: # PYI058 (use `Iterator`)
|
2 3 | from collections.abc import Generator
|
||||||
| ^^^^^^^^ PYI058
|
3 4 |
|
||||||
12 | """Fully documented, because I'm a runtime function!"""
|
4 5 | class IteratorReturningSimpleGenerator1:
|
||||||
13 | yield from "abcdefg"
|
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:
|
12 | class IteratorReturningSimpleGenerator2:
|
||||||
17 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: # PYI058 (use `Iterator`)
|
13 | def __iter__(self) -> typing.Generator:
|
||||||
| ^^^^^^^^ PYI058
|
| ^^^^^^^^ PYI058
|
||||||
18 | yield "a"
|
14 | ... # PYI058 (use `Iterator`)
|
||||||
19 | yield "b"
|
|
||||||
|
|
|
|
||||||
|
= 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
|
ℹ Safe fix
|
||||||
|
|
10 10 | import typing
|
||||||
23 | class AsyncIteratorReturningSimpleAsyncGenerator1:
|
11 11 |
|
||||||
24 | def __aiter__(self) -> typing.AsyncGenerator: pass # PYI058 (Use `AsyncIterator`)
|
12 12 | class IteratorReturningSimpleGenerator2:
|
||||||
| ^^^^^^^^^ PYI058
|
13 |- def __iter__(self) -> typing.Generator:
|
||||||
25 |
|
13 |+ def __iter__(self) -> typing.Iterator:
|
||||||
26 | class AsyncIteratorReturningSimpleAsyncGenerator2:
|
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:
|
20 | class IteratorReturningSimpleGenerator3:
|
||||||
27 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`)
|
21 | def __iter__(self) -> collections.abc.Generator:
|
||||||
| ^^^^^^^^^ PYI058
|
| ^^^^^^^^ PYI058
|
||||||
28 |
|
22 | ... # PYI058 (use `Iterator`)
|
||||||
29 | class AsyncIteratorReturningSimpleAsyncGenerator3:
|
|
||||||
|
|
|
|
||||||
|
= 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:
|
29 | class IteratorReturningSimpleGenerator4:
|
||||||
30 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: pass # PYI058 (Use `AsyncIterator`)
|
30 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]:
|
||||||
| ^^^^^^^^^ PYI058
|
| ^^^^^^^^ PYI058
|
||||||
31 |
|
31 | ... # PYI058 (use `Iterator`)
|
||||||
32 | class CorrectIterator:
|
|
||||||
|
|
|
|
||||||
|
= 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
|
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:
|
4 | class IteratorReturningSimpleGenerator1:
|
||||||
7 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`)
|
5 | def __iter__(self) -> Generator: ... # PYI058 (use `Iterator`)
|
||||||
| ^^^^^^^^ PYI058
|
| ^^^^^^^^ PYI058
|
||||||
8 |
|
6 |
|
||||||
9 | class IteratorReturningSimpleGenerator2:
|
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
|
ℹ Safe fix
|
||||||
|
|
1 |+from collections.abc import Iterator
|
||||||
9 | class IteratorReturningSimpleGenerator2:
|
1 2 | def scope():
|
||||||
10 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`)
|
2 3 | from collections.abc import Generator
|
||||||
| ^^^^^^^^ PYI058
|
3 4 |
|
||||||
11 |
|
4 5 | class IteratorReturningSimpleGenerator1:
|
||||||
12 | class IteratorReturningSimpleGenerator3:
|
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:
|
10 | class IteratorReturningSimpleGenerator2:
|
||||||
13 | def __iter__(self, /) -> collections.abc.Generator[str, None, typing.Any]: ... # PYI058 (use `Iterator`)
|
11 | def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`)
|
||||||
| ^^^^^^^^ PYI058
|
| ^^^^^^^^ PYI058
|
||||||
14 |
|
12 |
|
||||||
15 | class AsyncIteratorReturningSimpleAsyncGenerator1:
|
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
|
ℹ Safe fix
|
||||||
|
|
8 8 | import typing
|
||||||
15 | class AsyncIteratorReturningSimpleAsyncGenerator1:
|
9 9 |
|
||||||
16 | def __aiter__(self) -> typing.AsyncGenerator: ... # PYI058 (Use `AsyncIterator`)
|
10 10 | class IteratorReturningSimpleGenerator2:
|
||||||
| ^^^^^^^^^ PYI058
|
11 |- def __iter__(self) -> typing.Generator: ... # PYI058 (use `Iterator`)
|
||||||
17 |
|
11 |+ def __iter__(self) -> typing.Iterator: ... # PYI058 (use `Iterator`)
|
||||||
18 | class AsyncIteratorReturningSimpleAsyncGenerator2:
|
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:
|
16 | class IteratorReturningSimpleGenerator3:
|
||||||
19 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, Any]: ... # PYI058 (Use `AsyncIterator`)
|
17 | def __iter__(self) -> collections.abc.Generator: ... # PYI058 (use `Iterator`)
|
||||||
| ^^^^^^^^^ PYI058
|
| ^^^^^^^^ PYI058
|
||||||
20 |
|
18 |
|
||||||
21 | class AsyncIteratorReturningSimpleAsyncGenerator3:
|
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:
|
23 | class IteratorReturningSimpleGenerator4:
|
||||||
22 | def __aiter__(self, /) -> collections.abc.AsyncGenerator[str, None]: ... # PYI058 (Use `AsyncIterator`)
|
24 | def __iter__(self, /) -> collections.abc.Generator[str, Any, None]: ... # PYI058 (use `Iterator`)
|
||||||
| ^^^^^^^^^ PYI058
|
| ^^^^^^^^ PYI058
|
||||||
23 |
|
25 |
|
||||||
24 | class CorrectIterator:
|
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