CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyflakes

Static analysis tool that detects errors in Python code without importing it.

Pending
Overview
Eval results
Files

checker-system.mddocs/

Checker System

Advanced code analysis engine that performs AST-based static analysis with scope tracking and binding management. The Checker class provides fine-grained control over the analysis process and access to detailed results, supporting custom analysis workflows and integration with development tools.

The Checker class is the core of pyflakes' analysis engine, implementing a comprehensive visitor pattern for Python AST nodes with sophisticated scope and binding management.

Capabilities

Core Checker Class

Main analysis engine that walks Python AST nodes and maintains scope information to detect various code issues.

class Checker:
    """I check the cleanliness and sanity of Python code."""
    
    def __init__(self, tree, filename='(none)', builtins=None, withDoctest='PYFLAKES_DOCTEST' in os.environ, file_tokens=()):
        """
        Initialize the checker with an AST tree.

        Parameters:
        - tree: AST tree from ast.parse()
        - filename (str): Name of the file being checked (for error reporting)
        - builtins (set, optional): Additional builtin names to recognize (extends default set)
        - withDoctest (bool): Whether to check doctest code blocks (controlled by PYFLAKES_DOCTEST env var)
        - file_tokens (tuple): Deprecated parameter for token information (warns when used)
        """
    
    # Core attributes
    messages: list          # List of Message objects representing found issues
    filename: str           # Filename being checked
    deadScopes: list        # List of completed scopes for analysis
    scopeStack: list        # Stack of active scopes during traversal
    exceptHandlers: list    # Stack of exception handler names for context
    root                    # Root AST node
    nodeDepth: int          # Current depth in AST traversal
    offset                  # Line/column offset for node adjustment
    withDoctest: bool       # Whether doctest processing is enabled
    
    # Properties
    @property
    def futuresAllowed(self) -> bool:
        """Whether __future__ imports are allowed at current position."""
    
    @property
    def annotationsFutureEnabled(self) -> bool:
        """Whether 'from __future__ import annotations' is active."""
    
    @property
    def scope(self):
        """Current scope (last item in scopeStack)."""
    
    @property
    def _in_postponed_annotation(self) -> bool:
        """Whether currently in a postponed annotation context."""

Usage:

import ast
import pyflakes.checker

# Parse Python code into AST
code = """
import os
unused_var = 42
print(undefined_var)
"""

tree = ast.parse(code, filename='example.py')
checker = pyflakes.checker.Checker(tree, filename='example.py')

# Access results
print(f"Found {len(checker.messages)} issues:")
for message in checker.messages:
    print(f"  {message}")

# Access scope information
print(f"Analyzed {len(checker.deadScopes)} scopes")

Core Analysis Methods

Primary methods for managing the analysis process and reporting issues.

def report(self, messageClass, *args, **kwargs):
    """Add a message to the results."""

def deferFunction(self, callable):
    """Schedule a function handler to be called just before completion."""

def checkDeadScopes(self):
    """Analyze completed scopes for unused names."""

def handleNode(self, node, parent):
    """Process an AST node during analysis."""

def handleChildren(self, tree, omit=None):
    """Process child nodes with optional omissions."""

def getNodeHandler(self, node_class):
    """Get handler method for AST node type."""

def addBinding(self, node, value):
    """Add name binding to current scope."""

Scope Management Methods

Methods for managing Python scopes and their relationships.

def in_scope(self, cls):
    """Context manager for scope management."""

def getScopeNode(self, node):
    """Find scope-defining ancestor."""

def getParent(self, node):
    """Get meaningful parent of a node."""

def getCommonAncestor(self, lnode, rnode, stop):
    """Find common ancestor of two nodes."""

def descendantOf(self, node, ancestors, stop):
    """Check if node is descendant of ancestors."""

def differentForks(self, lnode, rnode):
    """Check if nodes are on different conditional branches."""

Name Binding Methods

Methods for handling variable assignments and name resolution.

def handleNodeLoad(self, node, parent):
    """Handle name loading/usage."""

def handleNodeStore(self, node):
    """Handle name storage/assignment."""

def handleNodeDelete(self, node):
    """Handle name deletion."""

Annotation Processing Methods

Methods for handling type annotations and forward references.

def handleAnnotation(self, annotation, node):
    """Process type annotations."""

def handleStringAnnotation(self, s, node, ref_lineno, ref_col_offset, err):
    """Process string annotations."""

def handle_annotation_always_deferred(self, annotation, parent):
    """Always defer annotation processing."""

def _enter_annotation(self, ann_type=AnnotationState.BARE):
    """Context manager for annotations."""

AST Node Handlers

The Checker class implements handlers for all Python AST node types. Each handler is named after the AST node type in uppercase.

Statement Handlers

def FUNCTIONDEF(self, node):
    """Handle function definitions."""

def ASYNCFUNCTIONDEF(self, node):
    """Handle async function definitions."""

def CLASSDEF(self, node):
    """Handle class definitions."""

def RETURN(self, node):
    """Handle return statements with scope validation."""

def YIELD(self, node):
    """Handle yield expressions with scope validation."""

def GLOBAL(self, node):
    """Handle global declarations."""

def IMPORT(self, node):
    """Handle import statements."""

def IMPORTFROM(self, node):
    """Handle 'from X import Y' statements."""

def ASSIGN(self, node):
    """Handle assignments."""

def AUGASSIGN(self, node):
    """Handle augmented assignments (+=, etc.)."""

def ANNASSIGN(self, node):
    """Handle annotated assignments."""

def FOR(self, node):
    """Handle for loops."""

def ASYNCFOR(self, node):
    """Handle async for loops."""

def WHILE(self, node):
    """Handle while loops."""

def IF(self, node):
    """Handle if statements with tuple condition checking."""

def WITH(self, node):
    """Handle with statements."""

def ASYNCWITH(self, node):
    """Handle async with statements."""

def TRY(self, node):
    """Handle try/except blocks with handler tracking."""

def EXCEPTHANDLER(self, node):
    """Handle exception handlers."""

def RAISE(self, node):
    """Handle raise statements with NotImplemented checking."""

def ASSERT(self, node):
    """Handle assert statements with tuple condition checking."""

def CONTINUE(self, node):
    """Handle continue/break statements with context validation."""

def BREAK(self, node):
    """Handle break statements."""

def PASS(self, node):
    """Handle pass statements."""

Expression Handlers

def NAME(self, node):
    """Handle name references (load/store/delete)."""

def CALL(self, node):
    """Handle function calls with format string validation."""

def SUBSCRIPT(self, node):
    """Handle subscript expressions with special typing logic."""

def ATTRIBUTE(self, node):
    """Handle attribute access."""

def BINOP(self, node):
    """Handle binary operations with percent format checking."""

def COMPARE(self, node):
    """Handle comparison operations with literal checking."""

def TUPLE(self, node):
    """Handle tuples with starred expression validation."""

def LIST(self, node):
    """Handle lists."""

def DICT(self, node):
    """Handle dictionaries with duplicate key detection."""

def SET(self, node):
    """Handle sets."""

def CONSTANT(self, node):
    """Handle constants with string annotation support."""

def JOINEDSTR(self, node):
    """Handle f-strings with placeholder validation."""

def TEMPLATESTR(self, node):
    """Handle template strings (t-strings)."""

def LAMBDA(self, node):
    """Handle lambda expressions and function arguments."""

def GENERATOREXP(self, node):
    """Handle generator expressions and comprehensions."""

def LISTCOMP(self, node):
    """Handle list comprehensions."""

def DICTCOMP(self, node):
    """Handle dictionary comprehensions."""

def SETCOMP(self, node):
    """Handle set comprehensions."""

Type System Handlers (Python 3.12+)

def TYPEVAR(self, node):
    """Handle type variable definitions."""

def TYPEALIAS(self, node):
    """Handle type alias definitions."""

def PARAMSPEC(self, node):
    """Handle parameter specifications."""

def TYPEVARTUPLE(self, node):
    """Handle type variable tuples."""

Pattern Matching Handlers (Python 3.10+)

def MATCH(self, node):
    """Handle match statements."""

def MATCH_CASE(self, node):
    """Handle match cases."""

def MATCHAS(self, node):
    """Handle match as patterns."""

def MATCHCLASS(self, node):
    """Handle match class patterns."""

def MATCHMAPPING(self, node):
    """Handle match mapping patterns."""

def MATCHOR(self, node):
    """Handle match or patterns."""

def MATCHSEQUENCE(self, node):
    """Handle match sequence patterns."""

def MATCHSINGLETON(self, node):
    """Handle match singleton patterns."""

def MATCHSTAR(self, node):
    """Handle match star patterns."""

def MATCHVALUE(self, node):
    """Handle match value patterns."""

Utility Methods

Helper methods for common analysis tasks.

def isLiteralTupleUnpacking(self, node) -> bool:
    """Check for literal tuple unpacking."""

def isDocstring(self, node) -> bool:
    """Check if node is a docstring."""

def getDocstring(self, node):
    """Extract docstring content and line number."""

def handleDoctests(self, node):
    """Process doctest examples in docstrings."""

def ignore(self, node):
    """No-op handler for ignored node types."""

def _in_doctest(self) -> bool:
    """Check if currently in doctest scope."""

Format String Validation

Methods for validating string formatting operations.

def _handle_string_dot_format(self, node):
    """Validate .format() method calls."""

def _handle_percent_format(self, node):
    """Validate % format operations."""

Internal Systems

Deferred Analysis System

The checker implements a deferred analysis system to handle forward references and ensure proper name resolution order.

# Deferred functions are stored and executed after initial traversal
checker._deferred = []  # Queue of deferred function handlers
checker._run_deferred()  # Execute all deferred functions

Key Features:

  • Functions bodies are analyzed after global scope is complete
  • Ensures all global names are visible to function analysis
  • Preserves scope context when handlers are eventually executed
  • Critical for handling forward references and decorators

Binding Management System

Comprehensive system for tracking name bindings across all scopes using a hierarchical class system.

Binding Class Hierarchy

class Binding:
    """Base class for all name bindings."""
    
    def __init__(self, name, source):
        """
        Parameters:
        - name (str): The bound name
        - source: AST node where binding occurs
        """
    
    name: str           # The bound name
    source             # AST node where binding occurs  
    used               # Usage tracking: False or (scope, node) tuple
    
    def redefines(self, other) -> bool:
        """Determines if this binding redefines another."""

class Definition(Binding):
    """Base class for bindings that define functions or classes."""
    
    def redefines(self, other) -> bool:
        """Can redefine other definitions or assignments."""

class Assignment(Binding):
    """Regular variable assignments (x = value)."""

class NamedExprAssignment(Assignment):
    """Walrus operator assignments (x := value)."""

class Annotation(Binding):
    """Type annotations without values (x: int)."""
    
    def redefines(self, other) -> bool:
        """Annotations don't redefine names."""

class Argument(Binding):
    """Function parameters."""

class FunctionDefinition(Definition):
    """Function definitions (def func():)."""

class ClassDefinition(Definition):
    """Class definitions (class MyClass:)."""

class Builtin(Definition):
    """Built-in names (like print, len)."""

Import-Related Bindings

class Importation(Definition):
    """Standard import statements (import module)."""
    
    def __init__(self, name, source, full_name=None):
        """
        Parameters:
        - name (str): Local name for the import
        - source: AST node
        - full_name (str): Complete import path
        """
    
    fullName: str       # Complete import path
    redefined: list     # List of redefinition nodes
    
    def _has_alias(self) -> bool:
        """Whether import uses 'as' clause."""
    
    @property
    def source_statement(self) -> str:
        """Reconstructs the original import statement."""

class SubmoduleImportation(Importation):
    """Submodule imports (import package.module)."""

class ImportationFrom(Importation):
    """From imports (from module import name)."""

class StarImportation(Importation):
    """Star imports (from module import *)."""

class FutureImportation(ImportationFrom):
    """Future imports (from __future__ import feature)."""

class ExportBinding(Binding):
    """__all__ assignments for module exports."""
    
    def __init__(self, name, source, scope):
        """Parses __all__ assignments and concatenations."""
    
    names: list         # List of exported names from __all__

Binding Creation Process

Bindings are created through several key methods:

Name Storage (handleNodeStore):

  • Creates Annotation for type-only annotations (x: int)
  • Creates Binding for loop variables and tuple unpacking
  • Creates ExportBinding for __all__ assignments
  • Creates NamedExprAssignment for walrus operators (x := value)
  • Creates Assignment for regular assignments (x = value)

Import Processing:

  • IMPORT method creates Importation or SubmoduleImportation
  • IMPORTFROM method creates ImportationFrom, StarImportation, or FutureImportation

Definition Processing:

  • FUNCTIONDEF creates FunctionDefinition bindings
  • CLASSDEF creates ClassDefinition bindings
  • ARG creates Argument bindings for function parameters

Usage Tracking

Bindings track their usage through the used attribute:

  • False - Never used
  • (scope, node) tuple - Used, with reference to usage location

Usage is recorded in handleNodeLoad(node, parent) when names are accessed.

Scope System

Manages nested Python scopes with their specific rules and behaviors using a hierarchy of scope classes.

Scope Class Hierarchy

class Scope(dict):
    """Base scope class - dictionary of name -> binding."""
    
    def __init__(self, filename=None):
        """Initialize scope with optional filename."""
    
    def __contains__(self, key):
        """Check if name is bound in this scope."""
    
    def unusedAssignments(self):
        """Return unused assignments in this scope."""

class ModuleScope(Scope):
    """Module-level scope."""
    
    def __init__(self, filename=None):
        self.importStarred = False  # Whether any star imports exist
        self.futureAnnotations = False  # Whether __future__ annotations enabled

class ClassScope(Scope):
    """Class definition scope."""
    
    def __init__(self, filename=None):
        # Classes don't participate in normal name resolution
        pass

class FunctionScope(Scope):
    """Function and method scope."""
    
    def __init__(self, filename=None):
        self.globals = {}           # Global declarations
        self.nonlocals = {}         # Nonlocal declarations
        self.indirectGlobals = {}   # Names that may become global
        self.indirectNonlocals = {}  # Names that may become nonlocal
    
    def unused_assignments(self):
        """Return unused assignments in function scope."""
    
    def unused_annotations(self):
        """Return unused type annotations in function scope."""

class GeneratorScope(Scope):
    """Generator expressions and comprehensions."""
    
    def __init__(self, filename=None):
        # Generator scopes can access class scope variables
        pass

class TypeScope(Scope):
    """Type parameter scope (Python 3.12+)."""

class DoctestScope(ModuleScope):
    """Doctest execution scope."""

Scope Stack Management

The checker maintains a stack of active scopes during AST traversal:

# Core scope management attributes
scopeStack: list        # Stack of active scopes during traversal
deadScopes: list        # List of completed scopes for analysis

@property
def scope(self):
    """Current scope (last item in scopeStack)."""
    return self.scopeStack[-1] if self.scopeStack else None

def in_scope(self, cls):
    """Context manager for scope management."""
    # Pushes new scope, yields it, then pops and moves to deadScopes

Scope Resolution Rules

Name Lookup Order:

  1. Current scope (innermost)
  2. Enclosing function scopes (if any)
  3. Global scope (module level)
  4. Built-in scope

Special Scoping Rules:

  • Class scopes don't participate in normal name resolution for most names
  • Generator scopes can access class variables directly
  • Global/nonlocal declarations affect where names are stored
  • Star imports set scope.importStarred = True to disable undefined name warnings

Scope-Binding Integration

Scopes store bindings and provide methods for analysis:

# Adding bindings to scopes
def addBinding(self, node, value):
    """Add name binding to appropriate scope."""
    # Finds correct scope in stack based on global/nonlocal declarations
    # Handles redefinition checking and reporting
    # Updates binding usage information

# Scope analysis after traversal
def checkDeadScopes(self):
    """Analyze completed scopes for unused names."""
    # Reports unused imports, variables, and annotations
    # Validates __all__ exports
    # Processes star import usage patterns

Advanced Scope Features

Module Scope Features:

  • Tracks future imports (from __future__ import annotations)
  • Manages star imports and their effects on undefined name detection
  • Handles __all__ export validation

Function Scope Features:

  • Tracks global and nonlocal declarations
  • Identifies unused assignments and annotations
  • Manages function argument bindings
  • Handles closure variable access

Class Scope Features:

  • Class variables are not visible in methods (normal Python scoping)
  • Class scope bindings tracked but don't affect name resolution
  • Special handling for class decorators and base classes

Generator Scope Features:

  • Comprehensions create isolated scopes
  • Can access enclosing class variables (unlike functions)
  • Iterator variables don't leak to enclosing scope

Annotation Processing System

Handles Python type annotations with support for forward references and from __future__ import annotations.

Annotation States:

  • BARE - Direct annotation context
  • STRINGIZED - String annotation context
  • POSTPONED - Deferred annotation context

Features:

  • String annotation parsing with error handling
  • Support for typing module constructs
  • Forward reference resolution
  • Future annotations behavior

Message Reporting System

Structured system for collecting and reporting code issues.

Features:

  • All messages stored in messages list
  • Detailed location information (file, line, column)
  • Message classification by severity and type
  • Integration with reporter system for output formatting

Advanced Usage Examples

Custom Analysis with Deferred Functions

import ast
import pyflakes.checker

def custom_analysis_callback():
    """Custom analysis to run after main traversal."""
    print("Running custom analysis...")
    # Access checker state here
    return

code = """
def func():
    global_var = 42  # This will be analyzed after global scope
    
global_var = 10
"""

tree = ast.parse(code, 'test.py')
checker = pyflakes.checker.Checker(tree, 'test.py')

# Add custom deferred analysis
checker.deferFunction(custom_analysis_callback)

# Analysis completes with our custom function
print(f"Found {len(checker.messages)} issues")

Scope Analysis

import ast
import pyflakes.checker

code = """
class MyClass:
    x = 1
    
    def method(self):
        y = 2
        
        def inner():
            z = 3
"""

tree = ast.parse(code, 'test.py')
checker = pyflakes.checker.Checker(tree, 'test.py')

# Analyze completed scopes
print(f"Analyzed {len(checker.deadScopes)} scopes:")
for i, scope in enumerate(checker.deadScopes):
    print(f"  Scope {i}: {type(scope).__name__}")
    print(f"    Bindings: {list(scope.keys())}")

Message Analysis by Type

import ast
import pyflakes.checker
from pyflakes.messages import UnusedImport, UndefinedName

code = """
import os
import sys
print(undefined_name)
unused_var = 42
"""

tree = ast.parse(code, 'test.py')
checker = pyflakes.checker.Checker(tree, 'test.py')

# Categorize messages
unused_imports = [m for m in checker.messages if isinstance(m, UnusedImport)]
undefined_names = [m for m in checker.messages if isinstance(m, UndefinedName)]

print(f"Unused imports: {len(unused_imports)}")
print(f"Undefined names: {len(undefined_names)}")

for msg in checker.messages:
    print(f"{type(msg).__name__}: {msg}")

Annotation System Usage

The checker provides sophisticated annotation handling that can be extended:

import ast
import pyflakes.checker

code = '''
from typing import List, Dict
from __future__ import annotations

def func(x: List[int]) -> Dict[str, int]:
    """Function with type annotations."""
    return {"key": x[0]}

# Forward reference
def forward_ref() -> SomeClass:
    pass

class SomeClass:
    pass
'''

tree = ast.parse(code, 'test.py')
checker = pyflakes.checker.Checker(tree, 'test.py')

# Check if future annotations are enabled
print(f"Future annotations enabled: {checker.annotationsFutureEnabled}")

# All annotations are processed appropriately
print(f"Analysis complete with {len(checker.messages)} issues")

Install with Tessl CLI

npx tessl i tessl/pypi-pyflakes

docs

checker-system.md

core-api.md

index.md

message-types.md

reporter-system.md

tile.json