bear-utils 0.8.26__py3-none-any.whl → 0.9.0__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 +10 -4
- bear_utils/cli/__init__.py +5 -0
- bear_utils/cli/_args.py +12 -0
- bear_utils/cli/shell/_base_shell.py +4 -17
- bear_utils/constants/__init__.py +52 -1
- bear_utils/constants/_exit_code.py +60 -0
- bear_utils/constants/_http_status_code.py +37 -0
- bear_utils/constants/_meta.py +107 -0
- bear_utils/extras/responses/__init__.py +2 -6
- bear_utils/extras/responses/function_response.py +0 -4
- bear_utils/graphics/font/__init__.py +11 -0
- bear_utils/graphics/font/_raw_block_letters.py +463 -0
- bear_utils/graphics/font/_theme.py +11 -0
- bear_utils/graphics/font/block_font.py +150 -0
- bear_utils/graphics/font/glitch_font.py +63 -0
- bear_utils/logger_manager/__init__.py +15 -3
- bear_utils/logger_manager/_log_level.py +12 -88
- bear_utils/logger_manager/logger_protocol.py +22 -25
- bear_utils/logger_manager/loggers/_console.py +204 -0
- bear_utils/logger_manager/loggers/_logger.py +19 -0
- bear_utils/logger_manager/loggers/base_logger.py +7 -7
- bear_utils/logger_manager/loggers/fastapi_logger.py +235 -110
- bear_utils/logger_manager/loggers/simple_logger.py +39 -24
- {bear_utils-0.8.26.dist-info → bear_utils-0.9.0.dist-info}/METADATA +3 -3
- {bear_utils-0.8.26.dist-info → bear_utils-0.9.0.dist-info}/RECORD +26 -16
- bear_utils/constants/server.py +0 -16
- {bear_utils-0.8.26.dist-info → bear_utils-0.9.0.dist-info}/WHEEL +0 -0
@@ -3,12 +3,14 @@
|
|
3
3
|
from typing import Any
|
4
4
|
|
5
5
|
from bear_utils.logger_manager._common import VERBOSE_CONSOLE_FORMAT
|
6
|
-
from bear_utils.logger_manager.
|
6
|
+
from bear_utils.logger_manager._log_level import DEBUG, ERROR, FAILURE, INFO, SUCCESS, VERBOSE, WARNING, LogLevel
|
7
|
+
from bear_utils.logger_manager._styles import DEFAULT_THEME
|
7
8
|
from bear_utils.logger_manager.logger_protocol import AsyncLoggerProtocol, LoggerProtocol
|
9
|
+
from bear_utils.logger_manager.loggers._console import LogConsole
|
8
10
|
from bear_utils.logger_manager.loggers.base_logger import BaseLogger
|
9
11
|
from bear_utils.logger_manager.loggers.buffer_logger import BufferLogger
|
10
12
|
from bear_utils.logger_manager.loggers.console_logger import ConsoleLogger
|
11
|
-
from bear_utils.logger_manager.loggers.fastapi_logger import
|
13
|
+
from bear_utils.logger_manager.loggers.fastapi_logger import LoggingClient, LoggingServer
|
12
14
|
from bear_utils.logger_manager.loggers.file_logger import FileLogger
|
13
15
|
from bear_utils.logger_manager.loggers.simple_logger import SimpleLogger
|
14
16
|
from bear_utils.logger_manager.loggers.sub_logger import SubConsoleLogger
|
@@ -80,15 +82,25 @@ def get_sub_logger(
|
|
80
82
|
|
81
83
|
|
82
84
|
__all__ = [
|
85
|
+
"DEBUG",
|
86
|
+
"DEFAULT_THEME",
|
87
|
+
"ERROR",
|
88
|
+
"FAILURE",
|
89
|
+
"INFO",
|
90
|
+
"SUCCESS",
|
83
91
|
"VERBOSE",
|
84
92
|
"VERBOSE_CONSOLE_FORMAT",
|
93
|
+
"WARNING",
|
85
94
|
"AsyncLoggerProtocol",
|
86
95
|
"BaseLogger",
|
87
96
|
"BufferLogger",
|
88
97
|
"ConsoleLogger",
|
89
98
|
"FileLogger",
|
99
|
+
"LogConsole",
|
100
|
+
"LogLevel",
|
90
101
|
"LoggerProtocol",
|
91
|
-
"
|
102
|
+
"LoggingClient",
|
103
|
+
"LoggingServer",
|
92
104
|
"SimpleLogger",
|
93
105
|
"SubConsoleLogger",
|
94
106
|
"get_console",
|
@@ -1,9 +1,6 @@
|
|
1
|
-
from
|
2
|
-
from typing import Any, Literal, overload
|
1
|
+
from typing import Literal
|
3
2
|
|
4
|
-
from
|
5
|
-
|
6
|
-
from bear_utils.extras.wrappers.add_methods import add_comparison_methods
|
3
|
+
from bear_utils.constants._meta import RichIntEnum, Value
|
7
4
|
|
8
5
|
FAILURE: Literal[45] = 45
|
9
6
|
ERROR: Literal[40] = 40
|
@@ -40,87 +37,14 @@ name_to_level = {
|
|
40
37
|
}
|
41
38
|
|
42
39
|
|
43
|
-
|
44
|
-
|
45
|
-
"""Model to represent a logging level."""
|
46
|
-
|
47
|
-
name: str = Field(default="NOTSET", description="Name of the logging level")
|
48
|
-
value: int = Field(default=NOTSET, description="Numeric value of the logging level")
|
49
|
-
|
50
|
-
@field_validator("value")
|
51
|
-
@classmethod
|
52
|
-
def validate_value(cls, value: int) -> int:
|
53
|
-
if value not in level_to_name:
|
54
|
-
raise ValueError(f"Invalid logging level value: {value!r}. Valid values are: {list(level_to_name.keys())}")
|
55
|
-
return value
|
56
|
-
|
57
|
-
@field_validator("name")
|
58
|
-
@classmethod
|
59
|
-
def validate_name(cls, name: str) -> str:
|
60
|
-
if name not in name_to_level:
|
61
|
-
raise ValueError(f"Invalid logging level name: {name!r}. Valid names are: {list(name_to_level.keys())}")
|
62
|
-
return name
|
63
|
-
|
64
|
-
|
65
|
-
class LogLevels(BaseModel):
|
66
|
-
"""Model to represent a collection of logging levels."""
|
67
|
-
|
68
|
-
notset: LogLevel = Field(default=LogLevel(name="NOTSET", value=NOTSET))
|
69
|
-
verbose: LogLevel = Field(default=LogLevel(name="VERBOSE", value=VERBOSE))
|
70
|
-
debug: LogLevel = Field(default=LogLevel(name="DEBUG", value=DEBUG))
|
71
|
-
info: LogLevel = Field(default=LogLevel(name="INFO", value=INFO))
|
72
|
-
success: LogLevel = Field(default=LogLevel(name="SUCCESS", value=SUCCESS))
|
73
|
-
warning: LogLevel = Field(default=LogLevel(name="WARNING", value=WARNING))
|
74
|
-
error: LogLevel = Field(default=LogLevel(name="ERROR", value=ERROR))
|
75
|
-
failure: LogLevel = Field(default=LogLevel(name="FAILURE", value=FAILURE))
|
76
|
-
|
77
|
-
model_config = {"arbitrary_types_allowed": True, "extra": "forbid"}
|
78
|
-
|
79
|
-
@cached_property
|
80
|
-
def keys(self) -> list[str]:
|
81
|
-
"""Get the names of all logging levels."""
|
82
|
-
return [key.upper() for key in LogLevels.model_fields]
|
83
|
-
|
84
|
-
@cached_property
|
85
|
-
def levels(self): # noqa: ANN202
|
86
|
-
item_dict: dict[str, Any] = {
|
87
|
-
level_name.lower(): getattr(self, level_name.lower()).value for level_name in self.keys
|
88
|
-
}
|
89
|
-
return item_dict.items()
|
90
|
-
|
91
|
-
def get_int(self, name: str) -> int:
|
92
|
-
"""Get the integer value of a logging level by name."""
|
93
|
-
if not hasattr(self, name):
|
94
|
-
raise ValueError(f"Invalid logging level name: {name!r}. Valid names are: {self.keys}")
|
95
|
-
return getattr(self, name).value
|
96
|
-
|
97
|
-
@overload
|
98
|
-
def get(self, v: LogLevel) -> LogLevel: ...
|
99
|
-
|
100
|
-
@overload
|
101
|
-
def get(self, v: str) -> LogLevel: ...
|
102
|
-
|
103
|
-
@overload
|
104
|
-
def get(self, v: int) -> LogLevel: ...
|
105
|
-
|
106
|
-
def get(self, v: int | str | LogLevel) -> LogLevel:
|
107
|
-
"""Get a logging level by name or value."""
|
108
|
-
if isinstance(v, LogLevel):
|
109
|
-
return v
|
110
|
-
if isinstance(v, str) and v.lower() in self.keys:
|
111
|
-
return getattr(self, v.lower())
|
112
|
-
if isinstance(v, int):
|
113
|
-
for level_name, level_value in self.levels:
|
114
|
-
if level_value == v:
|
115
|
-
return getattr(self, level_name.lower())
|
116
|
-
return self.notset # Default to NOTSET if no match found
|
117
|
-
|
118
|
-
def get_name(self, value: int) -> str:
|
119
|
-
"""Get the name of a logging level by its integer value."""
|
120
|
-
for level_name, level_value in self.levels:
|
121
|
-
if level_value == value:
|
122
|
-
return level_name
|
123
|
-
raise ValueError(f"Invalid logging level value: {value!r}. Valid values are: {self.keys}")
|
124
|
-
|
40
|
+
class LogLevel(RichIntEnum):
|
41
|
+
"""Enumeration for logging levels."""
|
125
42
|
|
126
|
-
|
43
|
+
NOTSET = Value(NOTSET, "NOTSET")
|
44
|
+
VERBOSE = Value(VERBOSE, "VERBOSE")
|
45
|
+
DEBUG = Value(DEBUG, "DEBUG")
|
46
|
+
INFO = Value(INFO, "INFO")
|
47
|
+
WARNING = Value(WARNING, "WARNING")
|
48
|
+
ERROR = Value(ERROR, "ERROR")
|
49
|
+
FAILURE = Value(FAILURE, "FAILURE")
|
50
|
+
SUCCESS = Value(SUCCESS, "SUCCESS")
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# ruff: noqa: D102
|
1
2
|
"""A protocol for logging classes for general use."""
|
2
3
|
|
3
4
|
from typing import Protocol, runtime_checkable
|
@@ -5,41 +6,37 @@ from typing import Protocol, runtime_checkable
|
|
5
6
|
|
6
7
|
@runtime_checkable
|
7
8
|
class LoggerProtocol(Protocol):
|
8
|
-
"""A protocol for logging classes."""
|
9
|
+
"""A protocol for logging classes with extra methods."""
|
9
10
|
|
10
|
-
def debug(self, msg: object, *args, **kwargs) -> None:
|
11
|
-
"""Log a debug message."""
|
12
|
-
...
|
11
|
+
def debug(self, msg: object, *args, **kwargs) -> None: ...
|
13
12
|
|
14
|
-
def info(self, msg: object, *args, **kwargs) -> None:
|
15
|
-
"""Log an info message."""
|
16
|
-
...
|
13
|
+
def info(self, msg: object, *args, **kwargs) -> None: ...
|
17
14
|
|
18
|
-
def warning(self, msg: object, *args, **kwargs) -> None:
|
19
|
-
"""Log a warning message."""
|
20
|
-
...
|
15
|
+
def warning(self, msg: object, *args, **kwargs) -> None: ...
|
21
16
|
|
22
|
-
def error(self, msg: object, *args, **kwargs) -> None:
|
23
|
-
|
24
|
-
|
17
|
+
def error(self, msg: object, *args, **kwargs) -> None: ...
|
18
|
+
|
19
|
+
def verbose(self, msg: object, *args, **kwargs) -> None: ...
|
20
|
+
|
21
|
+
def success(self, msg: object, *args, **kwargs) -> None: ...
|
22
|
+
|
23
|
+
def failure(self, msg: object, *args, **kwargs) -> None: ...
|
25
24
|
|
26
25
|
|
27
26
|
@runtime_checkable
|
28
27
|
class AsyncLoggerProtocol(Protocol):
|
29
28
|
"""A protocol for asynchronous logging classes."""
|
30
29
|
|
31
|
-
async def debug(self, msg: object, *args, **kwargs) -> None:
|
32
|
-
|
33
|
-
|
30
|
+
async def debug(self, msg: object, *args, **kwargs) -> None: ...
|
31
|
+
|
32
|
+
async def info(self, msg: object, *args, **kwargs) -> None: ...
|
33
|
+
|
34
|
+
async def warning(self, msg: object, *args, **kwargs) -> None: ...
|
35
|
+
|
36
|
+
async def error(self, msg: object, *args, **kwargs) -> None: ...
|
34
37
|
|
35
|
-
async def
|
36
|
-
"""Log an info message asynchronously."""
|
37
|
-
...
|
38
|
+
async def verbose(self, msg: object, *args, **kwargs) -> None: ...
|
38
39
|
|
39
|
-
async def
|
40
|
-
"""Log a warning message asynchronously."""
|
41
|
-
...
|
40
|
+
async def success(self, msg: object, *args, **kwargs) -> None: ...
|
42
41
|
|
43
|
-
async def
|
44
|
-
"""Log an error message asynchronously."""
|
45
|
-
...
|
42
|
+
async def failure(self, msg: object, *args, **kwargs) -> None: ...
|
@@ -0,0 +1,204 @@
|
|
1
|
+
from collections.abc import Callable, Mapping
|
2
|
+
from datetime import datetime
|
3
|
+
import sys
|
4
|
+
import threading
|
5
|
+
from time import monotonic
|
6
|
+
from typing import TYPE_CHECKING, Literal, TextIO, cast
|
7
|
+
|
8
|
+
from rich._log_render import FormatTimeCallable, LogRender
|
9
|
+
from rich._null_file import NULL_FILE
|
10
|
+
from rich.console import COLOR_SYSTEMS, Console, ConsoleThreadLocals, RenderHook, _is_jupyter, detect_legacy_windows
|
11
|
+
from rich.emoji import EmojiVariant
|
12
|
+
from rich.highlighter import NullHighlighter, ReprHighlighter
|
13
|
+
from rich.style import StyleType
|
14
|
+
from rich.text import Text
|
15
|
+
from rich.theme import Theme, ThemeStack
|
16
|
+
from rich.themes import DEFAULT
|
17
|
+
|
18
|
+
if TYPE_CHECKING:
|
19
|
+
from rich.color import ColorSystem
|
20
|
+
from rich.live import Live
|
21
|
+
from rich.segment import Segment
|
22
|
+
|
23
|
+
HighlighterType = Callable[[str, Text], Text] | Text
|
24
|
+
JUPYTER_DEFAULT_COLUMNS = 115
|
25
|
+
JUPYTER_DEFAULT_LINES = 100
|
26
|
+
WINDOWS = sys.platform == "win32"
|
27
|
+
|
28
|
+
_null_highlighter = NullHighlighter()
|
29
|
+
|
30
|
+
|
31
|
+
class LogConsole[T: TextIO](Console):
|
32
|
+
"""A Console from Rich that has added methods named after the logger methods."""
|
33
|
+
|
34
|
+
def __init__(
|
35
|
+
self,
|
36
|
+
*,
|
37
|
+
color_system: Literal["auto", "standard", "256", "truecolor", "windows"] | None = "auto",
|
38
|
+
force_terminal: bool | None = None,
|
39
|
+
force_jupyter: bool | None = None,
|
40
|
+
force_interactive: bool | None = None,
|
41
|
+
soft_wrap: bool = False,
|
42
|
+
theme: Theme | None = None,
|
43
|
+
stderr: bool = False,
|
44
|
+
file: T | None = None,
|
45
|
+
quiet: bool = False,
|
46
|
+
width: int | None = None,
|
47
|
+
height: int | None = None,
|
48
|
+
style: StyleType | None = None,
|
49
|
+
no_color: bool | None = None,
|
50
|
+
tab_size: int = 8,
|
51
|
+
record: bool = False,
|
52
|
+
markup: bool = True,
|
53
|
+
emoji: bool = True,
|
54
|
+
emoji_variant: EmojiVariant | None = None,
|
55
|
+
highlight: bool = True,
|
56
|
+
log_time: bool = True,
|
57
|
+
log_path: bool = True,
|
58
|
+
log_time_format: str | FormatTimeCallable = "[%X]",
|
59
|
+
highlighter: HighlighterType | None = ReprHighlighter(), # type: ignore[assignment] # noqa: B008
|
60
|
+
legacy_windows: bool | None = None,
|
61
|
+
safe_box: bool = True,
|
62
|
+
get_datetime: Callable[[], datetime] | None = None,
|
63
|
+
get_time: Callable[[], float] | None = None,
|
64
|
+
_environ: Mapping[str, str] | None = None,
|
65
|
+
):
|
66
|
+
# Copy of os.environ allows us to replace it for testing
|
67
|
+
if _environ is not None:
|
68
|
+
self._environ = _environ
|
69
|
+
|
70
|
+
self.is_jupyter = _is_jupyter() if force_jupyter is None else force_jupyter
|
71
|
+
if self.is_jupyter:
|
72
|
+
if width is None:
|
73
|
+
jupyter_columns = self._environ.get("JUPYTER_COLUMNS")
|
74
|
+
if jupyter_columns is not None and jupyter_columns.isdigit():
|
75
|
+
width = int(jupyter_columns)
|
76
|
+
else:
|
77
|
+
width = JUPYTER_DEFAULT_COLUMNS
|
78
|
+
if height is None:
|
79
|
+
jupyter_lines = self._environ.get("JUPYTER_LINES")
|
80
|
+
if jupyter_lines is not None and jupyter_lines.isdigit():
|
81
|
+
height = int(jupyter_lines)
|
82
|
+
else:
|
83
|
+
height = JUPYTER_DEFAULT_LINES
|
84
|
+
|
85
|
+
self.tab_size = tab_size
|
86
|
+
self.record = record
|
87
|
+
self._markup = markup
|
88
|
+
self._emoji = emoji
|
89
|
+
self._emoji_variant: EmojiVariant | None = emoji_variant
|
90
|
+
self._highlight = highlight
|
91
|
+
self.legacy_windows: bool = (
|
92
|
+
(detect_legacy_windows() and not self.is_jupyter) if legacy_windows is None else legacy_windows
|
93
|
+
)
|
94
|
+
|
95
|
+
if width is None:
|
96
|
+
columns = self._environ.get("COLUMNS")
|
97
|
+
if columns is not None and columns.isdigit():
|
98
|
+
width = int(columns) - self.legacy_windows
|
99
|
+
if height is None:
|
100
|
+
lines = self._environ.get("LINES")
|
101
|
+
if lines is not None and lines.isdigit():
|
102
|
+
height = int(lines)
|
103
|
+
|
104
|
+
self.soft_wrap = soft_wrap
|
105
|
+
self._width = width
|
106
|
+
self._height = height
|
107
|
+
|
108
|
+
self._color_system: ColorSystem | None
|
109
|
+
|
110
|
+
self._force_terminal = None
|
111
|
+
if force_terminal is not None:
|
112
|
+
self._force_terminal = force_terminal
|
113
|
+
|
114
|
+
self._file: T | None = file
|
115
|
+
self.quiet = quiet
|
116
|
+
self.stderr = stderr
|
117
|
+
|
118
|
+
if color_system is None:
|
119
|
+
self._color_system = None
|
120
|
+
elif color_system == "auto":
|
121
|
+
self._color_system = self._detect_color_system()
|
122
|
+
else:
|
123
|
+
self._color_system = COLOR_SYSTEMS[color_system]
|
124
|
+
|
125
|
+
self._lock = threading.RLock()
|
126
|
+
self._log_render = LogRender(
|
127
|
+
show_time=log_time,
|
128
|
+
show_path=log_path,
|
129
|
+
time_format=log_time_format,
|
130
|
+
)
|
131
|
+
self.highlighter: HighlighterType = highlighter or _null_highlighter # type: ignore[assignment]
|
132
|
+
self.safe_box = safe_box
|
133
|
+
self.get_datetime = get_datetime or datetime.now
|
134
|
+
self.get_time = get_time or monotonic
|
135
|
+
self.style = style
|
136
|
+
self.no_color = no_color if no_color is not None else self._environ.get("NO_COLOR", "") != ""
|
137
|
+
self.is_interactive = (
|
138
|
+
(self.is_terminal and not self.is_dumb_terminal) if force_interactive is None else force_interactive
|
139
|
+
)
|
140
|
+
|
141
|
+
self._record_buffer_lock = threading.RLock()
|
142
|
+
self._thread_locals = ConsoleThreadLocals(theme_stack=ThemeStack(DEFAULT if theme is None else theme))
|
143
|
+
self._record_buffer: list[Segment] = []
|
144
|
+
self._render_hooks: list[RenderHook] = []
|
145
|
+
self._live: Live | None = None
|
146
|
+
self._is_alt_screen = False
|
147
|
+
|
148
|
+
@property
|
149
|
+
def file(self) -> T:
|
150
|
+
"""Get the file object to write to."""
|
151
|
+
file = self._file or (sys.stderr if self.stderr else sys.stdout)
|
152
|
+
file = getattr(file, "rich_proxied_file", file)
|
153
|
+
if file is None:
|
154
|
+
file = NULL_FILE
|
155
|
+
return cast("T", file)
|
156
|
+
|
157
|
+
@file.setter
|
158
|
+
def file(self, new_file: T) -> None: # type: ignore[override]
|
159
|
+
"""Set a new file object."""
|
160
|
+
self._file = new_file
|
161
|
+
|
162
|
+
def info(self, msg: object, *args, **kwargs) -> None:
|
163
|
+
"""Log an informational message to the console."""
|
164
|
+
self.log(msg, *args, **kwargs)
|
165
|
+
|
166
|
+
def warning(self, msg: object, *args, **kwargs) -> None:
|
167
|
+
"""Log a warning message to the console."""
|
168
|
+
self.log(msg, *args, **kwargs)
|
169
|
+
|
170
|
+
def error(self, msg: object, *args, **kwargs) -> None:
|
171
|
+
"""Log an error message to the console."""
|
172
|
+
self.log(msg, *args, **kwargs)
|
173
|
+
|
174
|
+
def debug(self, msg: object, *args, **kwargs) -> None:
|
175
|
+
"""Log a debug message to the console."""
|
176
|
+
self.log(msg, *args, **kwargs)
|
177
|
+
|
178
|
+
def verbose(self, msg: object, *args, **kwargs) -> None:
|
179
|
+
"""Log a verbose message to the console."""
|
180
|
+
self.log(msg, *args, **kwargs)
|
181
|
+
|
182
|
+
def success(self, msg: object, *args, **kwargs) -> None:
|
183
|
+
"""Log a success message to the console."""
|
184
|
+
self.log(msg, *args, **kwargs)
|
185
|
+
|
186
|
+
def failure(self, msg: object, *args, **kwargs) -> None:
|
187
|
+
"""Log a failure message to the console."""
|
188
|
+
self.log(msg, *args, **kwargs)
|
189
|
+
|
190
|
+
def exception(self, msg: object, *args, **kwargs) -> None:
|
191
|
+
"""Log an exception message to the console."""
|
192
|
+
self.log(msg, *args, **kwargs)
|
193
|
+
|
194
|
+
|
195
|
+
if __name__ == "__main__":
|
196
|
+
from io import StringIO
|
197
|
+
|
198
|
+
console = LogConsole(file=StringIO())
|
199
|
+
|
200
|
+
console.info("This is an info message")
|
201
|
+
|
202
|
+
value = console.file
|
203
|
+
|
204
|
+
print(value.getvalue()) # Print the captured log messages from StringIO
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from logging import Logger
|
2
|
+
|
3
|
+
from bear_utils.logger_manager._log_level import FAILURE, SUCCESS, VERBOSE
|
4
|
+
|
5
|
+
|
6
|
+
class LoggerExtra(Logger):
|
7
|
+
"""A custom logger that just includes a few extra methods."""
|
8
|
+
|
9
|
+
def verbose(self, msg: object, *args, **kwargs) -> None:
|
10
|
+
"""Log a verbose message."""
|
11
|
+
self.log(VERBOSE, msg, *args, **kwargs)
|
12
|
+
|
13
|
+
def success(self, msg: object, *args, **kwargs) -> None:
|
14
|
+
"""Log a success message."""
|
15
|
+
self.log(SUCCESS, msg, *args, **kwargs)
|
16
|
+
|
17
|
+
def failure(self, msg: object, *args, **kwargs) -> None:
|
18
|
+
"""Log a failure message."""
|
19
|
+
self.log(FAILURE, msg, *args, **kwargs)
|
@@ -7,7 +7,6 @@ from typing import Any, Self
|
|
7
7
|
|
8
8
|
from prompt_toolkit.formatted_text import ANSI, FormattedText, to_formatted_text
|
9
9
|
from prompt_toolkit.shortcuts import print_formatted_text
|
10
|
-
from rich.console import Console
|
11
10
|
from rich.text import Text
|
12
11
|
from rich.theme import Theme
|
13
12
|
from rich.traceback import Traceback
|
@@ -15,6 +14,7 @@ from singleton_base import SingletonBase
|
|
15
14
|
|
16
15
|
from bear_utils.logger_manager._common import ExecValues, StackLevelTracker
|
17
16
|
from bear_utils.logger_manager._styles import DEFAULT_THEME, LOGGER_METHODS, LoggerExtraInfo
|
17
|
+
from bear_utils.logger_manager.loggers._console import LogConsole
|
18
18
|
|
19
19
|
from ._level_sin import INFO, add_level_name, check_level, lvl_exists
|
20
20
|
from .sub_logger import SubConsoleLogger
|
@@ -43,23 +43,23 @@ class BaseLogger(SingletonBase):
|
|
43
43
|
self.logger_mode: bool = logger_mode
|
44
44
|
self.theme: Theme = DEFAULT_THEME if theme is None else theme
|
45
45
|
self.style_disabled: bool = style_disabled
|
46
|
-
self.console:
|
47
|
-
self.console_buffer: StringIO = self.console.file
|
48
|
-
self.backup_console =
|
46
|
+
self.console: LogConsole[StringIO] = self.get_console(self.theme, style_disabled)
|
47
|
+
self.console_buffer: StringIO = self.console.file
|
48
|
+
self.backup_console = LogConsole(theme=self.theme, highlight=True, force_terminal=True)
|
49
49
|
self._generate_style_methods()
|
50
50
|
|
51
51
|
@staticmethod
|
52
|
-
def get_console(theme: Theme, style_disabled: bool) ->
|
52
|
+
def get_console(theme: Theme, style_disabled: bool) -> LogConsole:
|
53
53
|
"""Create and return a Console instance with the specified theme and styling options."""
|
54
54
|
if style_disabled:
|
55
|
-
console =
|
55
|
+
console = LogConsole(
|
56
56
|
file=StringIO(),
|
57
57
|
highlight=False,
|
58
58
|
force_terminal=True,
|
59
59
|
style=None, # Disable styling
|
60
60
|
)
|
61
61
|
else:
|
62
|
-
console:
|
62
|
+
console: LogConsole = LogConsole(
|
63
63
|
file=StringIO(),
|
64
64
|
highlight=False,
|
65
65
|
force_terminal=True,
|