How to Troubleshoot Issues
Task: Diagnose and fix common FailExtract problems quickly
This guide provides systematic troubleshooting steps for common issues, error messages, and unexpected behavior when using FailExtract.
Installation and Import Issues
Problem: “No module named ‘failextract’”
ImportError: No module named 'failextract'
Diagnosis Steps:
# Check if FailExtract is installed
pip list | grep failextract
# Check which Python interpreter you're using
which python
python --version
# Try importing in Python directly
python -c "import failextract; print('FailExtract available')"
Solutions:
# Install FailExtract
pip install failextract
# If using virtual environment, ensure it's activated
source venv/bin/activate # Linux/Mac
# venv\Scripts\activate # Windows
# Install in user directory if permission issues
pip install --user failextract
# Force reinstall if corrupted
pip uninstall failextract
pip install failextract
Problem: “ImportError: No module named ‘yaml’”
ImportError: No module named 'yaml'
Cause: YAML formatter requested but optional dependency not installed.
Solution:
# Install YAML support
pip install failextract[formatters]
# Or install PyYAML directly
pip install pyyaml
Alternative: Use other formats if YAML isn’t essential:
from failextract import OutputConfig, FailureExtractor
# Use JSON instead of YAML
config = OutputConfig("failures.json", format="json")
# Instead of: config = OutputConfig("failures.yaml", format="yaml")
Decorator and Capture Issues
Problem: “No failures captured” despite test failures
Diagnosis Steps:
# Check if decorator is properly applied
from failextract import extract_on_failure, FailureExtractor
@extract_on_failure
def test_debug():
assert False, "This should be captured"
# Run test and check
try:
test_debug()
except AssertionError:
pass
extractor = FailureExtractor()
print(f"Captured failures: {len(extractor.failures)}")
# If zero, decorator isn't working properly
Common Causes and Solutions:
Decorator not applied:
# Wrong - decorator missing def test_example(): assert False, "Not captured" # Correct - decorator applied @extract_on_failure def test_example(): assert False, "Will be captured"
Test doesn’t actually fail:
# Debug the test logic @extract_on_failure def test_debug(): result = some_function() print(f"Debug: result = {result}") # Add debug output assert result == expected_value, f"Expected {expected_value}, got {result}"
Exception handled before extraction:
# Problematic - exception caught @extract_on_failure def test_with_handler(): try: assert False, "This won't be captured" except AssertionError: pass # Exception handled, not extracted # Better - let extraction happen first @extract_on_failure def test_proper(): assert False, "This will be captured"
Problem: Extraction happens but no local variables captured
Diagnosis:
# Check decorator configuration
@extract_on_failure(include_locals=True) # Explicitly enable locals
def test_with_locals():
important_data = {"key": "value"}
assert False, "Check if important_data is captured"
Solution: Ensure include_locals=True (it’s the default, but verify):
@extract_on_failure(
include_locals=True, # Capture local variables
max_depth=10 # Ensure sufficient depth
)
def test_with_variables():
user_data = {"id": 123, "name": "Alice"}
config = {"timeout": 30}
assert False, "Both variables should be captured"
Report Generation Issues
Problem: “Permission denied” when saving reports
PermissionError: [Errno 13] Permission denied: 'failures.json'
Solutions:
import os
from pathlib import Path
from failextract import OutputConfig, FailureExtractor
# Check current directory permissions
print(f"Current directory: {os.getcwd()}")
print(f"Can write: {os.access('.', os.W_OK)}")
# Use alternative directory
reports_dir = Path("reports")
reports_dir.mkdir(exist_ok=True)
config = OutputConfig(str(reports_dir / "failures.json"))
# Or use temporary directory
import tempfile
with tempfile.TemporaryDirectory() as temp_dir:
config = OutputConfig(f"{temp_dir}/failures.json")
Problem: “No data available for operation”
Cause: Trying to generate report when no failures exist.
Solution:
extractor = FailureExtractor()
# Check before generating report
if extractor.failures:
config = OutputConfig("failures.json")
extractor.save_report(config)
print(f"Generated report with {len(extractor.failures)} failures")
else:
print("No failures to report")
Problem: Reports are empty or have unexpected content
Diagnosis:
# Debug the extraction process
extractor = FailureExtractor()
print(f"Total failures: {len(extractor.failures)}")
print(f"Total passed: {len(extractor.passed)}")
# Examine failure structure
if extractor.failures:
print("Sample failure:")
import json
print(json.dumps(extractor.failures[0], indent=2, default=str))
Problem: Format-specific errors
# Test each format individually
def test_formats():
extractor = FailureExtractor()
if not extractor.failures:
print("No failures to test with")
return
formats = ["json", "markdown", "xml", "csv"]
for fmt in formats:
try:
config = OutputConfig(f"test.{fmt}", format=fmt)
extractor.save_report(config)
print(f"✓ {fmt} format works")
except Exception as e:
print(f"✗ {fmt} format error: {e}")
Memory and Performance Issues
Problem: Excessive memory usage
Diagnosis:
from failextract import FailureExtractor
# Check memory usage
extractor = FailureExtractor()
stats = extractor.get_stats()
limits = extractor.get_memory_limits()
print(f"Memory usage:")
print(f" Failures: {stats['failures_count']}/{limits['max_failures']}")
print(f" Passed: {stats['passed_count']}/{limits['max_passed']}")
print(f" At limits: {stats['failures_at_limit']}, {stats['passed_at_limit']}")
Solutions:
# Set conservative memory limits
extractor.set_memory_limits(
max_failures=100, # Reduce from default
max_passed=50 # Reduce from default
)
# Clear data regularly
def run_test_batch():
# Run tests...
# Generate report and clear
if extractor.failures:
config = OutputConfig("batch_failures.json")
extractor.save_report(config)
extractor.clear()
Problem: Tests running slowly
Diagnosis:
import time
from failextract import extract_on_failure
# Measure overhead
def measure_decorator_overhead():
# Test without decorator
def baseline_test():
assert False, "Baseline"
# Test with decorator
@extract_on_failure
def decorated_test():
assert False, "Decorated"
# Time both
iterations = 100
# Baseline
start = time.time()
for _ in range(iterations):
try:
baseline_test()
except:
pass
baseline_time = time.time() - start
# Decorated
start = time.time()
for _ in range(iterations):
try:
decorated_test()
except:
pass
decorated_time = time.time() - start
overhead = (decorated_time / baseline_time - 1) * 100
print(f"Overhead: {overhead:.1f}%")
Solutions:
# Use performance-optimized configuration
@extract_on_failure(
include_locals=False, # Skip variables for speed
max_depth=3, # Minimal depth
skip_stdlib=True # Skip standard library frames
)
def optimized_test():
assert False, "Fast extraction"
Configuration and Environment Issues
Problem: Configuration not working as expected
Diagnosis:
from failextract import OutputConfig
# Test configuration parsing
def debug_config():
# Test different configurations
configs = [
OutputConfig("test.json"),
OutputConfig("test.yaml", format="yaml"),
OutputConfig("test.md", format="markdown"),
]
for config in configs:
print(f"File: {config.filename}")
print(f"Format: {config.format}")
print(f"Append: {config.append}")
print("---")
Problem: Environment-specific behavior
import os
# Debug environment detection
def debug_environment():
print("Environment variables:")
relevant_vars = ["CI", "PYTEST_CURRENT_TEST", "PYTHONPATH"]
for var in relevant_vars:
value = os.getenv(var)
print(f" {var}: {value}")
print(f"Current working directory: {os.getcwd()}")
print(f"Python path: {os.sys.path[:3]}...") # First 3 entries
Integration Issues
Problem: pytest integration not working
Diagnosis:
# Check if conftest.py is being loaded
# Add this to your conftest.py for debugging
print("conftest.py loaded")
# Check if hooks are being called
def pytest_runtest_makereport(item, call):
print(f"Hook called for: {item.name}, phase: {call.when}")
Problem: CI/CD integration failures
Common Issues and Solutions:
Path issues in CI:
# Debug paths in CI echo "Current directory: $(pwd)" echo "Python path: $PYTHONPATH" ls -la # Check file permissions # Generate reports with explicit paths failextract report --format json --output ./reports/failures.json
Missing dependencies in CI:
# .github/workflows/test.yml - name: Install dependencies with all extras run: | pip install failextract[formatters,cli] pip list | grep failextract # Verify installation
Timeout issues:
- name: Run tests with timeout run: | timeout 30m pytest --tb=short || true timeout 5m failextract report --format json --output failures.json
Problem: Report artifacts not uploading
# Debug artifact creation
- name: Debug report generation
run: |
failextract stats
ls -la *.json *.md 2>/dev/null || echo "No report files found"
- name: Generate reports with error handling
run: |
if ! failextract report --format json --output failures.json; then
echo "Failed to generate JSON report"
exit 1
fi
Error Message Decoder
Common Error Patterns:
# Error: "AttributeError: module 'failextract' has no attribute 'extract_on_failure'"
# Cause: Import error or wrong package
# Solution: pip install failextract
# Error: "ValueError: Invalid format 'invalid_format'"
# Cause: Unsupported output format specified
# Solution: Use json, markdown, xml, csv, or yaml
# Error: "TypeError: OutputConfig() missing 1 required positional argument"
# Cause: Filename not provided to OutputConfig
# Solution: config = OutputConfig("filename.json")
# Error: "RuntimeError: No data available"
# Cause: Trying to generate report with no failures
# Solution: Check if failures exist before generating report
Debugging Tools and Utilities
Create a Debug Test Suite
#!/usr/bin/env python3
"""FailExtract debugging utility"""
from failextract import extract_on_failure, FailureExtractor, OutputConfig
import json
import os
def run_debug_tests():
"""Run comprehensive debugging tests."""
print("FailExtract Debug Test Suite")
print("=" * 40)
# Test 1: Basic functionality
print("1. Testing basic functionality...")
@extract_on_failure
def debug_test():
test_data = {"value": 42}
assert test_data["value"] == 99, "Debug test assertion"
try:
debug_test()
except AssertionError:
pass
extractor = FailureExtractor()
print(f" Captured failures: {len(extractor.failures)}")
# Test 2: Report generation
print("2. Testing report generation...")
if extractor.failures:
formats = ["json", "markdown", "xml", "csv"]
for fmt in formats:
try:
config = OutputConfig(f"debug.{fmt}", format=fmt)
extractor.save_report(config)
print(f" ✓ {fmt} format works")
except Exception as e:
print(f" ✗ {fmt} format failed: {e}")
# Test 3: Memory management
print("3. Testing memory management...")
stats = extractor.get_stats()
limits = extractor.get_memory_limits()
print(f" Current usage: {stats['failures_count']} failures, {stats['passed_count']} passed")
print(f" Memory limits: {limits['max_failures']} failures, {limits['max_passed']} passed")
# Test 4: Configuration
print("4. Testing configuration...")
try:
config = OutputConfig("test.yaml", format="yaml")
print(" ✓ YAML configuration accepted")
except Exception as e:
print(f" ⚠ YAML not available: {e}")
print("\nDebug test complete!")
if __name__ == "__main__":
run_debug_tests()
Environment Information Script
#!/usr/bin/env python3
"""Collect environment information for troubleshooting"""
import sys
import os
import platform
def collect_environment_info():
"""Collect comprehensive environment information."""
print("FailExtract Environment Information")
print("=" * 50)
# Python information
print(f"Python version: {sys.version}")
print(f"Python executable: {sys.executable}")
print(f"Platform: {platform.platform()}")
# FailExtract information
try:
import failextract
print(f"FailExtract version: {failextract.__version__}")
print(f"FailExtract location: {failextract.__file__}")
except ImportError as e:
print(f"FailExtract import error: {e}")
# Dependencies
print("\nDependencies:")
try:
import yaml
print(" ✓ PyYAML available")
except ImportError:
print(" ✗ PyYAML not available")
try:
import rich
print(" ✓ Rich available")
except ImportError:
print(" ✗ Rich not available")
# Environment variables
print("\nRelevant environment variables:")
env_vars = ["CI", "PYTEST_CURRENT_TEST", "PYTHONPATH", "PATH"]
for var in env_vars:
value = os.getenv(var, "Not set")
print(f" {var}: {value}")
# File system
print(f"\nCurrent directory: {os.getcwd()}")
print(f"Directory writable: {os.access('.', os.W_OK)}")
if __name__ == "__main__":
collect_environment_info()
Getting Help
Escalation Steps:
Check this troubleshooting guide for common issues
Run debug utilities to gather information
Search existing issues in the project repository
Create a minimal reproduction case
Report the issue with environment information
Information to Include in Bug Reports:
- FailExtract version
- Python version and platform
- Minimal code example that reproduces the issue
- Full error message and traceback
- Environment information (CI/local, OS, etc.)
- Expected vs. actual behavior
Quick Health Check Script:
#!/usr/bin/env python3
"""Quick health check for FailExtract"""
def health_check():
"""Perform quick health check."""
checks = []
# Import test
try:
import failextract
checks.append(("Import", True, ""))
except Exception as e:
checks.append(("Import", False, str(e)))
return checks
# Basic functionality
try:
from failextract import extract_on_failure, FailureExtractor
@extract_on_failure
def test():
assert False, "Test"
try:
test()
except AssertionError:
pass
extractor = FailureExtractor()
if len(extractor.failures) > 0:
checks.append(("Capture", True, ""))
else:
checks.append(("Capture", False, "No failures captured"))
except Exception as e:
checks.append(("Capture", False, str(e)))
# Report generation
try:
from failextract import OutputConfig
config = OutputConfig("test.json")
extractor.save_report(config)
checks.append(("Reporting", True, ""))
except Exception as e:
checks.append(("Reporting", False, str(e)))
# Print results
print("FailExtract Health Check:")
for check_name, passed, error in checks:
status = "✓" if passed else "✗"
print(f" {status} {check_name}")
if error:
print(f" Error: {error}")
return all(check[1] for check in checks)
if __name__ == "__main__":
healthy = health_check()
exit(0 if healthy else 1)
Prevention Strategies
Best Practices to Avoid Issues:
Use virtual environments to avoid dependency conflicts
Pin versions in production environments
Test in CI before deploying
Monitor memory usage in long-running tests
Clear data regularly to prevent memory issues
Use proper error handling around report generation
Validate configurations before using them
Monitoring Setup:
def setup_monitoring():
"""Set up monitoring to catch issues early."""
extractor = FailureExtractor()
# Check memory usage periodically
stats = extractor.get_stats()
limits = extractor.get_memory_limits()
usage_percent = stats['failures_count'] / limits['max_failures'] * 100
if usage_percent > 80:
print(f"⚠ High memory usage: {usage_percent:.1f}%")
# Could trigger alert or automatic cleanup
You now have comprehensive troubleshooting capabilities for any FailExtract issue!