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.
Files changed (83) hide show
  1. bear_utils/__init__.py +13 -0
  2. bear_utils/ai/__init__.py +30 -0
  3. bear_utils/ai/ai_helpers/__init__.py +130 -0
  4. bear_utils/ai/ai_helpers/_common.py +19 -0
  5. bear_utils/ai/ai_helpers/_config.py +24 -0
  6. bear_utils/ai/ai_helpers/_parsers.py +188 -0
  7. bear_utils/ai/ai_helpers/_types.py +20 -0
  8. bear_utils/cache/__init__.py +119 -0
  9. bear_utils/cli/__init__.py +4 -0
  10. bear_utils/cli/commands.py +59 -0
  11. bear_utils/cli/prompt_helpers.py +166 -0
  12. bear_utils/cli/shell/__init__.py +0 -0
  13. bear_utils/cli/shell/_base_command.py +74 -0
  14. bear_utils/cli/shell/_base_shell.py +390 -0
  15. bear_utils/cli/shell/_common.py +19 -0
  16. bear_utils/config/__init__.py +11 -0
  17. bear_utils/config/config_manager.py +92 -0
  18. bear_utils/config/dir_manager.py +64 -0
  19. bear_utils/config/settings_manager.py +232 -0
  20. bear_utils/constants/__init__.py +16 -0
  21. bear_utils/constants/_exceptions.py +3 -0
  22. bear_utils/constants/_lazy_typing.py +15 -0
  23. bear_utils/constants/date_related.py +36 -0
  24. bear_utils/constants/time_related.py +22 -0
  25. bear_utils/database/__init__.py +6 -0
  26. bear_utils/database/_db_manager.py +104 -0
  27. bear_utils/events/__init__.py +16 -0
  28. bear_utils/events/events_class.py +52 -0
  29. bear_utils/events/events_module.py +65 -0
  30. bear_utils/extras/__init__.py +17 -0
  31. bear_utils/extras/_async_helpers.py +15 -0
  32. bear_utils/extras/_tools.py +178 -0
  33. bear_utils/extras/platform_utils.py +53 -0
  34. bear_utils/extras/wrappers/__init__.py +0 -0
  35. bear_utils/extras/wrappers/add_methods.py +98 -0
  36. bear_utils/files/__init__.py +4 -0
  37. bear_utils/files/file_handlers/__init__.py +3 -0
  38. bear_utils/files/file_handlers/_base_file_handler.py +93 -0
  39. bear_utils/files/file_handlers/file_handler_factory.py +278 -0
  40. bear_utils/files/file_handlers/json_file_handler.py +44 -0
  41. bear_utils/files/file_handlers/log_file_handler.py +33 -0
  42. bear_utils/files/file_handlers/txt_file_handler.py +34 -0
  43. bear_utils/files/file_handlers/yaml_file_handler.py +57 -0
  44. bear_utils/files/ignore_parser.py +298 -0
  45. bear_utils/graphics/__init__.py +4 -0
  46. bear_utils/graphics/bear_gradient.py +140 -0
  47. bear_utils/graphics/image_helpers.py +39 -0
  48. bear_utils/gui/__init__.py +3 -0
  49. bear_utils/gui/gui_tools/__init__.py +5 -0
  50. bear_utils/gui/gui_tools/_settings.py +37 -0
  51. bear_utils/gui/gui_tools/_types.py +12 -0
  52. bear_utils/gui/gui_tools/qt_app.py +145 -0
  53. bear_utils/gui/gui_tools/qt_color_picker.py +119 -0
  54. bear_utils/gui/gui_tools/qt_file_handler.py +138 -0
  55. bear_utils/gui/gui_tools/qt_input_dialog.py +306 -0
  56. bear_utils/logging/__init__.py +25 -0
  57. bear_utils/logging/logger_manager/__init__.py +0 -0
  58. bear_utils/logging/logger_manager/_common.py +47 -0
  59. bear_utils/logging/logger_manager/_console_junk.py +131 -0
  60. bear_utils/logging/logger_manager/_styles.py +91 -0
  61. bear_utils/logging/logger_manager/loggers/__init__.py +0 -0
  62. bear_utils/logging/logger_manager/loggers/_base_logger.py +238 -0
  63. bear_utils/logging/logger_manager/loggers/_base_logger.pyi +50 -0
  64. bear_utils/logging/logger_manager/loggers/_buffer_logger.py +55 -0
  65. bear_utils/logging/logger_manager/loggers/_console_logger.py +249 -0
  66. bear_utils/logging/logger_manager/loggers/_console_logger.pyi +64 -0
  67. bear_utils/logging/logger_manager/loggers/_file_logger.py +141 -0
  68. bear_utils/logging/logger_manager/loggers/_level_sin.py +58 -0
  69. bear_utils/logging/logger_manager/loggers/_logger.py +18 -0
  70. bear_utils/logging/logger_manager/loggers/_sub_logger.py +110 -0
  71. bear_utils/logging/logger_manager/loggers/_sub_logger.pyi +38 -0
  72. bear_utils/logging/loggers.py +76 -0
  73. bear_utils/monitoring/__init__.py +10 -0
  74. bear_utils/monitoring/host_monitor.py +350 -0
  75. bear_utils/time/__init__.py +16 -0
  76. bear_utils/time/_helpers.py +91 -0
  77. bear_utils/time/_time_class.py +316 -0
  78. bear_utils/time/_timer.py +80 -0
  79. bear_utils/time/_tools.py +17 -0
  80. bear_utils/time/time_manager.py +218 -0
  81. bear_utils-0.7.11.dist-info/METADATA +260 -0
  82. bear_utils-0.7.11.dist-info/RECORD +83 -0
  83. bear_utils-0.7.11.dist-info/WHEEL +4 -0
@@ -0,0 +1,249 @@
1
+ # region Imports
2
+ from functools import cached_property
3
+ from logging import DEBUG, Formatter, Handler, Logger
4
+ from logging.handlers import QueueHandler, QueueListener, RotatingFileHandler
5
+ from queue import Queue
6
+ from typing import override
7
+
8
+ from prompt_toolkit import PromptSession
9
+ from rich.text import Text
10
+ from rich.theme import Theme
11
+ from rich.traceback import Traceback
12
+
13
+ from bear_utils.constants.date_related import DATE_TIME_FORMAT
14
+
15
+ from .._common import FIVE_MEGABYTES, VERBOSE_CONSOLE_FORMAT, VERBOSE_FORMAT, ExecValues
16
+ from .._console_junk import ConsoleBuffering, ConsoleFormatter, ConsoleHandler
17
+ from ._base_logger import BaseLogger
18
+
19
+ # endregion Imports
20
+
21
+
22
+ class ConsoleLogger(Logger, BaseLogger):
23
+ """
24
+ A comprehensive console logger that combines Python's logging framework with Rich console styling.
25
+
26
+ This logger provides styled console output with configurable file logging, queue handling,
27
+ buffering, and interactive input capabilities. It dynamically creates logging methods
28
+ (info, error, debug, etc.) that forward to Rich's styled console printing.
29
+
30
+ Features:
31
+ - Rich styled console output with themes
32
+ - Optional file logging with rotation
33
+ - Queue-based async logging
34
+ - Message buffering capabilities
35
+ - Interactive prompt integration
36
+ - Exception tracebacks with local variables
37
+
38
+ Example:
39
+ logger = ConsoleLogger.get_instance(init=True, verbose=True, name="MyLogger", level=DEBUG)
40
+ logger.info("This is styled info")
41
+ logger.error("This is styled error")
42
+ logger.success("This is styled success")
43
+ """
44
+
45
+ # region Setup
46
+ def __init__(
47
+ self,
48
+ theme: Theme | None = None,
49
+ name: str = "ConsoleLogger",
50
+ level: int = DEBUG,
51
+ disabled: bool = True,
52
+ console: bool = True,
53
+ file: bool = False,
54
+ queue_handler: bool = False,
55
+ buffering: bool = False,
56
+ *args,
57
+ **kwargs,
58
+ ) -> None:
59
+ Logger.__init__(self, name=name, level=level)
60
+ BaseLogger.__init__(
61
+ self,
62
+ output_handler=self._console_output,
63
+ theme=theme,
64
+ style_disabled=kwargs.get("style_disabled", False),
65
+ logger_mode=kwargs.get("logger_mode", True),
66
+ level=level,
67
+ )
68
+ self.name = name
69
+ self.level = level
70
+ self.setLevel(level)
71
+ self.session = None
72
+ self.disabled = disabled
73
+ self._handlers: list[Handler] = []
74
+ self.logger_mode: bool = kwargs.pop("logger_mode", True)
75
+ if self.logger_mode:
76
+ self.disabled = False
77
+ self._handle_enable_booleans(
78
+ file=file,
79
+ console=console,
80
+ buffering=buffering,
81
+ queue_handler=queue_handler,
82
+ **kwargs,
83
+ )
84
+
85
+ def _handle_enable_booleans(self, file, console, buffering, queue_handler, **kwargs) -> None:
86
+ """Configure logging handlers based on initialization parameters."""
87
+ if console or buffering:
88
+ self.console_handler: ConsoleHandler = ConsoleHandler(self.print, self.output_buffer)
89
+ self.console_handler.setFormatter(ConsoleFormatter(fmt=VERBOSE_CONSOLE_FORMAT, datefmt=DATE_TIME_FORMAT))
90
+ self.console_handler.setLevel(self.level)
91
+ if console:
92
+ self._handlers.append(self.console_handler)
93
+ if buffering:
94
+ self.buffer_handler: ConsoleBuffering = ConsoleBuffering(console_handler=self.console_handler)
95
+ self.addHandler(self.buffer_handler)
96
+ if file:
97
+ self.file_handler: RotatingFileHandler = RotatingFileHandler(
98
+ filename=kwargs.get("file_path", "console.log"),
99
+ maxBytes=kwargs.get("max_bytes", FIVE_MEGABYTES),
100
+ backupCount=kwargs.get("backup_count", 5),
101
+ )
102
+ self.file_handler.setFormatter(Formatter(fmt=VERBOSE_FORMAT, datefmt=DATE_TIME_FORMAT))
103
+ self.file_handler.setLevel(self.level)
104
+ self._handlers.append(self.file_handler)
105
+ if queue_handler:
106
+ self.queue = Queue()
107
+ self.queue_handler = QueueHandler(self.queue)
108
+ self.addHandler(self.queue_handler)
109
+ self.listener = QueueListener(self.queue, *self._handlers)
110
+ self.listener.start()
111
+ else:
112
+ for handler in self._handlers:
113
+ self.addHandler(handler)
114
+
115
+ def stop_queue_listener(self) -> None:
116
+ """Stop the queue listener if it exists and clean up resources."""
117
+ if hasattr(self, "listener"):
118
+ self.verbose("ConsoleLogger: QueueListener stopped and cleaned up.")
119
+ self.listener.stop()
120
+ del self.listener
121
+ del self.queue
122
+ del self.queue_handler
123
+
124
+ def trigger_buffer_flush(self) -> Text:
125
+ """Flush buffered messages to console output."""
126
+ if hasattr(self, "buffer_handler"):
127
+ return self.buffer_handler.flush_to_output()
128
+ return Text("No buffering handler available.", style="bold red")
129
+
130
+ def set_base_level(self, level: int) -> None:
131
+ """Set the base logging level for the console logger."""
132
+ super().set_base_level(level)
133
+ self.setLevel(level)
134
+ if hasattr(self, "console_handler"):
135
+ self.console_handler.setLevel(level)
136
+ if hasattr(self, "buffer_handler"):
137
+ self.buffer_handler.setLevel(level)
138
+ if hasattr(self, "queue_handler"):
139
+ self.queue_handler.setLevel(level)
140
+
141
+ def _console_output(self, msg: object, extra, *args, **kwargs) -> None:
142
+ """Console-specific output handler that integrates with logging module."""
143
+ if not self.logger_mode:
144
+ self.print(msg, *args, **kwargs)
145
+ else:
146
+ kwargs.pop("style", None)
147
+ self.log(
148
+ level=extra.get("log_level", DEBUG),
149
+ msg=msg,
150
+ extra=extra,
151
+ *args,
152
+ **kwargs,
153
+ )
154
+
155
+ # endregion Setup
156
+
157
+ # region Utility Methods
158
+
159
+ async def input(self, msg: str, style="info", **kwargs) -> str:
160
+ """Display a styled prompt and return user input asynchronously."""
161
+ if not self.session:
162
+ self.session = PromptSession(**kwargs)
163
+ self.print(msg, style=style)
164
+ return await self.session.prompt_async()
165
+
166
+ def output_buffer(
167
+ self, msg: object, end="\n", exc_info=None, exec_values: ExecValues | None = None, *args, **kwargs
168
+ ) -> str:
169
+ """Capture console output to a string buffer without printing to terminal."""
170
+ if exc_info and exec_values:
171
+ exception: Traceback = self._get_exception(manual=True, exec_values=exec_values)
172
+ self.console.print(exception, end=end)
173
+ self.console.print(msg, end="", style=kwargs.get("style", "info"))
174
+ output = self.console_buffer.getvalue()
175
+ self._reset_buffer()
176
+ return output
177
+
178
+ # endregion Utility Methods
179
+
180
+ # region Enhanced Print Methods
181
+
182
+ def print(self, msg: object, end="\n", exc_info=None, extra: dict | None = None, *args, **kwargs) -> None | str:
183
+ """
184
+ Print styled messages with enhanced exception handling and JSON support.
185
+
186
+ Extends the base print method with proper exception tracebacks and
187
+ integrated JSON printing for structured data output.
188
+ """
189
+ if exc_info is not None:
190
+ try:
191
+ self._print(self._get_exception(), end=end, width=100, show_locals=True, **kwargs)
192
+ except ValueError:
193
+ ...
194
+
195
+ self._print(msg, end=end, *args, **kwargs)
196
+
197
+ if extra:
198
+ self._print(msg=extra, end=end, json=True, indent=4)
199
+
200
+ @cached_property
201
+ def stack_level(self) -> int:
202
+ """Cached property to retrieve the current stack level."""
203
+ return self.stack_tracker.record_end()
204
+
205
+ @override
206
+ def _log(self, level, msg, args, exc_info=None, extra=None, stack_info=False, stacklevel=None):
207
+ """
208
+ Custom logging implementation with enhanced exception handling.
209
+
210
+ Overrides the standard logging._log method to provide better exception
211
+ value extraction for Rich traceback integration while respecting log levels.
212
+ """
213
+ stacklevel = stacklevel or self.stack_level
214
+ try:
215
+ fn, lno, func, sinfo = self.findCaller(stack_info, stacklevel)
216
+ except ValueError:
217
+ fn, lno, func, sinfo = "(unknown file)", 0, "(unknown function)", None
218
+
219
+ final_extra = extra or {}
220
+
221
+ if exc_info is not None:
222
+ exec_values = self._extract_exception_values(exc_info)
223
+ if exec_values:
224
+ final_extra = {**final_extra, "exec_values": exec_values}
225
+
226
+ record = self.makeRecord(
227
+ name=self.name,
228
+ level=level,
229
+ fn=fn,
230
+ lno=lno,
231
+ msg=msg,
232
+ args=args,
233
+ exc_info=None,
234
+ func=func,
235
+ extra=final_extra,
236
+ sinfo=sinfo,
237
+ )
238
+
239
+ self.handle(record)
240
+
241
+ def exit(self) -> None:
242
+ """Clean up resources including queue listeners and console buffers."""
243
+ if hasattr(self, "queue_handler"):
244
+ self.queue_handler.flush()
245
+ self.stop_queue_listener()
246
+
247
+ self.console_buffer.close()
248
+
249
+ # endregion Enhanced Print Methods
@@ -0,0 +1,64 @@
1
+ from logging import DEBUG, Logger
2
+ from logging.handlers import QueueHandler, RotatingFileHandler
3
+ from typing import Any
4
+
5
+ from rich.text import Text
6
+ from rich.theme import Theme
7
+
8
+ from .._console_junk import ConsoleBuffering, ConsoleHandler
9
+ from ._base_logger import BaseLogger
10
+ from ._sub_logger import SubConsoleLogger
11
+
12
+ class ConsoleLogger(Logger, BaseLogger):
13
+
14
+ name: str
15
+ level: int
16
+ file: bool
17
+ queue_handler: QueueHandler
18
+ buffer_handler: ConsoleBuffering
19
+ console_handler: ConsoleHandler
20
+ file_handler: RotatingFileHandler
21
+ sub_logger: dict[str, "SubConsoleLogger[ConsoleLogger]"]
22
+
23
+ def __init__(
24
+ self,
25
+ theme: Theme | None = None,
26
+ name: str = "ConsoleLogger",
27
+ level: int = DEBUG,
28
+ disabled: bool = True,
29
+ queue_handler: bool = False,
30
+ buffering: bool = False,
31
+ file: bool = False,
32
+ console: bool = True,
33
+ style_disabled: bool = False,
34
+ logger_mode: bool = True,
35
+ *args,
36
+ **kwargs,
37
+ ) -> None:
38
+ """
39
+ A general purpose console logger that that by default is a wrapper around both Rich and Python's logging module.
40
+
41
+ Args:
42
+ theme (Theme | None): The theme for the logger.
43
+ name (str): The name of the logger.
44
+ level (int): The logging level.
45
+ disabled (bool): Whether the logger is disabled.
46
+ queue_handler (bool): Whether to use a queue handler.
47
+ buffering (bool): Whether to use buffering.
48
+ file (bool): Whether to log to a file.
49
+ style_disabled (bool): Whether to disable styling.
50
+ logger_mode (bool): Whether to enable logger mode.
51
+ """
52
+ # fmt: off
53
+ def debug(self, msg: object, *args, **kwargs: Any) -> None: ...
54
+ def info(self, msg: object, *args, **kwargs: Any) -> None: ...
55
+ def warning(self, msg: object, *args, **kwargs: Any) -> None: ...
56
+ def error(self, msg: object, *args, **kwargs: Any) -> None: ...
57
+ def success(self, msg: str, *args, **kwargs: Any) -> None: ...
58
+ def failure(self, msg: str, *args, **kwargs: Any) -> None: ...
59
+ def verbose(self, msg: str, *args, **kwargs: Any) -> None: ...
60
+ def set_base_level(self, level: int) -> None: ...
61
+ def input(self, msg: str, style: str = "info") -> str: ...
62
+ def trigger_buffer_flush(self) -> str | Text: ...
63
+ def print(self, msg: object, end: str="\n", exc_info=None, extra: dict | None = None, *args, **kwargs) -> None | str: ...
64
+ def exit(self) -> None: ...
@@ -0,0 +1,141 @@
1
+ from logging import DEBUG
2
+ from pathlib import Path
3
+ from typing import Any, override
4
+
5
+ from rich.theme import Theme
6
+
7
+ from bear_utils.constants.date_related import DATE_TIME_FORMAT
8
+
9
+ from .._common import FIVE_MEGABYTES
10
+ from .._styles import LoggerExtraInfo
11
+ from ._console_logger import ConsoleLogger
12
+ from ._sub_logger import SubConsoleLogger
13
+
14
+
15
+ class FileLogger(ConsoleLogger):
16
+ """
17
+ A file-based logger that writes styled log messages to files.
18
+
19
+ Combines Python's logging framework with Rich console styling, but outputs
20
+ to files instead of console. Supports file rotation, custom formatting,
21
+ and maintains the same interface as other loggers.
22
+
23
+ Features:
24
+ - File logging with rotation
25
+ - Rich-style method generation (info, error, debug, etc.)
26
+ - Consistent print() interface
27
+ - Exception tracebacks in file format
28
+ - JSON logging support
29
+
30
+ Example:
31
+ logger = FileLogger.get_instance(
32
+ init=True,
33
+ name="FileLogger",
34
+ file_path="app.log",
35
+ max_bytes=10*1024*1024,
36
+ backup_count=5
37
+ )
38
+ logger.info("This goes to the file")
39
+ logger.error("This error is logged to file")
40
+ """
41
+
42
+ def __init__(
43
+ self,
44
+ theme: Theme | None = None,
45
+ name: str = "FileLogger",
46
+ level: int = DEBUG,
47
+ file_path: str = "app.log",
48
+ max_bytes: int = FIVE_MEGABYTES,
49
+ backup_count: int = 5,
50
+ *args,
51
+ **kwargs,
52
+ ) -> None:
53
+ self.file_path = Path(file_path) # TODO: Add more options for filename patterns (timestamps, process IDs, etc.)
54
+ self.max_bytes = max_bytes
55
+ self.backup_count = backup_count
56
+ ConsoleLogger.__init__(
57
+ self,
58
+ name=name,
59
+ level=level,
60
+ file=True,
61
+ console=False,
62
+ queue_handler=kwargs.pop("queue_handler", False),
63
+ file_path=self.file_path,
64
+ max_bytes=self.max_bytes,
65
+ backup_count=self.backup_count,
66
+ theme=theme,
67
+ style_disabled=True,
68
+ logger_mode=True,
69
+ **kwargs,
70
+ )
71
+
72
+ @override
73
+ def get_sub_logger(self, namespace: str, **kwargs: Any) -> SubConsoleLogger:
74
+ return SubConsoleLogger(self, namespace, **kwargs)
75
+
76
+ @override
77
+ def replacement_method(self, msg: object, *args, **kwargs) -> None:
78
+ """Handle logging method calls with proper file logging integration."""
79
+ extra: LoggerExtraInfo = kwargs.pop("injected_extra")
80
+ if kwargs.get("extra"):
81
+ extra.update(kwargs.pop("extra"))
82
+ if extra.get("namespace"):
83
+ msg = f"<{extra.get('namespace')}> {msg}"
84
+ if self.stack_tracker.not_set:
85
+ self.stack_tracker.record_start()
86
+
87
+ self.log(
88
+ level=extra.get("log_level", DEBUG),
89
+ msg=msg,
90
+ extra=extra,
91
+ *args,
92
+ **kwargs,
93
+ )
94
+
95
+ def print(self, msg: object, end: str = "\n", exc_info=None, extra: dict | None = None, *args, **kwargs) -> None:
96
+ """
97
+ Print a message to the file with proper formatting.
98
+
99
+ Maintains the same interface as other loggers but writes to file instead of console.
100
+ """
101
+ try:
102
+ # For file logging, we want to use the logging system rather than direct file writing
103
+ # This ensures proper formatting, rotation, etc.
104
+ if exc_info is not None:
105
+ # Log with exception info
106
+ self.error(f"{msg}", exc_info=exc_info, extra=extra)
107
+ else:
108
+ # Regular info log
109
+ self.info(f"{msg}", extra=extra)
110
+
111
+ if extra:
112
+ # Log extra data as JSON-like format
113
+ self.info(f"Extra data: {extra}")
114
+ except Exception as e:
115
+ print(f"FileLogger: Failed to write to log file. Message: {msg}, Error: {e}")
116
+
117
+ def get_file_size(self) -> int:
118
+ """Get current size of the log file in bytes."""
119
+ if self.file_path.exists():
120
+ return self.file_path.stat().st_size
121
+ return 0
122
+
123
+ def get_file_path(self) -> Path:
124
+ """Get the current log file path."""
125
+ return self.file_path
126
+
127
+ def rotate_file(self) -> None:
128
+ """Manually trigger file rotation."""
129
+ if hasattr(self.file_handler, "doRollover"):
130
+ self.file_handler.doRollover()
131
+
132
+ @override
133
+ def exit(self) -> None:
134
+ """Clean up file resources."""
135
+ if hasattr(self, "file_handler"):
136
+ self.file_handler.flush()
137
+ self.file_handler.close()
138
+ self.removeHandler(self.file_handler)
139
+
140
+ # Call parent exit
141
+ super().exit()
@@ -0,0 +1,58 @@
1
+ import threading
2
+ from logging import addLevelName
3
+ from typing import Literal
4
+
5
+ ERROR: Literal[40] = 40
6
+ WARNING: Literal[30] = 30
7
+ WARN: Literal[30] = WARNING
8
+ INFO: Literal[20] = 20
9
+ DEBUG: Literal[10] = 10
10
+ NOTSET: Literal[0] = 0
11
+
12
+ _levelToName = {
13
+ ERROR: "ERROR",
14
+ WARNING: "WARNING",
15
+ INFO: "INFO",
16
+ DEBUG: "DEBUG",
17
+ NOTSET: "NOTSET",
18
+ }
19
+ _nameToLevel = {
20
+ "ERROR": ERROR,
21
+ "WARN": WARNING,
22
+ "WARNING": WARNING,
23
+ "INFO": INFO,
24
+ "DEBUG": DEBUG,
25
+ "NOTSET": NOTSET,
26
+ }
27
+
28
+ _lock = threading.RLock()
29
+
30
+
31
+ def lvl_exists(level: int | str) -> bool:
32
+ """Check if a logging level already exists."""
33
+ with _lock:
34
+ level = check_level(level, fail=False)
35
+ return level in _levelToName
36
+
37
+
38
+ def add_level_name(level: int, name: str) -> None:
39
+ """Add a custom logging level name."""
40
+ with _lock:
41
+ if level in _levelToName:
42
+ raise ValueError(f"Level {level} already exists with name {_levelToName[level]}")
43
+ _levelToName[level] = name.upper()
44
+ _nameToLevel[name.upper()] = level
45
+ addLevelName(level, name)
46
+
47
+
48
+ def check_level(level: int | str, fail: bool = True) -> int:
49
+ """Validate and normalize logging level to integer."""
50
+ if isinstance(level, str) and level.upper() in _nameToLevel:
51
+ return _nameToLevel[level.upper()]
52
+ if isinstance(level, int) and level in _levelToName:
53
+ return level
54
+ if fail:
55
+ if not isinstance(level, (int, str)):
56
+ raise TypeError(f"Level must be int or str, got {type(level).__name__}: {level!r}")
57
+ raise ValueError(f"Invalid logging level: {level!r}. Valid levels are: {list(_nameToLevel.keys())}")
58
+ return 999 # Return a high value to indicate invalid level
@@ -0,0 +1,18 @@
1
+ from logging import Logger
2
+
3
+ from ._base_logger import BaseLogger
4
+
5
+
6
+ class ExtraBaseLogger(Logger, BaseLogger):
7
+ """
8
+ Base logger class that extends the standard logging.Logger and BaseLogger.
9
+ This class is intended to be used as a base for custom loggers that require
10
+ additional functionality or attributes.
11
+ """
12
+
13
+ def __init__(self, name, level=0, **kwargs):
14
+ """
15
+ Initialize the ExtraBaseLogger with a name and level.
16
+ """
17
+ super().__init__(name, level)
18
+ BaseLogger.__init__(self, **kwargs)
@@ -0,0 +1,110 @@
1
+ """
2
+ Logger adapter module that provides an alternative to SubLogger using standard library tools.
3
+
4
+ This implementation shows how to use LoggerAdapter and contextvars to maintain
5
+ all the functionality of the existing SubLogger while reducing complexity.
6
+ """
7
+
8
+ from functools import partial
9
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar
10
+
11
+ from rich.text import Text
12
+
13
+ from .._styles import LOGGER_METHODS, LoggerExtraInfo
14
+ from ._level_sin import check_level
15
+
16
+ if TYPE_CHECKING:
17
+ from ._base_logger import BaseLogger
18
+
19
+ T = TypeVar("T", bound="BaseLogger")
20
+
21
+
22
+ class SubConsoleLogger(Generic[T]):
23
+ def __init__(self, logger: T, namespace: str, **kwargs) -> None:
24
+ """
25
+ Initialize the SubConsoleLogger with a logger and a namespace.
26
+
27
+ Args:
28
+ logger: The underlying logger to wrap with a namespace.
29
+ namespace: The namespace to prefix log messages with
30
+ """
31
+ self.logger: T = logger
32
+ self._level: int = kwargs.get("level", None) or logger._level
33
+ self.logger_mode = logger.logger_mode
34
+ self.namespace: str = namespace
35
+ self._setup(self.namespace)
36
+
37
+ def print(
38
+ self,
39
+ msg: object,
40
+ end: str = "\n",
41
+ exc_info: Any = None,
42
+ extra: dict | None = None,
43
+ *args,
44
+ **kwargs,
45
+ ) -> None | str:
46
+ """
47
+ Print a message to the console with the specified formatting.
48
+
49
+ This method allows for printing messages with additional context and formatting.
50
+ """
51
+ return self.logger.print(msg, end=end, exc_info=exc_info, extra=extra, **kwargs)
52
+
53
+ def set_sub_level(self, level: int) -> None:
54
+ """
55
+ Set the logging level for the logger.
56
+
57
+ This method allows changing the logging level dynamically.
58
+ """
59
+ self._level = level
60
+
61
+ def filter_by_level(self, level: int | str) -> bool:
62
+ """
63
+ Filter method to determine if a message should be logged based on its level.
64
+
65
+ This method checks if the provided level is greater than or equal to the logger's level.
66
+
67
+ Args:
68
+ level (int | str): The logging level of the message.
69
+
70
+ Returns:
71
+ bool: True if the message should be logged, False otherwise.
72
+ """
73
+ level = check_level(level)
74
+ bool_check = level >= self._level
75
+ return bool_check
76
+
77
+ def trigger_buffer_flush(self) -> str | Text:
78
+ """
79
+ Flush buffered messages to console output.
80
+
81
+ This method is used to ensure that any buffered log messages are printed
82
+ to the console, similar to the SubLogger's buffer flush functionality.
83
+ """
84
+ return self.logger.trigger_buffer_flush()
85
+
86
+ def _setup(self, name: str) -> None:
87
+ """
88
+ Get an attribute from the logger.
89
+
90
+ This allows for accessing logger methods directly.
91
+ """
92
+ filter_func = self.filter_by_level
93
+ for style_name, og_extra in LOGGER_METHODS.items():
94
+ extra: LoggerExtraInfo = og_extra.copy()
95
+
96
+ extra["namespace"] = name
97
+
98
+ setattr(
99
+ self,
100
+ style_name,
101
+ partial(
102
+ self.logger.replacement_method,
103
+ injected_extra=extra,
104
+ filter_func=filter_func,
105
+ ),
106
+ )
107
+
108
+ def __repr__(self) -> str:
109
+ """String representation of the adapter."""
110
+ return f"ConsoleAdapter(namespace={self.namespace}"
@@ -0,0 +1,38 @@
1
+ """
2
+ Logger adapter module that provides an alternative to SubLogger using standard library tools.
3
+
4
+ This implementation shows how to use LoggerAdapter and contextvars to maintain
5
+ all the functionality of the existing SubLogger while reducing complexity.
6
+ """
7
+
8
+ from typing import Any, Generic, TypeVar
9
+
10
+ from ._base_logger import BaseLogger
11
+
12
+ T = TypeVar("T", bound=BaseLogger)
13
+
14
+ class SubConsoleLogger(Generic[T]):
15
+ """
16
+ Enhanced logger adapter that supports style-based logging and namespace prefixing.
17
+
18
+ This class provides an alternative to the SubLogger implementation while
19
+ maintaining compatibility with the existing ConsoleLogger.
20
+ """
21
+
22
+ logger: T
23
+ _level: int
24
+ namespace: str
25
+ extra: dict[str, Any] | None
26
+ log_level: int
27
+ # fmt: off
28
+ def __init__(self, logger: T, namespace: str, **kwargs) -> None: ...
29
+ def set_sub_level(self, level: int) -> None: ...
30
+ def success(self, msg: object, *args, **kwargs) -> None: ...
31
+ def failure(self, msg: object, *args, **kwargs) -> None: ...
32
+ def verbose(self, msg: object, *args, **kwargs) -> None: ...
33
+ def info(self, msg: object, *args, **kwargs) -> None: ...
34
+ def debug(self, msg: object, *args, **kwargs) -> None: ...
35
+ def warning(self, msg: object, *args, **kwargs) -> None: ...
36
+ def error(self, msg: object, *args, **kwargs) -> None: ...
37
+ def print(self, msg: object, end: str = "\n", exc_info: Any = None, extra: dict | None = None, *args, **kwargs: Any) -> None | str: ...
38
+ def __repr__(self) -> str: ...