mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-28 12:55:05 +00:00
[flake8-pyi
] Implement PYI059
(generic-not-last-base-class
) (#11233)
This commit is contained in:
parent
28cc71fb6b
commit
bc3f4fa3bc
10 changed files with 458 additions and 0 deletions
54
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI059.py
vendored
Normal file
54
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI059.py
vendored
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
from typing import Container, Generic, Iterable, List, Sized, Tuple, TypeVar
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
K = TypeVar('K')
|
||||||
|
V = TypeVar('V')
|
||||||
|
|
||||||
|
class LinkedList(Generic[T], Sized): # PYI059
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._items: List[T] = []
|
||||||
|
|
||||||
|
def push(self, item: T) -> None:
|
||||||
|
self._items.append(item)
|
||||||
|
|
||||||
|
class MyMapping( # PYI059
|
||||||
|
t.Generic[K, V],
|
||||||
|
Iterable[Tuple[K, V]],
|
||||||
|
Container[Tuple[K, V]],
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# Inheriting from just `Generic` is a TypeError, but it's probably fine
|
||||||
|
# to flag this issue in this case as well, since after fixing the error
|
||||||
|
# the Generic's position issue persists.
|
||||||
|
class Foo(Generic, LinkedList): # PYI059
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Foo( # comment about the bracket
|
||||||
|
# Part 1 of multiline comment 1
|
||||||
|
# Part 2 of multiline comment 1
|
||||||
|
Generic[T] # comment about Generic[T] # PYI059
|
||||||
|
# another comment?
|
||||||
|
, # comment about the comma?
|
||||||
|
# part 1 of multiline comment 2
|
||||||
|
# part 2 of multiline comment 2
|
||||||
|
int, # comment about int
|
||||||
|
# yet another comment?
|
||||||
|
): # and another one for good measure
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# in case of multiple Generic[] inheritance, don't fix it.
|
||||||
|
class C(Generic[T], Generic[K, V]): ... # PYI059
|
||||||
|
|
||||||
|
|
||||||
|
# Negative cases
|
||||||
|
class MyList(Sized, Generic[T]): # Generic already in last place
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._items: List[T] = []
|
||||||
|
|
||||||
|
class SomeGeneric(Generic[T]): # Only one generic
|
||||||
|
pass
|
48
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI059.pyi
vendored
Normal file
48
crates/ruff_linter/resources/test/fixtures/flake8_pyi/PYI059.pyi
vendored
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
from typing import Container, Generic, Iterable, Sized, Tuple, TypeVar
|
||||||
|
import typing as t
|
||||||
|
|
||||||
|
T = TypeVar('T')
|
||||||
|
K = TypeVar('K')
|
||||||
|
V = TypeVar('V')
|
||||||
|
|
||||||
|
class LinkedList(Generic[T], Sized): # PYI059
|
||||||
|
def __init__(self) -> None: ...
|
||||||
|
def push(self, item: T) -> None: ...
|
||||||
|
|
||||||
|
class MyMapping( # PYI059
|
||||||
|
t.Generic[K, V],
|
||||||
|
Iterable[Tuple[K, V]],
|
||||||
|
Container[Tuple[K, V]],
|
||||||
|
):
|
||||||
|
...
|
||||||
|
|
||||||
|
# Inheriting from just `Generic` is a TypeError, but it's probably fine
|
||||||
|
# to flag this issue in this case as well, since after fixing the error
|
||||||
|
# the Generic's position issue persists.
|
||||||
|
class Foo(Generic, LinkedList): ... # PYI059
|
||||||
|
|
||||||
|
|
||||||
|
class Foo( # comment about the bracket
|
||||||
|
# Part 1 of multiline comment 1
|
||||||
|
# Part 2 of multiline comment 1
|
||||||
|
Generic[T] # comment about Generic[T] # PYI059
|
||||||
|
# another comment?
|
||||||
|
, # comment about the comma?
|
||||||
|
# part 1 of multiline comment 2
|
||||||
|
# part 2 of multiline comment 2
|
||||||
|
int, # comment about int
|
||||||
|
# yet another comment?
|
||||||
|
): # and another one for good measure
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
# in case of multiple Generic[] inheritance, don't fix it.
|
||||||
|
class C(Generic[T], Generic[K, V]): ... # PYI059
|
||||||
|
|
||||||
|
|
||||||
|
# Negative cases
|
||||||
|
class MyList(Sized, Generic[T]): # Generic already in last place
|
||||||
|
def __init__(self) -> None: ...
|
||||||
|
|
||||||
|
class SomeGeneric(Generic[T]): # Only one generic
|
||||||
|
...
|
|
@ -478,6 +478,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
|
||||||
if checker.enabled(Rule::EllipsisInNonEmptyClassBody) {
|
if checker.enabled(Rule::EllipsisInNonEmptyClassBody) {
|
||||||
flake8_pyi::rules::ellipsis_in_non_empty_class_body(checker, body);
|
flake8_pyi::rules::ellipsis_in_non_empty_class_body(checker, body);
|
||||||
}
|
}
|
||||||
|
if checker.enabled(Rule::GenericNotLastBaseClass) {
|
||||||
|
flake8_pyi::rules::generic_not_last_base_class(checker, class_def);
|
||||||
|
}
|
||||||
if checker.enabled(Rule::PytestIncorrectMarkParenthesesStyle) {
|
if checker.enabled(Rule::PytestIncorrectMarkParenthesesStyle) {
|
||||||
flake8_pytest_style::rules::marks(checker, decorator_list);
|
flake8_pytest_style::rules::marks(checker, decorator_list);
|
||||||
}
|
}
|
||||||
|
|
|
@ -808,6 +808,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
|
||||||
(Flake8Pyi, "055") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnnecessaryTypeUnion),
|
(Flake8Pyi, "055") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnnecessaryTypeUnion),
|
||||||
(Flake8Pyi, "056") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnsupportedMethodCallOnAll),
|
(Flake8Pyi, "056") => (RuleGroup::Stable, rules::flake8_pyi::rules::UnsupportedMethodCallOnAll),
|
||||||
(Flake8Pyi, "058") => (RuleGroup::Stable, rules::flake8_pyi::rules::GeneratorReturnFromIterMethod),
|
(Flake8Pyi, "058") => (RuleGroup::Stable, rules::flake8_pyi::rules::GeneratorReturnFromIterMethod),
|
||||||
|
(Flake8Pyi, "059") => (RuleGroup::Preview, rules::flake8_pyi::rules::GenericNotLastBaseClass),
|
||||||
|
|
||||||
// flake8-pytest-style
|
// flake8-pytest-style
|
||||||
(Flake8PytestStyle, "001") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestFixtureIncorrectParenthesesStyle),
|
(Flake8PytestStyle, "001") => (RuleGroup::Stable, rules::flake8_pytest_style::rules::PytestFixtureIncorrectParenthesesStyle),
|
||||||
|
|
|
@ -41,6 +41,8 @@ mod tests {
|
||||||
#[test_case(Rule::FutureAnnotationsInStub, Path::new("PYI044.pyi"))]
|
#[test_case(Rule::FutureAnnotationsInStub, Path::new("PYI044.pyi"))]
|
||||||
#[test_case(Rule::GeneratorReturnFromIterMethod, Path::new("PYI058.py"))]
|
#[test_case(Rule::GeneratorReturnFromIterMethod, Path::new("PYI058.py"))]
|
||||||
#[test_case(Rule::GeneratorReturnFromIterMethod, Path::new("PYI058.pyi"))]
|
#[test_case(Rule::GeneratorReturnFromIterMethod, Path::new("PYI058.pyi"))]
|
||||||
|
#[test_case(Rule::GenericNotLastBaseClass, Path::new("PYI059.py"))]
|
||||||
|
#[test_case(Rule::GenericNotLastBaseClass, Path::new("PYI059.pyi"))]
|
||||||
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.py"))]
|
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.py"))]
|
||||||
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.pyi"))]
|
#[test_case(Rule::IterMethodReturnIterable, Path::new("PYI045.pyi"))]
|
||||||
#[test_case(Rule::NoReturnArgumentAnnotationInStub, Path::new("PYI050.py"))]
|
#[test_case(Rule::NoReturnArgumentAnnotationInStub, Path::new("PYI050.py"))]
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
use ruff_diagnostics::{Diagnostic, Fix, FixAvailability, Violation};
|
||||||
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
|
use ruff_python_ast::{self as ast, helpers::map_subscript};
|
||||||
|
use ruff_text_size::Ranged;
|
||||||
|
|
||||||
|
use crate::checkers::ast::Checker;
|
||||||
|
use crate::fix::edits::{add_argument, remove_argument, Parentheses};
|
||||||
|
|
||||||
|
/// ## What it does
|
||||||
|
/// Checks for classes inheriting from `typing.Generic[]` where `Generic[]` is
|
||||||
|
/// not the last base class in the bases tuple.
|
||||||
|
///
|
||||||
|
/// ## Why is this bad?
|
||||||
|
/// If `Generic[]` is not the final class in the bases tuple, unexpected
|
||||||
|
/// behaviour can occur at runtime (See [this CPython issue][1] for an example).
|
||||||
|
/// The rule is also applied to stub files, but, unlike at runtime,
|
||||||
|
/// in stubs it is purely enforced for stylistic consistency.
|
||||||
|
///
|
||||||
|
/// For example:
|
||||||
|
/// ```python
|
||||||
|
/// class LinkedList(Generic[T], Sized):
|
||||||
|
/// def push(self, item: T) -> None:
|
||||||
|
/// self._items.append(item)
|
||||||
|
///
|
||||||
|
/// class MyMapping(
|
||||||
|
/// Generic[K, V],
|
||||||
|
/// Iterable[Tuple[K, V]],
|
||||||
|
/// Container[Tuple[K, V]],
|
||||||
|
/// ):
|
||||||
|
/// ...
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Use instead:
|
||||||
|
/// ```python
|
||||||
|
/// class LinkedList(Sized, Generic[T]):
|
||||||
|
/// def push(self, item: T) -> None:
|
||||||
|
/// self._items.append(item)
|
||||||
|
///
|
||||||
|
/// class MyMapping(
|
||||||
|
/// Iterable[Tuple[K, V]],
|
||||||
|
/// Container[Tuple[K, V]],
|
||||||
|
/// Generic[K, V],
|
||||||
|
/// ):
|
||||||
|
/// ...
|
||||||
|
/// ```
|
||||||
|
/// ## References
|
||||||
|
/// - [`typing.Generic` documentation](https://docs.python.org/3/library/typing.html#typing.Generic)
|
||||||
|
///
|
||||||
|
/// [1]: https://github.com/python/cpython/issues/106102
|
||||||
|
#[violation]
|
||||||
|
pub struct GenericNotLastBaseClass;
|
||||||
|
|
||||||
|
impl Violation for GenericNotLastBaseClass {
|
||||||
|
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
|
||||||
|
|
||||||
|
#[derive_message_formats]
|
||||||
|
fn message(&self) -> String {
|
||||||
|
format!("`Generic[]` should always be the last base class")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fix_title(&self) -> Option<String> {
|
||||||
|
Some("Move `Generic[]` to the end".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// PYI059
|
||||||
|
pub(crate) fn generic_not_last_base_class(checker: &mut Checker, class_def: &ast::StmtClassDef) {
|
||||||
|
let Some(bases) = class_def.arguments.as_deref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let semantic = checker.semantic();
|
||||||
|
if !semantic.seen_typing() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(last_base) = bases.args.last() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut generic_base_iter = bases
|
||||||
|
.args
|
||||||
|
.iter()
|
||||||
|
.filter(|base| semantic.match_typing_expr(map_subscript(base), "Generic"));
|
||||||
|
|
||||||
|
let Some(generic_base) = generic_base_iter.next() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If `Generic[]` exists, but is the last base, don't emit a diagnostic.
|
||||||
|
if generic_base.range() == last_base.range() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut diagnostic = Diagnostic::new(GenericNotLastBaseClass, bases.range());
|
||||||
|
|
||||||
|
// No fix if multiple `Generic[]`s are seen in the class bases.
|
||||||
|
if generic_base_iter.next().is_none() {
|
||||||
|
diagnostic.try_set_fix(|| generate_fix(generic_base, bases, checker));
|
||||||
|
}
|
||||||
|
|
||||||
|
checker.diagnostics.push(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_fix(
|
||||||
|
generic_base: &ast::Expr,
|
||||||
|
arguments: &ast::Arguments,
|
||||||
|
checker: &Checker,
|
||||||
|
) -> anyhow::Result<Fix> {
|
||||||
|
let locator = checker.locator();
|
||||||
|
let source = locator.contents();
|
||||||
|
|
||||||
|
let deletion = remove_argument(generic_base, arguments, Parentheses::Preserve, source)?;
|
||||||
|
let insertion = add_argument(
|
||||||
|
locator.slice(generic_base),
|
||||||
|
arguments,
|
||||||
|
checker.indexer().comment_ranges(),
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Fix::safe_edits(deletion, [insertion]))
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ pub(crate) use duplicate_union_member::*;
|
||||||
pub(crate) use ellipsis_in_non_empty_class_body::*;
|
pub(crate) use ellipsis_in_non_empty_class_body::*;
|
||||||
pub(crate) use exit_annotations::*;
|
pub(crate) use exit_annotations::*;
|
||||||
pub(crate) use future_annotations_in_stub::*;
|
pub(crate) use future_annotations_in_stub::*;
|
||||||
|
pub(crate) use generic_not_last_base_class::*;
|
||||||
pub(crate) use iter_method_return_iterable::*;
|
pub(crate) use iter_method_return_iterable::*;
|
||||||
pub(crate) use no_return_argument_annotation::*;
|
pub(crate) use no_return_argument_annotation::*;
|
||||||
pub(crate) use non_empty_stub_body::*;
|
pub(crate) use non_empty_stub_body::*;
|
||||||
|
@ -48,6 +49,7 @@ mod duplicate_union_member;
|
||||||
mod ellipsis_in_non_empty_class_body;
|
mod ellipsis_in_non_empty_class_body;
|
||||||
mod exit_annotations;
|
mod exit_annotations;
|
||||||
mod future_annotations_in_stub;
|
mod future_annotations_in_stub;
|
||||||
|
mod generic_not_last_base_class;
|
||||||
mod iter_method_return_iterable;
|
mod iter_method_return_iterable;
|
||||||
mod no_return_argument_annotation;
|
mod no_return_argument_annotation;
|
||||||
mod non_empty_stub_body;
|
mod non_empty_stub_body;
|
||||||
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||||
|
---
|
||||||
|
PYI059.py:8:17: PYI059 [*] `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
6 | V = TypeVar('V')
|
||||||
|
7 |
|
||||||
|
8 | class LinkedList(Generic[T], Sized): # PYI059
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ PYI059
|
||||||
|
9 | def __init__(self) -> None:
|
||||||
|
10 | self._items: List[T] = []
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
5 5 | K = TypeVar('K')
|
||||||
|
6 6 | V = TypeVar('V')
|
||||||
|
7 7 |
|
||||||
|
8 |-class LinkedList(Generic[T], Sized): # PYI059
|
||||||
|
8 |+class LinkedList(Sized, Generic[T]): # PYI059
|
||||||
|
9 9 | def __init__(self) -> None:
|
||||||
|
10 10 | self._items: List[T] = []
|
||||||
|
11 11 |
|
||||||
|
|
||||||
|
PYI059.py:15:16: PYI059 [*] `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
13 | self._items.append(item)
|
||||||
|
14 |
|
||||||
|
15 | class MyMapping( # PYI059
|
||||||
|
| ________________^
|
||||||
|
16 | | t.Generic[K, V],
|
||||||
|
17 | | Iterable[Tuple[K, V]],
|
||||||
|
18 | | Container[Tuple[K, V]],
|
||||||
|
19 | | ):
|
||||||
|
| |_^ PYI059
|
||||||
|
20 | ...
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
13 13 | self._items.append(item)
|
||||||
|
14 14 |
|
||||||
|
15 15 | class MyMapping( # PYI059
|
||||||
|
16 |- t.Generic[K, V],
|
||||||
|
17 16 | Iterable[Tuple[K, V]],
|
||||||
|
18 |- Container[Tuple[K, V]],
|
||||||
|
17 |+ Container[Tuple[K, V]], t.Generic[K, V],
|
||||||
|
19 18 | ):
|
||||||
|
20 19 | ...
|
||||||
|
21 20 |
|
||||||
|
|
||||||
|
PYI059.py:26:10: PYI059 [*] `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
24 | # to flag this issue in this case as well, since after fixing the error
|
||||||
|
25 | # the Generic's position issue persists.
|
||||||
|
26 | class Foo(Generic, LinkedList): # PYI059
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ PYI059
|
||||||
|
27 | pass
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
23 23 | # Inheriting from just `Generic` is a TypeError, but it's probably fine
|
||||||
|
24 24 | # to flag this issue in this case as well, since after fixing the error
|
||||||
|
25 25 | # the Generic's position issue persists.
|
||||||
|
26 |-class Foo(Generic, LinkedList): # PYI059
|
||||||
|
26 |+class Foo(LinkedList, Generic): # PYI059
|
||||||
|
27 27 | pass
|
||||||
|
28 28 |
|
||||||
|
29 29 |
|
||||||
|
|
||||||
|
PYI059.py:30:10: PYI059 [*] `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
30 | class Foo( # comment about the bracket
|
||||||
|
| __________^
|
||||||
|
31 | | # Part 1 of multiline comment 1
|
||||||
|
32 | | # Part 2 of multiline comment 1
|
||||||
|
33 | | Generic[T] # comment about Generic[T] # PYI059
|
||||||
|
34 | | # another comment?
|
||||||
|
35 | | , # comment about the comma?
|
||||||
|
36 | | # part 1 of multiline comment 2
|
||||||
|
37 | | # part 2 of multiline comment 2
|
||||||
|
38 | | int, # comment about int
|
||||||
|
39 | | # yet another comment?
|
||||||
|
40 | | ): # and another one for good measure
|
||||||
|
| |_^ PYI059
|
||||||
|
41 | ...
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
30 30 | class Foo( # comment about the bracket
|
||||||
|
31 31 | # Part 1 of multiline comment 1
|
||||||
|
32 32 | # Part 2 of multiline comment 1
|
||||||
|
33 |- Generic[T] # comment about Generic[T] # PYI059
|
||||||
|
34 |- # another comment?
|
||||||
|
35 |- , # comment about the comma?
|
||||||
|
33 |+ # comment about the comma?
|
||||||
|
36 34 | # part 1 of multiline comment 2
|
||||||
|
37 35 | # part 2 of multiline comment 2
|
||||||
|
38 |- int, # comment about int
|
||||||
|
36 |+ int, Generic[T], # comment about int
|
||||||
|
39 37 | # yet another comment?
|
||||||
|
40 38 | ): # and another one for good measure
|
||||||
|
41 39 | ...
|
||||||
|
|
||||||
|
PYI059.py:45:8: PYI059 `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
44 | # in case of multiple Generic[] inheritance, don't fix it.
|
||||||
|
45 | class C(Generic[T], Generic[K, V]): ... # PYI059
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI059
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
|
@ -0,0 +1,112 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_linter/src/rules/flake8_pyi/mod.rs
|
||||||
|
---
|
||||||
|
PYI059.pyi:8:17: PYI059 [*] `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
6 | V = TypeVar('V')
|
||||||
|
7 |
|
||||||
|
8 | class LinkedList(Generic[T], Sized): # PYI059
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^ PYI059
|
||||||
|
9 | def __init__(self) -> None: ...
|
||||||
|
10 | def push(self, item: T) -> None: ...
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
5 5 | K = TypeVar('K')
|
||||||
|
6 6 | V = TypeVar('V')
|
||||||
|
7 7 |
|
||||||
|
8 |-class LinkedList(Generic[T], Sized): # PYI059
|
||||||
|
8 |+class LinkedList(Sized, Generic[T]): # PYI059
|
||||||
|
9 9 | def __init__(self) -> None: ...
|
||||||
|
10 10 | def push(self, item: T) -> None: ...
|
||||||
|
11 11 |
|
||||||
|
|
||||||
|
PYI059.pyi:12:16: PYI059 [*] `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
10 | def push(self, item: T) -> None: ...
|
||||||
|
11 |
|
||||||
|
12 | class MyMapping( # PYI059
|
||||||
|
| ________________^
|
||||||
|
13 | | t.Generic[K, V],
|
||||||
|
14 | | Iterable[Tuple[K, V]],
|
||||||
|
15 | | Container[Tuple[K, V]],
|
||||||
|
16 | | ):
|
||||||
|
| |_^ PYI059
|
||||||
|
17 | ...
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
10 10 | def push(self, item: T) -> None: ...
|
||||||
|
11 11 |
|
||||||
|
12 12 | class MyMapping( # PYI059
|
||||||
|
13 |- t.Generic[K, V],
|
||||||
|
14 13 | Iterable[Tuple[K, V]],
|
||||||
|
15 |- Container[Tuple[K, V]],
|
||||||
|
14 |+ Container[Tuple[K, V]], t.Generic[K, V],
|
||||||
|
16 15 | ):
|
||||||
|
17 16 | ...
|
||||||
|
18 17 |
|
||||||
|
|
||||||
|
PYI059.pyi:22:10: PYI059 [*] `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
20 | # to flag this issue in this case as well, since after fixing the error
|
||||||
|
21 | # the Generic's position issue persists.
|
||||||
|
22 | class Foo(Generic, LinkedList): ... # PYI059
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^ PYI059
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
19 19 | # Inheriting from just `Generic` is a TypeError, but it's probably fine
|
||||||
|
20 20 | # to flag this issue in this case as well, since after fixing the error
|
||||||
|
21 21 | # the Generic's position issue persists.
|
||||||
|
22 |-class Foo(Generic, LinkedList): ... # PYI059
|
||||||
|
22 |+class Foo(LinkedList, Generic): ... # PYI059
|
||||||
|
23 23 |
|
||||||
|
24 24 |
|
||||||
|
25 25 | class Foo( # comment about the bracket
|
||||||
|
|
||||||
|
PYI059.pyi:25:10: PYI059 [*] `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
25 | class Foo( # comment about the bracket
|
||||||
|
| __________^
|
||||||
|
26 | | # Part 1 of multiline comment 1
|
||||||
|
27 | | # Part 2 of multiline comment 1
|
||||||
|
28 | | Generic[T] # comment about Generic[T] # PYI059
|
||||||
|
29 | | # another comment?
|
||||||
|
30 | | , # comment about the comma?
|
||||||
|
31 | | # part 1 of multiline comment 2
|
||||||
|
32 | | # part 2 of multiline comment 2
|
||||||
|
33 | | int, # comment about int
|
||||||
|
34 | | # yet another comment?
|
||||||
|
35 | | ): # and another one for good measure
|
||||||
|
| |_^ PYI059
|
||||||
|
36 | ...
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
||||||
|
|
||||||
|
ℹ Safe fix
|
||||||
|
25 25 | class Foo( # comment about the bracket
|
||||||
|
26 26 | # Part 1 of multiline comment 1
|
||||||
|
27 27 | # Part 2 of multiline comment 1
|
||||||
|
28 |- Generic[T] # comment about Generic[T] # PYI059
|
||||||
|
29 |- # another comment?
|
||||||
|
30 |- , # comment about the comma?
|
||||||
|
28 |+ # comment about the comma?
|
||||||
|
31 29 | # part 1 of multiline comment 2
|
||||||
|
32 30 | # part 2 of multiline comment 2
|
||||||
|
33 |- int, # comment about int
|
||||||
|
31 |+ int, Generic[T], # comment about int
|
||||||
|
34 32 | # yet another comment?
|
||||||
|
35 33 | ): # and another one for good measure
|
||||||
|
36 34 | ...
|
||||||
|
|
||||||
|
PYI059.pyi:40:8: PYI059 `Generic[]` should always be the last base class
|
||||||
|
|
|
||||||
|
39 | # in case of multiple Generic[] inheritance, don't fix it.
|
||||||
|
40 | class C(Generic[T], Generic[K, V]): ... # PYI059
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ PYI059
|
||||||
|
|
|
||||||
|
= help: Move `Generic[]` to the end
|
1
ruff.schema.json
generated
1
ruff.schema.json
generated
|
@ -3573,6 +3573,7 @@
|
||||||
"PYI055",
|
"PYI055",
|
||||||
"PYI056",
|
"PYI056",
|
||||||
"PYI058",
|
"PYI058",
|
||||||
|
"PYI059",
|
||||||
"Q",
|
"Q",
|
||||||
"Q0",
|
"Q0",
|
||||||
"Q00",
|
"Q00",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue