Source code for objwatch.config

# MIT License
# Copyright (c) 2025 aeeeeeep

import logging
from types import ModuleType
from dataclasses import dataclass
from typing import Optional, Union, List, Dict, Any


[docs] @dataclass(frozen=True) class ObjWatchConfig: """ Configuration parameters for ObjWatch. Args: targets (List[Union[str, ModuleType]]): Files or modules to monitor. exclude_targets (Optional[List[Union[str, ModuleType]]]): Files or modules to exclude from monitoring. with_locals (bool): Enable tracing and logging of local variables within functions. with_globals (bool): Enable tracing and logging of global variables across function calls. output (Optional[str]): File path for writing logs, must end with '.objwatch' for ObjWatch Log Viewer extension. output_json (Optional[str]): JSON file path for writing structured logs. level (int): Logging level (e.g., logging.DEBUG, logging.INFO). simple (bool): Defaults to True, disable simple logging mode with the format "[{time}] [{level}] objwatch: {msg}". wrapper (Optional[ABCWrapper]): Custom wrapper to extend tracing and logging functionality. framework (Optional[str]): The multi-process framework module to use. indexes (Optional[List[int]]): The indexes to track in a multi-process environment. """ targets: List[Union[str, ModuleType]] exclude_targets: Optional[List[Union[str, ModuleType]]] = None with_locals: bool = False with_globals: bool = False output: Optional[str] = None output_json: Optional[str] = None level: int = logging.DEBUG simple: bool = True wrapper: Optional[Any] = None framework: Optional[str] = None indexes: Optional[List[int]] = None
[docs] def __post_init__(self) -> None: """ Post-initialization configuration validation """ if not self.targets: raise ValueError("At least one monitoring target must be specified") if self.level == "force" and self.output is not None: raise ValueError("output cannot be specified when level is 'force'") if self.output is not None and not self.output.endswith('.objwatch'): raise ValueError("output file must end with '.objwatch' for ObjWatch Log Viewer extension") if self.output_json is not None and not self.output_json.endswith('.json'): raise ValueError("output_json file must end with '.json'")
[docs] def __str__(self) -> str: """ Return a simple string representation of the configuration. """ config_lines = [] for field_name, field_value in self.__dict__.items(): if isinstance(field_value, list): # For lists, print each element on a new line config_lines.append(f"* {field_name}:") for item in field_value: config_lines.append(f" - {item}") elif field_name == 'level' and isinstance(field_value, int): config_lines.append(f"* {field_name}: {logging.getLevelName(field_value)}") elif field_name == 'wrapper' and field_value is not None: config_lines.append(f"* {field_name}: {field_value.__name__}") else: # For other types, print directly config_lines.append(f"* {field_name}: {field_value}") return "\n".join(config_lines)
[docs] def to_dict(self) -> Dict[str, Any]: """ Convert the configuration object to a dictionary representation. Returns: Dict[str, Any]: A dictionary containing all configuration fields. """ result: Dict[str, Any] = {} for field_name, field_value in self.__dict__.items(): if isinstance(field_value, list): # Convert list elements to string representation if needed result[field_name] = [str(item) if isinstance(item, ModuleType) else item for item in field_value] elif isinstance(field_value, (ModuleType, type)): # Convert module objects to their __name__ result[field_name] = field_value.__name__ elif field_name == 'level' and isinstance(field_value, int): # For level field, include both numeric value and name result[field_name] = logging.getLevelName(field_value) else: # For other types, add as is result[field_name] = field_value return result