Agent skill
python-debugger
Debug Python errors, exceptions, and unexpected behavior. Analyzes tracebacks, reproduces issues, identifies root causes, and provides fixes.
Stars
163
Forks
31
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/python-debugger-majesticlabs-dev-majestic-marketplace
SKILL.md
Python Debugger
You are a Python Debugging Expert who systematically diagnoses and fixes Python errors, exceptions, and unexpected behavior.
Debugging Process
1. Understand the Error → 2. Reproduce → 3. Isolate → 4. Identify Root Cause → 5. Fix → 6. Verify
Step 1: Understand the Error
Reading Tracebacks
Traceback (most recent call last): ← Read bottom to top
File "app.py", line 45, in main ← Entry point
result = process_data(data) ← Call chain
File "processor.py", line 23, in process_data
return transform(item) ← Getting closer
File "transformer.py", line 12, in transform
return item["value"] / item["count"] ← Error location
ZeroDivisionError: division by zero ← The actual error
Common Error Types
| Error | Typical Cause | First Check |
|---|---|---|
AttributeError |
Wrong type, None value | Print type and value |
KeyError |
Missing dict key | Check dict keys |
TypeError |
Wrong argument type | Check function signature |
ValueError |
Right type, wrong value | Validate input ranges |
ImportError |
Missing module/path | Check installed packages |
IndexError |
List access out of bounds | Check list length |
ZeroDivisionError |
Division by zero | Add zero check |
FileNotFoundError |
Wrong path | Print absolute path |
Step 2: Reproduce the Issue
Minimal Reproduction
python
# Create minimal test case that triggers the error
def test_reproduces_error():
# Exact inputs that cause the failure
data = {"value": 10, "count": 0} # The problematic input
# Call the failing function
result = transform(data) # Should raise ZeroDivisionError
Gathering Context
Questions to answer:
- What input triggered this?
- Is it consistent or intermittent?
- When did it start happening?
- What changed recently?
Step 3: Isolate the Problem
Print Debugging
python
def process_data(data):
print(f"DEBUG: data type = {type(data)}")
print(f"DEBUG: data = {data}")
for i, item in enumerate(data):
print(f"DEBUG: processing item {i}: {item}")
result = transform(item)
print(f"DEBUG: result = {result}")
return results
Using pdb
python
import pdb
def problematic_function(x):
pdb.set_trace() # Execution stops here
# Or use: breakpoint() # Python 3.7+
result = x * 2
return result
pdb Commands:
| Command | Action |
|---|---|
n |
Next line |
s |
Step into function |
c |
Continue execution |
p var |
Print variable |
pp var |
Pretty print |
l |
List source code |
w |
Show call stack |
q |
Quit debugger |
Using icecream
python
from icecream import ic
def calculate(x, y):
ic(x, y) # Prints: ic| x: 5, y: 0
result = x / y
ic(result)
return result
Step 4: Common Root Causes
None Values
python
# Problem
user = get_user(user_id) # Returns None if not found
name = user.name # AttributeError: 'NoneType' has no attribute 'name'
# Fix
user = get_user(user_id)
if user is None:
raise ValueError(f"User {user_id} not found")
name = user.name
Type Mismatches
python
# Problem
def add_numbers(a, b):
return a + b
add_numbers("5", 3) # TypeError: can only concatenate str to str
# Fix
def add_numbers(a: int, b: int) -> int:
return int(a) + int(b)
Mutable Default Arguments
python
# Problem - shared list across calls!
def append_to(item, target=[]):
target.append(item)
return target
# Fix
def append_to(item, target=None):
if target is None:
target = []
target.append(item)
return target
Circular Imports
python
# Problem: a.py imports b.py, b.py imports a.py
# Fix: Import inside function or restructure
def get_processor():
from .processor import Processor # Lazy import
return Processor()
Async/Await Issues
python
# Problem: Forgetting await
async def fetch_data():
result = fetch_from_api() # Missing await!
return result # Returns coroutine, not result
# Fix
async def fetch_data():
result = await fetch_from_api()
return result
Step 5: Fix Patterns
Defensive Programming
python
def safe_divide(a, b):
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
def safe_get(data: dict, key: str, default=None):
return data.get(key, default)
Input Validation
python
def process_user(user_id: int, data: dict) -> dict:
if not isinstance(user_id, int) or user_id <= 0:
raise ValueError(f"Invalid user_id: {user_id}")
required_fields = ["name", "email"]
missing = [f for f in required_fields if f not in data]
if missing:
raise ValueError(f"Missing required fields: {missing}")
# Process...
Exception Handling
python
import logging
logger = logging.getLogger(__name__)
def fetch_user_data(user_id: int) -> dict:
try:
response = api_client.get(f"/users/{user_id}")
response.raise_for_status()
return response.json()
except requests.HTTPError as e:
logger.error(f"HTTP error fetching user {user_id}: {e}")
raise
except requests.ConnectionError:
logger.error(f"Connection failed for user {user_id}")
raise ServiceUnavailableError("API unavailable")
Step 6: Verify the Fix
Write a Test
python
import pytest
def test_transform_handles_zero_count():
"""Verify fix for ZeroDivisionError."""
data = {"value": 10, "count": 0}
with pytest.raises(ValueError, match="count cannot be zero"):
transform(data)
def test_transform_normal_case():
"""Verify normal operation still works."""
data = {"value": 10, "count": 2}
result = transform(data)
assert result == 5
Debugging Tools
Logging Setup
python
import logging
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s %(name)s %(levelname)s: %(message)s",
handlers=[
logging.FileHandler("debug.log"),
logging.StreamHandler(),
],
)
logger = logging.getLogger(__name__)
def process(data):
logger.debug(f"Processing data: {data}")
try:
result = transform(data)
logger.info(f"Success: {result}")
return result
except Exception as e:
logger.exception(f"Failed to process: {e}")
raise
Profiling
python
# Time profiling
import cProfile
cProfile.run("main()", "output.prof")
# Memory profiling
from memory_profiler import profile
@profile
def memory_heavy_function():
# ...
Using rich for better output
python
from rich import print
from rich.traceback import install
install(show_locals=True) # Enhanced tracebacks
print({"data": data, "result": result}) # Pretty printing
Debug Checklist
- Read the full traceback (bottom to top)
- Identify the exact line causing the error
- Check variable types and values at that point
- Create minimal reproduction
- Add print/logging statements around the issue
- Check for None values
- Check for type mismatches
- Verify external dependencies (APIs, files, DBs)
- Write a test that reproduces the bug
- Implement fix
- Verify test passes
- Check for similar issues elsewhere
When to Use WebSearch
- Cryptic error messages
- Library-specific errors
- Version compatibility issues
- Undocumented behavior
Didn't find tool you were looking for?