Static analysis tool that detects errors in Python code without importing it.
—
Comprehensive set of structured warning and error classes representing different types of issues that can be detected in Python code. Each message type provides specific information about the problem and its location, enabling precise error reporting and programmatic handling of analysis results.
Pyflakes defines 48 message classes that inherit from the base Message class, each representing a specific type of code issue.
Foundation class for all Pyflakes messages, providing common functionality for error reporting and formatting.
class Message:
"""Base class for all pyflakes messages."""
message: str = '' # Format string for the message
message_args: tuple = () # Arguments for format string
def __init__(self, filename: str, loc):
"""
Initialize message with location information.
Parameters:
- filename (str): Name of file where issue was found
- loc: AST node with location information (lineno, col_offset)
"""
filename: str # File where issue occurs
lineno: int # Line number (1-based)
col: int # Column offset (0-based)
def __str__(self) -> str:
"""Format message as 'filename:line:col: description'."""Usage:
import pyflakes.api
from pyflakes.messages import UnusedImport, UndefinedName
# Get messages from analysis
code = "import os\nprint(x)"
checker_result = pyflakes.api.check(code, 'test.py')
# Messages are typically accessed through checker.messages
# Each message provides structured information:
# - message.filename: 'test.py'
# - message.lineno: line number
# - message.col: column position
# - str(message): formatted error stringMessages for issues related to import statements and module usage.
class UnusedImport(Message):
"""Import statement that is never used."""
message = '%r imported but unused'
def __init__(self, filename: str, loc, name: str): ...
class RedefinedWhileUnused(Message):
"""Name redefined before being used."""
message = 'redefinition of unused %r from line %r'
def __init__(self, filename: str, loc, name: str, orig_loc): ...
class ImportShadowedByLoopVar(Message):
"""Import name shadowed by loop variable."""
message = 'import %r from line %r shadowed by loop variable'
def __init__(self, filename: str, loc, name: str, orig_loc): ...
class ImportStarNotPermitted(Message):
"""Star import used outside module level."""
message = "'from %s import *' only allowed at module level"
def __init__(self, filename: str, loc, modname: str): ...
class ImportStarUsed(Message):
"""Star import used, limiting name detection."""
message = "'from %s import *' used; unable to detect undefined names"
def __init__(self, filename: str, loc, modname: str): ...
class ImportStarUsage(Message):
"""Name may be from star import."""
message = "%r may be undefined, or defined from star imports: %s"
def __init__(self, filename: str, loc, name: str, from_list: str): ...Messages for undefined names and variable usage issues.
class UndefinedName(Message):
"""Reference to undefined name."""
message = 'undefined name %r'
def __init__(self, filename: str, loc, name: str): ...
class UndefinedLocal(Message):
"""Local variable referenced before assignment."""
message = 'local variable %r {0} referenced before assignment'
default = 'defined in enclosing scope on line %r'
builtin = 'defined as a builtin'
def __init__(self, filename: str, loc, name: str, orig_loc): ...
class UndefinedExport(Message):
"""Name in __all__ is not defined."""
message = 'undefined name %r in __all__'
def __init__(self, filename: str, loc, name: str): ...Messages for unused variables and assignments.
class UnusedVariable(Message):
"""Variable assigned but never used."""
message = 'local variable %r is assigned to but never used'
def __init__(self, filename: str, loc, names: str): ...
class UnusedAnnotation(Message):
"""Type annotation without assignment or usage."""
message = 'local variable %r is annotated but never used'
def __init__(self, filename: str, loc, names: str): ...
class UnusedIndirectAssignment(Message):
"""Global or nonlocal statement where name is never reassigned."""
message = '`%s %s` is unused: name is never assigned in scope'
def __init__(self, filename: str, loc, name: str): ...Messages for syntax errors and structural issues.
class DoctestSyntaxError(Message):
"""Syntax error in doctest code."""
message = 'syntax error in doctest'
def __init__(self, filename: str, loc, position=None): ...
class DuplicateArgument(Message):
"""Duplicate argument in function definition."""
message = 'duplicate argument %r in function definition'
def __init__(self, filename: str, loc, name: str): ...
class MultiValueRepeatedKeyLiteral(Message):
"""Dictionary key repeated with different values."""
message = 'dictionary key %r repeated with different values'
def __init__(self, filename: str, loc, key): ...
class MultiValueRepeatedKeyVariable(Message):
"""Dictionary key variable repeated with different values."""
message = 'dictionary key variable %s repeated with different values'
def __init__(self, filename: str, loc, key): ...Messages for future import issues.
class LateFutureImport(Message):
"""__future__ import not at beginning of file."""
message = 'from __future__ imports must occur at the beginning of the file'
class FutureFeatureNotDefined(Message):
"""Unknown __future__ feature."""
message = 'future feature %s is not defined'
def __init__(self, filename: str, loc, name: str): ...Messages for control flow statement issues.
class ReturnOutsideFunction(Message):
"""Return statement outside function."""
message = "'return' outside function"
class YieldOutsideFunction(Message):
"""Yield statement outside function."""
message = "'yield' outside function"
class ContinueOutsideLoop(Message):
"""Continue statement outside loop."""
message = "'continue' not properly in loop"
class BreakOutsideLoop(Message):
"""Break statement outside loop."""
message = "'break' outside loop"Messages for exception handling issues.
class DefaultExceptNotLast(Message):
"""Default except clause not last."""
message = 'default \'except:\' must be last'Messages for assignment expression and unpacking issues.
class TwoStarredExpressions(Message):
"""Multiple starred expressions in assignment."""
message = 'two starred expressions in assignment'
class TooManyExpressionsInStarredAssignment(Message):
"""Too many expressions in starred assignment."""
message = 'too many expressions in star-unpacking assignment'Messages for comparison and literal usage issues.
class IfTuple(Message):
"""If statement with tuple condition (always true)."""
message = '\'if tuple literal\' is always true, perhaps remove accidental comma?'
class AssertTuple(Message):
"""Assert statement with tuple (always true)."""
message = 'assertion is always true, perhaps remove parentheses?'
class IsLiteral(Message):
"""Identity comparison with literal."""
message = 'use ==/!= to compare constant literals (str, bytes, int, float, tuple)'Messages for type annotation issues.
class ForwardAnnotationSyntaxError(Message):
"""Syntax error in forward annotation."""
message = 'syntax error in forward annotation %r'
def __init__(self, filename: str, loc, annotation: str): ...Messages for Python version compatibility issues.
class RaiseNotImplemented(Message):
"""Raise NotImplemented instead of NotImplementedError."""
message = "'raise NotImplemented' should be 'raise NotImplementedError'"
class InvalidPrintSyntax(Message):
"""Invalid print syntax (Python 2 style)."""
message = 'use of >> is invalid with print function'Messages for string formatting issues (f-strings, .format(), % formatting).
class FStringMissingPlaceholders(Message):
"""F-string without placeholders."""
message = 'f-string is missing placeholders'
class TStringMissingPlaceholders(Message):
"""T-string without placeholders."""
message = 't-string is missing placeholders'
class StringDotFormatExtraPositionalArguments(Message):
"""Extra positional arguments in .format() call."""
message = "'...'.format(...) has unused arguments at position(s): %s"
def __init__(self, filename: str, loc, extra_positions): ...
class StringDotFormatExtraNamedArguments(Message):
"""Extra named arguments in .format() call."""
message = "'...'.format(...) has unused named argument(s): %s"
def __init__(self, filename: str, loc, extra_keywords): ...
class StringDotFormatMissingArgument(Message):
"""Missing argument in .format() call."""
message = "'...'.format(...) is missing argument(s) for placeholder(s): %s"
def __init__(self, filename: str, loc, missing_arguments): ...
class StringDotFormatMixingAutomatic(Message):
"""Mixing automatic and manual field numbering."""
message = "'...'.format(...) mixes automatic and manual numbering"
class StringDotFormatInvalidFormat(Message):
"""Invalid format specifier."""
message = "'...'.format(...) has invalid format string: %s"
def __init__(self, filename: str, loc, error): ...Messages for percent-style string formatting issues.
class PercentFormatInvalidFormat(Message):
"""Invalid percent format string."""
message = "'...' %% ... has invalid format string: %s"
def __init__(self, filename: str, loc, error): ...
class PercentFormatMixedPositionalAndNamed(Message):
"""Mixed positional and named formatting."""
message = "'...' %% ... has mixed positional and named placeholders"
class PercentFormatUnsupportedFormatCharacter(Message):
"""Unsupported format character."""
message = "'...' %% ... has unsupported format character %r"
def __init__(self, filename: str, loc, c): ...
class PercentFormatPositionalCountMismatch(Message):
"""Wrong number of positional arguments."""
message = "'...' %% ... has %d placeholder(s) but %d substitution(s)"
def __init__(self, filename: str, loc, n_placeholders: int, n_substitutions: int): ...
class PercentFormatExtraNamedArguments(Message):
"""Extra named arguments in percent formatting."""
message = "'...' %% ... has unused named argument(s): %s"
def __init__(self, filename: str, loc, extra_keywords): ...
class PercentFormatMissingArgument(Message):
"""Missing argument in percent formatting."""
message = "'...' %% ... is missing argument(s) for placeholder(s): %s"
def __init__(self, filename: str, loc, missing_arguments): ...
class PercentFormatExpectedMapping(Message):
"""Expected mapping for named formatting."""
message = "'...' %% ... expected mapping but got sequence"
class PercentFormatExpectedSequence(Message):
"""Expected sequence for positional formatting."""
message = "'...' %% ... expected sequence but got mapping"
class PercentFormatStarRequiresSequence(Message):
"""Star format requires sequence."""
message = "'...' %% ... `*` specifier requires sequence"All 48 message classes available in pyflakes.messages:
Message - Base classUnusedImport - Unused import statementRedefinedWhileUnused - Name redefined before useImportShadowedByLoopVar - Import shadowed by loop variableImportStarNotPermitted - Star import outside module levelImportStarUsed - Star import usedImportStarUsage - Name may be from star importUndefinedName - Undefined name referenceDoctestSyntaxError - Doctest syntax errorUndefinedExport - Undefined name in allUndefinedLocal - Local variable referenced before assignmentDuplicateArgument - Duplicate function argumentMultiValueRepeatedKeyLiteral - Repeated dictionary key literalMultiValueRepeatedKeyVariable - Repeated dictionary key variableLateFutureImport - Late future importFutureFeatureNotDefined - Undefined future featureUnusedVariable - Unused variable assignmentUnusedAnnotation - Unused type annotationUnusedIndirectAssignment - Unused global/nonlocal statementReturnOutsideFunction - Return outside functionYieldOutsideFunction - Yield outside functionContinueOutsideLoop - Continue outside loopBreakOutsideLoop - Break outside loopDefaultExceptNotLast - Default except not lastTwoStarredExpressions - Multiple starred expressionsTooManyExpressionsInStarredAssignment - Too many expressions in starred assignmentIfTuple - If with tuple literalAssertTuple - Assert with tupleForwardAnnotationSyntaxError - Forward annotation syntax errorRaiseNotImplemented - Raise NotImplementedInvalidPrintSyntax - Invalid print syntaxIsLiteral - Identity comparison with literalFStringMissingPlaceholders - F-string without placeholdersTStringMissingPlaceholders - T-string without placeholdersStringDotFormatExtraPositionalArguments - Extra positional args in .format()StringDotFormatExtraNamedArguments - Extra named args in .format()StringDotFormatMissingArgument - Missing argument in .format()StringDotFormatMixingAutomatic - Mixed numbering in .format()StringDotFormatInvalidFormat - Invalid format in .format()PercentFormatInvalidFormat - Invalid percent formatPercentFormatMixedPositionalAndNamed - Mixed percent format typesPercentFormatUnsupportedFormatCharacter - Unsupported percent format charPercentFormatPositionalCountMismatch - Percent format count mismatchPercentFormatExtraNamedArguments - Extra named args in percent formatPercentFormatMissingArgument - Missing argument in percent formatPercentFormatExpectedMapping - Expected mapping in percent formatPercentFormatExpectedSequence - Expected sequence in percent formatPercentFormatStarRequiresSequence - Star requires sequence in percent formatimport pyflakes.api
from pyflakes.messages import UnusedImport, UnusedVariable, UndefinedName
def categorize_issues(code: str, filename: str):
"""Categorize issues by type."""
import ast
import pyflakes.checker
tree = ast.parse(code, filename=filename)
checker = pyflakes.checker.Checker(tree, filename=filename)
categories = {
'unused_imports': [],
'unused_variables': [],
'undefined_names': [],
'other': []
}
for message in checker.messages:
if isinstance(message, UnusedImport):
categories['unused_imports'].append(message)
elif isinstance(message, UnusedVariable):
categories['unused_variables'].append(message)
elif isinstance(message, UndefinedName):
categories['undefined_names'].append(message)
else:
categories['other'].append(message)
return categories
# Usage
code = """
import os
import sys
unused_var = 42
print(undefined_name)
"""
issues = categorize_issues(code, 'test.py')
print(f"Unused imports: {len(issues['unused_imports'])}")
print(f"Undefined names: {len(issues['undefined_names'])}")import pyflakes.api
from pyflakes.messages import Message
class CustomMessageHandler:
"""Custom handler for pyflakes messages."""
def __init__(self):
self.errors = []
self.warnings = []
self.info = []
def process_message(self, message: Message):
"""Process a single message."""
error_types = {
'UndefinedName', 'UndefinedLocal', 'UndefinedExport',
'DoctestSyntaxError', 'ForwardAnnotationSyntaxError'
}
warning_types = {
'UnusedImport', 'UnusedVariable', 'RedefinedWhileUnused',
'ImportStarUsed', 'IsLiteral'
}
message_type = type(message).__name__
if message_type in error_types:
self.errors.append(message)
elif message_type in warning_types:
self.warnings.append(message)
else:
self.info.append(message)
def get_summary(self):
"""Get summary of processed messages."""
return {
'errors': len(self.errors),
'warnings': len(self.warnings),
'info': len(self.info),
'total': len(self.errors) + len(self.warnings) + len(self.info)
}
# Usage with checker
import ast
import pyflakes.checker
code = """
import os
x = 1
print(y)
"""
tree = ast.parse(code, 'test.py')
checker = pyflakes.checker.Checker(tree, 'test.py')
handler = CustomMessageHandler()
for message in checker.messages:
handler.process_message(message)
summary = handler.get_summary()
print(f"Summary: {summary}")Install with Tessl CLI
npx tessl i tessl/pypi-pyflakes