[flake8-pyi] Implement unannotated-assignment-in-stub (PY052) (#4293)

This commit is contained in:
Sladyn 2023-05-15 19:06:55 -07:00 committed by GitHub
parent 1fe6954150
commit c711db11ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 460 additions and 39 deletions

View file

@ -0,0 +1,93 @@
import builtins
import typing
from typing import TypeAlias, Final
field1: int
field2: int = ...
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
field4: int = 0
field41: int = 0xFFFFFFFF
field42: int = 1234567890
field43: int = -0xFFFFFFFF
field44: int = -1234567890
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
field6 = 0 # Y052 Need type annotation for "field6"
field7 = b"" # Y052 Need type annotation for "field7"
field71 = "foo" # Y052 Need type annotation for "field71"
field72: str = "foo"
field8 = False # Y052 Need type annotation for "field8"
field81 = -1 # Y052 Need type annotation for "field81"
field82: float = -98.43
field83 = -42j # Y052 Need type annotation for "field83"
field84 = 5 + 42j # Y052 Need type annotation for "field84"
field85 = -5 - 42j # Y052 Need type annotation for "field85"
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
Field95: TypeAlias = None
Field96: TypeAlias = int | None
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
field191: list[int] = [1, 2, 3]
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
field201: tuple[int, ...] = (1, 2, 3)
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
field211: set[int] = {1, 2, 3}
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
field213: dict[str, str] = {"foo": "bar"}
field22: Final = {"foo": 5}
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
# We shouldn't emit Y015 within functions
def f():
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# We shouldn't emit Y015 for __slots__ or __match_args__
class Class1:
__slots__ = (
'_one',
'_two',
'_three',
'_four',
'_five',
'_six',
'_seven',
'_eight',
'_nine',
'_ten',
'_eleven',
)
__match_args__ = (
'one',
'two',
'three',
'four',
'five',
'six',
'seven',
'eight',
'nine',
'ten',
'eleven',
)
# We shouldn't emit Y015 for __all__
__all__ = ["Class1"]
# Ignore the following for PYI015
field26 = typing.Sequence[int]
field27 = list[str]
field28 = builtins.str
field29 = str
field30 = str | bytes | None

View file

@ -0,0 +1,100 @@
import builtins
import typing
from typing import TypeAlias, Final, NewType, TypeVar, TypeVarTuple, ParamSpec
# We shouldn't emit Y015 for simple default values
field1: int
field2: int = ...
field3 = ... # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int")
field4: int = 0
field41: int = 0xFFFFFFFF
field42: int = 1234567890
field43: int = -0xFFFFFFFF
field44: int = -1234567890
field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
field6 = 0 # Y052 Need type annotation for "field6"
field7 = b"" # Y052 Need type annotation for "field7"
field71 = "foo" # Y052 Need type annotation for "field71"
field72: str = "foo"
field8 = False # Y052 Need type annotation for "field8"
field81 = -1 # Y052 Need type annotation for "field81"
field82: float = -98.43
field83 = -42j # Y052 Need type annotation for "field83"
field84 = 5 + 42j # Y052 Need type annotation for "field84"
field85 = -5 - 42j # Y052 Need type annotation for "field85"
field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
Field95: TypeAlias = None
Field96: TypeAlias = int | None
Field97: TypeAlias = None | typing.SupportsInt | builtins.str | float | bool
Field98 = NewType('MyInt', int)
Field99 = TypeVar('Field99')
Field100 = TypeVarTuple('Field100')
Field101 = ParamSpec('Field101')
field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
field191: list[int] = [1, 2, 3]
field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
field201: tuple[int, ...] = (1, 2, 3)
field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
field211: set[int] = {1, 2, 3}
field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
field213: dict[str, str] = {"foo": "bar"}
field22: Final = {"foo": 5}
# We *should* emit Y015 for more complex default values
field221: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] # Y015 Only simple default values are allowed for assignments
field223: list[int] = [*range(10)] # Y015 Only simple default values are allowed for assignments
field224: list[int] = list(range(10)) # Y015 Only simple default values are allowed for assignments
field225: list[object] = [{}, 1, 2] # Y015 Only simple default values are allowed for assignments
field226: tuple[str | tuple[str, ...], ...] = ("foo", ("foo", "bar")) # Y015 Only simple default values are allowed for assignments
field227: dict[str, object] = {"foo": {"foo": "bar"}} # Y015 Only simple default values are allowed for assignments
field228: dict[str, list[object]] = {"foo": []} # Y015 Only simple default values are allowed for assignments
# When parsed, this case results in `None` being placed in the `.keys` list for the `ast.Dict` node
field229: dict[int, int] = {1: 2, **{3: 4}} # Y015 Only simple default values are allowed for assignments
field23 = "foo" + "bar" # Y015 Only simple default values are allowed for assignments
field24 = b"foo" + b"bar" # Y015 Only simple default values are allowed for assignments
field25 = 5 * 5 # Y015 Only simple default values are allowed for assignments
# We shouldn't emit Y015 within functions
def f():
field26: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
# We shouldn't emit Y015 for __slots__ or __match_args__
class Class1:
__slots__ = (
'_one',
'_two',
'_three',
'_four',
'_five',
'_six',
'_seven',
'_eight',
'_nine',
'_ten',
'_eleven',
)
__match_args__ = (
'one',
'two',
'three',
'four',
'five',
'six',
'seven',
'eight',
'nine',
'ten',
'eleven',
)
# We shouldn't emit Y015 for __all__
__all__ = ["Class1"]
# Ignore the following for PYI015
field26 = typing.Sequence[int]
field27 = list[str]
field28 = builtins.str
field29 = str
field30 = str | bytes | None

View file

@ -1752,11 +1752,11 @@ where
}
if self.is_stub {
if self
.settings
.rules
.any_enabled(&[Rule::UnprefixedTypeParam, Rule::AssignmentDefaultInStub])
{
if self.settings.rules.any_enabled(&[
Rule::UnprefixedTypeParam,
Rule::AssignmentDefaultInStub,
Rule::UnannotatedAssignmentInStub,
]) {
// Ignore assignments in function bodies; those are covered by other rules.
if !self.ctx.scopes().any(|scope| scope.kind.is_function()) {
if self.settings.rules.enabled(Rule::UnprefixedTypeParam) {
@ -1765,6 +1765,15 @@ where
if self.settings.rules.enabled(Rule::AssignmentDefaultInStub) {
flake8_pyi::rules::assignment_default_in_stub(self, targets, value);
}
if self
.settings
.rules
.enabled(Rule::UnannotatedAssignmentInStub)
{
flake8_pyi::rules::unannotated_assignment_in_stub(
self, targets, value,
);
}
}
}
}

View file

@ -610,6 +610,7 @@ pub fn code_to_rule(linter: Linter, code: &str) -> Option<Rule> {
(Flake8Pyi, "033") => Rule::TypeCommentInStub,
(Flake8Pyi, "042") => Rule::SnakeCaseTypeAlias,
(Flake8Pyi, "043") => Rule::TSuffixedTypeAlias,
(Flake8Pyi, "052") => Rule::UnannotatedAssignmentInStub,
// flake8-pytest-style
(Flake8PytestStyle, "001") => Rule::PytestFixtureIncorrectParenthesesStyle,

View file

@ -540,18 +540,19 @@ ruff_macros::register_rules!(
rules::flake8_pyi::rules::AssignmentDefaultInStub,
rules::flake8_pyi::rules::BadVersionInfoComparison,
rules::flake8_pyi::rules::DocstringInStub,
rules::flake8_pyi::rules::NonEmptyStubBody,
rules::flake8_pyi::rules::PassStatementStubBody,
rules::flake8_pyi::rules::TypeCommentInStub,
rules::flake8_pyi::rules::TypedArgumentDefaultInStub,
rules::flake8_pyi::rules::UnprefixedTypeParam,
rules::flake8_pyi::rules::UnrecognizedPlatformCheck,
rules::flake8_pyi::rules::UnrecognizedPlatformName,
rules::flake8_pyi::rules::PassInClassBody,
rules::flake8_pyi::rules::DuplicateUnionMember,
rules::flake8_pyi::rules::NonEmptyStubBody,
rules::flake8_pyi::rules::PassInClassBody,
rules::flake8_pyi::rules::PassStatementStubBody,
rules::flake8_pyi::rules::QuotedAnnotationInStub,
rules::flake8_pyi::rules::SnakeCaseTypeAlias,
rules::flake8_pyi::rules::TSuffixedTypeAlias,
rules::flake8_pyi::rules::TypeCommentInStub,
rules::flake8_pyi::rules::TypedArgumentDefaultInStub,
rules::flake8_pyi::rules::UnannotatedAssignmentInStub,
rules::flake8_pyi::rules::UnprefixedTypeParam,
rules::flake8_pyi::rules::UnrecognizedPlatformCheck,
rules::flake8_pyi::rules::UnrecognizedPlatformName,
// flake8-pytest-style
rules::flake8_pytest_style::rules::PytestFixtureIncorrectParenthesesStyle,
rules::flake8_pytest_style::rules::PytestFixturePositionalArgs,

View file

@ -13,38 +13,40 @@ mod tests {
use crate::test::test_path;
use crate::{assert_messages, settings};
#[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.py"))]
#[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.pyi"))]
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.py"))]
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))]
#[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.py"))]
#[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.pyi"))]
#[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.py"))]
#[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.pyi"))]
#[test_case(Rule::PassStatementStubBody, Path::new("PYI009.py"))]
#[test_case(Rule::PassStatementStubBody, Path::new("PYI009.pyi"))]
#[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.py"))]
#[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.pyi"))]
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.py"))]
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.pyi"))]
#[test_case(Rule::PassInClassBody, Path::new("PYI012.py"))]
#[test_case(Rule::PassInClassBody, Path::new("PYI012.pyi"))]
#[test_case(Rule::ArgumentDefaultInStub, Path::new("PYI014.py"))]
#[test_case(Rule::ArgumentDefaultInStub, Path::new("PYI014.pyi"))]
#[test_case(Rule::AssignmentDefaultInStub, Path::new("PYI015.py"))]
#[test_case(Rule::AssignmentDefaultInStub, Path::new("PYI015.pyi"))]
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.py"))]
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.pyi"))]
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.py"))]
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))]
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.py"))]
#[test_case(Rule::BadVersionInfoComparison, Path::new("PYI006.pyi"))]
#[test_case(Rule::DocstringInStub, Path::new("PYI021.py"))]
#[test_case(Rule::DocstringInStub, Path::new("PYI021.pyi"))]
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.py"))]
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.pyi"))]
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.py"))]
#[test_case(Rule::DuplicateUnionMember, Path::new("PYI016.pyi"))]
#[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.py"))]
#[test_case(Rule::NonEmptyStubBody, Path::new("PYI010.pyi"))]
#[test_case(Rule::PassInClassBody, Path::new("PYI012.py"))]
#[test_case(Rule::PassInClassBody, Path::new("PYI012.pyi"))]
#[test_case(Rule::PassStatementStubBody, Path::new("PYI009.py"))]
#[test_case(Rule::PassStatementStubBody, Path::new("PYI009.pyi"))]
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.py"))]
#[test_case(Rule::QuotedAnnotationInStub, Path::new("PYI020.pyi"))]
#[test_case(Rule::SnakeCaseTypeAlias, Path::new("PYI042.py"))]
#[test_case(Rule::SnakeCaseTypeAlias, Path::new("PYI042.pyi"))]
#[test_case(Rule::TSuffixedTypeAlias, Path::new("PYI043.py"))]
#[test_case(Rule::TSuffixedTypeAlias, Path::new("PYI043.pyi"))]
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.py"))]
#[test_case(Rule::TypeCommentInStub, Path::new("PYI033.pyi"))]
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.py"))]
#[test_case(Rule::TypedArgumentDefaultInStub, Path::new("PYI011.pyi"))]
#[test_case(Rule::UnannotatedAssignmentInStub, Path::new("PYI052.py"))]
#[test_case(Rule::UnannotatedAssignmentInStub, Path::new("PYI052.pyi"))]
#[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.py"))]
#[test_case(Rule::UnprefixedTypeParam, Path::new("PYI001.pyi"))]
#[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.py"))]
#[test_case(Rule::UnrecognizedPlatformCheck, Path::new("PYI007.pyi"))]
#[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.py"))]
#[test_case(Rule::UnrecognizedPlatformName, Path::new("PYI008.pyi"))]
fn rules(rule_code: Rule, path: &Path) -> Result<()> {
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
let diagnostics = test_path(

View file

@ -10,8 +10,8 @@ pub(crate) use prefix_type_params::{prefix_type_params, UnprefixedTypeParam};
pub(crate) use quoted_annotation_in_stub::{quoted_annotation_in_stub, QuotedAnnotationInStub};
pub(crate) use simple_defaults::{
annotated_assignment_default_in_stub, argument_simple_defaults, assignment_default_in_stub,
typed_argument_simple_defaults, ArgumentDefaultInStub, AssignmentDefaultInStub,
TypedArgumentDefaultInStub,
typed_argument_simple_defaults, unannotated_assignment_in_stub, ArgumentDefaultInStub,
AssignmentDefaultInStub, TypedArgumentDefaultInStub, UnannotatedAssignmentInStub,
};
pub(crate) use type_alias_naming::{
snake_case_type_alias, t_suffixed_type_alias, SnakeCaseTypeAlias, TSuffixedTypeAlias,

View file

@ -1,8 +1,9 @@
use rustpython_parser::ast::{self, Arguments, Constant, Expr, ExprKind, Operator, Unaryop};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix};
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Edit, Fix, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_semantic::context::Context;
use ruff_python_semantic::scope::{ClassDef, ScopeKind};
use crate::checkers::ast::Checker;
use crate::registry::AsRule;
@ -49,6 +50,19 @@ impl AlwaysAutofixableViolation for AssignmentDefaultInStub {
}
}
#[violation]
pub struct UnannotatedAssignmentInStub {
name: String,
}
impl Violation for UnannotatedAssignmentInStub {
#[derive_message_formats]
fn message(&self) -> String {
let UnannotatedAssignmentInStub { name } = self;
format!("Need type annotation for `{name}`")
}
}
const ALLOWED_MATH_ATTRIBUTES_IN_DEFAULTS: &[&[&str]] = &[
&["math", "inf"],
&["math", "nan"],
@ -285,6 +299,21 @@ fn is_special_assignment(context: &Context, target: &Expr) -> bool {
}
}
/// Returns `true` if the a class is an enum, based on its base classes.
fn is_enum(context: &Context, bases: &[Expr]) -> bool {
return bases.iter().any(|expr| {
context.resolve_call_path(expr).map_or(false, |call_path| {
matches!(
call_path.as_slice(),
[
"enum",
"Enum" | "Flag" | "IntEnum" | "IntFlag" | "StrEnum" | "ReprEnum"
]
)
})
});
}
/// PYI011
pub(crate) fn typed_argument_simple_defaults(checker: &mut Checker, args: &Arguments) {
if !args.defaults.is_empty() {
@ -401,7 +430,14 @@ pub(crate) fn argument_simple_defaults(checker: &mut Checker, args: &Arguments)
/// PYI015
pub(crate) fn assignment_default_in_stub(checker: &mut Checker, targets: &[Expr], value: &Expr) {
if targets.len() == 1 && is_special_assignment(&checker.ctx, &targets[0]) {
if targets.len() != 1 {
return;
}
let target = &targets[0];
if !matches!(target.node, ExprKind::Name(..)) {
return;
}
if is_special_assignment(&checker.ctx, target) {
return;
}
if is_type_var_like_call(&checker.ctx, value) {
@ -455,3 +491,42 @@ pub(crate) fn annotated_assignment_default_in_stub(
}
checker.diagnostics.push(diagnostic);
}
/// PYI052
pub(crate) fn unannotated_assignment_in_stub(
checker: &mut Checker,
targets: &[Expr],
value: &Expr,
) {
if targets.len() != 1 {
return;
}
let target = &targets[0];
let ExprKind::Name(ast::ExprName { id, .. }) = &target.node else {
return;
};
if is_special_assignment(&checker.ctx, target) {
return;
}
if is_type_var_like_call(&checker.ctx, value) {
return;
}
if is_valid_default_value_without_annotation(value) {
return;
}
if !is_valid_default_value_with_annotation(value, checker, true) {
return;
}
if let ScopeKind::Class(ClassDef { bases, .. }) = &checker.ctx.scope().kind {
if is_enum(&checker.ctx, bases) {
return;
}
}
checker.diagnostics.push(Diagnostic::new(
UnannotatedAssignmentInStub {
name: id.to_string(),
},
value.range(),
));
}

View file

@ -0,0 +1,4 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---

View file

@ -0,0 +1,134 @@
---
source: crates/ruff/src/rules/flake8_pyi/mod.rs
---
PYI052.pyi:14:10: PYI052 Need type annotation for `field5`
|
14 | field43: int = -0xFFFFFFFF
15 | field44: int = -1234567890
16 | field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
| ^ PYI052
17 | field6 = 0 # Y052 Need type annotation for "field6"
18 | field7 = b"" # Y052 Need type annotation for "field7"
|
PYI052.pyi:15:10: PYI052 Need type annotation for `field6`
|
15 | field44: int = -1234567890
16 | field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
17 | field6 = 0 # Y052 Need type annotation for "field6"
| ^ PYI052
18 | field7 = b"" # Y052 Need type annotation for "field7"
19 | field71 = "foo" # Y052 Need type annotation for "field71"
|
PYI052.pyi:16:10: PYI052 Need type annotation for `field7`
|
16 | field5 = 0 # type: int # Y033 Do not use type comments in stubs (e.g. use "x: int" instead of "x = ... # type: int") # Y052 Need type annotation for "field5"
17 | field6 = 0 # Y052 Need type annotation for "field6"
18 | field7 = b"" # Y052 Need type annotation for "field7"
| ^^^ PYI052
19 | field71 = "foo" # Y052 Need type annotation for "field71"
20 | field72: str = "foo"
|
PYI052.pyi:17:11: PYI052 Need type annotation for `field71`
|
17 | field6 = 0 # Y052 Need type annotation for "field6"
18 | field7 = b"" # Y052 Need type annotation for "field7"
19 | field71 = "foo" # Y052 Need type annotation for "field71"
| ^^^^^ PYI052
20 | field72: str = "foo"
21 | field8 = False # Y052 Need type annotation for "field8"
|
PYI052.pyi:19:10: PYI052 Need type annotation for `field8`
|
19 | field71 = "foo" # Y052 Need type annotation for "field71"
20 | field72: str = "foo"
21 | field8 = False # Y052 Need type annotation for "field8"
| ^^^^^ PYI052
22 | field81 = -1 # Y052 Need type annotation for "field81"
23 | field82: float = -98.43
|
PYI052.pyi:20:11: PYI052 Need type annotation for `field81`
|
20 | field72: str = "foo"
21 | field8 = False # Y052 Need type annotation for "field8"
22 | field81 = -1 # Y052 Need type annotation for "field81"
| ^^ PYI052
23 | field82: float = -98.43
24 | field83 = -42j # Y052 Need type annotation for "field83"
|
PYI052.pyi:22:11: PYI052 Need type annotation for `field83`
|
22 | field81 = -1 # Y052 Need type annotation for "field81"
23 | field82: float = -98.43
24 | field83 = -42j # Y052 Need type annotation for "field83"
| ^^^^ PYI052
25 | field84 = 5 + 42j # Y052 Need type annotation for "field84"
26 | field85 = -5 - 42j # Y052 Need type annotation for "field85"
|
PYI052.pyi:23:11: PYI052 Need type annotation for `field84`
|
23 | field82: float = -98.43
24 | field83 = -42j # Y052 Need type annotation for "field83"
25 | field84 = 5 + 42j # Y052 Need type annotation for "field84"
| ^^^^^^^ PYI052
26 | field85 = -5 - 42j # Y052 Need type annotation for "field85"
27 | field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
|
PYI052.pyi:24:11: PYI052 Need type annotation for `field85`
|
24 | field83 = -42j # Y052 Need type annotation for "field83"
25 | field84 = 5 + 42j # Y052 Need type annotation for "field84"
26 | field85 = -5 - 42j # Y052 Need type annotation for "field85"
| ^^^^^^^^ PYI052
27 | field9 = None # Y026 Use typing_extensions.TypeAlias for type aliases, e.g. "field9: TypeAlias = None"
28 | Field95: TypeAlias = None
|
PYI052.pyi:33:11: PYI052 Need type annotation for `field19`
|
33 | Field100 = TypeVarTuple('Field100')
34 | Field101 = ParamSpec('Field101')
35 | field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
| ^^^^^^^^^ PYI052
36 | field191: list[int] = [1, 2, 3]
37 | field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
|
PYI052.pyi:35:11: PYI052 Need type annotation for `field20`
|
35 | field19 = [1, 2, 3] # Y052 Need type annotation for "field19"
36 | field191: list[int] = [1, 2, 3]
37 | field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
| ^^^^^^^^^ PYI052
38 | field201: tuple[int, ...] = (1, 2, 3)
39 | field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
|
PYI052.pyi:37:11: PYI052 Need type annotation for `field21`
|
37 | field20 = (1, 2, 3) # Y052 Need type annotation for "field20"
38 | field201: tuple[int, ...] = (1, 2, 3)
39 | field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
| ^^^^^^^^^ PYI052
40 | field211: set[int] = {1, 2, 3}
41 | field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
|
PYI052.pyi:39:12: PYI052 Need type annotation for `field212`
|
39 | field21 = {1, 2, 3} # Y052 Need type annotation for "field21"
40 | field211: set[int] = {1, 2, 3}
41 | field212 = {"foo": "bar"} # Y052 Need type annotation for "field212"
| ^^^^^^^^^^^^^^ PYI052
42 | field213: dict[str, str] = {"foo": "bar"}
43 | field22: Final = {"foo": 5}
|