Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 6 additions & 42 deletions flake8_type_checking/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
ANNOTATION_PROPERTY,
ATTRIBUTE_PROPERTY,
ATTRS_DECORATORS,
ATTRS_IMPORTS,
BINOP_OPERAND_PROPERTY,
MISSING,
TC001,
Expand Down Expand Up @@ -53,7 +52,6 @@
HasPosition,
Import,
ImportTypeValue,
Name,
SupportsIsTyping,
)

Expand Down Expand Up @@ -130,53 +128,19 @@ class AttrsMixin:
"""

if TYPE_CHECKING:
third_party_imports: dict[str, Import]

def get_all_attrs_imports(self) -> dict[str | None, str]:
"""Return a map of all attrs/attr imports."""
attrs_imports: dict[str | None, str] = {} # map of alias to full import name

for node in self.third_party_imports.values():
module = getattr(node, 'module', '')
names: list[Name] = getattr(node, 'names', [])

for name in names:
if module in ATTRS_IMPORTS:
alias = name.name if name.asname is None else name.asname
attrs_imports[alias] = f'{module}.{name.name}'
elif name.name.split('.')[0] in ATTRS_IMPORTS:
attrs_imports[name.asname] = name.name

return attrs_imports
def lookup_full_name(self, node: ast.AST) -> str | None: # noqa: D102
...

def is_attrs_class(self, class_node: ast.ClassDef) -> bool:
"""Check whether an ast.ClassDef is an attrs class or not."""
attrs_imports = self.get_all_attrs_imports()
return any(self.is_attrs_decorator(decorator, attrs_imports) for decorator in class_node.decorator_list)
return any(self.is_attrs_decorator(decorator) for decorator in class_node.decorator_list)

def is_attrs_decorator(self, decorator: Any, attrs_imports: dict[str | None, str]) -> bool:
def is_attrs_decorator(self, decorator: ast.AST) -> bool:
"""Check whether a class decorator is an attrs decorator or not."""
if isinstance(decorator, ast.Call):
return self.is_attrs_decorator(decorator.func, attrs_imports)
elif isinstance(decorator, ast.Attribute):
return self.is_attrs_attribute(decorator)
elif isinstance(decorator, ast.Name):
return self.is_attrs_str(decorator.id, attrs_imports)
return False

@staticmethod
def is_attrs_attribute(attribute: ast.Attribute) -> bool:
"""Check whether an ast.Attribute is an attrs attribute or not."""
s1 = f"attr.{getattr(attribute, 'attr', '')}"
s2 = f"attrs.{getattr(attribute, 'attrs', '')}"
actual = [s1, s2]
return any(e for e in actual if e in ATTRS_DECORATORS)

@staticmethod
def is_attrs_str(attribute: str | ast.expr, attrs_imports: dict[str | None, str]) -> bool:
"""Check whether an ast.expr or string is an attrs string or not."""
actual = attrs_imports.get(str(attribute), '')
return actual in ATTRS_DECORATORS
decorator = decorator.func
return self.lookup_full_name(decorator) in ATTRS_DECORATORS


class DunderAllMixin:
Expand Down
1 change: 0 additions & 1 deletion flake8_type_checking/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
'attr.mutable',
'attr.s',
]
ATTRS_IMPORTS = {'attrs', 'attr'}

flake_version_gt_v4 = tuple(int(i) for i in flake8.__version__.split('.')) >= (4, 0, 0)

Expand Down
6 changes: 1 addition & 5 deletions flake8_type_checking/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,13 @@
if TYPE_CHECKING:
import ast
from collections.abc import Generator
from typing import Any, Optional, Protocol, Union
from typing import Any, Protocol, Union

Function = Union[ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda]
Comprehension = Union[ast.ListComp, ast.SetComp, ast.DictComp, ast.GeneratorExp]
Import = Union[ast.Import, ast.ImportFrom]
Flake8Generator = Generator[tuple[int, int, str, Any], None, None]

class Name(Protocol):
asname: Optional[str]
name: str

class HasPosition(Protocol):
@property
def lineno(self) -> int:
Expand Down
Loading