Ojasa Mirai

Ojasa Mirai

Python

Loading...

Learning Level

🟢 Beginner🔵 Advanced
Exceptions OverviewException TypesTry-Except BlocksRaising ExceptionsCustom ExceptionsMultiple ExceptionsFinally & CleanupDebugging TechniquesLogging Best Practices
Python/Error Handling/Debugging Techniques

🐛 Debugging Techniques — Advanced Professional Methods

Master production debugging, profiling, and diagnostic tools.


🔍 Advanced Traceback Analysis

# Comprehensive traceback inspection
import sys
import traceback
import linecache

class AdvancedTraceback:
    """Advanced traceback analysis"""

    @staticmethod
    def print_detailed_traceback():
        """Print detailed traceback with locals"""
        exc_type, exc_value, exc_traceback = sys.exc_info()

        print(f"Exception: {exc_type.__name__}")
        print(f"Message: {exc_value}")
        print("\nTraceback with local variables:")

        tb = exc_traceback
        frame_count = 0

        while tb is not None:
            frame = tb.tb_frame
            frame_count += 1

            print(f"\n--- Frame {frame_count} ---")
            print(f"File: {frame.f_code.co_filename}")
            print(f"Function: {frame.f_code.co_name}")
            print(f"Line: {tb.tb_lineno}")

            # Get source code
            code = linecache.getline(frame.f_code.co_filename, tb.tb_lineno).strip()
            print(f"Code: {code}")

            # Print local variables (non-private)
            print("Local variables:")
            for var_name, var_value in frame.f_locals.items():
                if not var_name.startswith('_'):
                    try:
                        var_repr = repr(var_value)
                        if len(var_repr) > 100:
                            var_repr = var_repr[:100] + "..."
                        print(f"  {var_name} = {var_repr}")
                    except Exception as e:
                        print(f"  {var_name} = <error: {e}>")

            tb = tb.tb_next

    @staticmethod
    def get_exception_chain():
        """Get full exception chain"""
        exc_type, exc_value, exc_traceback = sys.exc_info()
        chain = []

        current_exc = exc_value
        while current_exc:
            chain.append({
                'type': type(current_exc).__name__,
                'message': str(current_exc),
                'traceback': traceback.format_tb(current_exc.__traceback__)
            })
            current_exc = current_exc.__cause__ or current_exc.__context__

        return chain

# Usage
try:
    try:
        x = 1 / 0
    except ZeroDivisionError as e:
        raise ValueError("Math error") from e
except Exception:
    AdvancedTraceback.print_detailed_traceback()
    print("\nException chain:")
    for exc_info in AdvancedTraceback.get_exception_chain():
        print(f"  {exc_info['type']}: {exc_info['message']}")

📊 Profiling and Performance Debugging

# Advanced profiling
import cProfile
import pstats
from io import StringIO

class PerformanceProfiler:
    """Profile code performance"""

    def __init__(self):
        self.profiler = cProfile.Profile()

    def profile_function(self, func, *args, **kwargs):
        """Profile a function execution"""
        self.profiler.enable()
        result = func(*args, **kwargs)
        self.profiler.disable()
        return result

    def get_stats(self, sort_by='cumulative'):
        """Get profiling statistics"""
        s = StringIO()
        ps = pstats.Stats(self.profiler, stream=s).sort_stats(sort_by)
        ps.print_stats(10)  # Top 10
        return s.getvalue()

# Usage
profiler = PerformanceProfiler()

def slow_function():
    result = []
    for i in range(1000000):
        result.append(i * 2)
    return sum(result)

profiler.profile_function(slow_function)
print(profiler.get_stats())

# Memory profiling
from memory_profiler import profile

@profile
def memory_intensive_function():
    """Trace memory usage"""
    large_list = list(range(1000000))
    large_dict = {i: i*2 for i in range(100000)}
    return len(large_list) + len(large_dict)

# Line-by-line profiling
from line_profiler import LineProfiler

profiler = LineProfiler()

def function_to_profile():
    total = 0
    for i in range(1000):
        total += i
    return total

profiler.add_function(function_to_profile)
profiler.enable()
function_to_profile()
profiler.disable()
profiler.print_stats()

🎯 Interactive Debugging with pdb

# Advanced pdb usage
import pdb

def advanced_debug():
    """Use pdb for interactive debugging"""
    data = [1, 2, 3, 4, 5]
    pdb.set_trace()  # Execution pauses here
    # Interactive commands:
    # p data - print variable
    # pp data - pretty print
    # l - list code
    # n - next line
    # s - step into function
    # c - continue
    # w - print stack
    # h - help
    # j 10 - jump to line 10
    # b 10 - set breakpoint at line 10
    # cl - clear all breakpoints
    # a - print function arguments
    # whatis data - type of variable
    # interact - start interactive interpreter
    result = sum(data)
    return result

# Post-mortem debugging
def post_mortem_debug():
    """Debug after exception"""
    try:
        x = 1 / 0
    except Exception:
        # Debug the exception
        pdb.post_mortem()  # Opens debugger at exception point

# Conditional breakpoints
def conditional_debugging():
    """Break only on specific conditions"""
    for i in range(1000):
        if i == 500:
            pdb.set_trace()  # Only stop at i=500
        print(i)

# Debugger commands as code
import sys

def programmatic_debug():
    """Set breakpoints programmatically"""
    debugger = pdb.Pdb()
    sys.set_trace = debugger.set_trace

    # Set breakpoint
    pdb.set_trace()

🔍 Distributed Tracing

# Trace requests across systems
import uuid
import functools
from datetime import datetime

class RequestTracer:
    """Trace requests across application"""

    _trace_context = {}

    @classmethod
    def start_trace(cls, request_id=None):
        """Start a new trace"""
        trace_id = request_id or str(uuid.uuid4())
        cls._trace_context = {
            'trace_id': trace_id,
            'spans': [],
            'start_time': datetime.now()
        }
        return trace_id

    @classmethod
    def create_span(cls, operation_name):
        """Create a span within trace"""
        return RequestTracer.Span(operation_name)

    class Span:
        """Represents operation within trace"""
        def __init__(self, name):
            self.name = name
            self.start_time = datetime.now()
            self.duration = None
            self.tags = {}
            self.logs = []

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            self.duration = (datetime.now() - self.start_time).total_seconds()
            RequestTracer._trace_context['spans'].append(self.__dict__)

        def tag(self, key, value):
            """Add tag to span"""
            self.tags[key] = value
            return self

        def log(self, message):
            """Log message in span"""
            self.logs.append({'time': datetime.now(), 'message': message})

    @classmethod
    def get_trace(cls):
        """Get full trace information"""
        return cls._trace_context

# Usage
trace_id = RequestTracer.start_trace()

with RequestTracer.create_span('api_call') as span:
    span.tag('endpoint', '/users')
    span.tag('method', 'GET')
    # Simulate work
    time.sleep(0.1)

with RequestTracer.create_span('database_query') as span:
    span.tag('query', 'SELECT * FROM users')
    # Simulate work
    time.sleep(0.2)

print(RequestTracer.get_trace())

🔐 Logging Instrumentation

# Advanced logging for debugging
import logging
import functools
import inspect

class DebuggingLogger:
    """Logger for debugging complex flows"""

    def __init__(self, name):
        self.logger = logging.getLogger(name)
        self.call_stack = []

    def debug_function(self, func):
        """Decorator to debug function calls"""
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            func_name = func.__name__
            self.call_stack.append(func_name)

            # Log entry
            indent = "  " * (len(self.call_stack) - 1)
            self.logger.debug(f"{indent}→ {func_name}()")

            # Log arguments
            sig = inspect.signature(func)
            bound_args = sig.bind(*args, **kwargs)
            bound_args.apply_defaults()
            for param_name, param_value in bound_args.arguments.items():
                self.logger.debug(f"{indent}  {param_name}={repr(param_value)[:50]}")

            try:
                result = func(*args, **kwargs)
                self.logger.debug(f"{indent}← {func_name}() = {repr(result)[:50]}")
                return result
            except Exception as e:
                self.logger.error(
                    f"{indent}✗ {func_name}() raised {type(e).__name__}: {e}",
                    exc_info=True
                )
                raise
            finally:
                self.call_stack.pop()

        return wrapper

# Usage
logger = DebuggingLogger('myapp')

@logger.debug_function
def process_data(data):
    return sum(data)

@logger.debug_function
def main():
    return process_data([1, 2, 3])

main()

🎨 Assertion-Based Testing and Debugging

# Use assertions for debugging
def debug_with_assertions(data):
    """Use assertions to verify assumptions"""
    assert isinstance(data, list), f"Expected list, got {type(data)}"
    assert len(data) > 0, "Data cannot be empty"
    assert all(isinstance(x, int) for x in data), "All items must be integers"

    # Process with confidence
    return sum(data)

# Assertion context manager
class AssertionContext:
    """Context manager for assertions"""

    def __init__(self, condition, message="Assertion failed"):
        self.condition = condition
        self.message = message

    def __enter__(self):
        assert self.condition, self.message
        return self

    def __exit__(self, *args):
        pass

# Usage
with AssertionContext(len(data) > 0, "Cannot process empty data"):
    process(data)

# Debug information decorator
def with_debug_info(func):
    """Decorator to include debug info on error"""
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception as e:
            # Add debug context
            debug_info = {
                'function': func.__name__,
                'args': str(args)[:100],
                'kwargs': str(kwargs)[:100],
                'locals': {
                    k: str(v)[:50]
                    for k, v in wrapper.__wrapped__.__code__.co_varnames
                }
            }
            e.debug_info = debug_info
            raise
    return wrapper

🎯 Key Takeaways

  • ✅ Use advanced traceback analysis for debugging
  • ✅ Profile code to identify performance issues
  • ✅ Use pdb for interactive debugging
  • ✅ Implement distributed tracing for complex systems
  • ✅ Use logging decorators to trace function calls
  • ✅ Leverage assertions for validation during development
  • ✅ Capture context information with exceptions


Resources

Python Docs

Ojasa Mirai

Master AI-powered development skills through structured learning, real projects, and verified credentials. Whether you're upskilling your team or launching your career, we deliver the skills companies actually need.

Learn Deep • Build Real • Verify Skills • Launch Forward

Courses

PythonFastapiReactJSCloud

© 2026 Ojasa Mirai. All rights reserved.

TwitterGitHubLinkedIn