mirror of https://github.com/astral-sh/ruff
[`pyupgrade`] Fix false negative for `TypeVar` with default argument in `non-pep695-generic-class` (`UP046`) (#20660)
## Summary Fixes #20656 --------- Co-authored-by: Brent Westbrook <brentrwestbrook@gmail.com>
This commit is contained in:
parent
9e404a30c3
commit
c06c3f9505
|
|
@ -43,7 +43,7 @@ class Foo:
|
|||
T = typing.TypeVar(*args)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
# `default` should be skipped for now, added in Python 3.13
|
||||
# `default` was added in Python 3.13
|
||||
T = typing.TypeVar("T", default=Any)
|
||||
x: typing.TypeAlias = list[T]
|
||||
|
||||
|
|
@ -90,9 +90,9 @@ PositiveList = TypeAliasType(
|
|||
"PositiveList2", list[Annotated[T, Gt(0)]], type_params=(T,)
|
||||
)
|
||||
|
||||
# `default` should be skipped for now, added in Python 3.13
|
||||
# `default` was added in Python 3.13
|
||||
T = typing.TypeVar("T", default=Any)
|
||||
AnyList = TypeAliasType("AnyList", list[T], typep_params=(T,))
|
||||
AnyList = TypeAliasType("AnyList", list[T], type_params=(T,))
|
||||
|
||||
# unsafe fix if comments within the fix
|
||||
T = TypeVar("T")
|
||||
|
|
@ -128,3 +128,7 @@ T: TypeAlias = ( # comment0
|
|||
str # comment6
|
||||
# comment7
|
||||
) # comment8
|
||||
|
||||
# Test case for TypeVar with default - should be converted when preview mode is enabled
|
||||
T_default = TypeVar("T_default", default=int)
|
||||
DefaultList: TypeAlias = list[T_default]
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ class MixedGenerics[U]:
|
|||
return (u, t)
|
||||
|
||||
|
||||
# TODO(brent) default requires 3.13
|
||||
# default requires 3.13
|
||||
V = TypeVar("V", default=Any, bound=str)
|
||||
|
||||
|
||||
|
|
@ -130,6 +130,14 @@ class DefaultTypeVar(Generic[V]): # -> [V: str = Any]
|
|||
var: V
|
||||
|
||||
|
||||
# Test case for TypeVar with default but no bound
|
||||
W = TypeVar("W", default=int)
|
||||
|
||||
|
||||
class DefaultOnlyTypeVar(Generic[W]): # -> [W = int]
|
||||
var: W
|
||||
|
||||
|
||||
# nested classes and functions are skipped
|
||||
class Outer:
|
||||
class Inner(Generic[T]):
|
||||
|
|
|
|||
|
|
@ -44,9 +44,7 @@ def any_str_param(s: AnyStr) -> AnyStr:
|
|||
return s
|
||||
|
||||
|
||||
# these cases are not handled
|
||||
|
||||
# TODO(brent) default requires 3.13
|
||||
# default requires 3.13
|
||||
V = TypeVar("V", default=Any, bound=str)
|
||||
|
||||
|
||||
|
|
@ -54,6 +52,8 @@ def default_var(v: V) -> V:
|
|||
return v
|
||||
|
||||
|
||||
# these cases are not handled
|
||||
|
||||
def outer():
|
||||
def inner(t: T) -> T:
|
||||
return t
|
||||
|
|
|
|||
|
|
@ -242,6 +242,11 @@ pub(crate) const fn is_refined_submodule_import_match_enabled(settings: &LinterS
|
|||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// https://github.com/astral-sh/ruff/pull/20660
|
||||
pub(crate) const fn is_type_var_default_enabled(settings: &LinterSettings) -> bool {
|
||||
settings.preview.is_enabled()
|
||||
}
|
||||
|
||||
// github.com/astral-sh/ruff/issues/20004
|
||||
pub(crate) const fn is_b006_check_guaranteed_mutable_expr_enabled(
|
||||
settings: &LinterSettings,
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ mod tests {
|
|||
use crate::rules::{isort, pyupgrade};
|
||||
use crate::settings::types::PreviewMode;
|
||||
use crate::test::{test_path, test_snippet};
|
||||
use crate::{assert_diagnostics, settings};
|
||||
use crate::{assert_diagnostics, assert_diagnostics_diff, settings};
|
||||
|
||||
#[test_case(Rule::ConvertNamedTupleFunctionalToClass, Path::new("UP014.py"))]
|
||||
#[test_case(Rule::ConvertTypedDictFunctionalToClass, Path::new("UP013.py"))]
|
||||
|
|
@ -140,6 +140,28 @@ mod tests {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::NonPEP695TypeAlias, Path::new("UP040.py"))]
|
||||
#[test_case(Rule::NonPEP695TypeAlias, Path::new("UP040.pyi"))]
|
||||
#[test_case(Rule::NonPEP695GenericClass, Path::new("UP046_0.py"))]
|
||||
#[test_case(Rule::NonPEP695GenericClass, Path::new("UP046_1.py"))]
|
||||
#[test_case(Rule::NonPEP695GenericFunction, Path::new("UP047.py"))]
|
||||
fn type_var_default_preview(rule_code: Rule, path: &Path) -> Result<()> {
|
||||
let snapshot = format!("{}__preview_diff", path.to_string_lossy());
|
||||
assert_diagnostics_diff!(
|
||||
snapshot,
|
||||
Path::new("pyupgrade").join(path).as_path(),
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Disabled,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
&settings::LinterSettings {
|
||||
preview: PreviewMode::Enabled,
|
||||
..settings::LinterSettings::for_rule(rule_code)
|
||||
},
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_0.py"))]
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_1.py"))]
|
||||
#[test_case(Rule::QuotedAnnotation, Path::new("UP037_2.pyi"))]
|
||||
|
|
|
|||
|
|
@ -14,13 +14,14 @@ use ruff_python_ast::{
|
|||
use ruff_python_semantic::SemanticModel;
|
||||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_type_var_default_enabled;
|
||||
|
||||
pub(crate) use non_pep695_generic_class::*;
|
||||
pub(crate) use non_pep695_generic_function::*;
|
||||
pub(crate) use non_pep695_type_alias::*;
|
||||
pub(crate) use private_type_parameter::*;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
mod non_pep695_generic_class;
|
||||
mod non_pep695_generic_function;
|
||||
mod non_pep695_type_alias;
|
||||
|
|
@ -122,6 +123,10 @@ impl Display for DisplayTypeVar<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
if let Some(default) = self.type_var.default {
|
||||
f.write_str(" = ")?;
|
||||
f.write_str(&self.source[default.range()])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -133,66 +138,63 @@ impl<'a> From<&'a TypeVar<'a>> for TypeParam {
|
|||
name,
|
||||
restriction,
|
||||
kind,
|
||||
default: _, // TODO(brent) see below
|
||||
default,
|
||||
}: &'a TypeVar<'a>,
|
||||
) -> Self {
|
||||
let default = default.map(|expr| Box::new(expr.clone()));
|
||||
match kind {
|
||||
TypeParamKind::TypeVar => {
|
||||
TypeParam::TypeVar(TypeParamTypeVar {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
name: Identifier::new(*name, TextRange::default()),
|
||||
bound: match restriction {
|
||||
Some(TypeVarRestriction::Bound(bound)) => Some(Box::new((*bound).clone())),
|
||||
Some(TypeVarRestriction::Constraint(constraints)) => {
|
||||
Some(Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
elts: constraints.iter().map(|expr| (*expr).clone()).collect(),
|
||||
ctx: ast::ExprContext::Load,
|
||||
parenthesized: true,
|
||||
})))
|
||||
}
|
||||
Some(TypeVarRestriction::AnyStr) => {
|
||||
Some(Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
elts: vec![
|
||||
Expr::Name(ExprName {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
id: Name::from("str"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
}),
|
||||
Expr::Name(ExprName {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
id: Name::from("bytes"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
}),
|
||||
],
|
||||
ctx: ast::ExprContext::Load,
|
||||
parenthesized: true,
|
||||
})))
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
// We don't handle defaults here yet. Should perhaps be a different rule since
|
||||
// defaults are only valid in 3.13+.
|
||||
default: None,
|
||||
})
|
||||
}
|
||||
TypeParamKind::TypeVar => TypeParam::TypeVar(TypeParamTypeVar {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
name: Identifier::new(*name, TextRange::default()),
|
||||
bound: match restriction {
|
||||
Some(TypeVarRestriction::Bound(bound)) => Some(Box::new((*bound).clone())),
|
||||
Some(TypeVarRestriction::Constraint(constraints)) => {
|
||||
Some(Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
elts: constraints.iter().map(|expr| (*expr).clone()).collect(),
|
||||
ctx: ast::ExprContext::Load,
|
||||
parenthesized: true,
|
||||
})))
|
||||
}
|
||||
Some(TypeVarRestriction::AnyStr) => {
|
||||
Some(Box::new(Expr::Tuple(ast::ExprTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
elts: vec![
|
||||
Expr::Name(ExprName {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
id: Name::from("str"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
}),
|
||||
Expr::Name(ExprName {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
id: Name::from("bytes"),
|
||||
ctx: ast::ExprContext::Load,
|
||||
}),
|
||||
],
|
||||
ctx: ast::ExprContext::Load,
|
||||
parenthesized: true,
|
||||
})))
|
||||
}
|
||||
None => None,
|
||||
},
|
||||
default,
|
||||
}),
|
||||
TypeParamKind::TypeVarTuple => TypeParam::TypeVarTuple(TypeParamTypeVarTuple {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
name: Identifier::new(*name, TextRange::default()),
|
||||
default: None,
|
||||
default,
|
||||
}),
|
||||
TypeParamKind::ParamSpec => TypeParam::ParamSpec(TypeParamParamSpec {
|
||||
range: TextRange::default(),
|
||||
node_index: ruff_python_ast::AtomicNodeIndex::NONE,
|
||||
name: Identifier::new(*name, TextRange::default()),
|
||||
default: None,
|
||||
default,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
|
@ -318,8 +320,8 @@ pub(crate) fn expr_name_to_type_var<'a>(
|
|||
.first()
|
||||
.is_some_and(Expr::is_string_literal_expr)
|
||||
{
|
||||
// TODO(brent) `default` was added in PEP 696 and Python 3.13 but can't be used in
|
||||
// generic type parameters before that
|
||||
// `default` was added in PEP 696 and Python 3.13. We now support converting
|
||||
// TypeVars with defaults to PEP 695 type parameters.
|
||||
//
|
||||
// ```python
|
||||
// T = TypeVar("T", default=Any, bound=str)
|
||||
|
|
@ -367,21 +369,22 @@ fn in_nested_context(checker: &Checker) -> bool {
|
|||
}
|
||||
|
||||
/// Deduplicate `vars`, returning `None` if `vars` is empty or any duplicates are found.
|
||||
fn check_type_vars(vars: Vec<TypeVar<'_>>) -> Option<Vec<TypeVar<'_>>> {
|
||||
/// Also returns `None` if any `TypeVar` has a default value and preview mode is not enabled.
|
||||
fn check_type_vars<'a>(vars: Vec<TypeVar<'a>>, checker: &Checker) -> Option<Vec<TypeVar<'a>>> {
|
||||
if vars.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// If any type variables have defaults and preview mode is not enabled, skip the rule
|
||||
if vars.iter().any(|tv| tv.default.is_some())
|
||||
&& !is_type_var_default_enabled(checker.settings())
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
// If any type variables were not unique, just bail out here. this is a runtime error and we
|
||||
// can't predict what the user wanted. also bail out if any Python 3.13+ default values are
|
||||
// found on the type parameters
|
||||
(vars
|
||||
.iter()
|
||||
.unique_by(|tvar| tvar.name)
|
||||
.filter(|tvar| tvar.default.is_none())
|
||||
.count()
|
||||
== vars.len())
|
||||
.then_some(vars)
|
||||
// can't predict what the user wanted.
|
||||
(vars.iter().unique_by(|tvar| tvar.name).count() == vars.len()).then_some(vars)
|
||||
}
|
||||
|
||||
/// Search `class_bases` for a `typing.Generic` base class. Returns the `Generic` expression (if
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ pub(crate) fn non_pep695_generic_class(checker: &Checker, class_def: &StmtClassD
|
|||
//
|
||||
// just because we can't confirm that `SomethingElse` is a `TypeVar`
|
||||
if !visitor.any_skipped {
|
||||
let Some(type_vars) = check_type_vars(visitor.vars) else {
|
||||
let Some(type_vars) = check_type_vars(visitor.vars, checker) else {
|
||||
diagnostic.defuse();
|
||||
return;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ pub(crate) fn non_pep695_generic_function(checker: &Checker, function_def: &Stmt
|
|||
}
|
||||
}
|
||||
|
||||
let Some(type_vars) = check_type_vars(type_vars) else {
|
||||
let Some(type_vars) = check_type_vars(type_vars, checker) else {
|
||||
return;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use ruff_python_ast::{Expr, ExprCall, ExprName, Keyword, StmtAnnAssign, StmtAssi
|
|||
use ruff_text_size::{Ranged, TextRange};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::preview::is_type_var_default_enabled;
|
||||
use crate::{Applicability, Edit, Fix, FixAvailability, Violation};
|
||||
use ruff_python_ast::PythonVersion;
|
||||
|
||||
|
|
@ -232,8 +233,10 @@ pub(crate) fn non_pep695_type_alias(checker: &Checker, stmt: &StmtAnnAssign) {
|
|||
.unique_by(|tvar| tvar.name)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// TODO(brent) handle `default` arg for Python 3.13+
|
||||
if vars.iter().any(|tv| tv.default.is_some()) {
|
||||
// Skip if any TypeVar has defaults and preview mode is not enabled
|
||||
if vars.iter().any(|tv| tv.default.is_some())
|
||||
&& !is_type_var_default_enabled(checker.settings())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of the `type` keywo
|
|||
44 | x: typing.TypeAlias = list[T]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
45 |
|
||||
46 | # `default` should be skipped for now, added in Python 3.13
|
||||
46 | # `default` was added in Python 3.13
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
41 |
|
||||
|
|
@ -226,7 +226,7 @@ help: Use the `type` keyword
|
|||
- x: typing.TypeAlias = list[T]
|
||||
44 + type x = list[T]
|
||||
45 |
|
||||
46 | # `default` should be skipped for now, added in Python 3.13
|
||||
46 | # `default` was added in Python 3.13
|
||||
47 | T = typing.TypeVar("T", default=Any)
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
|
|
@ -355,6 +355,26 @@ help: Use the `type` keyword
|
|||
87 | # OK: Other name
|
||||
88 | T = TypeVar("T", bound=SupportGt)
|
||||
|
||||
UP040 [*] Type alias `AnyList` uses `TypeAliasType` assignment instead of the `type` keyword
|
||||
--> UP040.py:95:1
|
||||
|
|
||||
93 | # `default` was added in Python 3.13
|
||||
94 | T = typing.TypeVar("T", default=Any)
|
||||
95 | AnyList = TypeAliasType("AnyList", list[T], type_params=(T,))
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
96 |
|
||||
97 | # unsafe fix if comments within the fix
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
92 |
|
||||
93 | # `default` was added in Python 3.13
|
||||
94 | T = typing.TypeVar("T", default=Any)
|
||||
- AnyList = TypeAliasType("AnyList", list[T], type_params=(T,))
|
||||
95 + type AnyList[T = Any] = list[T]
|
||||
96 |
|
||||
97 | # unsafe fix if comments within the fix
|
||||
98 | T = TypeVar("T")
|
||||
|
||||
UP040 [*] Type alias `PositiveList` uses `TypeAliasType` assignment instead of the `type` keyword
|
||||
--> UP040.py:99:1
|
||||
|
|
||||
|
|
@ -469,6 +489,8 @@ UP040 [*] Type alias `T` uses `TypeAlias` annotation instead of the `type` keywo
|
|||
129 | | # comment7
|
||||
130 | | ) # comment8
|
||||
| |_^
|
||||
131 |
|
||||
132 | # Test case for TypeVar with default - should be converted when preview mode is enabled
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
119 | | str
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 2
|
||||
|
||||
--- Added ---
|
||||
UP040 [*] Type alias `x` uses `TypeAlias` annotation instead of the `type` keyword
|
||||
--> UP040.py:48:1
|
||||
|
|
||||
46 | # `default` was added in Python 3.13
|
||||
47 | T = typing.TypeVar("T", default=Any)
|
||||
48 | x: typing.TypeAlias = list[T]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
49 |
|
||||
50 | # OK
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
45 |
|
||||
46 | # `default` was added in Python 3.13
|
||||
47 | T = typing.TypeVar("T", default=Any)
|
||||
- x: typing.TypeAlias = list[T]
|
||||
48 + type x[T = Any] = list[T]
|
||||
49 |
|
||||
50 | # OK
|
||||
51 | x: TypeAlias
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
|
||||
UP040 [*] Type alias `DefaultList` uses `TypeAlias` annotation instead of the `type` keyword
|
||||
--> UP040.py:134:1
|
||||
|
|
||||
132 | # Test case for TypeVar with default - should be converted when preview mode is enabled
|
||||
133 | T_default = TypeVar("T_default", default=int)
|
||||
134 | DefaultList: TypeAlias = list[T_default]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: Use the `type` keyword
|
||||
131 |
|
||||
132 | # Test case for TypeVar with default - should be converted when preview mode is enabled
|
||||
133 | T_default = TypeVar("T_default", default=int)
|
||||
- DefaultList: TypeAlias = list[T_default]
|
||||
134 + type DefaultList[T_default = int] = list[T_default]
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 0
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 2
|
||||
|
||||
--- Added ---
|
||||
UP046 [*] Generic class `DefaultTypeVar` uses `Generic` subclass instead of type parameters
|
||||
--> UP046_0.py:129:22
|
||||
|
|
||||
129 | class DefaultTypeVar(Generic[V]): # -> [V: str = Any]
|
||||
| ^^^^^^^^^^
|
||||
130 | var: V
|
||||
|
|
||||
help: Use type parameters
|
||||
126 | V = TypeVar("V", default=Any, bound=str)
|
||||
127 |
|
||||
128 |
|
||||
- class DefaultTypeVar(Generic[V]): # -> [V: str = Any]
|
||||
129 + class DefaultTypeVar[V: str = Any]: # -> [V: str = Any]
|
||||
130 | var: V
|
||||
131 |
|
||||
132 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
||||
|
||||
UP046 [*] Generic class `DefaultOnlyTypeVar` uses `Generic` subclass instead of type parameters
|
||||
--> UP046_0.py:137:26
|
||||
|
|
||||
137 | class DefaultOnlyTypeVar(Generic[W]): # -> [W = int]
|
||||
| ^^^^^^^^^^
|
||||
138 | var: W
|
||||
|
|
||||
help: Use type parameters
|
||||
134 | W = TypeVar("W", default=int)
|
||||
135 |
|
||||
136 |
|
||||
- class DefaultOnlyTypeVar(Generic[W]): # -> [W = int]
|
||||
137 + class DefaultOnlyTypeVar[W = int]: # -> [W = int]
|
||||
138 | var: W
|
||||
139 |
|
||||
140 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 0
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
source: crates/ruff_linter/src/rules/pyupgrade/mod.rs
|
||||
---
|
||||
--- Linter settings ---
|
||||
-linter.preview = disabled
|
||||
+linter.preview = enabled
|
||||
|
||||
--- Summary ---
|
||||
Removed: 0
|
||||
Added: 1
|
||||
|
||||
--- Added ---
|
||||
UP047 [*] Generic function `default_var` should use type parameters
|
||||
--> UP047.py:51:5
|
||||
|
|
||||
51 | def default_var(v: V) -> V:
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
52 | return v
|
||||
|
|
||||
help: Use type parameters
|
||||
48 | V = TypeVar("V", default=Any, bound=str)
|
||||
49 |
|
||||
50 |
|
||||
- def default_var(v: V) -> V:
|
||||
51 + def default_var[V: str = Any](v: V) -> V:
|
||||
52 | return v
|
||||
53 |
|
||||
54 |
|
||||
note: This is an unsafe fix and may change runtime behavior
|
||||
Loading…
Reference in New Issue