@@ -470,9 +470,10 @@ which you can use to judge whether the data is sufficient for your analysis.
470470Profiling modes
471471===============
472472
473- The sampling profiler supports three modes that control which samples are
473+ The sampling profiler supports four modes that control which samples are
474474recorded. The mode determines what the profile measures: total elapsed time,
475- CPU execution time, or time spent holding the global interpreter lock.
475+ CPU execution time, time spent holding the global interpreter lock, or
476+ exception handling.
476477
477478
478479Wall-clock mode
@@ -553,6 +554,67 @@ single-threaded programs to distinguish Python execution time from time spent
553554in C extensions or I/O.
554555
555556
557+ Exception mode
558+ --------------
559+
560+ Exception mode (``--mode=exception ``) records samples only when a thread has
561+ an active exception::
562+
563+ python -m profiling.sampling run --mode=exception script.py
564+
565+ Samples are recorded in two situations: when an exception is being propagated
566+ up the call stack (after ``raise `` but before being caught), or when code is
567+ executing inside an ``except `` block where exception information is still
568+ present in the thread state.
569+
570+ The following example illustrates which code regions are captured:
571+
572+ .. code-block :: python
573+
574+ def example ():
575+ try :
576+ raise ValueError (" error" ) # Captured: exception being raised
577+ except ValueError :
578+ process_error() # Captured: inside except block
579+ finally :
580+ cleanup() # NOT captured: exception already handled
581+
582+ def example_propagating ():
583+ try :
584+ try :
585+ raise ValueError (" error" )
586+ finally :
587+ cleanup() # Captured: exception propagating through
588+ except ValueError :
589+ pass
590+
591+ def example_no_exception ():
592+ try :
593+ do_work()
594+ finally :
595+ cleanup() # NOT captured: no exception involved
596+
597+ Note that ``finally `` blocks are only captured when an exception is actively
598+ propagating through them. Once an ``except `` block finishes executing, Python
599+ clears the exception information before running any subsequent ``finally ``
600+ block. Similarly, ``finally `` blocks that run during normal execution (when no
601+ exception was raised) are not captured because no exception state is present.
602+
603+ This mode is useful for understanding where your program spends time handling
604+ errors. Exception handling can be a significant source of overhead in code
605+ that uses exceptions for flow control (such as ``StopIteration `` in iterators)
606+ or in applications that process many error conditions (such as network servers
607+ handling connection failures).
608+
609+ Exception mode helps answer questions like "how much time is spent handling
610+ exceptions?" and "which exception handlers are the most expensive?" It can
611+ reveal hidden performance costs in code that catches and processes many
612+ exceptions, even when those exceptions are handled gracefully. For example,
613+ if a parsing library uses exceptions internally to signal format errors, this
614+ mode will capture time spent in those handlers even if the calling code never
615+ sees the exceptions.
616+
617+
556618Output formats
557619==============
558620
@@ -1006,8 +1068,9 @@ Mode options
10061068
10071069.. option :: --mode <mode >
10081070
1009- Sampling mode: ``wall `` (default), ``cpu ``, or ``gil ``.
1010- The ``cpu `` and ``gil `` modes are incompatible with ``--async-aware ``.
1071+ Sampling mode: ``wall `` (default), ``cpu ``, ``gil ``, or ``exception ``.
1072+ The ``cpu ``, ``gil ``, and ``exception `` modes are incompatible with
1073+ ``--async-aware ``.
10111074
10121075.. option :: --async-mode <mode >
10131076
0 commit comments