updated panel display
This commit is contained in:
+116
-26
@@ -5,7 +5,7 @@ import inspect
|
||||
import logging
|
||||
import threading
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Any, Optional, Sequence
|
||||
|
||||
from SYS.rich_display import console_for
|
||||
|
||||
@@ -43,6 +43,106 @@ def is_debug_enabled() -> bool:
|
||||
return _DEBUG_ENABLED
|
||||
|
||||
|
||||
def _debug_output_suppressed() -> bool:
|
||||
try:
|
||||
stderr_name = getattr(sys.stderr, "name", "")
|
||||
return "nul" in str(stderr_name).lower() or "/dev/null" in str(stderr_name)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _debug_output_file(file=None):
|
||||
stream = get_thread_stream()
|
||||
if stream is not None:
|
||||
return stream
|
||||
if file is not None:
|
||||
return file
|
||||
return sys.stderr
|
||||
|
||||
|
||||
def _is_rich_renderable(value: Any) -> bool:
|
||||
if value is None:
|
||||
return False
|
||||
if isinstance(value, (str, bytes, bytearray)):
|
||||
return False
|
||||
return bool(
|
||||
hasattr(value, "__rich_console__")
|
||||
or hasattr(value, "__rich__")
|
||||
or value.__class__.__module__.startswith("rich.")
|
||||
)
|
||||
|
||||
|
||||
def _caller_location(depth: int = 1) -> tuple[str, str]:
|
||||
frame = inspect.currentframe()
|
||||
current = frame
|
||||
try:
|
||||
for _ in range(max(0, int(depth))):
|
||||
if current is None:
|
||||
break
|
||||
current = current.f_back
|
||||
|
||||
if current is None:
|
||||
return "", ""
|
||||
|
||||
return Path(current.f_code.co_filename).stem, current.f_code.co_name
|
||||
finally:
|
||||
del frame
|
||||
|
||||
|
||||
def _debug_db_log(*, caller_name: str, message: str, level: str = "DEBUG") -> None:
|
||||
if not _DB_LOGGER:
|
||||
return
|
||||
try:
|
||||
_DB_LOGGER(level, caller_name, message)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def debug_panel(
|
||||
title: str,
|
||||
rows: Sequence[tuple[str, Any]],
|
||||
*,
|
||||
file=None,
|
||||
border_style: str = "cyan",
|
||||
) -> None:
|
||||
"""Render a compact key/value debug panel when debug logging is enabled."""
|
||||
if not _DEBUG_ENABLED or _debug_output_suppressed():
|
||||
return
|
||||
|
||||
target_file = _debug_output_file(file)
|
||||
try:
|
||||
from rich.panel import Panel
|
||||
from rich.table import Table as RichTable
|
||||
|
||||
grid = RichTable.grid(padding=(0, 1))
|
||||
grid.add_column("Key", style="cyan", no_wrap=True)
|
||||
grid.add_column("Value")
|
||||
for key, val in rows:
|
||||
try:
|
||||
grid.add_row(str(key), str(val))
|
||||
except Exception:
|
||||
grid.add_row(str(key), "<unprintable>")
|
||||
|
||||
debug(
|
||||
Panel(
|
||||
grid,
|
||||
title=str(title or "Debug"),
|
||||
expand=False,
|
||||
border_style=border_style,
|
||||
),
|
||||
file=target_file,
|
||||
)
|
||||
|
||||
file_name, func_name = _caller_location(depth=2)
|
||||
caller_name = f"{file_name}.{func_name}" if file_name and func_name else ""
|
||||
_debug_db_log(
|
||||
caller_name=caller_name,
|
||||
message=f"[{title}] " + "; ".join(f"{k}={v}" for k, v in rows),
|
||||
)
|
||||
except Exception:
|
||||
debug(title, list(rows), file=target_file)
|
||||
|
||||
|
||||
def debug(*args, **kwargs) -> None:
|
||||
"""Print debug message if debug logging is enabled.
|
||||
|
||||
@@ -53,26 +153,24 @@ def debug(*args, **kwargs) -> None:
|
||||
|
||||
# Check if stderr has been redirected to /dev/null (quiet mode)
|
||||
# If so, skip output to avoid queuing in background worker's capture
|
||||
try:
|
||||
stderr_name = getattr(sys.stderr, "name", "")
|
||||
if "nul" in str(stderr_name).lower() or "/dev/null" in str(stderr_name):
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
if _debug_output_suppressed():
|
||||
return
|
||||
|
||||
# Check for thread-local stream first
|
||||
stream = get_thread_stream()
|
||||
if stream:
|
||||
kwargs["file"] = stream
|
||||
# Set default to stderr for debug messages
|
||||
elif "file" not in kwargs:
|
||||
kwargs["file"] = sys.stderr
|
||||
target_file = _debug_output_file(kwargs.pop("file", None))
|
||||
|
||||
if len(args) == 1 and _is_rich_renderable(args[0]):
|
||||
renderable = args[0]
|
||||
console_for(target_file).print(renderable)
|
||||
file_name, func_name = _caller_location(depth=1)
|
||||
caller_name = f"{file_name}.{func_name}" if file_name and func_name else ""
|
||||
_debug_db_log(caller_name=caller_name, message=f"<rich:{type(renderable).__name__}>")
|
||||
return
|
||||
|
||||
# Prepend DEBUG label
|
||||
args = ("DEBUG:", *args)
|
||||
|
||||
# Use the same logic as log()
|
||||
log(*args, **kwargs)
|
||||
log(*args, file=target_file, **kwargs)
|
||||
|
||||
|
||||
def debug_inspect(
|
||||
@@ -97,19 +195,11 @@ def debug_inspect(
|
||||
return
|
||||
|
||||
# Mirror debug() quiet-mode guard.
|
||||
try:
|
||||
stderr_name = getattr(sys.stderr, "name", "")
|
||||
if "nul" in str(stderr_name).lower() or "/dev/null" in str(stderr_name):
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
if _debug_output_suppressed():
|
||||
return
|
||||
|
||||
# Resolve destination stream.
|
||||
stream = get_thread_stream()
|
||||
if stream is not None:
|
||||
file = stream
|
||||
elif file is None:
|
||||
file = sys.stderr
|
||||
file = _debug_output_file(file)
|
||||
|
||||
# Compute caller prefix (same as log()).
|
||||
prefix = None
|
||||
|
||||
Reference in New Issue
Block a user