or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/pypi-flake8-bugbear

A plugin for flake8 finding likely bugs and design problems in your program.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/flake8-bugbear@24.12.x

To install, run

npx @tessl/cli install tessl/pypi-flake8-bugbear@24.12.0

index.mddocs/

flake8-bugbear

A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle. This plugin implements over 70 opinionated linting rules that catch subtle bugs, design problems, and code quality issues that standard tools miss.

Package Information

  • Package Name: flake8-bugbear
  • Language: Python
  • Installation: pip install flake8-bugbear

Core Imports

The plugin automatically integrates with flake8 once installed. For programmatic usage:

from bugbear import BugBearChecker

Basic Usage

As a flake8 plugin (standard usage)

Once installed, flake8-bugbear automatically runs as part of flake8:

# Install the plugin
pip install flake8-bugbear

# Run flake8 - bugbear rules are automatically included
flake8 mycode.py

# Check that the plugin is loaded
flake8 --version

Programmatic usage

import ast
from bugbear import BugBearChecker

# Parse some Python code
code = '''
def example():
    try:
        risky_operation()
    except:  # B001: bare except
        pass
'''

tree = ast.parse(code)
lines = code.splitlines()

# Create and run the checker
checker = BugBearChecker(tree=tree, filename="example.py", lines=lines)
errors = list(checker.run())

for error in errors:
    print(f"{error.lineno}:{error.col} {error.message}")

Architecture

flake8-bugbear follows flake8's plugin architecture:

  • BugBearChecker: Main plugin class implementing flake8's checker interface
  • BugBearVisitor: AST visitor that traverses code and detects rule violations
  • Error definitions: 70+ predefined error types with specific codes (B001-B950)
  • Configuration options: Support for extending rules and customizing behavior

The plugin registers with flake8 via entry point: flake8.extension = {B = "bugbear:BugBearChecker"}

Configuration Options

flake8-bugbear supports the following configuration options:

[flake8]
# Extend the list of immutable function calls for B008/B039
extend-immutable-calls = frozenset,tuple

# Customize classmethod decorators for B902
classmethod-decorators = classmethod,my_classmethod_decorator

Capabilities

Main Plugin Interface

The BugBearChecker class provides the primary interface for flake8 integration and programmatic usage.

class BugBearChecker:
    """
    Main checker class implementing flake8 plugin interface.
    
    Attributes:
        name (str): Plugin name "flake8-bugbear"
        version (str): Plugin version
        tree (ast.AST): AST tree to analyze
        filename (str): Name of file being analyzed
        lines (List[str]): Source code lines
        max_line_length (int): Maximum line length setting
        options (object): Configuration options
    """
    
    def run(self):
        """
        Main entry point that yields error instances.
        
        Yields:
            Error objects with line number, column, and message
        """
    
    def load_file(self) -> None:
        """Load file content into tree and lines attributes."""
    
    def gen_line_based_checks(self):
        """
        Generator for line-based lint checks.
        
        Yields:
            Error objects for line-based violations
        """
    
    @classmethod
    def adapt_error(cls, e):
        """
        Adapt extended error namedtuple to be compatible with flake8.
        
        Args:
            e: Error namedtuple instance
            
        Returns:
            Adapted error tuple
        """
    
    @staticmethod
    def add_options(optmanager) -> None:
        """
        Register configuration options with flake8.
        
        Args:
            optmanager: flake8 option manager instance
        """
    
    def should_warn(self, code: str) -> bool:
        """
        Check if warning code should be reported.
        
        Args:
            code: Error code (e.g., "B001")
            
        Returns:
            True if warning should be reported
        """

Error Detection Rules

flake8-bugbear implements over 70 specific error detection rules. Each rule has a unique code (B001-B950) and targets specific code patterns that are likely bugs or poor design choices.

Logic and Exception Handling Errors (B001-B030)

# B001: Bare except clauses
try:
    risky_operation()
except:  # Error: Use "except Exception:" instead
    pass

# B002: Unary prefix increment  
++n  # Error: Python doesn't support this, use n += 1

# B003: os.environ assignment
os.environ = {}  # Error: Use os.environ.clear() instead

# B004: hasattr() with __call__
hasattr(obj, '__call__')  # Error: Use callable(obj) instead

# B005: strip() with multi-character strings
text.strip('abc')  # Error: Misleading, use replace() or regex

# B006: Mutable default arguments
def func(items=[]):  # Error: Use items=None, then items = items or []
    pass

# B007: Unused loop variables
for i in range(10):  # Error: If unused, name it _i
    print("hello")

# B008: Function calls in argument defaults
def func(timestamp=time.time()):  # Error: Called once at definition
    pass

# B009-B010: getattr/setattr with known attributes
getattr(obj, 'attr')  # Error: Use obj.attr directly
setattr(obj, 'attr', val)  # Error: Use obj.attr = val

# B011: assert False
assert False  # Error: Use raise AssertionError() instead

# B012: Control flow in finally blocks
try:
    operation()
finally:
    return value  # Error: Will silence exceptions

# B013-B014: Exception handling issues
except (ValueError,):  # B013: Redundant tuple
except (ValueError, ValueError):  # B014: Duplicate exception types

Language Feature Misuse (B015-B030)

# B015: Redundant comparisons
if x == True:  # Error: Use if x: instead

# B016: Raising in finally
try:
    operation()
finally:
    raise Exception()  # Error: Will override original exception

# B017: pytest.raises without match
with pytest.raises(ValueError):  # Error: Add match= parameter
    risky_operation()

# B018: Useless expressions
x + 1  # Error: Statement has no effect

# B019: lru_cache without parameters
@lru_cache  # Error: Use @lru_cache() with parentheses

# B020: Loop variable usage outside loop
for i in range(10):
    items.append(i)
print(i)  # Error: i is not guaranteed to be defined

# B021: f-string in format()
"{}".format(f"{x}")  # Error: Use f-string directly

# B022: Useless contextlib.suppress
with contextlib.suppress():  # Error: No exception types specified
    operation()

# B023: Function definition scope issues
for i in range(3):
    def func():
        return i  # Error: Will always return final loop value

# B024: Abstract methods without @abstractmethod
class Base:
    def method(self):
        raise NotImplementedError()  # Error: Use @abstractmethod

# B025: Duplicate except handlers
try:
    operation()
except ValueError:
    handle_error()
except ValueError:  # Error: Duplicate handler
    handle_error()

# B026: Star-arg unpacking after keyword argument
def func(a, b, **kwargs, *args):  # Error: *args after **kwargs is discouraged
    pass

# B027: Empty abstract method without decorator
class Base:
    def method(self):  # Error: Empty method should use @abstractmethod
        pass

# B028: warnings.warn without stacklevel
import warnings
warnings.warn("message")  # Error: Should specify stacklevel=2

# B029: Empty except tuple
try:
    operation()
except ():  # Error: Empty tuple catches nothing
    pass

Advanced Pattern Detection (B030-B041)

# B030: Except handler names
except ValueError as e:  # OK
except ValueError as ValueError:  # Error: Shadowing exception class

# B031: itertools.islice with generators
list(itertools.islice(generator, 10))  # Error: Consider using itertools.takewhile

# B032: Possible unintentional type annotations
def func():
    x: int  # Error: Missing assignment, use x: int = 0

# B033: Duplicate set elements
{1, 2, 1}  # Error: Duplicate element

# B034: re.sub without flags
re.sub(r'pattern', 'replacement', text)  # Error: Consider adding flags

# B035: Static key in dict comprehension
{key: value for item in items}  # Error: key doesn't depend on item

# B036: Found f-string with incorrect prefix
rf"regex {pattern}"  # Error: Use fr"regex {pattern}" instead

# B037: __exit__ return value
def __exit__(self, exc_type, exc_val, exc_tb):
    return True  # Error: Usually should return None

# B039: contextlib.suppress with BaseException
with contextlib.suppress(BaseException):  # Error: Too broad
    operation()

# B040: Exception caught without being used
try:
    operation()
except ValueError as e:  # Error: e is never used
    log("Error occurred")

# B041: Repeated key-value pairs in dict literals
{"key": 1, "key": 2}  # Error: Duplicate key

Class and Method Design (B901-B911)

# B901: return in generator
def generator():
    yield 1
    return 2  # Error: Use return without value

# B902: Invalid first argument names
class Example:
    def method(bad_self):  # Error: Should be 'self'
        pass
    
    @classmethod  
    def classmethod(bad_cls):  # Error: Should be 'cls'
        pass

# B903: Data class without slots
@dataclass
class Example:  # Error: Consider @dataclass(slots=True) for performance
    value: int

# B904: re-raise without from
try:
    operation()
except ValueError:
    raise RuntimeError("Failed")  # Error: Use "raise ... from e"

# B905: zip() without strict parameter
zip(list1, list2)  # Error: Use zip(list1, list2, strict=True)

# B906: visit_ methods without isinstance check
def visit_Name(self, node):  # Error: Add isinstance(node, ast.Name) check
    pass

# B907: JSON loads without secure defaults
json.loads(data)  # Error: Consider security implications

# B908: Context manager protocols
with context_manager() as ctx:
    pass  # Various context manager usage patterns

# B909: Mutation during iteration
for item in items:
    items.remove(item)  # Error: Modifying collection during iteration

# B910: Counter() instead of defaultdict(int)
defaultdict(int)  # Error: Use Counter() for better memory efficiency

# B911: itertools.batched() without explicit strict parameter
itertools.batched(data, 3)  # Error: Add strict=True parameter

Line Length and Formatting (B950)

# B950: Line too long
very_long_line = "This line exceeds the configured maximum length and will trigger B950"  # Error if > max_line_length

Version Information

Access to package version information:

__version__: str  # Package version string (e.g., "24.12.12")

AST Visitor Implementation

The BugBearVisitor class handles the actual AST traversal and error detection:

class BugBearVisitor:
    """
    AST visitor that traverses code and detects rule violations.
    
    Attributes:
        filename (str): Name of file being analyzed
        lines (List[str]): Source code lines
        b008_b039_extend_immutable_calls (set): Extended immutable calls configuration
        b902_classmethod_decorators (set): Classmethod decorators configuration
        node_window (list): Recent AST nodes for context
        errors (list): Collected error instances
        contexts (list): Current context stack
        b040_caught_exception: Exception context for B040 rule
    """
    
    def visit(self, node):
        """Visit an AST node and apply bugbear rules."""
        
    def generic_visit(self, node):
        """Generic visit implementation for unhandled node types."""

Utility Functions

Public utility functions for AST analysis:

def compose_call_path(node):
    """
    Compose call path from AST call node.
    
    Args:
        node: AST node (Call, Attribute, or Name)
        
    Yields:
        str: Components of the call path
        
    Example:
        For `foo.bar.baz()`, yields: "foo", "bar", "baz"
    """

def is_name(node: ast.expr, name: str) -> bool:
    """
    Check if AST node represents a specific name.
    
    Args:
        node: AST expression node to check
        name: Name to match (supports dotted names like "typing.Generator")
        
    Returns:
        bool: True if node matches the given name
        
    Example:
        is_name(node, "typing.Generator") matches typing.Generator references
    """

Error Message Format

All errors follow a consistent format:

{filename}:{line}:{column} {code} {message}

Examples:

example.py:5:8 B001 Do not use bare `except:`, it also catches unexpected events
example.py:12:4 B006 Do not use mutable data structures for argument defaults
example.py:20:1 B950 line too long (95 > 79 characters)

Integration Examples

With pre-commit

repos:
  - repo: https://github.com/PyCQA/flake8
    rev: 6.0.0
    hooks:
      - id: flake8
        additional_dependencies: [flake8-bugbear]

With tox

[testenv:lint]
deps = 
    flake8
    flake8-bugbear
commands = flake8 src tests

With GitHub Actions

- name: Run flake8
  run: |
    pip install flake8 flake8-bugbear
    flake8 .