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

reporter-system.mddocs/

Reporter System

Flexible output formatting system for presenting analysis results to users. The Reporter class can be customized to integrate Pyflakes into different development environments and workflows, supporting various output formats and destinations.

Capabilities

Core Reporter Class

Main class for formatting and outputting Pyflakes analysis results, supporting separate streams for warnings and errors.

class Reporter:
    """Formats the results of pyflakes checks to users."""
    
    def __init__(self, warningStream, errorStream):
        """
        Construct a Reporter.

        Parameters:
        - warningStream: A file-like object where warnings will be written to.
          The stream's write method must accept unicode. sys.stdout is a good value.
        - errorStream: A file-like object where error output will be written to.
          The stream's write method must accept unicode. sys.stderr is a good value.
        """
    
    def unexpectedError(self, filename: str, msg: str):
        """
        An unexpected error occurred trying to process filename.

        Parameters:
        - filename (str): The path to a file that we could not process
        - msg (str): A message explaining the problem
        """
    
    def syntaxError(self, filename: str, msg: str, lineno: int, offset: int, text: str):
        """
        There was a syntax error in filename.

        Parameters:
        - filename (str): The path to the file with the syntax error
        - msg (str): An explanation of the syntax error
        - lineno (int): The line number where the syntax error occurred
        - offset (int): The column on which the syntax error occurred, or None
        - text (str): The source code containing the syntax error
        """
    
    def flake(self, message):
        """
        Pyflakes found something wrong with the code.

        Parameters:
        - message: A pyflakes.messages.Message instance
        """

Usage:

import sys
import pyflakes.api
from pyflakes.reporter import Reporter

# Create custom reporter
reporter = Reporter(sys.stdout, sys.stderr)

# Use with pyflakes API
code = """
import os
print(undefined_var)
"""

warnings = pyflakes.api.check(code, 'test.py', reporter)

Default Reporter Factory

Convenience function for creating a standard reporter using system stdout and stderr streams.

def _makeDefaultReporter():
    """
    Make a reporter that can be used when no reporter is specified.
    
    Returns:
    Reporter: A Reporter instance using sys.stdout and sys.stderr
    """

Usage:

from pyflakes.reporter import _makeDefaultReporter
import pyflakes.api

# Get default reporter
reporter = _makeDefaultReporter()

# Use with API functions
warnings = pyflakes.api.checkPath('myfile.py', reporter)

Custom Reporter Examples

Structured Output Reporter

Reporter that collects results in structured format rather than printing immediately.

import io
from pyflakes.reporter import Reporter
from pyflakes.messages import Message

class StructuredReporter(Reporter):
    """Reporter that collects structured results."""
    
    def __init__(self):
        # Use string buffers instead of real streams
        super().__init__(io.StringIO(), io.StringIO())
        self.messages = []
        self.syntax_errors = []
        self.unexpected_errors = []
    
    def flake(self, message: Message):
        """Collect flake messages."""
        self.messages.append({
            'type': type(message).__name__,
            'filename': message.filename,
            'lineno': message.lineno,
            'col': message.col,
            'message': str(message)
        })
    
    def syntaxError(self, filename: str, msg: str, lineno: int, offset: int, text: str):
        """Collect syntax errors."""
        self.syntax_errors.append({
            'filename': filename,
            'message': msg,
            'lineno': lineno,
            'offset': offset,
            'text': text
        })
    
    def unexpectedError(self, filename: str, msg: str):
        """Collect unexpected errors."""
        self.unexpected_errors.append({
            'filename': filename,
            'message': msg
        })
    
    def get_results(self):
        """Get all collected results."""
        return {
            'messages': self.messages,
            'syntax_errors': self.syntax_errors,
            'unexpected_errors': self.unexpected_errors,
            'total_issues': len(self.messages) + len(self.syntax_errors) + len(self.unexpected_errors)
        }

# Usage
import pyflakes.api

reporter = StructuredReporter()
code = """
import os
print(undefined_var
"""  # Intentional syntax error

pyflakes.api.check(code, 'test.py', reporter)
results = reporter.get_results()

print(f"Found {results['total_issues']} issues:")
for msg in results['messages']:
    print(f"  {msg['type']}: {msg['message']}")
for err in results['syntax_errors']:
    print(f"  Syntax Error: {err['message']}")

JSON Output Reporter

Reporter that formats output as JSON for integration with tools and IDEs.

import json
import sys
from pyflakes.reporter import Reporter

class JSONReporter(Reporter):
    """Reporter that outputs results as JSON."""
    
    def __init__(self, output_stream=None):
        self.output_stream = output_stream or sys.stdout
        super().__init__(output_stream, sys.stderr)
        self.results = []
    
    def flake(self, message):
        """Add flake message to results."""
        self.results.append({
            'type': 'warning',
            'category': type(message).__name__,
            'filename': message.filename,
            'line': message.lineno,
            'column': message.col + 1,  # Convert to 1-based
            'message': str(message).split(': ', 1)[1] if ': ' in str(message) else str(message)
        })
    
    def syntaxError(self, filename: str, msg: str, lineno: int, offset: int, text: str):
        """Add syntax error to results."""
        self.results.append({
            'type': 'error',
            'category': 'SyntaxError',
            'filename': filename,
            'line': lineno or 1,
            'column': offset or 1,
            'message': msg
        })
    
    def unexpectedError(self, filename: str, msg: str):
        """Add unexpected error to results."""
        self.results.append({
            'type': 'error',
            'category': 'UnexpectedError',
            'filename': filename,
            'line': 1,
            'column': 1,
            'message': msg
        })
    
    def output_results(self):
        """Output all results as JSON."""
        json.dump({
            'tool': 'pyflakes',
            'version': '3.4.0',
            'results': self.results,
            'summary': {
                'total': len(self.results),
                'errors': len([r for r in self.results if r['type'] == 'error']),
                'warnings': len([r for r in self.results if r['type'] == 'warning'])
            }
        }, self.output_stream, indent=2)

# Usage
import pyflakes.api

reporter = JSONReporter()
code = """
import os
import sys
x = undefined_var
"""

pyflakes.api.check(code, 'test.py', reporter)
reporter.output_results()

Colored Terminal Reporter

Reporter that adds color coding for different message types in terminal output.

import sys
from pyflakes.reporter import Reporter

class ColoredReporter(Reporter):
    """Reporter with colored terminal output."""
    
    # ANSI color codes
    RED = '\033[91m'
    YELLOW = '\033[93m'
    BLUE = '\033[94m'
    GREEN = '\033[92m'
    RESET = '\033[0m'
    BOLD = '\033[1m'
    
    def __init__(self, use_colors=None):
        super().__init__(sys.stdout, sys.stderr)
        # Auto-detect color support
        self.use_colors = use_colors if use_colors is not None else sys.stdout.isatty()
    
    def _colorize(self, text: str, color: str) -> str:
        """Apply color to text if colors are enabled."""
        if self.use_colors:
            return f"{color}{text}{self.RESET}"
        return text
    
    def flake(self, message):
        """Output colored flake message."""
        msg_str = str(message)
        
        # Color code by message type
        message_type = type(message).__name__
        if 'Undefined' in message_type:
            colored_msg = self._colorize(msg_str, self.RED)
        elif 'Unused' in message_type:
            colored_msg = self._colorize(msg_str, self.YELLOW)
        else:
            colored_msg = self._colorize(msg_str, self.BLUE)
        
        self._stdout.write(colored_msg + '\n')
    
    def syntaxError(self, filename: str, msg: str, lineno: int, offset: int, text: str):
        """Output colored syntax error."""
        error_msg = f"{filename}:{lineno or 1}:{offset or 1}: {msg}"
        colored_error = self._colorize(error_msg, self.RED + self.BOLD)
        self._stderr.write(colored_error + '\n')
        
        if text is not None:
            lines = text.splitlines()
            if lines:
                line = lines[-1]
                self._stderr.write(line + '\n')
                if offset is not None and offset > 0:
                    pointer = ' ' * (offset - 1) + '^'
                    self._stderr.write(self._colorize(pointer, self.RED) + '\n')
    
    def unexpectedError(self, filename: str, msg: str):
        """Output colored unexpected error."""
        error_msg = f"{filename}: {msg}"
        colored_error = self._colorize(error_msg, self.RED + self.BOLD)
        self._stderr.write(colored_error + '\n')

# Usage
import pyflakes.api

reporter = ColoredReporter()
code = """
import os
unused_var = 1
print(undefined_var)
"""

pyflakes.api.check(code, 'test.py', reporter)

File-based Reporter

Reporter that writes results to files instead of streams.

import os
from pyflakes.reporter import Reporter

class FileReporter(Reporter):
    """Reporter that writes to separate files."""
    
    def __init__(self, base_path: str):
        self.base_path = base_path
        self.warning_file = open(f"{base_path}.warnings", 'w')
        self.error_file = open(f"{base_path}.errors", 'w')
        super().__init__(self.warning_file, self.error_file)
        self.stats = {'warnings': 0, 'errors': 0}
    
    def flake(self, message):
        """Write flake to warnings file."""
        super().flake(message)
        self.stats['warnings'] += 1
    
    def syntaxError(self, filename: str, msg: str, lineno: int, offset: int, text: str):
        """Write syntax error to errors file."""
        super().syntaxError(filename, msg, lineno, offset, text)
        self.stats['errors'] += 1
    
    def unexpectedError(self, filename: str, msg: str):
        """Write unexpected error to errors file."""
        super().unexpectedError(filename, msg)
        self.stats['errors'] += 1
    
    def close(self):
        """Close files and write summary."""
        self.warning_file.close()
        self.error_file.close()
        
        # Write summary file
        with open(f"{self.base_path}.summary", 'w') as f:
            f.write(f"Warnings: {self.stats['warnings']}\n")
            f.write(f"Errors: {self.stats['errors']}\n")
            f.write(f"Total: {sum(self.stats.values())}\n")

# Usage
import pyflakes.api

reporter = FileReporter('pyflakes_results')
try:
    pyflakes.api.checkRecursive(['src/'], reporter)
finally:
    reporter.close()

# Results are now in:
# - pyflakes_results.warnings
# - pyflakes_results.errors  
# - pyflakes_results.summary

Integration Examples

IDE Integration

import pyflakes.api
from pyflakes.reporter import Reporter
import io

class IDEReporter(Reporter):
    """Reporter optimized for IDE integration."""
    
    def __init__(self, callback):
        super().__init__(io.StringIO(), io.StringIO())
        self.callback = callback  # Function to call with each issue
    
    def flake(self, message):
        """Send message to IDE via callback."""
        self.callback({
            'severity': 'warning',
            'message': str(message),
            'filename': message.filename,
            'line': message.lineno,
            'column': message.col,
            'source': 'pyflakes'
        })
    
    def syntaxError(self, filename: str, msg: str, lineno: int, offset: int, text: str):
        """Send syntax error to IDE."""
        self.callback({
            'severity': 'error',
            'message': msg,
            'filename': filename,
            'line': lineno or 1,
            'column': offset or 1,
            'source': 'pyflakes'
        })

def ide_callback(issue):
    """Example IDE callback function."""
    print(f"IDE: {issue['severity'].upper()} in {issue['filename']}:{issue['line']} - {issue['message']}")

# Usage
reporter = IDEReporter(ide_callback)
pyflakes.api.checkPath('myfile.py', reporter)

Batch Processing with Custom Reporting

import pyflakes.api
from pyflakes.reporter import Reporter
import os
import json
from collections import defaultdict

class BatchReporter(Reporter):
    """Reporter for batch processing multiple files."""
    
    def __init__(self):
        super().__init__(None, None)
        self.file_results = defaultdict(list)
    
    def flake(self, message):
        """Collect message by filename."""
        self.file_results[message.filename].append({
            'type': 'warning',
            'line': message.lineno,
            'column': message.col,
            'message': str(message),
            'category': type(message).__name__
        })
    
    def get_summary(self):
        """Get summary across all files."""
        total_issues = sum(len(issues) for issues in self.file_results.values())
        return {
            'total_files': len(self.file_results),
            'total_issues': total_issues,
            'files_with_issues': list(self.file_results.keys()),
            'average_issues_per_file': total_issues / len(self.file_results) if self.file_results else 0
        }
    
    def save_report(self, output_file: str):
        """Save complete report to JSON file."""
        report = {
            'summary': self.get_summary(),
            'files': dict(self.file_results)
        }
        
        with open(output_file, 'w') as f:
            json.dump(report, f, indent=2)

# Usage for batch processing
def batch_check_project(project_path: str, output_file: str):
    """Check entire project and generate report."""
    reporter = BatchReporter()
    
    # Find all Python files
    python_files = []
    for root, dirs, files in os.walk(project_path):
        for file in files:
            if file.endswith('.py'):
                python_files.append(os.path.join(root, file))
    
    # Check each file
    for py_file in python_files:
        pyflakes.api.checkPath(py_file, reporter)
    
    # Generate report
    reporter.save_report(output_file)
    
    # Print summary
    summary = reporter.get_summary()
    print(f"Checked {summary['total_files']} files")
    print(f"Found {summary['total_issues']} total issues")
    print(f"Average {summary['average_issues_per_file']:.1f} issues per file")

# batch_check_project('my_project/', 'pyflakes_report.json')

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