bear-utils 0.7.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bear_utils/__init__.py +13 -0
- bear_utils/ai/__init__.py +30 -0
- bear_utils/ai/ai_helpers/__init__.py +130 -0
- bear_utils/ai/ai_helpers/_common.py +19 -0
- bear_utils/ai/ai_helpers/_config.py +24 -0
- bear_utils/ai/ai_helpers/_parsers.py +188 -0
- bear_utils/ai/ai_helpers/_types.py +20 -0
- bear_utils/cache/__init__.py +119 -0
- bear_utils/cli/__init__.py +4 -0
- bear_utils/cli/commands.py +59 -0
- bear_utils/cli/prompt_helpers.py +166 -0
- bear_utils/cli/shell/__init__.py +0 -0
- bear_utils/cli/shell/_base_command.py +74 -0
- bear_utils/cli/shell/_base_shell.py +390 -0
- bear_utils/cli/shell/_common.py +19 -0
- bear_utils/config/__init__.py +11 -0
- bear_utils/config/config_manager.py +92 -0
- bear_utils/config/dir_manager.py +64 -0
- bear_utils/config/settings_manager.py +232 -0
- bear_utils/constants/__init__.py +16 -0
- bear_utils/constants/_exceptions.py +3 -0
- bear_utils/constants/_lazy_typing.py +15 -0
- bear_utils/constants/date_related.py +36 -0
- bear_utils/constants/time_related.py +22 -0
- bear_utils/database/__init__.py +6 -0
- bear_utils/database/_db_manager.py +104 -0
- bear_utils/events/__init__.py +16 -0
- bear_utils/events/events_class.py +52 -0
- bear_utils/events/events_module.py +65 -0
- bear_utils/extras/__init__.py +17 -0
- bear_utils/extras/_async_helpers.py +15 -0
- bear_utils/extras/_tools.py +178 -0
- bear_utils/extras/platform_utils.py +53 -0
- bear_utils/extras/wrappers/__init__.py +0 -0
- bear_utils/extras/wrappers/add_methods.py +98 -0
- bear_utils/files/__init__.py +4 -0
- bear_utils/files/file_handlers/__init__.py +3 -0
- bear_utils/files/file_handlers/_base_file_handler.py +93 -0
- bear_utils/files/file_handlers/file_handler_factory.py +278 -0
- bear_utils/files/file_handlers/json_file_handler.py +44 -0
- bear_utils/files/file_handlers/log_file_handler.py +33 -0
- bear_utils/files/file_handlers/txt_file_handler.py +34 -0
- bear_utils/files/file_handlers/yaml_file_handler.py +57 -0
- bear_utils/files/ignore_parser.py +298 -0
- bear_utils/graphics/__init__.py +4 -0
- bear_utils/graphics/bear_gradient.py +140 -0
- bear_utils/graphics/image_helpers.py +39 -0
- bear_utils/gui/__init__.py +3 -0
- bear_utils/gui/gui_tools/__init__.py +5 -0
- bear_utils/gui/gui_tools/_settings.py +37 -0
- bear_utils/gui/gui_tools/_types.py +12 -0
- bear_utils/gui/gui_tools/qt_app.py +145 -0
- bear_utils/gui/gui_tools/qt_color_picker.py +119 -0
- bear_utils/gui/gui_tools/qt_file_handler.py +138 -0
- bear_utils/gui/gui_tools/qt_input_dialog.py +306 -0
- bear_utils/logging/__init__.py +25 -0
- bear_utils/logging/logger_manager/__init__.py +0 -0
- bear_utils/logging/logger_manager/_common.py +47 -0
- bear_utils/logging/logger_manager/_console_junk.py +131 -0
- bear_utils/logging/logger_manager/_styles.py +91 -0
- bear_utils/logging/logger_manager/loggers/__init__.py +0 -0
- bear_utils/logging/logger_manager/loggers/_base_logger.py +238 -0
- bear_utils/logging/logger_manager/loggers/_base_logger.pyi +50 -0
- bear_utils/logging/logger_manager/loggers/_buffer_logger.py +55 -0
- bear_utils/logging/logger_manager/loggers/_console_logger.py +249 -0
- bear_utils/logging/logger_manager/loggers/_console_logger.pyi +64 -0
- bear_utils/logging/logger_manager/loggers/_file_logger.py +141 -0
- bear_utils/logging/logger_manager/loggers/_level_sin.py +58 -0
- bear_utils/logging/logger_manager/loggers/_logger.py +18 -0
- bear_utils/logging/logger_manager/loggers/_sub_logger.py +110 -0
- bear_utils/logging/logger_manager/loggers/_sub_logger.pyi +38 -0
- bear_utils/logging/loggers.py +76 -0
- bear_utils/monitoring/__init__.py +10 -0
- bear_utils/monitoring/host_monitor.py +350 -0
- bear_utils/time/__init__.py +16 -0
- bear_utils/time/_helpers.py +91 -0
- bear_utils/time/_time_class.py +316 -0
- bear_utils/time/_timer.py +80 -0
- bear_utils/time/_tools.py +17 -0
- bear_utils/time/time_manager.py +218 -0
- bear_utils-0.7.11.dist-info/METADATA +260 -0
- bear_utils-0.7.11.dist-info/RECORD +83 -0
- bear_utils-0.7.11.dist-info/WHEEL +4 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
import threading
|
2
|
+
from collections.abc import Callable
|
3
|
+
from io import StringIO
|
4
|
+
from logging import Formatter, Handler, LogRecord
|
5
|
+
from logging.handlers import BufferingHandler
|
6
|
+
|
7
|
+
from prompt_toolkit import print_formatted_text
|
8
|
+
from prompt_toolkit.output.defaults import create_output
|
9
|
+
from rich.text import Text
|
10
|
+
|
11
|
+
from ...constants.date_related import DATE_TIME_FORMAT
|
12
|
+
from ._common import SIMPLE_FORMAT, VERBOSE_CONSOLE_FORMAT, ExecValues
|
13
|
+
from ._styles import LoggerExtraInfo
|
14
|
+
|
15
|
+
|
16
|
+
def get_extra(record: LogRecord) -> LoggerExtraInfo:
|
17
|
+
"""Get extra information from the log record."""
|
18
|
+
extra: LoggerExtraInfo = {
|
19
|
+
"style_name": record.__dict__.get("style_name", ""),
|
20
|
+
"style": record.__dict__.get("style", ""),
|
21
|
+
"log_level": record.__dict__.get("log_level", ""),
|
22
|
+
"log_level_style": record.__dict__.get("log_level_style", ""),
|
23
|
+
"namespace": record.__dict__.get("namespace", ""),
|
24
|
+
}
|
25
|
+
return extra
|
26
|
+
|
27
|
+
|
28
|
+
def extract_exec_info(record: LogRecord) -> dict[str, ExecValues] | None:
|
29
|
+
"""Extract execution info from the log record."""
|
30
|
+
exec_values: dict[str, ExecValues] | None = record.__dict__.get("exec_values", {})
|
31
|
+
if exec_values is not None:
|
32
|
+
return exec_values
|
33
|
+
return None
|
34
|
+
|
35
|
+
|
36
|
+
class ConsoleHandler(Handler):
|
37
|
+
def __init__(self, print_func, buffer_output):
|
38
|
+
super().__init__()
|
39
|
+
self.print_func: Callable = print_func
|
40
|
+
self.buffer_func: Callable = buffer_output
|
41
|
+
|
42
|
+
def emit(self, record: LogRecord, return_str: bool = False):
|
43
|
+
"""
|
44
|
+
Emit a log record either to console or return as string.
|
45
|
+
|
46
|
+
Args:
|
47
|
+
record: The LogRecord to emit
|
48
|
+
return_str: If True, return formatted string instead of printing
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
str if return_str=True, None otherwise
|
52
|
+
"""
|
53
|
+
formatted_msg: str = self.format(record)
|
54
|
+
extra: LoggerExtraInfo = get_extra(record)
|
55
|
+
exec_values: dict[str, ExecValues] | None = extract_exec_info(record)
|
56
|
+
exc_info: bool = True if exec_values is not None else False
|
57
|
+
style_name = extra.get("style_name", "")
|
58
|
+
|
59
|
+
print_kwargs = {
|
60
|
+
"msg": formatted_msg,
|
61
|
+
"style": style_name,
|
62
|
+
"exc_info": exc_info if exc_info is not None else False,
|
63
|
+
"exec_values": exec_values,
|
64
|
+
"return_str": return_str,
|
65
|
+
}
|
66
|
+
if return_str:
|
67
|
+
return self.buffer_func(**print_kwargs)
|
68
|
+
|
69
|
+
return self.print_func(**print_kwargs)
|
70
|
+
|
71
|
+
|
72
|
+
class ConsoleFormatter(Formatter):
|
73
|
+
def __init__(self, fmt: str = SIMPLE_FORMAT, datefmt: str = DATE_TIME_FORMAT):
|
74
|
+
super().__init__(fmt=fmt, datefmt=datefmt)
|
75
|
+
self.log_format: str = fmt
|
76
|
+
|
77
|
+
def format(self, record: LogRecord) -> str:
|
78
|
+
extra: LoggerExtraInfo = get_extra(record)
|
79
|
+
if self.log_format == VERBOSE_CONSOLE_FORMAT:
|
80
|
+
log_level_color: str = extra["log_level_style"]
|
81
|
+
style_name: str = extra.get("style_name", "")
|
82
|
+
dynamic_format = self.log_format.format(
|
83
|
+
log_level_color,
|
84
|
+
style_name.upper(),
|
85
|
+
log_level_color,
|
86
|
+
)
|
87
|
+
temp_formatter = Formatter(fmt=dynamic_format, datefmt=self.datefmt)
|
88
|
+
return temp_formatter.format(record)
|
89
|
+
|
90
|
+
if self.log_format == SIMPLE_FORMAT:
|
91
|
+
record.msg = f"{record.msg}"
|
92
|
+
|
93
|
+
return super().format(record)
|
94
|
+
|
95
|
+
|
96
|
+
class ConsoleBuffering(BufferingHandler):
|
97
|
+
def __init__(
|
98
|
+
self,
|
99
|
+
capacity: int = 9999,
|
100
|
+
console_handler=ConsoleHandler(print_func=lambda **kwargs: None, buffer_output=lambda **kwargs: ""),
|
101
|
+
return_auto: bool = False,
|
102
|
+
):
|
103
|
+
super().__init__(capacity=capacity)
|
104
|
+
self.console_handler: ConsoleHandler = console_handler # should come with a formatter before getting here
|
105
|
+
self._lock = threading.RLock()
|
106
|
+
self.flush_auto = return_auto
|
107
|
+
|
108
|
+
def flush_to_output(self) -> Text:
|
109
|
+
"""Flush all buffered records to the console handler."""
|
110
|
+
with self._lock:
|
111
|
+
output_buffer = StringIO()
|
112
|
+
output = create_output(stdout=output_buffer)
|
113
|
+
for record in self.buffer:
|
114
|
+
formatted_msg = self.console_handler.emit(record, return_str=True)
|
115
|
+
print_formatted_text(formatted_msg, output=output, end="\n")
|
116
|
+
output = output_buffer.getvalue()
|
117
|
+
output_buffer.close()
|
118
|
+
self.buffer.clear()
|
119
|
+
return Text.from_ansi(output)
|
120
|
+
|
121
|
+
def trigger_flush(self):
|
122
|
+
"""Immediately flush all buffered records to console."""
|
123
|
+
self.flush()
|
124
|
+
|
125
|
+
def flush(self) -> None:
|
126
|
+
"""Flush all buffered records to the console handler."""
|
127
|
+
if self.flush_auto:
|
128
|
+
with self._lock:
|
129
|
+
for record in self.buffer:
|
130
|
+
self.console_handler.emit(record)
|
131
|
+
self.buffer.clear()
|
@@ -0,0 +1,91 @@
|
|
1
|
+
from logging import DEBUG, ERROR, INFO, WARNING
|
2
|
+
from typing import Literal, NotRequired, Required, TypedDict
|
3
|
+
|
4
|
+
from rich.theme import Theme
|
5
|
+
|
6
|
+
VERBOSE: Literal[5] = 5
|
7
|
+
SUCCESS: Literal[15] = 15
|
8
|
+
FAILURE: Literal[45] = 45
|
9
|
+
|
10
|
+
|
11
|
+
class LoggerExtraInfo(TypedDict):
|
12
|
+
"""Type definition for extra info that can be added to log records."""
|
13
|
+
|
14
|
+
style_name: Required[str]
|
15
|
+
style: Required[str]
|
16
|
+
namespace: NotRequired[str]
|
17
|
+
log_level: Required[int]
|
18
|
+
log_level_style: Required[str]
|
19
|
+
|
20
|
+
|
21
|
+
LOGGER_METHODS: dict[str, LoggerExtraInfo] = {
|
22
|
+
"info": {
|
23
|
+
"style_name": "info",
|
24
|
+
"style": "dim green",
|
25
|
+
"log_level": INFO,
|
26
|
+
"log_level_style": "black on white",
|
27
|
+
},
|
28
|
+
"debug": {
|
29
|
+
"style_name": "debug",
|
30
|
+
"style": "bold blue",
|
31
|
+
"log_level": DEBUG,
|
32
|
+
"log_level_style": "black on blue",
|
33
|
+
},
|
34
|
+
"warning": {
|
35
|
+
"style_name": "warning",
|
36
|
+
"style": "bold yellow",
|
37
|
+
"log_level": WARNING,
|
38
|
+
"log_level_style": "yellow on black",
|
39
|
+
},
|
40
|
+
"error": {
|
41
|
+
"style_name": "error",
|
42
|
+
"style": "bold red",
|
43
|
+
"log_level": ERROR,
|
44
|
+
"log_level_style": "bold white on red",
|
45
|
+
},
|
46
|
+
"exception": {
|
47
|
+
"style_name": "exception",
|
48
|
+
"style": "bold red",
|
49
|
+
"log_level": ERROR,
|
50
|
+
"log_level_style": "bold white on red",
|
51
|
+
},
|
52
|
+
"success": {
|
53
|
+
"style_name": "success",
|
54
|
+
"style": "bold green",
|
55
|
+
"log_level": SUCCESS,
|
56
|
+
"log_level_style": "black on bright_green",
|
57
|
+
},
|
58
|
+
"failure": {
|
59
|
+
"style_name": "failure",
|
60
|
+
"style": "bold red underline",
|
61
|
+
"log_level": FAILURE,
|
62
|
+
"log_level_style": "bold red on white",
|
63
|
+
},
|
64
|
+
"verbose": {
|
65
|
+
"style_name": "verbose",
|
66
|
+
"style": "bold blue",
|
67
|
+
"log_level": VERBOSE,
|
68
|
+
"log_level_style": "black on bright_blue",
|
69
|
+
},
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
def get_method(name: str) -> LoggerExtraInfo:
|
74
|
+
"""
|
75
|
+
Get the name info from the logger methods.
|
76
|
+
|
77
|
+
Args:
|
78
|
+
name (str): The name of the logger method.
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
LoggerExtraInfo | dict: The info of the logger method or an empty dict if not found.
|
82
|
+
"""
|
83
|
+
if not LOGGER_METHODS.get(name):
|
84
|
+
raise ValueError(f"Logger method '{name}' does not exist. Available methods: {list(LOGGER_METHODS.keys())}")
|
85
|
+
return LOGGER_METHODS[name]
|
86
|
+
|
87
|
+
|
88
|
+
DEFAULT_STYLES: dict[str, str] = {**{method: info["style"].upper() for method, info in LOGGER_METHODS.items()}}
|
89
|
+
"""Just the styles of the logger methods, used to create the theme."""
|
90
|
+
|
91
|
+
DEFAULT_THEME = Theme(styles=DEFAULT_STYLES)
|
File without changes
|
@@ -0,0 +1,238 @@
|
|
1
|
+
import sys
|
2
|
+
from functools import partial
|
3
|
+
from io import StringIO
|
4
|
+
from typing import Self
|
5
|
+
|
6
|
+
from prompt_toolkit.formatted_text import ANSI, FormattedText, to_formatted_text
|
7
|
+
from prompt_toolkit.shortcuts import print_formatted_text
|
8
|
+
from rich.console import Console
|
9
|
+
from rich.text import Text
|
10
|
+
from rich.theme import Theme
|
11
|
+
from rich.traceback import Traceback
|
12
|
+
from singleton_base import SingletonBase
|
13
|
+
|
14
|
+
from .._common import ExecValues, StackLevelTracker
|
15
|
+
from .._styles import DEFAULT_THEME, LOGGER_METHODS, LoggerExtraInfo
|
16
|
+
from ._level_sin import INFO, add_level_name, check_level, lvl_exists
|
17
|
+
from ._sub_logger import SubConsoleLogger
|
18
|
+
|
19
|
+
|
20
|
+
class BaseLogger(SingletonBase):
|
21
|
+
def __init__(
|
22
|
+
self,
|
23
|
+
output_handler=None,
|
24
|
+
theme: Theme | None = None,
|
25
|
+
style_disabled: bool = False,
|
26
|
+
logger_mode: bool = False,
|
27
|
+
**kwargs,
|
28
|
+
) -> None:
|
29
|
+
"""
|
30
|
+
Initialize the BaseLogger with an optional style_disabled flag.
|
31
|
+
This flag can be used to disable styled output in the logger.
|
32
|
+
"""
|
33
|
+
self.output_handler = output_handler or self._default_output
|
34
|
+
self._level: int = kwargs.get("level", INFO)
|
35
|
+
self.stack_tracker: StackLevelTracker = StackLevelTracker()
|
36
|
+
self.logger_mode: bool = logger_mode
|
37
|
+
self.theme: Theme = DEFAULT_THEME if theme is None else theme
|
38
|
+
self.style_disabled: bool = style_disabled
|
39
|
+
self.console: Console = self.get_console(self.theme, style_disabled)
|
40
|
+
self.console_buffer: StringIO = self.console.file # type: ignore
|
41
|
+
self.backup_console = Console(theme=self.theme, highlight=True, force_terminal=True)
|
42
|
+
self._generate_style_methods()
|
43
|
+
|
44
|
+
@staticmethod
|
45
|
+
def get_console(theme: Theme, style_disabled: bool) -> Console:
|
46
|
+
if style_disabled:
|
47
|
+
console = Console(
|
48
|
+
file=StringIO(),
|
49
|
+
highlight=False,
|
50
|
+
force_terminal=True,
|
51
|
+
style=None, # Disable styling
|
52
|
+
)
|
53
|
+
else:
|
54
|
+
console: Console = Console(
|
55
|
+
file=StringIO(),
|
56
|
+
highlight=False,
|
57
|
+
force_terminal=True,
|
58
|
+
color_system="truecolor",
|
59
|
+
theme=theme,
|
60
|
+
)
|
61
|
+
return console
|
62
|
+
|
63
|
+
def _default_output(self, msg: object, extra: LoggerExtraInfo, *args, **kwargs) -> None:
|
64
|
+
"""Default output handler that prints to console."""
|
65
|
+
if not self.logger_mode:
|
66
|
+
self.print(msg, *args, **kwargs)
|
67
|
+
|
68
|
+
def __enter__(self) -> Self:
|
69
|
+
"""
|
70
|
+
Enter the context manager, returning the ConsoleLogger instance.
|
71
|
+
This allows for using the logger in a with statement.
|
72
|
+
"""
|
73
|
+
return self
|
74
|
+
|
75
|
+
def __exit__(self, exc_type, exc_value, traceback) -> None:
|
76
|
+
"""
|
77
|
+
Exit the context manager, cleaning up resources.
|
78
|
+
This is called when the with statement is exited.
|
79
|
+
"""
|
80
|
+
self.exit()
|
81
|
+
|
82
|
+
def trigger_buffer_flush(self) -> str | Text:
|
83
|
+
return "No buffering handler available."
|
84
|
+
|
85
|
+
def set_base_level(self, level: int | str) -> None:
|
86
|
+
"""
|
87
|
+
Set the logging level for the console.
|
88
|
+
This method allows changing the logging level dynamically.
|
89
|
+
|
90
|
+
This isn't actually a logging logger so we are having to add this while avoiding messing with
|
91
|
+
any subclasses that do subclass Logger.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
level (int): The logging level to set. This should be a valid logging level constant.
|
95
|
+
"""
|
96
|
+
self._level = check_level(level)
|
97
|
+
|
98
|
+
def filter_by_level(self, level: str | int) -> bool:
|
99
|
+
"""
|
100
|
+
Filter method to determine if a message should be logged based on its level.
|
101
|
+
This method checks if the provided level is greater than or equal to the logger's level.
|
102
|
+
|
103
|
+
Args:
|
104
|
+
level (int): The logging level of the message.
|
105
|
+
|
106
|
+
Returns:
|
107
|
+
bool: True if the message should be logged, False otherwise.
|
108
|
+
"""
|
109
|
+
level = check_level(level)
|
110
|
+
return level >= self._level
|
111
|
+
|
112
|
+
def get_sub_logger(self, namespace: str, **kwargs) -> "SubConsoleLogger":
|
113
|
+
return SubConsoleLogger(self, namespace, **kwargs) # type: ignore[return-value]
|
114
|
+
|
115
|
+
def _generate_style_methods(self) -> None:
|
116
|
+
"""Generate dynamic logging methods with proper log level registration."""
|
117
|
+
for style_name, extra in LOGGER_METHODS.items():
|
118
|
+
if not lvl_exists(extra["log_level"]): # I'd rather if things crash than silently fail
|
119
|
+
add_level_name(extra["log_level"], extra.get("style_name"))
|
120
|
+
setattr(self, style_name, partial(self.replacement_method, injected_extra=extra.copy()))
|
121
|
+
|
122
|
+
def replacement_method(self, msg: object, *args, **kwargs) -> None:
|
123
|
+
"""Replacement method for logging messages with additional context."""
|
124
|
+
if self.stack_tracker.not_set:
|
125
|
+
self.stack_tracker.record_start()
|
126
|
+
filter_func = kwargs.pop("filter_func", self.filter_by_level)
|
127
|
+
extra: LoggerExtraInfo = kwargs.pop("injected_extra") | kwargs.pop("extra", {})
|
128
|
+
kwargs["style"] = extra.pop("style", None)
|
129
|
+
if extra.get("namespace"):
|
130
|
+
msg = f"<{extra.get('namespace')}> {msg}"
|
131
|
+
if filter_func(extra.get("log_level", self._level)):
|
132
|
+
self.output_handler(msg, extra, *args, **kwargs)
|
133
|
+
|
134
|
+
def print(
|
135
|
+
self,
|
136
|
+
msg: object,
|
137
|
+
end: str = "\n",
|
138
|
+
exc_info=None,
|
139
|
+
extra: dict | None = None,
|
140
|
+
*args,
|
141
|
+
**kwargs,
|
142
|
+
) -> None | str:
|
143
|
+
"""
|
144
|
+
Print a message to the console with the specified formatting.
|
145
|
+
This method allows for printing messages with additional context and formatting.
|
146
|
+
"""
|
147
|
+
if exc_info is not None:
|
148
|
+
try:
|
149
|
+
exception: Traceback = self._get_exception(manual=True)
|
150
|
+
self._print(exception, end=end, **kwargs)
|
151
|
+
except Exception as e:
|
152
|
+
print(
|
153
|
+
f"ConsoleLogger: Error printing exception traceback. Message: {msg} Exception: {exc_info} Error: {e}"
|
154
|
+
)
|
155
|
+
|
156
|
+
self._print(msg, end, style=kwargs.pop("style", None))
|
157
|
+
|
158
|
+
if extra:
|
159
|
+
self._print(msg=extra, end=end, json=True, indent=4)
|
160
|
+
|
161
|
+
def print_json(
|
162
|
+
self,
|
163
|
+
json: str | None = None,
|
164
|
+
data: dict | None = None,
|
165
|
+
indent: int = 2,
|
166
|
+
sort: bool = False,
|
167
|
+
**kwargs,
|
168
|
+
):
|
169
|
+
"""Just a pass-through to the console.print_json method."""
|
170
|
+
self.console.print_json(json=json, data=data, indent=indent, sort_keys=sort, **kwargs)
|
171
|
+
|
172
|
+
def raw_print(self, msg: object, end: str = "\n", *args, **kwargs) -> None:
|
173
|
+
"""
|
174
|
+
Use the underlying console directly and bypass all the extra formatting and handling.
|
175
|
+
|
176
|
+
Args:
|
177
|
+
msg (object): The message to print.
|
178
|
+
end (str, optional): The string appended after the message. Defaults to "\n".
|
179
|
+
"""
|
180
|
+
self.backup_console.print(msg, end=end, style=kwargs.pop("style", "white"), **kwargs)
|
181
|
+
|
182
|
+
def _print(self, msg: object, end: str, json: bool = False, *args, **kwargs) -> None:
|
183
|
+
"""
|
184
|
+
Print a message to the console with the specified formatting.
|
185
|
+
This method allows for printing messages with additional context and formatting.
|
186
|
+
"""
|
187
|
+
try:
|
188
|
+
if json:
|
189
|
+
self.print_json(data=msg, *args, **kwargs) # type: ignore[arg-type]
|
190
|
+
else:
|
191
|
+
self.console.print(msg, end="", style=kwargs.pop("style", "white"))
|
192
|
+
formatted_text: FormattedText = to_formatted_text(ANSI(self.console_buffer.getvalue()))
|
193
|
+
print_formatted_text(formatted_text, end=end)
|
194
|
+
self._reset_buffer()
|
195
|
+
except Exception as e:
|
196
|
+
print(
|
197
|
+
f"{self.__class__.__name__.upper()}: Error printing message. Message: {msg} Exception: {e}. "
|
198
|
+
"Please check the console buffer for more details."
|
199
|
+
)
|
200
|
+
self._reset_buffer()
|
201
|
+
|
202
|
+
def _extract_exception_values(self, exc_info) -> ExecValues | None:
|
203
|
+
"""Extract exception values in a clean, reusable way."""
|
204
|
+
if isinstance(exc_info, BaseException):
|
205
|
+
exc_tuple = (type(exc_info), exc_info, exc_info.__traceback__)
|
206
|
+
elif exc_info is True:
|
207
|
+
exc_tuple = sys.exc_info()
|
208
|
+
else:
|
209
|
+
exc_tuple = exc_info
|
210
|
+
|
211
|
+
if exc_tuple:
|
212
|
+
exc_type, exc_value, exc_traceback = exc_tuple
|
213
|
+
if exc_type is not None and exc_value is not None and exc_traceback is not None:
|
214
|
+
return {"exc_type": exc_type, "exc_value": exc_value, "exc_traceback": exc_traceback}
|
215
|
+
return None
|
216
|
+
|
217
|
+
def _get_exception(self, manual: bool = False, exec_values: ExecValues | None = None) -> Traceback:
|
218
|
+
if manual and exec_values:
|
219
|
+
return Traceback.from_exception(
|
220
|
+
exc_type=exec_values["exc_type"],
|
221
|
+
exc_value=exec_values["exc_value"],
|
222
|
+
traceback=exec_values["exc_traceback"],
|
223
|
+
show_locals=True,
|
224
|
+
width=100,
|
225
|
+
)
|
226
|
+
return Traceback(show_locals=True, width=100)
|
227
|
+
|
228
|
+
def _reset_buffer(self) -> None:
|
229
|
+
"""Reset the console buffer."""
|
230
|
+
self.console_buffer.truncate(0)
|
231
|
+
self.console_buffer.seek(0)
|
232
|
+
|
233
|
+
def exit(self) -> None:
|
234
|
+
"""
|
235
|
+
Exit the console logger.
|
236
|
+
This method is called when the program exits to clean up resources.
|
237
|
+
"""
|
238
|
+
self.console_buffer.close()
|
@@ -0,0 +1,50 @@
|
|
1
|
+
from io import StringIO
|
2
|
+
from typing import Any, Self
|
3
|
+
|
4
|
+
from rich.console import Console
|
5
|
+
from rich.text import Text
|
6
|
+
from rich.theme import Theme
|
7
|
+
from rich.traceback import Traceback
|
8
|
+
from singleton_base import SingletonBase
|
9
|
+
|
10
|
+
from .._common import ExecValues, StackLevelTracker
|
11
|
+
from ._sub_logger import SubConsoleLogger
|
12
|
+
|
13
|
+
class BaseLogger(SingletonBase):
|
14
|
+
output_handler: Any
|
15
|
+
stack_tracker: StackLevelTracker
|
16
|
+
theme: Theme
|
17
|
+
_level: int
|
18
|
+
style_disabled: bool
|
19
|
+
console: Console
|
20
|
+
console_buffer: StringIO
|
21
|
+
logger_mode: bool
|
22
|
+
sub_logger: dict[str, SubConsoleLogger]
|
23
|
+
# fmt: off
|
24
|
+
def __init__(self, output_handler = None, theme: Theme | None = ..., style_disabled: bool = ..., logger_mode: bool = ..., **kwargs) -> None: ...
|
25
|
+
@staticmethod
|
26
|
+
def get_console(theme: Theme, style_disabled: bool) -> Console: ...
|
27
|
+
def __enter__(self) -> Self: ...
|
28
|
+
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None: ...
|
29
|
+
def _generate_style_methods(self, **kwargs) -> None: ...
|
30
|
+
def get_sub_logger(self, namespace: str, **kwargs: Any) -> SubConsoleLogger: ...
|
31
|
+
def replacement_method(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
32
|
+
def print(self, msg: object, end: str="\n", exc_info=None, extra: dict | None = None, *args, **kwargs) -> None | str: ...
|
33
|
+
def print_json(self, data: Any | None = ..., indent: int = ..., sort: bool = ...) -> None: ...
|
34
|
+
def set_base_level(self, level: int) -> None: ...
|
35
|
+
def filter_by_level(self, level: int) -> bool: ...
|
36
|
+
def trigger_buffer_flush(self) -> str | Text: ...
|
37
|
+
def _print(self, msg: object, end: str, json: bool = ..., *args: Any, **kwargs: Any) -> None: ...
|
38
|
+
def _get_exception(self, manual: bool = ..., exec_values: ExecValues | None = ...) -> Traceback: ...
|
39
|
+
def _extract_exception_values(self, exc_info) -> ExecValues | None: ...
|
40
|
+
def _reset_buffer(self) -> None: ...
|
41
|
+
def exit(self) -> None: ...
|
42
|
+
def debug(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
43
|
+
def info(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
44
|
+
def exception(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
45
|
+
def warning(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
46
|
+
def error(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
47
|
+
def critical(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
48
|
+
def success(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
49
|
+
def failure(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
50
|
+
def verbose(self, msg: object, *args: Any, **kwargs: Any) -> None: ...
|
@@ -0,0 +1,55 @@
|
|
1
|
+
from logging import DEBUG
|
2
|
+
from typing import Any, override
|
3
|
+
|
4
|
+
from rich.theme import Theme
|
5
|
+
|
6
|
+
from ._console_logger import ConsoleLogger
|
7
|
+
from ._sub_logger import SubConsoleLogger
|
8
|
+
|
9
|
+
|
10
|
+
class BufferLogger(ConsoleLogger):
|
11
|
+
"""
|
12
|
+
A buffer-based logger that writes styled log messages to a buffer.
|
13
|
+
|
14
|
+
Combines Python's logging framework with Rich console styling, but outputs
|
15
|
+
to a buffer instead of console or file. Supports buffering of log messages,
|
16
|
+
|
17
|
+
Features:
|
18
|
+
- Buffering of log messages
|
19
|
+
- Rich-style method generation (info, error, debug, etc.)
|
20
|
+
- Consistent print() interface
|
21
|
+
- Exception tracebacks in buffer format
|
22
|
+
|
23
|
+
Example:
|
24
|
+
logger = BufferLogger.get_instance(
|
25
|
+
init=True,
|
26
|
+
name="BufferLogger",
|
27
|
+
)
|
28
|
+
logger.info("This goes to the buffer")
|
29
|
+
logger.error("This error is logged to buffer")
|
30
|
+
"""
|
31
|
+
|
32
|
+
def __init__(
|
33
|
+
self,
|
34
|
+
theme: Theme | None = None,
|
35
|
+
name: str = "BufferLogger",
|
36
|
+
level: int = DEBUG,
|
37
|
+
*args,
|
38
|
+
**kwargs,
|
39
|
+
) -> None:
|
40
|
+
ConsoleLogger.__init__(
|
41
|
+
self,
|
42
|
+
name=name,
|
43
|
+
level=level,
|
44
|
+
file=False,
|
45
|
+
console=False,
|
46
|
+
buffering=True,
|
47
|
+
queue_handler=kwargs.pop("queue_handler", False),
|
48
|
+
theme=theme,
|
49
|
+
logger_mode=True,
|
50
|
+
**kwargs,
|
51
|
+
)
|
52
|
+
|
53
|
+
@override
|
54
|
+
def get_sub_logger(self, namespace: str, **kwargs: Any) -> SubConsoleLogger:
|
55
|
+
return SubConsoleLogger(self, namespace, **kwargs)
|