[ruff] Classes with mixed type variable style (RUF053) (#15841)

This commit is contained in:
InSync 2025-02-07 01:35:51 +07:00 committed by GitHub
parent ba2f0e998d
commit 84ceddcbd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 900 additions and 32 deletions

View file

@ -0,0 +1,109 @@
from typing import Generic, ParamSpec, TypeVar, TypeVarTuple, Unpack
_A = TypeVar('_A')
_B = TypeVar('_B', bound=int)
_C = TypeVar('_C', str, bytes)
_D = TypeVar('_D', default=int)
_E = TypeVar('_E', bound=int, default=int)
_F = TypeVar('_F', str, bytes, default=str)
_G = TypeVar('_G', str, a := int)
_As = TypeVarTuple('_As')
_Bs = TypeVarTuple('_Bs', bound=tuple[int, str])
_Cs = TypeVarTuple('_Cs', default=tuple[int, str])
_P1 = ParamSpec('_P1')
_P2 = ParamSpec('_P2', bound=[bytes, bool])
_P3 = ParamSpec('_P3', default=[int, str])
### Errors
class C[T](Generic[_A]): ...
class C[T](Generic[_B], str): ...
class C[T](int, Generic[_C]): ...
class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
class C[T](Generic[_E], list[_E]): ... # TODO: Type parameter defaults
class C[T](list[_F], Generic[_F]): ... # TODO: Type parameter defaults
class C[*Ts](Generic[*_As]): ...
class C[*Ts](Generic[Unpack[_As]]): ...
class C[*Ts](Generic[Unpack[_Bs]], tuple[*Bs]): ...
class C[*Ts](Callable[[*_Cs], tuple[*Ts]], Generic[_Cs]): ... # TODO: Type parameter defaults
class C[**P](Generic[_P1]): ...
class C[**P](Generic[_P2]): ...
class C[**P](Generic[_P3]): ... # TODO: Type parameter defaults
class C[T](Generic[T, _A]): ...
# See `is_existing_param_of_same_class`.
# `expr_name_to_type_var` doesn't handle named expressions,
# only simple assignments, so there is no fix.
class C[T: (_Z := TypeVar('_Z'))](Generic[_Z]): ...
class C(Generic[_B]):
class D[T](Generic[_B, T]): ...
class C[T]:
class D[U](Generic[T, U]): ...
# In a single run, only the first is reported.
# Others will be reported/fixed in following iterations.
class C[T](Generic[_C], Generic[_D]): ...
class C[T, _C: (str, bytes)](Generic[_D]): ... # TODO: Type parameter defaults
class C[
T # Comment
](Generic[_E]): ... # TODO: Type parameter defaults
class C[T](Generic[Generic[_F]]): ...
class C[T](Generic[Unpack[_A]]): ...
class C[T](Generic[Unpack[_P1]]): ...
class C[T](Generic[Unpack[Unpack[_P2]]]): ...
class C[T](Generic[Unpack[*_As]]): ...
class C[T](Generic[Unpack[_As, _Bs]]): ...
class C[T](Generic[_A, _A]): ...
class C[T](Generic[_A, Unpack[_As]]): ...
class C[T](Generic[*_As, _A]): ...
from somewhere import APublicTypeVar
class C[T](Generic[APublicTypeVar]): ...
class C[T](Generic[APublicTypeVar, _A]): ...
# `_G` has two constraints: `str` and `a := int`.
# The latter cannot be used as a PEP 695 constraint,
# as named expressions are forbidden within type parameter lists.
# See also the `_Z` example above.
class C[T](Generic[_G]): ... # Should be moved down below eventually
# Single-element constraints should not be converted to a bound.
class C[T: (str,)](Generic[_A]): ...
class C[T: [a]](Generic[_A]): ...
# Existing bounds should not be deparenthesized.
# class C[T: (_Y := int)](Generic[_A]): ... # TODO: Uncomment this
# class C[T: (*a,)](Generic[_A]): ... # TODO: Uncomment this
### No errors
class C(Generic[_A]): ...
class C[_A]: ...
class C[_A](list[_A]): ...
class C[_A](list[Generic[_A]]): ...

View file

@ -554,6 +554,9 @@ pub(crate) fn statement(stmt: &Stmt, checker: &mut Checker) {
if checker.enabled(Rule::NonPEP695GenericClass) {
pyupgrade::rules::non_pep695_generic_class(checker, class_def);
}
if checker.enabled(Rule::ClassWithMixedTypeVars) {
ruff::rules::class_with_mixed_type_vars(checker, class_def);
}
}
Stmt::Import(ast::StmtImport { names, range: _ }) => {
if checker.enabled(Rule::MultipleImportsOnOneLine) {

View file

@ -1005,6 +1005,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<(RuleGroup, Rule)> {
(Ruff, "049") => (RuleGroup::Preview, rules::ruff::rules::DataclassEnum),
(Ruff, "051") => (RuleGroup::Preview, rules::ruff::rules::IfKeyInDictDel),
(Ruff, "052") => (RuleGroup::Preview, rules::ruff::rules::UsedDummyVariable),
(Ruff, "053") => (RuleGroup::Preview, rules::ruff::rules::ClassWithMixedTypeVars),
(Ruff, "055") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRegularExpression),
(Ruff, "056") => (RuleGroup::Preview, rules::ruff::rules::FalsyDictGetFallback),
(Ruff, "057") => (RuleGroup::Preview, rules::ruff::rules::UnnecessaryRound),

View file

@ -57,7 +57,7 @@ mod native_literals;
mod open_alias;
mod os_error_alias;
mod outdated_version_block;
mod pep695;
pub(crate) mod pep695;
mod printf_string_formatting;
mod quoted_annotation;
mod redundant_open_modes;

View file

@ -9,7 +9,7 @@ use ruff_python_ast::{
self as ast,
name::Name,
visitor::{self, Visitor},
Expr, ExprCall, ExprName, ExprSubscript, Identifier, Stmt, StmtAssign, TypeParam,
Arguments, Expr, ExprCall, ExprName, ExprSubscript, Identifier, Stmt, StmtAssign, TypeParam,
TypeParamParamSpec, TypeParamTypeVar, TypeParamTypeVarTuple,
};
use ruff_python_semantic::SemanticModel;
@ -28,7 +28,7 @@ mod non_pep695_type_alias;
mod private_type_parameter;
#[derive(Debug)]
enum TypeVarRestriction<'a> {
pub(crate) enum TypeVarRestriction<'a> {
/// A type variable with a bound, e.g., `TypeVar("T", bound=int)`.
Bound(&'a Expr),
/// A type variable with constraints, e.g., `TypeVar("T", int, str)`.
@ -39,25 +39,25 @@ enum TypeVarRestriction<'a> {
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum TypeParamKind {
pub(crate) enum TypeParamKind {
TypeVar,
TypeVarTuple,
ParamSpec,
}
#[derive(Debug)]
struct TypeVar<'a> {
name: &'a str,
restriction: Option<TypeVarRestriction<'a>>,
kind: TypeParamKind,
default: Option<&'a Expr>,
pub(crate) struct TypeVar<'a> {
pub(crate) name: &'a str,
pub(crate) restriction: Option<TypeVarRestriction<'a>>,
pub(crate) kind: TypeParamKind,
pub(crate) default: Option<&'a Expr>,
}
/// Wrapper for formatting a sequence of [`TypeVar`]s for use as a generic type parameter (e.g. `[T,
/// *Ts, **P]`). See [`DisplayTypeVar`] for further details.
struct DisplayTypeVars<'a> {
type_vars: &'a [TypeVar<'a>],
source: &'a str,
pub(crate) struct DisplayTypeVars<'a> {
pub(crate) type_vars: &'a [TypeVar<'a>],
pub(crate) source: &'a str,
}
impl Display for DisplayTypeVars<'_> {
@ -81,7 +81,7 @@ impl Display for DisplayTypeVars<'_> {
/// Used for displaying `type_var`. `source` is the whole file, which will be sliced to recover the
/// `TypeVarRestriction` values for generic bounds and constraints.
struct DisplayTypeVar<'a> {
pub(crate) struct DisplayTypeVar<'a> {
type_var: &'a TypeVar<'a>,
source: &'a str,
}
@ -192,6 +192,34 @@ impl<'a> From<&'a TypeVar<'a>> for TypeParam {
}
}
impl<'a> From<&'a TypeParam> for TypeVar<'a> {
fn from(param: &'a TypeParam) -> Self {
let (kind, restriction) = match param {
TypeParam::TypeVarTuple(_) => (TypeParamKind::TypeVarTuple, None),
TypeParam::ParamSpec(_) => (TypeParamKind::ParamSpec, None),
TypeParam::TypeVar(param) => {
let restriction = match param.bound.as_deref() {
None => None,
Some(Expr::Tuple(constraints)) => Some(TypeVarRestriction::Constraint(
constraints.elts.iter().collect::<Vec<_>>(),
)),
Some(bound) => Some(TypeVarRestriction::Bound(bound)),
};
(TypeParamKind::TypeVar, restriction)
}
};
Self {
name: param.name(),
kind,
restriction,
default: param.default(),
}
}
}
struct TypeVarReferenceVisitor<'a> {
vars: Vec<TypeVar<'a>>,
semantic: &'a SemanticModel<'a>,
@ -242,7 +270,7 @@ impl<'a> Visitor<'a> for TypeVarReferenceVisitor<'a> {
}
}
fn expr_name_to_type_var<'a>(
pub(crate) fn expr_name_to_type_var<'a>(
semantic: &'a SemanticModel,
name: &'a ExprName,
) -> Option<TypeVar<'a>> {
@ -349,3 +377,18 @@ fn check_type_vars(vars: Vec<TypeVar<'_>>) -> Option<Vec<TypeVar<'_>>> {
== vars.len())
.then_some(vars)
}
/// Search `class_bases` for a `typing.Generic` base class. Returns the `Generic` expression (if
/// any), along with its index in the class's bases tuple.
pub(crate) fn find_generic<'a>(
class_bases: &'a Arguments,
semantic: &SemanticModel,
) -> Option<(usize, &'a ExprSubscript)> {
class_bases.args.iter().enumerate().find_map(|(idx, expr)| {
expr.as_subscript_expr().and_then(|sub_expr| {
semantic
.match_typing_expr(&sub_expr.value, "Generic")
.then_some((idx, sub_expr))
})
})
}

View file

@ -1,15 +1,16 @@
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::visitor::Visitor;
use ruff_python_ast::{Arguments, ExprSubscript, StmtClassDef};
use ruff_python_semantic::SemanticModel;
use ruff_python_ast::{ExprSubscript, StmtClassDef};
use ruff_text_size::Ranged;
use crate::checkers::ast::Checker;
use crate::fix::edits::{remove_argument, Parentheses};
use crate::settings::types::PythonVersion;
use super::{check_type_vars, in_nested_context, DisplayTypeVars, TypeVarReferenceVisitor};
use super::{
check_type_vars, find_generic, in_nested_context, DisplayTypeVars, TypeVarReferenceVisitor,
};
/// ## What it does
///
@ -211,18 +212,3 @@ pub(crate) fn non_pep695_generic_class(checker: &mut Checker, class_def: &StmtCl
checker.diagnostics.push(diagnostic);
}
/// Search `class_bases` for a `typing.Generic` base class. Returns the `Generic` expression (if
/// any), along with its index in the class's bases tuple.
fn find_generic<'a>(
class_bases: &'a Arguments,
semantic: &SemanticModel,
) -> Option<(usize, &'a ExprSubscript)> {
class_bases.args.iter().enumerate().find_map(|(idx, expr)| {
expr.as_subscript_expr().and_then(|sub_expr| {
semantic
.match_typing_expr(&sub_expr.value, "Generic")
.then_some((idx, sub_expr))
})
})
}

View file

@ -435,6 +435,7 @@ mod tests {
#[test_case(Rule::DataclassEnum, Path::new("RUF049.py"))]
#[test_case(Rule::StarmapZip, Path::new("RUF058_0.py"))]
#[test_case(Rule::StarmapZip, Path::new("RUF058_1.py"))]
#[test_case(Rule::ClassWithMixedTypeVars, Path::new("RUF053.py"))]
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!(
"preview__{}_{}",

View file

@ -0,0 +1,247 @@
use rustc_hash::FxHashSet;
use std::iter;
use ruff_diagnostics::{Diagnostic, Edit, Fix, FixAvailability, Violation};
use ruff_macros::{derive_message_formats, ViolationMetadata};
use ruff_python_ast::{
Arguments, Expr, ExprStarred, ExprSubscript, ExprTuple, StmtClassDef, TypeParams,
};
use ruff_python_semantic::SemanticModel;
use crate::checkers::ast::Checker;
use crate::fix::edits::{remove_argument, Parentheses};
use crate::rules::pyupgrade::rules::pep695::{
expr_name_to_type_var, find_generic, DisplayTypeVars, TypeParamKind, TypeVar,
};
use crate::settings::types::PythonVersion;
/// ## What it does
/// Checks for classes that have [PEP 695] [type parameter lists]
/// while also inheriting from `typing.Generic` or `typing_extensions.Generic`.
///
/// ## Why is this bad?
/// Such classes cause errors at runtime:
///
/// ```python
/// from typing import Generic, TypeVar
///
/// U = TypeVar("U")
///
/// # TypeError: Cannot inherit from Generic[...] multiple times.
/// class C[T](Generic[U]): ...
/// ```
///
/// ## Example
///
/// ```python
/// from typing import Generic, ParamSpec, TypeVar, TypeVarTuple
///
/// U = TypeVar("U")
/// P = ParamSpec("P")
/// Ts = TypeVarTuple("Ts")
///
///
/// class C[T](Generic[U, P, *Ts]): ...
/// ```
///
/// Use instead:
///
/// ```python
/// class C[T, U, **P, *Ts]: ...
/// ```
///
/// ## Fix safety
/// As the fix changes runtime behaviour, it is always marked as unsafe.
/// Additionally, comments within the fix range will not be preserved.
///
/// ## References
/// - [Python documentation: User-defined generic types](https://docs.python.org/3/library/typing.html#user-defined-generic-types)
/// - [Python documentation: type parameter lists](https://docs.python.org/3/reference/compound_stmts.html#type-params)
/// - [PEP 695 - Type Parameter Syntax](https://peps.python.org/pep-0695/)
///
/// [PEP 695]: https://peps.python.org/pep-0695/
/// [type parameter lists]: https://docs.python.org/3/reference/compound_stmts.html#type-params
#[derive(ViolationMetadata)]
pub(crate) struct ClassWithMixedTypeVars;
impl Violation for ClassWithMixedTypeVars {
const FIX_AVAILABILITY: FixAvailability = FixAvailability::Sometimes;
#[derive_message_formats]
fn message(&self) -> String {
"Class with type parameter list inherits from `Generic`".to_string()
}
fn fix_title(&self) -> Option<String> {
Some("Remove `Generic` base class".to_string())
}
}
/// RUF053
pub(crate) fn class_with_mixed_type_vars(checker: &mut Checker, class_def: &StmtClassDef) {
if checker.settings.target_version < PythonVersion::Py312 {
return;
}
let StmtClassDef {
type_params,
arguments,
..
} = class_def;
let Some(type_params) = type_params else {
return;
};
let Some(arguments) = arguments else {
return;
};
let Some((generic_base, old_style_type_vars)) =
typing_generic_base_and_arguments(arguments, checker.semantic())
else {
return;
};
let mut diagnostic = Diagnostic::new(ClassWithMixedTypeVars, generic_base.range);
diagnostic.try_set_optional_fix(|| {
convert_type_vars(
generic_base,
old_style_type_vars,
type_params,
arguments,
checker,
)
});
checker.diagnostics.push(diagnostic);
}
fn typing_generic_base_and_arguments<'a>(
class_arguments: &'a Arguments,
semantic: &SemanticModel,
) -> Option<(&'a ExprSubscript, &'a Expr)> {
let (_, base @ ExprSubscript { slice, .. }) = find_generic(class_arguments, semantic)?;
Some((base, slice.as_ref()))
}
fn convert_type_vars(
generic_base: &ExprSubscript,
old_style_type_vars: &Expr,
type_params: &TypeParams,
class_arguments: &Arguments,
checker: &Checker,
) -> anyhow::Result<Option<Fix>> {
let mut type_vars: Vec<_> = type_params.type_params.iter().map(TypeVar::from).collect();
let semantic = checker.semantic();
let converted_type_vars = match old_style_type_vars {
Expr::Tuple(ExprTuple { elts, .. }) => {
generic_arguments_to_type_vars(elts.iter(), type_params, semantic)
}
expr @ (Expr::Subscript(_) | Expr::Name(_)) => {
generic_arguments_to_type_vars(iter::once(expr), type_params, semantic)
}
_ => None,
};
let Some(converted_type_vars) = converted_type_vars else {
return Ok(None);
};
type_vars.extend(converted_type_vars);
let source = checker.source();
let new_type_params = DisplayTypeVars {
type_vars: &type_vars,
source,
};
let remove_generic_base =
remove_argument(generic_base, class_arguments, Parentheses::Remove, source)?;
let replace_type_params =
Edit::range_replacement(new_type_params.to_string(), type_params.range);
Ok(Some(Fix::unsafe_edits(
remove_generic_base,
[replace_type_params],
)))
}
/// Returns the type variables `exprs` represent.
///
/// If at least one of them cannot be converted to [`TypeVar`],
/// `None` is returned.
fn generic_arguments_to_type_vars<'a>(
exprs: impl Iterator<Item = &'a Expr>,
existing_type_params: &TypeParams,
semantic: &'a SemanticModel,
) -> Option<Vec<TypeVar<'a>>> {
let mut type_vars = vec![];
let mut encountered: FxHashSet<&str> = existing_type_params
.iter()
.map(|tp| tp.name().as_str())
.collect();
for expr in exprs {
let (name, unpacked) = match expr {
Expr::Name(name) => (name, false),
Expr::Starred(ExprStarred { value, .. }) => (value.as_name_expr()?, true),
Expr::Subscript(ExprSubscript { value, slice, .. }) => {
if !semantic.match_typing_expr(value, "Unpack") {
return None;
}
(slice.as_name_expr()?, true)
}
_ => return None,
};
if !encountered.insert(name.id.as_str()) {
continue;
}
let type_var = expr_name_to_type_var(semantic, name)?;
if !type_var_is_valid(&type_var, unpacked) {
return None;
}
// TODO: Type parameter defaults
if type_var.default.is_some() {
return None;
}
type_vars.push(type_var);
}
Some(type_vars)
}
/// Returns true in the following cases:
///
/// * If `type_var` is a `TypeVar`:
/// * It must not be unpacked
/// * If `type_var` is a `TypeVarTuple`:
/// * It must be unpacked
/// * It must not have any restrictions
/// * If `type_var` is a `ParamSpec`:
/// * It must not be unpacked
/// * It must not have any restrictions
fn type_var_is_valid(type_var: &TypeVar, unpacked: bool) -> bool {
let is_type_var_tuple = matches!(&type_var.kind, TypeParamKind::TypeVarTuple);
if is_type_var_tuple && !unpacked || !is_type_var_tuple && unpacked {
return false;
}
if !matches!(&type_var.kind, TypeParamKind::TypeVar) && type_var.restriction.is_some() {
return false;
}
true
}

View file

@ -2,6 +2,7 @@ pub(crate) use ambiguous_unicode_character::*;
pub(crate) use assert_with_print_message::*;
pub(crate) use assignment_in_assert::*;
pub(crate) use asyncio_dangling_task::*;
pub(crate) use class_with_mixed_type_vars::*;
pub(crate) use collection_literal_concatenation::*;
pub(crate) use dataclass_enum::*;
pub(crate) use decimal_from_float_literal::*;
@ -55,6 +56,7 @@ mod ambiguous_unicode_character;
mod assert_with_print_message;
mod assignment_in_assert;
mod asyncio_dangling_task;
mod class_with_mixed_type_vars;
mod collection_literal_concatenation;
mod confusables;
mod dataclass_enum;

View file

@ -0,0 +1,475 @@
---
source: crates/ruff_linter/src/rules/ruff/mod.rs
---
RUF053.py:23:12: RUF053 [*] Class with type parameter list inherits from `Generic`
|
21 | ### Errors
22 |
23 | class C[T](Generic[_A]): ...
| ^^^^^^^^^^^ RUF053
24 | class C[T](Generic[_B], str): ...
25 | class C[T](int, Generic[_C]): ...
|
= help: Remove `Generic` base class
Unsafe fix
20 20 |
21 21 | ### Errors
22 22 |
23 |-class C[T](Generic[_A]): ...
23 |+class C[T, _A]: ...
24 24 | class C[T](Generic[_B], str): ...
25 25 | class C[T](int, Generic[_C]): ...
26 26 | class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
RUF053.py:24:12: RUF053 [*] Class with type parameter list inherits from `Generic`
|
23 | class C[T](Generic[_A]): ...
24 | class C[T](Generic[_B], str): ...
| ^^^^^^^^^^^ RUF053
25 | class C[T](int, Generic[_C]): ...
26 | class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
Unsafe fix
21 21 | ### Errors
22 22 |
23 23 | class C[T](Generic[_A]): ...
24 |-class C[T](Generic[_B], str): ...
24 |+class C[T, _B: int](str): ...
25 25 | class C[T](int, Generic[_C]): ...
26 26 | class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
27 27 | class C[T](Generic[_E], list[_E]): ... # TODO: Type parameter defaults
RUF053.py:25:17: RUF053 [*] Class with type parameter list inherits from `Generic`
|
23 | class C[T](Generic[_A]): ...
24 | class C[T](Generic[_B], str): ...
25 | class C[T](int, Generic[_C]): ...
| ^^^^^^^^^^^ RUF053
26 | class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
27 | class C[T](Generic[_E], list[_E]): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
Unsafe fix
22 22 |
23 23 | class C[T](Generic[_A]): ...
24 24 | class C[T](Generic[_B], str): ...
25 |-class C[T](int, Generic[_C]): ...
25 |+class C[T, _C: (str, bytes)](int): ...
26 26 | class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
27 27 | class C[T](Generic[_E], list[_E]): ... # TODO: Type parameter defaults
28 28 | class C[T](list[_F], Generic[_F]): ... # TODO: Type parameter defaults
RUF053.py:26:19: RUF053 Class with type parameter list inherits from `Generic`
|
24 | class C[T](Generic[_B], str): ...
25 | class C[T](int, Generic[_C]): ...
26 | class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
| ^^^^^^^^^^^ RUF053
27 | class C[T](Generic[_E], list[_E]): ... # TODO: Type parameter defaults
28 | class C[T](list[_F], Generic[_F]): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
RUF053.py:27:12: RUF053 Class with type parameter list inherits from `Generic`
|
25 | class C[T](int, Generic[_C]): ...
26 | class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
27 | class C[T](Generic[_E], list[_E]): ... # TODO: Type parameter defaults
| ^^^^^^^^^^^ RUF053
28 | class C[T](list[_F], Generic[_F]): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
RUF053.py:28:22: RUF053 Class with type parameter list inherits from `Generic`
|
26 | class C[T](bytes, Generic[_D], bool): ... # TODO: Type parameter defaults
27 | class C[T](Generic[_E], list[_E]): ... # TODO: Type parameter defaults
28 | class C[T](list[_F], Generic[_F]): ... # TODO: Type parameter defaults
| ^^^^^^^^^^^ RUF053
29 |
30 | class C[*Ts](Generic[*_As]): ...
|
= help: Remove `Generic` base class
RUF053.py:30:14: RUF053 [*] Class with type parameter list inherits from `Generic`
|
28 | class C[T](list[_F], Generic[_F]): ... # TODO: Type parameter defaults
29 |
30 | class C[*Ts](Generic[*_As]): ...
| ^^^^^^^^^^^^^ RUF053
31 | class C[*Ts](Generic[Unpack[_As]]): ...
32 | class C[*Ts](Generic[Unpack[_Bs]], tuple[*Bs]): ...
|
= help: Remove `Generic` base class
Unsafe fix
27 27 | class C[T](Generic[_E], list[_E]): ... # TODO: Type parameter defaults
28 28 | class C[T](list[_F], Generic[_F]): ... # TODO: Type parameter defaults
29 29 |
30 |-class C[*Ts](Generic[*_As]): ...
30 |+class C[*Ts, *_As]: ...
31 31 | class C[*Ts](Generic[Unpack[_As]]): ...
32 32 | class C[*Ts](Generic[Unpack[_Bs]], tuple[*Bs]): ...
33 33 | class C[*Ts](Callable[[*_Cs], tuple[*Ts]], Generic[_Cs]): ... # TODO: Type parameter defaults
RUF053.py:31:14: RUF053 [*] Class with type parameter list inherits from `Generic`
|
30 | class C[*Ts](Generic[*_As]): ...
31 | class C[*Ts](Generic[Unpack[_As]]): ...
| ^^^^^^^^^^^^^^^^^^^^ RUF053
32 | class C[*Ts](Generic[Unpack[_Bs]], tuple[*Bs]): ...
33 | class C[*Ts](Callable[[*_Cs], tuple[*Ts]], Generic[_Cs]): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
Unsafe fix
28 28 | class C[T](list[_F], Generic[_F]): ... # TODO: Type parameter defaults
29 29 |
30 30 | class C[*Ts](Generic[*_As]): ...
31 |-class C[*Ts](Generic[Unpack[_As]]): ...
31 |+class C[*Ts, *_As]: ...
32 32 | class C[*Ts](Generic[Unpack[_Bs]], tuple[*Bs]): ...
33 33 | class C[*Ts](Callable[[*_Cs], tuple[*Ts]], Generic[_Cs]): ... # TODO: Type parameter defaults
34 34 |
RUF053.py:32:14: RUF053 Class with type parameter list inherits from `Generic`
|
30 | class C[*Ts](Generic[*_As]): ...
31 | class C[*Ts](Generic[Unpack[_As]]): ...
32 | class C[*Ts](Generic[Unpack[_Bs]], tuple[*Bs]): ...
| ^^^^^^^^^^^^^^^^^^^^ RUF053
33 | class C[*Ts](Callable[[*_Cs], tuple[*Ts]], Generic[_Cs]): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
RUF053.py:33:44: RUF053 Class with type parameter list inherits from `Generic`
|
31 | class C[*Ts](Generic[Unpack[_As]]): ...
32 | class C[*Ts](Generic[Unpack[_Bs]], tuple[*Bs]): ...
33 | class C[*Ts](Callable[[*_Cs], tuple[*Ts]], Generic[_Cs]): ... # TODO: Type parameter defaults
| ^^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
RUF053.py:36:14: RUF053 [*] Class with type parameter list inherits from `Generic`
|
36 | class C[**P](Generic[_P1]): ...
| ^^^^^^^^^^^^ RUF053
37 | class C[**P](Generic[_P2]): ...
38 | class C[**P](Generic[_P3]): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
Unsafe fix
33 33 | class C[*Ts](Callable[[*_Cs], tuple[*Ts]], Generic[_Cs]): ... # TODO: Type parameter defaults
34 34 |
35 35 |
36 |-class C[**P](Generic[_P1]): ...
36 |+class C[**P, **_P1]: ...
37 37 | class C[**P](Generic[_P2]): ...
38 38 | class C[**P](Generic[_P3]): ... # TODO: Type parameter defaults
39 39 |
RUF053.py:37:14: RUF053 Class with type parameter list inherits from `Generic`
|
36 | class C[**P](Generic[_P1]): ...
37 | class C[**P](Generic[_P2]): ...
| ^^^^^^^^^^^^ RUF053
38 | class C[**P](Generic[_P3]): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
RUF053.py:38:14: RUF053 Class with type parameter list inherits from `Generic`
|
36 | class C[**P](Generic[_P1]): ...
37 | class C[**P](Generic[_P2]): ...
38 | class C[**P](Generic[_P3]): ... # TODO: Type parameter defaults
| ^^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
RUF053.py:41:12: RUF053 [*] Class with type parameter list inherits from `Generic`
|
41 | class C[T](Generic[T, _A]): ...
| ^^^^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
Unsafe fix
38 38 | class C[**P](Generic[_P3]): ... # TODO: Type parameter defaults
39 39 |
40 40 |
41 |-class C[T](Generic[T, _A]): ...
41 |+class C[T, _A]: ...
42 42 |
43 43 |
44 44 | # See `is_existing_param_of_same_class`.
RUF053.py:47:35: RUF053 Class with type parameter list inherits from `Generic`
|
45 | # `expr_name_to_type_var` doesn't handle named expressions,
46 | # only simple assignments, so there is no fix.
47 | class C[T: (_Z := TypeVar('_Z'))](Generic[_Z]): ...
| ^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
RUF053.py:51:16: RUF053 [*] Class with type parameter list inherits from `Generic`
|
50 | class C(Generic[_B]):
51 | class D[T](Generic[_B, T]): ...
| ^^^^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
Unsafe fix
48 48 |
49 49 |
50 50 | class C(Generic[_B]):
51 |- class D[T](Generic[_B, T]): ...
51 |+ class D[T, _B: int]: ...
52 52 |
53 53 |
54 54 | class C[T]:
RUF053.py:55:16: RUF053 Class with type parameter list inherits from `Generic`
|
54 | class C[T]:
55 | class D[U](Generic[T, U]): ...
| ^^^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
RUF053.py:60:12: RUF053 [*] Class with type parameter list inherits from `Generic`
|
58 | # In a single run, only the first is reported.
59 | # Others will be reported/fixed in following iterations.
60 | class C[T](Generic[_C], Generic[_D]): ...
| ^^^^^^^^^^^ RUF053
61 | class C[T, _C: (str, bytes)](Generic[_D]): ... # TODO: Type parameter defaults
|
= help: Remove `Generic` base class
Unsafe fix
57 57 |
58 58 | # In a single run, only the first is reported.
59 59 | # Others will be reported/fixed in following iterations.
60 |-class C[T](Generic[_C], Generic[_D]): ...
60 |+class C[T, _C: (str, bytes)](Generic[_D]): ...
61 61 | class C[T, _C: (str, bytes)](Generic[_D]): ... # TODO: Type parameter defaults
62 62 |
63 63 |
RUF053.py:61:30: RUF053 Class with type parameter list inherits from `Generic`
|
59 | # Others will be reported/fixed in following iterations.
60 | class C[T](Generic[_C], Generic[_D]): ...
61 | class C[T, _C: (str, bytes)](Generic[_D]): ... # TODO: Type parameter defaults
| ^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
RUF053.py:66:3: RUF053 Class with type parameter list inherits from `Generic`
|
64 | class C[
65 | T # Comment
66 | ](Generic[_E]): ... # TODO: Type parameter defaults
| ^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
RUF053.py:69:12: RUF053 Class with type parameter list inherits from `Generic`
|
69 | class C[T](Generic[Generic[_F]]): ...
| ^^^^^^^^^^^^^^^^^^^^ RUF053
70 | class C[T](Generic[Unpack[_A]]): ...
71 | class C[T](Generic[Unpack[_P1]]): ...
|
= help: Remove `Generic` base class
RUF053.py:70:12: RUF053 Class with type parameter list inherits from `Generic`
|
69 | class C[T](Generic[Generic[_F]]): ...
70 | class C[T](Generic[Unpack[_A]]): ...
| ^^^^^^^^^^^^^^^^^^^ RUF053
71 | class C[T](Generic[Unpack[_P1]]): ...
72 | class C[T](Generic[Unpack[Unpack[_P2]]]): ...
|
= help: Remove `Generic` base class
RUF053.py:71:12: RUF053 Class with type parameter list inherits from `Generic`
|
69 | class C[T](Generic[Generic[_F]]): ...
70 | class C[T](Generic[Unpack[_A]]): ...
71 | class C[T](Generic[Unpack[_P1]]): ...
| ^^^^^^^^^^^^^^^^^^^^ RUF053
72 | class C[T](Generic[Unpack[Unpack[_P2]]]): ...
73 | class C[T](Generic[Unpack[*_As]]): ...
|
= help: Remove `Generic` base class
RUF053.py:72:12: RUF053 Class with type parameter list inherits from `Generic`
|
70 | class C[T](Generic[Unpack[_A]]): ...
71 | class C[T](Generic[Unpack[_P1]]): ...
72 | class C[T](Generic[Unpack[Unpack[_P2]]]): ...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF053
73 | class C[T](Generic[Unpack[*_As]]): ...
74 | class C[T](Generic[Unpack[_As, _Bs]]): ...
|
= help: Remove `Generic` base class
RUF053.py:73:12: RUF053 Class with type parameter list inherits from `Generic`
|
71 | class C[T](Generic[Unpack[_P1]]): ...
72 | class C[T](Generic[Unpack[Unpack[_P2]]]): ...
73 | class C[T](Generic[Unpack[*_As]]): ...
| ^^^^^^^^^^^^^^^^^^^^^ RUF053
74 | class C[T](Generic[Unpack[_As, _Bs]]): ...
|
= help: Remove `Generic` base class
RUF053.py:74:12: RUF053 Class with type parameter list inherits from `Generic`
|
72 | class C[T](Generic[Unpack[Unpack[_P2]]]): ...
73 | class C[T](Generic[Unpack[*_As]]): ...
74 | class C[T](Generic[Unpack[_As, _Bs]]): ...
| ^^^^^^^^^^^^^^^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
RUF053.py:77:12: RUF053 [*] Class with type parameter list inherits from `Generic`
|
77 | class C[T](Generic[_A, _A]): ...
| ^^^^^^^^^^^^^^^ RUF053
78 | class C[T](Generic[_A, Unpack[_As]]): ...
79 | class C[T](Generic[*_As, _A]): ...
|
= help: Remove `Generic` base class
Unsafe fix
74 74 | class C[T](Generic[Unpack[_As, _Bs]]): ...
75 75 |
76 76 |
77 |-class C[T](Generic[_A, _A]): ...
77 |+class C[T, _A]: ...
78 78 | class C[T](Generic[_A, Unpack[_As]]): ...
79 79 | class C[T](Generic[*_As, _A]): ...
80 80 |
RUF053.py:78:12: RUF053 [*] Class with type parameter list inherits from `Generic`
|
77 | class C[T](Generic[_A, _A]): ...
78 | class C[T](Generic[_A, Unpack[_As]]): ...
| ^^^^^^^^^^^^^^^^^^^^^^^^ RUF053
79 | class C[T](Generic[*_As, _A]): ...
|
= help: Remove `Generic` base class
Unsafe fix
75 75 |
76 76 |
77 77 | class C[T](Generic[_A, _A]): ...
78 |-class C[T](Generic[_A, Unpack[_As]]): ...
78 |+class C[T, _A, *_As]: ...
79 79 | class C[T](Generic[*_As, _A]): ...
80 80 |
81 81 |
RUF053.py:79:12: RUF053 [*] Class with type parameter list inherits from `Generic`
|
77 | class C[T](Generic[_A, _A]): ...
78 | class C[T](Generic[_A, Unpack[_As]]): ...
79 | class C[T](Generic[*_As, _A]): ...
| ^^^^^^^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
Unsafe fix
76 76 |
77 77 | class C[T](Generic[_A, _A]): ...
78 78 | class C[T](Generic[_A, Unpack[_As]]): ...
79 |-class C[T](Generic[*_As, _A]): ...
79 |+class C[T, *_As, _A]: ...
80 80 |
81 81 |
82 82 | from somewhere import APublicTypeVar
RUF053.py:83:12: RUF053 Class with type parameter list inherits from `Generic`
|
82 | from somewhere import APublicTypeVar
83 | class C[T](Generic[APublicTypeVar]): ...
| ^^^^^^^^^^^^^^^^^^^^^^^ RUF053
84 | class C[T](Generic[APublicTypeVar, _A]): ...
|
= help: Remove `Generic` base class
RUF053.py:84:12: RUF053 Class with type parameter list inherits from `Generic`
|
82 | from somewhere import APublicTypeVar
83 | class C[T](Generic[APublicTypeVar]): ...
84 | class C[T](Generic[APublicTypeVar, _A]): ...
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
RUF053.py:91:12: RUF053 [*] Class with type parameter list inherits from `Generic`
|
89 | # as named expressions are forbidden within type parameter lists.
90 | # See also the `_Z` example above.
91 | class C[T](Generic[_G]): ... # Should be moved down below eventually
| ^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
Unsafe fix
88 88 | # The latter cannot be used as a PEP 695 constraint,
89 89 | # as named expressions are forbidden within type parameter lists.
90 90 | # See also the `_Z` example above.
91 |-class C[T](Generic[_G]): ... # Should be moved down below eventually
91 |+class C[T, _G: (str, a := int)]: ... # Should be moved down below eventually
92 92 |
93 93 |
94 94 | # Single-element constraints should not be converted to a bound.
RUF053.py:95:20: RUF053 [*] Class with type parameter list inherits from `Generic`
|
94 | # Single-element constraints should not be converted to a bound.
95 | class C[T: (str,)](Generic[_A]): ...
| ^^^^^^^^^^^ RUF053
96 | class C[T: [a]](Generic[_A]): ...
|
= help: Remove `Generic` base class
Unsafe fix
92 92 |
93 93 |
94 94 | # Single-element constraints should not be converted to a bound.
95 |-class C[T: (str,)](Generic[_A]): ...
95 |+class C[T: (str), _A]: ...
96 96 | class C[T: [a]](Generic[_A]): ...
97 97 |
98 98 |
RUF053.py:96:17: RUF053 [*] Class with type parameter list inherits from `Generic`
|
94 | # Single-element constraints should not be converted to a bound.
95 | class C[T: (str,)](Generic[_A]): ...
96 | class C[T: [a]](Generic[_A]): ...
| ^^^^^^^^^^^ RUF053
|
= help: Remove `Generic` base class
Unsafe fix
93 93 |
94 94 | # Single-element constraints should not be converted to a bound.
95 95 | class C[T: (str,)](Generic[_A]): ...
96 |-class C[T: [a]](Generic[_A]): ...
96 |+class C[T: [a], _A]: ...
97 97 |
98 98 |
99 99 | # Existing bounds should not be deparenthesized.