Skip to content

Improve flake8-bugbear #13829

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 15, 2025
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
38 changes: 37 additions & 1 deletion stubs/flake8-bugbear/@tests/stubtest_allowlist.txt
Original file line number Diff line number Diff line change
@@ -1 +1,37 @@
bugbear.BugBearChecker.__getattr__
# Autogenerated methods using @attr.* decorators
bugbear.B040CaughtException.__attrs_attrs__
bugbear.B040CaughtException.__attrs_own_setattr__
bugbear.B040CaughtException.__match_args__
bugbear.B041VariableKeyType.__attrs_attrs__
bugbear.B041VariableKeyType.__match_args__
bugbear.BugBearChecker.__attrs_attrs__
bugbear.BugBearChecker.__ge__
bugbear.BugBearChecker.__gt__
bugbear.BugBearChecker.__le__
bugbear.BugBearChecker.__lt__
bugbear.BugBearChecker.__match_args__
bugbear.BugBearVisitor.__attrs_attrs__
bugbear.BugBearVisitor.__ge__
bugbear.BugBearVisitor.__gt__
bugbear.BugBearVisitor.__le__
bugbear.BugBearVisitor.__lt__
bugbear.BugBearVisitor.__match_args__
bugbear.NameFinder.__attrs_attrs__
bugbear.NameFinder.__ge__
bugbear.NameFinder.__gt__
bugbear.NameFinder.__le__
bugbear.NameFinder.__lt__
bugbear.NameFinder.__match_args__
bugbear.NamedExprFinder.__attrs_attrs__
bugbear.NamedExprFinder.__ge__
bugbear.NamedExprFinder.__gt__
bugbear.NamedExprFinder.__le__
bugbear.NamedExprFinder.__lt__
bugbear.NamedExprFinder.__match_args__
# >= Python 3.13
bugbear.B040CaughtException.__replace__
bugbear.B041VariableKeyType.__replace__
bugbear.BugBearChecker.__replace__
bugbear.BugBearVisitor.__replace__
bugbear.NameFinder.__replace__
bugbear.NamedExprFinder.__replace__
4 changes: 0 additions & 4 deletions stubs/flake8-bugbear/METADATA.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
version = "24.12.12"
upstream_repository = "https://github.com/PyCQA/flake8-bugbear"
partial_stub = true

[tool.stubtest]
ignore_missing_stub = true
281 changes: 274 additions & 7 deletions stubs/flake8-bugbear/bugbear.pyi
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
import argparse
import ast
import sys
from _typeshed import Incomplete
from collections.abc import Sequence
from typing import Any
from collections.abc import Callable, Generator, Iterable, Sequence
from functools import partial
from logging import Logger
from typing import Any, ClassVar, Final, Literal, NamedTuple, overload

__version__: Final[str]
LOG: Logger
CONTEXTFUL_NODES: Final[tuple[type[ast.AST], ...]]
FUNCTION_NODES: Final[tuple[type[ast.AST], ...]]
B908_pytest_functions: Final[set[str]]
B908_unittest_methods: Final[set[str]]
B902_default_decorators: Final[set[str]]

class Context(NamedTuple):
node: ast.AST
stack: list[str]

class BugBearChecker:
name: str
version: str
name: ClassVar[str]
version: ClassVar[str]
tree: ast.AST | None
filename: str
lines: Sequence[str] | None
max_line_length: int
visitor: ast.NodeVisitor
options: argparse.Namespace | None
def run(self) -> None: ...
def run(self) -> Generator[error]: ...
def gen_line_based_checks(self) -> Generator[error]: ...
@classmethod
def adapt_error(cls, e: error) -> tuple[int, int, str, type[BugBearChecker]]: ...
def load_file(self) -> None: ...
@staticmethod
def add_options(optmanager: Any) -> None: ...
def __init__(
Expand All @@ -24,6 +43,254 @@ class BugBearChecker:
max_line_length: int = ...,
options: argparse.Namespace | None = ...,
) -> None: ...
def __getattr__(self, name: str) -> Incomplete: ... # incomplete (other attributes are normally not accessed)
def should_warn(self, code: str) -> bool: ...

def names_from_assignments(assign_target: ast.AST) -> Generator[str]: ...
def children_in_scope(node: ast.AST) -> Generator[ast.AST]: ...
def walk_list(nodes: Iterable[ast.AST]) -> Generator[ast.AST]: ...

class ExceptBaseExceptionVisitor(ast.NodeVisitor):
root: ast.ExceptHandler
def __init__(self, except_node: ast.ExceptHandler) -> None: ...
def re_raised(self) -> bool: ...
def visit_Raise(self, node: ast.Raise) -> Incomplete | None: ...
def visit_ExceptHandler(self, node: ast.ExceptHandler) -> Incomplete | None: ...

class B040CaughtException:
name: str
has_note: bool
def __init__(self, name: str, has_note: bool) -> None: ...

class B041UnhandledKeyType: ...

class B041VariableKeyType:
name: str
def __init__(self, name: str) -> None: ...

class BugBearVisitor(ast.NodeVisitor):
NODE_WINDOW_SIZE: ClassVar[int] = 4
in_trystar: str
def __init__(
self,
filename: str,
lines: Sequence[str] | None,
b008_b039_extend_immutable_calls: set[str] = ...,
b902_classmethod_decorators: set[str] = ...,
node_window: list[ast.AST] = ...,
errors: list[error] = ...,
contexts: list[Context] = ...,
b040_caught_exception: B040CaughtException | None = None,
in_trystar: str = "",
) -> None: ...
@property
def node_stack(self) -> list[Context]: ...
def in_class_init(self) -> bool: ...
def visit_Return(self, node: ast.Return) -> None: ...
def visit_Yield(self, node: ast.Yield) -> None: ...
def visit_YieldFrom(self, node: ast.YieldFrom) -> None: ...
def visit(self, node: ast.AST) -> None: ...
def visit_ExceptHandler(self, node: ast.ExceptHandler) -> None: ...
def visit_UAdd(self, node: ast.UAdd) -> None: ...
def visit_Call(self, node: ast.Call) -> None: ...
def visit_Module(self, node: ast.Module) -> None: ...
def visit_Assign(self, node: ast.Assign) -> None: ...
def visit_For(self, node: ast.For) -> None: ...
def visit_AsyncFor(self, node: ast.AsyncFor) -> None: ...
def visit_While(self, node: ast.While) -> None: ...
def visit_ListComp(self, node: ast.ListComp) -> None: ...
def visit_SetComp(self, node: ast.SetComp) -> None: ...
def visit_DictComp(self, node: ast.DictComp) -> None: ...
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> None: ...
def visit_Assert(self, node: ast.Assert) -> None: ...
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None: ...
def visit_FunctionDef(self, node: ast.FunctionDef) -> None: ...
def visit_ClassDef(self, node: ast.ClassDef) -> None: ...
def visit_Try(self, node: ast.Try) -> None: ...
if sys.version_info >= (3, 11):
def visit_TryStar(self, node: ast.TryStar) -> None: ...
else:
def visit_TryStar(self, node: ast.Try) -> None: ...

def visit_Compare(self, node: ast.Compare) -> None: ...
def visit_Raise(self, node: ast.Raise) -> None: ...
def visit_With(self, node: ast.With) -> None: ...
def visit_JoinedStr(self, node: ast.JoinedStr) -> None: ...
def visit_AnnAssign(self, node: ast.AnnAssign) -> None: ...
def visit_Import(self, node: ast.Import) -> None: ...
def visit_ImportFrom(self, node: ast.ImportFrom) -> None: ...
def visit_Set(self, node: ast.Set) -> None: ...
def visit_Dict(self, node: ast.Dict) -> None: ...
def check_for_b041(self, node: ast.Dict) -> None: ...
def check_for_b005(self, node: ast.Import | ast.ImportFrom | ast.Call) -> None: ...
def check_for_b006_and_b008(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None: ...
def check_for_b039(self, node: ast.Call) -> None: ...
def check_for_b007(self, node: ast.For | ast.AsyncFor) -> None: ...
def check_for_b011(self, node: ast.Assert) -> None: ...
if sys.version_info >= (3, 11):
def check_for_b012(self, node: ast.Try | ast.TryStar) -> None: ...
else:
def check_for_b012(self, node: ast.Try) -> None: ...

def check_for_b013_b014_b029_b030(self, node: ast.ExceptHandler) -> list[str]: ...
def check_for_b015(self, node: ast.Compare) -> None: ...
def check_for_b016(self, node: ast.Raise) -> None: ...
def check_for_b017(self, node: ast.With | ast.AsyncWith) -> None: ...
def check_for_b019(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None: ...
def check_for_b020(self, node: ast.For | ast.AsyncFor | ast.comprehension) -> None: ...
def check_for_b023(self, loop_node: ast.For | ast.AsyncFor | ast.comprehension) -> None: ...
def check_for_b024_and_b027(self, node: ast.ClassDef) -> None: ...
def check_for_b026(self, call: ast.Call) -> None: ...
def check_for_b031(self, loop_node: ast.For | ast.AsyncFor) -> None: ...
def check_for_b035(self, node: ast.DictComp) -> None: ...
def check_for_b040_add_note(self, node: ast.Attribute) -> bool: ...
def check_for_b040_usage(self, node: ast.expr | None) -> None: ...
def check_for_b904(self, node: ast.Raise) -> None: ...
def walk_function_body(
self, node: ast.FunctionDef | ast.AsyncFunctionDef
) -> tuple[ast.FunctionDef | ast.AsyncFunctionDef, ast.stmt]: ...
def check_for_b901(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None: ...
@overload
@classmethod
def find_decorator_name(cls, d: ast.Name | ast.Attribute | ast.Call) -> str: ...
@overload
@classmethod
def find_decorator_name(cls, d: ast.AST) -> str | None: ...
def check_for_b902(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None: ...
def check_for_b903(self, node: ast.ClassDef) -> None: ...
def check_for_b018(self, node: ast.Expr) -> None: ...
def check_for_b021(self, node: ast.AsyncFunctionDef | ast.FunctionDef | ast.ClassDef | ast.Module) -> None: ...
def check_for_b022(self, node: ast.With | ast.AsyncWith) -> None: ...
def check_for_b908(self, node: ast.With) -> None: ...
def check_for_b025(self, node: ast.Try) -> None: ...
def check_for_b905(self, node: ast.Call) -> None: ...
def check_for_b906(self, node: ast.FunctionDef) -> None: ...
def check_for_b907(self, node: ast.JoinedStr) -> None: ...
def check_for_b028(self, node: ast.Call) -> None: ...
def check_for_b032(self, node: ast.AnnAssign) -> None: ...
def check_for_b033(self, node: ast.Set | ast.List | ast.Tuple) -> None: ...
def check_for_b034(self, node: ast.Call) -> None: ...
def check_for_b909(self, node: ast.For) -> None: ...
def check_for_b910(self, node: ast.Call) -> None: ...
def check_for_b911(self, node: ast.Call) -> None: ...

def compose_call_path(node: ast.expr) -> Generator[str]: ...
def is_name(node: ast.expr, name: str) -> bool: ...

class B909Checker(ast.NodeVisitor):
MUTATING_FUNCTIONS: ClassVar[tuple[str, ...]]
name: str
key: str
mutations: dict[int, list[ast.AST]]
def __init__(self, name: str, key: str) -> None: ...
def visit_Assign(self, node: ast.Assign) -> None: ...
def visit_AugAssign(self, node: ast.AugAssign) -> None: ...
def visit_Delete(self, node: ast.Delete) -> None: ...
def visit_Call(self, node: ast.Call) -> None: ...
def visit_If(self, node: ast.If) -> None: ...
def visit(self, node: ast.AST | list[ast.AST]) -> Any: ...

class NameFinder(ast.NodeVisitor):
names: dict[str, list[ast.Name]]
def __init__(self, names: dict[str, list[ast.Name]] = ...) -> None: ...
def visit_Name(self, node: ast.Name) -> None: ...
def visit(self, node: ast.AST | list[ast.AST]) -> Any: ...

class NamedExprFinder(ast.NodeVisitor):
names: dict[str, list[ast.Name]]
def __init__(self, names: dict[str, list[ast.Name]] = ...) -> None: ...
def visit_NamedExpr(self, node: ast.NamedExpr) -> None: ...
def visit(self, node: ast.AST | list[ast.AST]) -> Any: ...

class FunctionDefDefaultsVisitor(ast.NodeVisitor):
def __init__(
self,
error_code_calls: partial[error],
error_code_literals: partial[error],
b008_b039_extend_immutable_calls: set[str] | None = None,
) -> None: ...
def visit_mutable_literal_or_comprehension(
self, node: ast.List | ast.Dict | ast.Set | ast.ListComp | ast.DictComp | ast.SetComp
) -> None: ...
def visit_Call(self, node: ast.Call) -> None: ...
def visit_Lambda(self, node: ast.Lambda) -> None: ...
def visit(self, node: ast.AST | list[ast.AST]) -> None: ...

class B020NameFinder(NameFinder):
def visit_GeneratorExp(self, node: ast.GeneratorExp) -> None: ...
def visit_ListComp(self, node: ast.ListComp) -> None: ...
def visit_DictComp(self, node: ast.DictComp) -> None: ...
def visit_comprehension(self, node: ast.comprehension) -> None: ...
def visit_Lambda(self, node: ast.Lambda) -> None: ...

class error(NamedTuple):
lineno: int
col: int
message: str
type: type[BugBearChecker]
vars: tuple[Incomplete]

def __getattr__(name: str) -> Incomplete: ...
Error: Callable[..., partial[error]]
B001: partial[error]
B002: partial[error]
B003: partial[error]
B004: partial[error]
B005: partial[error]
B005_METHODS: Final[set[str]]
B006: partial[error]
B006_MUTABLE_LITERALS: Final[tuple[Literal["Dict"], Literal["List"], Literal["Set"]]]
B006_MUTABLE_COMPREHENSIONS: Final[tuple[Literal["ListComp"], Literal["DictComp"], Literal["SetComp"]]]
B006_MUTABLE_CALLS: Final[set[str]]
B007: partial[error]
B008: partial[error]
B008_IMMUTABLE_CALLS: Final[set[str]]
B009: partial[error]
B010: partial[error]
B011: partial[error]
B012: partial[error]
B013: partial[error]
B014: partial[error]
B014_REDUNDANT_EXCEPTIONS: Final[dict[Literal["OSError", "ValueError"], set[str]]]
B015: partial[error]
B016: partial[error]
B017: partial[error]
B018: partial[error]
B019: partial[error]
B019_CACHES: Final[set[str]]
B020: partial[error]
B021: partial[error]
B022: partial[error]
B023: partial[error]
B024: partial[error]
B025: partial[error]
B026: partial[error]
B027: partial[error]
B028: partial[error]
B029: partial[error]
B030: partial[error]
B031: partial[error]
B032: partial[error]
B033: partial[error]
B034: partial[error]
B035: partial[error]
B036: partial[error]
B037: partial[error]
B039: partial[error]
B040: partial[error]
B041: partial[error]
B901: partial[error]
B902: partial[error]
B902_IMPLICIT_CLASSMETHODS: Final[set[str]]
B902_SELF: Final[list[str]]
B902_CLS: Final[list[str]]
B902_METACLS: Final[list[str]]
B903: partial[error]
B904: partial[error]
B905: partial[error]
B906: partial[error]
B907: partial[error]
B908: partial[error]
B909: partial[error]
B910: partial[error]
B911: partial[error]
B950: partial[error]
disabled_by_default: Final[list[str]]