Coverage for src / tracekit / core / debug.py: 95%
67 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 23:04 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-11 23:04 +0000
1"""Debug mode and verbosity control infrastructure.
3This module provides programmatic debug mode control with multiple
4verbosity levels for troubleshooting and diagnostics.
7Example:
8 >>> from tracekit.core.debug import enable_debug, is_debug_enabled, debug_context
9 >>> enable_debug(level='verbose')
10 >>> if is_debug_enabled():
11 ... logger.debug("Extra diagnostic information")
12 >>> with debug_context(level='trace'):
13 ... # Temporarily increase verbosity
14 ... analyze_complex_signal()
16References:
17 - Debugging and diagnostic best practices
18 - Python logging levels integration
19"""
21from __future__ import annotations
23import contextvars
24import logging
25import os
26from enum import IntEnum
27from typing import Any, Literal
29# Context variable for debug state
30_debug_level: contextvars.ContextVar[int] = contextvars.ContextVar("debug_level", default=0)
33class DebugLevel(IntEnum):
34 """Debug verbosity levels.
36 Levels:
37 DISABLED: No debug output (0)
38 MINIMAL: Basic debug info (1)
39 NORMAL: Standard debug info (2)
40 VERBOSE: Detailed debug info (3)
41 TRACE: Very detailed trace info (4)
43 References:
44 LOG-007: Programmatic Debug Mode
45 """
47 DISABLED = 0
48 MINIMAL = 1
49 NORMAL = 2
50 VERBOSE = 3
51 TRACE = 4
54# String to enum mapping
55_LEVEL_MAP: dict[str, DebugLevel] = {
56 "disabled": DebugLevel.DISABLED,
57 "minimal": DebugLevel.MINIMAL,
58 "normal": DebugLevel.NORMAL,
59 "verbose": DebugLevel.VERBOSE,
60 "trace": DebugLevel.TRACE,
61}
64def enable_debug(
65 level: Literal["minimal", "normal", "verbose", "trace"] = "normal",
66) -> None:
67 """Enable debug mode with specified verbosity level.
69 Sets the global debug level and adjusts logging configuration
70 accordingly. Higher levels include all output from lower levels.
72 Args:
73 level: Debug verbosity level.
75 Example:
76 >>> enable_debug(level='verbose')
77 >>> # Now all debug logging at verbose level is active
79 >>> enable_debug(level='trace')
80 >>> # Extremely detailed trace logging
82 References:
83 LOG-007: Programmatic Debug Mode
84 """
85 debug_level = _LEVEL_MAP[level]
86 _debug_level.set(debug_level)
88 # Adjust logging level based on debug level
89 from tracekit.core.logging import set_log_level
91 if debug_level == DebugLevel.TRACE:
92 set_log_level("DEBUG") # Most verbose
93 elif debug_level >= DebugLevel.NORMAL:
94 set_log_level("DEBUG")
95 elif debug_level == DebugLevel.MINIMAL: 95 ↛ 98line 95 didn't jump to line 98 because the condition on line 95 was always true
96 set_log_level("INFO")
97 else:
98 set_log_level("WARNING")
101def disable_debug() -> None:
102 """Disable debug mode.
104 Resets debug level to DISABLED and sets logging to WARNING.
106 Example:
107 >>> disable_debug()
108 >>> # Debug output is now suppressed
110 References:
111 LOG-007: Programmatic Debug Mode
112 """
113 _debug_level.set(DebugLevel.DISABLED)
115 from tracekit.core.logging import set_log_level
117 set_log_level("WARNING")
120def is_debug_enabled(min_level: DebugLevel = DebugLevel.MINIMAL) -> bool:
121 """Check if debug mode is enabled at or above the specified level.
123 Args:
124 min_level: Minimum debug level to check for.
126 Returns:
127 True if debug is enabled at or above min_level.
129 Example:
130 >>> enable_debug(level='verbose')
131 >>> is_debug_enabled()
132 True
133 >>> is_debug_enabled(DebugLevel.TRACE)
134 False
136 References:
137 LOG-007: Programmatic Debug Mode
138 """
139 current = _debug_level.get()
140 return current >= min_level
143def get_debug_level() -> DebugLevel:
144 """Get the current debug level.
146 Returns:
147 Current debug level enum value.
149 Example:
150 >>> level = get_debug_level()
151 >>> if level >= DebugLevel.VERBOSE:
152 ... print("Verbose debugging enabled")
154 References:
155 LOG-007: Programmatic Debug Mode
156 """
157 return DebugLevel(_debug_level.get())
160class debug_context:
161 """Context manager for temporary debug level changes.
163 Temporarily sets a debug level for the duration of a code block,
164 then restores the previous level.
166 Args:
167 level: Debug level to set within the context.
169 Example:
170 >>> # Normal debug level
171 >>> with debug_context(level='trace'):
172 ... # Temporarily enable trace-level debugging
173 ... analyze_signal(complex_data)
174 >>> # Back to normal debug level
176 References:
177 LOG-007: Programmatic Debug Mode
178 """
180 def __init__(
181 self,
182 level: Literal["disabled", "minimal", "normal", "verbose", "trace"],
183 ):
184 """Initialize debug context.
186 Args:
187 level: Debug level to set within the context.
188 """
189 self.level = _LEVEL_MAP[level]
190 self.token: contextvars.Token | None = None # type: ignore[type-arg]
191 self.previous_log_level: str | None = None
193 def __enter__(self) -> debug_context:
194 """Enter the debug context and set new level."""
195 # Save current debug level
196 self.token = _debug_level.set(self.level)
198 # Adjust logging
199 from tracekit.core.logging import get_logger
201 root_logger = get_logger("tracekit")
202 self.previous_log_level = logging.getLevelName(root_logger.level)
204 # Set appropriate log level for debug level
205 from tracekit.core.logging import set_log_level
207 if self.level in (DebugLevel.TRACE, DebugLevel.VERBOSE, DebugLevel.NORMAL):
208 set_log_level("DEBUG")
209 elif self.level == DebugLevel.MINIMAL:
210 set_log_level("INFO")
211 else:
212 set_log_level("WARNING")
214 return self
216 def __exit__(self, *args: Any) -> None:
217 """Exit the debug context and restore previous level."""
218 # Restore debug level
219 if self.token: 219 ↛ 223line 219 didn't jump to line 223 because the condition on line 219 was always true
220 _debug_level.reset(self.token)
222 # Restore logging level
223 if self.previous_log_level: 223 ↛ exitline 223 didn't return from function '__exit__' because the condition on line 223 was always true
224 from tracekit.core.logging import set_log_level
226 set_log_level(self.previous_log_level)
229def should_log_debug(min_level: DebugLevel = DebugLevel.NORMAL) -> bool:
230 """Check if debug logging should occur at specified level.
232 Helper function for conditional debug logging.
234 Args:
235 min_level: Minimum level required for logging.
237 Returns:
238 True if current debug level meets or exceeds min_level.
240 Example:
241 >>> if should_log_debug(DebugLevel.VERBOSE):
242 ... logger.debug("Detailed diagnostic: %s", expensive_computation())
244 References:
245 LOG-007: Programmatic Debug Mode
246 """
247 return is_debug_enabled(min_level)
250def configure_debug_from_env() -> None:
251 """Configure debug mode from environment variables.
253 Reads TRACEKIT_DEBUG environment variable and sets debug level
254 accordingly.
256 Environment Variables:
257 TRACEKIT_DEBUG: Debug level (minimal, normal, verbose, trace)
259 Example:
260 >>> import os
261 >>> os.environ['TRACEKIT_DEBUG'] = 'verbose'
262 >>> configure_debug_from_env()
264 References:
265 LOG-007: Programmatic Debug Mode
266 """
267 debug_env = os.environ.get("TRACEKIT_DEBUG", "").lower()
268 if debug_env in _LEVEL_MAP:
269 enable_debug(level=debug_env) # type: ignore[arg-type]
272def debug_log(
273 logger: logging.Logger,
274 message: str,
275 min_level: DebugLevel = DebugLevel.NORMAL,
276 **kwargs: Any,
277) -> None:
278 """Conditionally log debug message based on debug level.
280 Only logs if current debug level meets or exceeds min_level.
282 Args:
283 logger: Logger to use for output.
284 message: Message to log.
285 min_level: Minimum debug level required.
286 **kwargs: Additional keyword arguments for logger.
288 Example:
289 >>> from tracekit.core.logging import get_logger
290 >>> logger = get_logger(__name__)
291 >>> debug_log(logger, "Processing FFT", DebugLevel.VERBOSE, samples=1000)
293 References:
294 LOG-007: Programmatic Debug Mode
295 """
296 if is_debug_enabled(min_level):
297 logger.debug(message, **kwargs)
300# Auto-configure from environment on import
301configure_debug_from_env()