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:

TypePrefixDescriptionSeverityScore Impact
FatalFFatal error that prevents further processingHighest-10 points
ErrorEProgramming error that may cause exceptionsHigh-10 points
WarningWPotential issues that need attentionMedium-2 points
ConventionCViolation of coding conventionsLow-1 point
RefactorRCode refactoring suggestionsMedium-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
  1. Group by type: Handle fatal errors and errors first, then warnings and conventions
  2. Batch fix: Use editor’s find and replace to batch fix similar issues
  3. Understand root cause: Don’t just disable messages, understand why they appear
  4. Progressive improvement: Large projects can improve gradually, don’t need to fix everything at once
  5. Automation tools: Use black, isort and other tools to automatically fix format issues
Common Misconceptions
  1. Blind disabling: Don’t disable useful checks just to improve the score
  2. Over-optimization: Don’t over-complicate code to eliminate all messages
  3. Ignoring context: Some rules may not apply in specific contexts
  4. 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.