Agent skill

python-debugging

Python debugging methodology and problem-solving framework. Use when investigating exceptions, async issues, logging problems, or MCP integration failures in Python code.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/python-debugging

SKILL.md

Python Debugging Methodology

Systematic problem-solving framework for Python code debugging.

When to Use This Skill

  • Investigating exceptions and stack traces
  • Debugging asynchronous code (async/await)
  • Resolving logging configuration issues
  • Troubleshooting MCP server communication errors
  • Diagnosing test failures (pytest, unittest)
  • Analyzing performance problems

Debugging Workflow (推論プロセス)

1. Interpret Error Messages

Objective: Extract actionable information from error output

Error message anatomy:

Traceback (most recent call last):
  File "script.py", line 42, in main        ← Calling context
    result = process_data(input)            ← Line that triggered error
  File "utils.py", line 15, in process_data ← Where exception was raised
    return data['key']                      ← Failing operation
KeyError: 'key'                             ← Exception type and message

Questions to answer:

  • What is the exception type? (KeyError, AttributeError, TypeError, etc.)
  • What was the last successful operation?
  • What input triggered the failure?

2. Read Stack Traces

Objective: Understand execution path to failure point

Reading strategy:

Top-down (most common):

  • Start from outermost frame (entry point)
  • Trace execution flow downward
  • Understand calling context

Bottom-up (for root cause):

  • Start from innermost frame (where exception was raised)
  • Identify the specific operation that failed
  • Trace why inputs were invalid

Focus points:

  • Function arguments at each level
  • Local variables at failure point
  • Library code vs application code boundaries

3. Reproduce Consistently

Objective: Create minimal failing test case

Simplification steps:

  1. Isolate the failing function
  2. Extract minimal input that triggers failure
  3. Remove dependencies (use mocks if needed)
  4. Create standalone script (~10 lines)

Example:

python
# Minimal reproducer
data = {'wrong_key': 123}
result = data['key']  # KeyError

4. Form Hypotheses

Objective: List plausible causes based on exception type

Hypothesis by exception type:

Exception Common Causes Investigation Strategy
KeyError Missing dict key, typo Print dict.keys(), check spelling
AttributeError Wrong object type, typo Print type(obj), check method name
TypeError Wrong argument type, arity mismatch Print type(args), check function signature
ValueError Invalid value range, format error Print actual value, check constraints
IndexError List/string index out of bounds Print len(sequence), check index calculation
ImportError Module not installed, wrong path Check pip list, sys.path
JSONDecodeError Malformed JSON, encoding issue Print raw string, validate JSON syntax

For asynchronous code:

  • Is await missing? (RuntimeWarning: coroutine never awaited)
  • Is blocking I/O used in async function?
  • Are multiple tasks accessing shared state?

For MCP/RPC issues:

  • Is JSON-RPC request malformed?
  • Is server process running?
  • Are method names spelled correctly?
  • Is timeout too short?

5. Select Debugging Strategy

Objective: Choose appropriate tool for the problem

Decision tree:

Is the error location obvious from stack trace?
├─ Yes → Use print() or logging
│        Fast iteration, minimal setup
└─ No → Use pdb debugger
         Interactive inspection needed

Is the problem in async code?
├─ Yes → Use logging with timestamps
│        await points are not debugger-friendly
└─ No → pdb works well

Is it a test failure?
├─ Yes → pytest --pdb
│        Drops into debugger at failure point
└─ No → Standard debugging approach

6. Apply Debugging Tool

Objective: Gather evidence to confirm/reject hypothesis

Tool selection:

print() / logging: Quick hypothesis testing

python
print(f"DEBUG: variable={variable}, type={type(variable)}")

pdb: Interactive investigation

python
import pdb; pdb.set_trace()  # Breakpoint here

logging: Persistent diagnostics

python
logger.debug(f"Processing {count} items")
logger.warning(f"Unexpected value: {value}")

Debugging Strategy Selection

When to Use print()

Use for:

  • Quick value inspection
  • Hypothesis: "If I print X here, I'll see Y"
  • Short-lived debugging sessions

Limitations:

  • Clutters code
  • Must re-run for each new hypothesis
  • No interactive inspection

When to Use pdb

Use for:

  • Multiple hypotheses to test
  • Need to inspect many variables
  • Control flow is unclear
  • Complex data structures

Basic commands:

(Pdb) l          # List code around current line
(Pdb) n          # Next line (step over)
(Pdb) s          # Step into function
(Pdb) c          # Continue to next breakpoint
(Pdb) p variable # Print variable value
(Pdb) pp obj     # Pretty-print object
(Pdb) w          # Where am I? (stack trace)

When to Use logging

Use for:

  • Production code debugging
  • Long-running processes
  • Intermittent failures
  • Timeline reconstruction

Configuration:

python
import logging
logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

Error Pattern Recognition

Common Python Patterns

Pattern: "NoneType has no attribute X"

python
# Cause: Function returned None instead of expected object
result = get_data()  # Returns None on error
result.process()     # AttributeError: 'NoneType' has no attribute 'process'

# Investigation: Why did get_data() return None?

Pattern: "Dictionary changed size during iteration"

python
# Cause: Modifying dict while iterating
for key in data:
    if condition:
        del data[key]  # RuntimeError

# Fix: Iterate over copy
for key in list(data.keys()):
    if condition:
        del data[key]

Pattern: "JSON decode error: Expecting value"

python
# Cause: Empty or non-JSON response
response = requests.get(url)
data = response.json()  # JSONDecodeError if response is HTML or empty

# Investigation: Print response.text to see actual content

Async/Await Patterns

Pattern: "Coroutine was never awaited"

python
# Wrong: Calling async function without await
result = async_function()  # Returns coroutine object, doesn't execute

# Right: Await the coroutine
result = await async_function()

Pattern: "Event loop already running"

python
# Cause: Nested asyncio.run() calls
async def outer():
    asyncio.run(inner())  # Error: loop already running

# Fix: Just await
async def outer():
    await inner()

Hypothesis Testing Techniques

Binary Search Debugging

Narrow down the failing region:

python
# Add checkpoints to partition code
def process_data(data):
    print("CHECKPOINT 1: Input valid")  # Check 1
    
    intermediate = transform(data)
    print("CHECKPOINT 2: Transform OK")  # Check 2
    
    result = finalize(intermediate)
    print("CHECKPOINT 3: Finalize OK")  # Check 3
    
    return result

# Run: Which checkpoint is NOT printed? Problem is before it.

Differential Analysis

Compare working vs failing cases:

python
# Working case
process_data({'key': 'value'})  # ✓ Success

# Failing case  
process_data({'wrong_key': 'value'})  # ✗ KeyError

# Insight: Code assumes 'key' always exists
# Fix: Add default or validation

Isolation Testing

Test components independently:

python
# Hypothesis: Function X works correctly in isolation
def test_function_x():
    input_data = {'key': 'value'}
    result = function_x(input_data)
    assert result == expected_value

# If test passes: Problem is in calling context or inputs
# If test fails: Problem is in function_x implementation

Logging Strategies

Log Level Guidelines

Level When to Use Example
DEBUG Detailed diagnosis, temporary logger.debug(f"Processing item {i}/{total}")
INFO Important events, milestones logger.info("MCP server started on port 8000")
WARNING Recoverable issues, deprecations logger.warning("Timeout, retrying...")
ERROR Errors that need attention logger.error("Failed to connect to DSIM")
CRITICAL System failure, requires immediate action logger.critical("License server unreachable")

Structured Logging

Include context in log messages:

python
# Good: Context-rich logging
logger.info(f"Test {test_name} compilation completed in {duration}s")

# Bad: Ambiguous logging  
logger.info("Compilation completed")

Async Code Logging

Log await points to trace execution:

python
async def process_request(request_id):
    logger.debug(f"[{request_id}] Starting")
    
    data = await fetch_data()
    logger.debug(f"[{request_id}] Data fetched")
    
    result = await process(data)
    logger.debug(f"[{request_id}] Processing complete")
    
    return result

MCP Server Debugging

Common MCP Issues

Pattern: "Method not found"

python
# Cause: Typo in method name or not registered
# Investigation: Check server tool registry, verify spelling

Pattern: "Connection refused"

python
# Cause: Server not running or wrong port
# Investigation: Check server process, verify port number

Pattern: "Timeout waiting for response"

python
# Cause: Server hung, processing too slow, timeout too short
# Investigation: Check server logs, increase timeout, add progress logging

MCP Debugging Workflow

  1. Verify server is running

    python
    # Check process list for MCP server
    # Check server startup logs
    
  2. Check request format

    python
    logger.debug(f"Sending request: {json.dumps(request, indent=2)}")
    
  3. Monitor server response

    python
    logger.debug(f"Received response: {json.dumps(response, indent=2)}")
    
  4. Validate JSON-RPC structure

    • Is "jsonrpc": "2.0" present?
    • Is "method" spelled correctly?
    • Are "params" in correct format?

Test Debugging

pytest Integration

Run test with debugger:

bash
pytest test_file.py::test_function --pdb
# Drops into pdb on first failure

Add breakpoint in test:

python
def test_processing():
    data = load_test_data()
    import pdb; pdb.set_trace()  # Inspect here
    result = process(data)
    assert result == expected

Debugging Test Isolation

Problem: Test passes alone, fails in suite

Investigation:

  • Check for shared state (module-level variables)
  • Check for fixture side effects
  • Run tests in different orders to isolate interaction

Common Pitfalls

Don't Ignore Warnings

python
# RuntimeWarning: coroutine 'func' was never awaited
# ↑ This WILL cause bugs, fix immediately

Don't Assume Exception Type

python
# Wrong: Catching generic exception
try:
    data = json.loads(text)
except:  # Catches everything, including KeyboardInterrupt
    pass

# Right: Catch specific exception
try:
    data = json.loads(text)
except json.JSONDecodeError as e:
    logger.error(f"Invalid JSON: {e}")

Don't Debug in Production

python
# Wrong: Leaving debug code in production
import pdb; pdb.set_trace()  # Will hang production server

# Right: Use logging with appropriate level
logger.debug("Debug information")  # Only in dev mode

Integration with Other Skills

  • mcp-workflow: Use when debugging MCP server integration issues
  • dsim-debugging: Coordinate when Python MCP server controls DSIM simulator
  • uvm-verification: Debug UVM test invocation from Python scripts

Summary

Python debugging is systematic problem-solving:

  1. Interpret error messages for exception type and context
  2. Read stack traces to understand execution path
  3. Reproduce with minimal test case
  4. Form hypotheses based on exception patterns
  5. Select appropriate debugging tool (print/pdb/logging)
  6. Test hypotheses systematically

Key principle: Understand the error, then investigate the cause. Don't guess—use evidence from stack traces, logs, and debugger inspection.

Didn't find tool you were looking for?

Be as detailed as possible for better results