Chapter 2: Message Types and Error Analysis
Haiyue
17min
Chapter 2: Message Types and Error Analysis
Learning Objectives
- Master the five message types of Pylint
- Understand different levels such as errors, warnings, and conventions
- Learn how to interpret Pylint reports
- Master identification and fixing of common issues
Knowledge Points
Pylint Message Type System
Pylint categorizes check results into five types, each identified with a different letter prefix:
| Type | Prefix | Description | Severity | Score Impact |
|---|---|---|---|---|
| Fatal | F | Fatal error that prevents further processing | Highest | -10 points |
| Error | E | Programming error that may cause exceptions | High | -10 points |
| Warning | W | Potential issues that need attention | Medium | -2 points |
| Convention | C | Violation of coding conventions | Low | -1 point |
| Refactor | R | Code refactoring suggestions | Medium | -5 points |
Message Format Parsing
filename:line:column: message_type_code: message_description (message_name)
🔄 正在渲染 Mermaid 图表...
Sample Code
Fatal Error (F) Examples
# fatal_errors.py - Fatal error examples
# F0001: Syntax error
def broken_syntax()
return "missing colon" # Syntax error: missing colon
# F0002: Cannot import
import non_existent_module # Module doesn't exist
# F0010: Cannot parse file
# If file encoding has issues or contains invalid characters
# Run result:
# fatal_errors.py:4:0: F0001: Error parsing the module: invalid syntax (missing ':'). (syntax-error)
Error (E) Examples
# error_examples.py - Error examples
class MyClass:
def __init__(self):
self.value = 10
# E1101: Access non-existent attribute
obj = MyClass()
print(obj.nonexistent_attribute) # AttributeError
# E1102: Calling non-callable object
number = 42
result = number() # TypeError: 'int' object is not callable
# E1111: Assignment to function call
def get_value():
return 10
get_value() = 20 # SyntaxError: can't assign to function call
# E1120: Missing required argument
def add_numbers(a, b):
return a + b
result = add_numbers(5) # TypeError: missing required argument
# E1121: Too many arguments
result = add_numbers(1, 2, 3) # TypeError: too many arguments
# E0602: Undefined variable
print(undefined_variable) # NameError
# E0611: Import non-existent name
from math import nonexistent_function
# Common error fix examples
class FixedClass:
def __init__(self):
self.value = 10
self.valid_attribute = "exists"
obj = FixedClass()
print(obj.valid_attribute) # Correctly access attribute
# Correct function call
result = add_numbers(1, 2) # Provide correct number of arguments
# Define variable before use
defined_variable = "Hello"
print(defined_variable)
Warning (W) Examples
# warning_examples.py - Warning examples
import os
import sys
import unused_module # W0611: Unused import
# W0612: Unused variable
def function_with_unused_vars():
used_var = "I am used"
unused_var = "I am not used" # Warning: unused variable
return used_var
# W0613: Unused argument
def function_with_unused_args(used_arg, unused_arg):
return used_arg * 2
# W0622: Redefining built-in name
def len(sequence): # Warning: redefining built-in function len
return 42
# W0621: Redefining outer scope variable
global_var = "global"
def function_with_redefined_var():
global_var = "local" # Warning: redefining outer variable
return global_var
# W0107: Unnecessary pass statement
def empty_function():
pass # If function is truly empty, this is unnecessary
# W0102: Dangerous default argument
def append_to_list(item, target_list=[]): # Dangerous: mutable default argument
target_list.append(item)
return target_list
# W0603: Using global statement
global_counter = 0
def increment_counter():
global global_counter # Warning: using global statement
global_counter += 1
# Fix examples
def fixed_append_to_list(item, target_list=None):
"""Fix dangerous default argument"""
if target_list is None:
target_list = []
target_list.append(item)
return target_list
def fixed_function_with_unused_args(used_arg, _unused_arg):
"""Use underscore prefix to indicate unused parameters"""
return used_arg * 2
# Remove unused imports
# import unused_module # Delete this line
# Use the unused variable
def fixed_function_with_vars():
used_var = "I am used"
another_var = "I am also used"
return f"{used_var} and {another_var}"
Convention (C) Examples
# convention_examples.py - Convention examples
# C0114: Missing module docstring
"""
This is a module demonstrating coding conventions
This module contains various examples of violations and compliance with Python coding conventions.
"""
# C0103: Naming doesn't conform to convention
camelCaseVariable = "should be snake_case" # Should use underscore naming
LOWERCASE_CONSTANT = "should be uppercase" # Constants should be uppercase
ClassName = "should be PascalCase" # Class names should use PascalCase
# Correct naming conventions
snake_case_variable = "correct naming"
UPPERCASE_CONSTANT = "correct constant"
class ProperClassName: # Correct class naming
"""Correct class naming example"""
# C0115: Missing class docstring
class ClassWithoutDocstring:
def __init__(self):
self.value = 10
# C0116: Missing function docstring
def function_without_docstring():
return True
# Correct docstring examples
class ClassWithDocstring:
"""
A class with proper documentation
This class demonstrates correct docstring formatting.
"""
def __init__(self):
"""Initialization method"""
self.value = 10
def method_with_docstring(self):
"""
A method with proper documentation
Returns:
str: String value returned
"""
return "method result"
def function_with_docstring(param1, param2):
"""
A function with proper documentation
Args:
param1 (str): First parameter
param2 (int): Second parameter
Returns:
str: Formatted string
"""
return f"{param1}: {param2}"
# C0200: Unnecessary index loop
items = ["a", "b", "c"]
# Bad way
for i in range(len(items)):
print(f"Item {i}: {items[i]}")
# Good way
for i, item in enumerate(items):
print(f"Item {i}: {item}")
# Or simpler way
for item in items:
print(f"Item: {item}")
# C0201: Unnecessary key traversal
data = {"key1": "value1", "key2": "value2"}
# Bad way
for key in data.keys():
print(f"{key}: {data[key]}")
# Good way
for key, value in data.items():
print(f"{key}: {value}")
Refactor (R) Examples
# refactor_examples.py - Refactoring suggestion examples
# R0903: Too few public methods
class TooFewMethods:
"""Class with only one public method"""
def __init__(self):
self._value = 10
def get_value(self):
return self._value
# R0913: Too many arguments
def function_with_too_many_args(arg1, arg2, arg3, arg4, arg5, arg6, arg7):
"""Function with too many parameters"""
return arg1 + arg2 + arg3 + arg4 + arg5 + arg6 + arg7
# R0914: Too many local variables
def function_with_too_many_locals():
"""Function with too many local variables"""
var1 = 1
var2 = 2
var3 = 3
var4 = 4
var5 = 5
var6 = 6
var7 = 7
var8 = 8
var9 = 9
var10 = 10
var11 = 11
var12 = 12
var13 = 13
var14 = 14
var15 = 15
var16 = 16 # Exceeds default limit
return sum([var1, var2, var3, var4, var5, var6, var7, var8,
var9, var10, var11, var12, var13, var14, var15, var16])
# R0915: Too many statements
def function_with_too_many_statements():
"""Function with too many statements"""
result = 0
result += 1
result += 2
result += 3
result += 4
result += 5
result += 6
result += 7
result += 8
result += 9
result += 10
result += 11
result += 12
result += 13
result += 14
result += 15
result += 16
result += 17
result += 18
result += 19
result += 20
result += 21
result += 22
result += 23
result += 24
result += 25
result += 26
result += 27
result += 28
result += 29
result += 30
result += 31
result += 32
result += 33
result += 34
result += 35
result += 36
result += 37
result += 38
result += 39
result += 40
result += 41
result += 42
result += 43
result += 44
result += 45
result += 46
result += 47
result += 48
result += 49
result += 50
result += 51 # Exceeds default limit of 50 statements
return result
# Refactored improved versions
# Use configuration class to improve too many parameters issue
class FunctionConfig:
"""Function configuration class"""
def __init__(self, arg1=1, arg2=2, arg3=3, arg4=4, arg5=5, arg6=6, arg7=7):
self.arg1 = arg1
self.arg2 = arg2
self.arg3 = arg3
self.arg4 = arg4
self.arg5 = arg5
self.arg6 = arg6
self.arg7 = arg7
def improved_function(config: FunctionConfig):
"""Improved function using configuration object"""
return (config.arg1 + config.arg2 + config.arg3 + config.arg4 +
config.arg5 + config.arg6 + config.arg7)
# Split large function
def calculate_sum_1_to_10():
"""Calculate sum of 1 to 10"""
return sum(range(1, 11))
def calculate_sum_11_to_20():
"""Calculate sum of 11 to 20"""
return sum(range(11, 21))
def improved_large_calculation():
"""Improved large calculation function"""
sum1 = calculate_sum_1_to_10()
sum2 = calculate_sum_11_to_20()
return sum1 + sum2
# Use dataclass to improve class with too few methods
from dataclasses import dataclass
@dataclass
class ImprovedDataClass:
"""Improved data class"""
value: int = 10
def get_value(self):
"""Get value"""
return self.value
def set_value(self, value):
"""Set value"""
self.value = value
def double_value(self):
"""Double the value"""
self.value *= 2
def is_positive(self):
"""Check if value is positive"""
return self.value > 0
Message Analysis and Fix Strategies
# message_analysis.py - Message analysis and fix strategies
def analyze_pylint_output():
"""Strategies for analyzing Pylint output"""
# 1. Priority ordering
priority_order = [
"Fatal (F) - Fix immediately",
"Error (E) - High priority fix",
"Warning (W) - Medium priority fix",
"Refactor (R) - Refactoring improvements",
"Convention (C) - Coding standards"
]
# 2. Common fix patterns
fix_patterns = {
"missing-docstring": "Add docstrings",
"invalid-name": "Change to conform to naming standards",
"unused-variable": "Delete unused variable or use underscore prefix",
"unused-import": "Delete unused import",
"too-many-arguments": "Use configuration object or split function",
"too-many-locals": "Extract subfunctions",
"line-too-long": "Split long lines",
"trailing-whitespace": "Delete trailing spaces"
}
return priority_order, fix_patterns
# Batch fix examples
def batch_fix_examples():
"""Batch fix common issues example"""
# Code before fix
def problematic_function(a,b,c,d,e): # Missing spaces, too many parameters
x=a+b # Missing spaces
y=c+d
z=e
unused_var="not used" # Unused variable
return x+y+z # Missing spaces
# Code after fix
class ParameterConfig:
"""Parameter configuration class"""
def __init__(self, a, b, c, d, e):
self.a = a
self.b = b
self.c = c
self.d = d
self.e = e
def improved_function(config: ParameterConfig):
"""
Improved function
Args:
config (ParameterConfig): Parameter configuration object
Returns:
int: Calculation result
"""
x = config.a + config.b
y = config.c + config.d
z = config.e
return x + y + z
# Automated fix script example
def create_auto_fix_script():
"""Example of creating automated fix script"""
import re
def fix_spacing_issues(code):
"""Fix spacing issues"""
# Fix spaces around operators
code = re.sub(r'(\w)=(\w)', r'\1 = \2', code)
code = re.sub(r'(\w)\+(\w)', r'\1 + \2', code)
code = re.sub(r'(\w)-(\w)', r'\1 - \2', code)
return code
def add_missing_docstrings(code):
"""Add missing docstrings"""
# This is a simplified example
if 'def ' in code and '"""' not in code:
# Add basic docstring after function definition
return code.replace('def ', 'def ') # Actual implementation would be more complex
return code
return fix_spacing_issues, add_missing_docstrings
Message Analysis Tips
- Group by type: Handle fatal errors and errors first, then warnings and conventions
- Batch fix: Use editor’s find and replace to batch fix similar issues
- Understand root cause: Don’t just disable messages, understand why they appear
- Progressive improvement: Large projects can improve gradually, don’t need to fix everything at once
- Automation tools: Use black, isort and other tools to automatically fix format issues
Common Misconceptions
- Blind disabling: Don’t disable useful checks just to improve the score
- Over-optimization: Don’t over-complicate code to eliminate all messages
- Ignoring context: Some rules may not apply in specific contexts
- Mechanical fixes: Understand the meaning of messages rather than fixing mechanically
Message Statistical Analysis
# Script for statistical analysis of Pylint messages
import re
import json
from collections import defaultdict, Counter
def parse_pylint_output(output):
"""Parse Pylint output"""
messages = []
pattern = r'(.+):(\d+):(\d+): ([EWCRF]\d+): (.+) \((.+)\)'
for line in output.split('\n'):
match = re.match(pattern, line.strip())
if match:
file_path, line_num, col_num, msg_id, description, symbol = match.groups()
messages.append({
'file': file_path,
'line': int(line_num),
'column': int(col_num),
'id': msg_id,
'type': msg_id[0],
'description': description,
'symbol': symbol
})
return messages
def analyze_messages(messages):
"""Analyze message statistics"""
type_counts = Counter(msg['type'] for msg in messages)
symbol_counts = Counter(msg['symbol'] for msg in messages)
file_counts = Counter(msg['file'] for msg in messages)
return {
'total': len(messages),
'by_type': dict(type_counts),
'by_symbol': dict(symbol_counts.most_common(10)),
'by_file': dict(file_counts.most_common(10))
}
# Example usage
sample_output = """
sample.py:1:0: C0114: Missing module docstring (missing-module-docstring)
sample.py:3:0: C0116: Missing function or method docstring (missing-function-docstring)
sample.py:3:4: C0103: Constant name "pi" doesn't conform to UPPER_CASE naming style (invalid-name)
sample.py:5:4: W0612: Unused variable 'unused_var' (unused-variable)
"""
messages = parse_pylint_output(sample_output)
stats = analyze_messages(messages)
print(json.dumps(stats, indent=2))
Understanding Pylint’s message type system is key to effectively using this tool. By systematically analyzing and fixing different types of problems, code quality can be significantly improved.