bear-utils 0.0.1__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 (107) hide show
  1. bear_utils/__init__.py +51 -0
  2. bear_utils/__main__.py +14 -0
  3. bear_utils/_internal/__init__.py +0 -0
  4. bear_utils/_internal/_version.py +1 -0
  5. bear_utils/_internal/cli.py +119 -0
  6. bear_utils/_internal/debug.py +174 -0
  7. bear_utils/ai/__init__.py +30 -0
  8. bear_utils/ai/ai_helpers/__init__.py +136 -0
  9. bear_utils/ai/ai_helpers/_common.py +19 -0
  10. bear_utils/ai/ai_helpers/_config.py +24 -0
  11. bear_utils/ai/ai_helpers/_parsers.py +194 -0
  12. bear_utils/ai/ai_helpers/_types.py +15 -0
  13. bear_utils/cache/__init__.py +131 -0
  14. bear_utils/cli/__init__.py +22 -0
  15. bear_utils/cli/_args.py +12 -0
  16. bear_utils/cli/_get_version.py +207 -0
  17. bear_utils/cli/commands.py +105 -0
  18. bear_utils/cli/prompt_helpers.py +186 -0
  19. bear_utils/cli/shell/__init__.py +1 -0
  20. bear_utils/cli/shell/_base_command.py +81 -0
  21. bear_utils/cli/shell/_base_shell.py +430 -0
  22. bear_utils/cli/shell/_common.py +19 -0
  23. bear_utils/cli/typer_bridge.py +90 -0
  24. bear_utils/config/__init__.py +13 -0
  25. bear_utils/config/config_manager.py +229 -0
  26. bear_utils/config/dir_manager.py +69 -0
  27. bear_utils/config/settings_manager.py +179 -0
  28. bear_utils/constants/__init__.py +90 -0
  29. bear_utils/constants/_exceptions.py +8 -0
  30. bear_utils/constants/_exit_code.py +60 -0
  31. bear_utils/constants/_http_status_code.py +37 -0
  32. bear_utils/constants/_lazy_typing.py +15 -0
  33. bear_utils/constants/_meta.py +196 -0
  34. bear_utils/constants/date_related.py +25 -0
  35. bear_utils/constants/time_related.py +24 -0
  36. bear_utils/database/__init__.py +8 -0
  37. bear_utils/database/_db_manager.py +98 -0
  38. bear_utils/events/__init__.py +18 -0
  39. bear_utils/events/events_class.py +52 -0
  40. bear_utils/events/events_module.py +74 -0
  41. bear_utils/extras/__init__.py +28 -0
  42. bear_utils/extras/_async_helpers.py +67 -0
  43. bear_utils/extras/_tools.py +185 -0
  44. bear_utils/extras/_zapper.py +399 -0
  45. bear_utils/extras/platform_utils.py +57 -0
  46. bear_utils/extras/responses/__init__.py +5 -0
  47. bear_utils/extras/responses/function_response.py +451 -0
  48. bear_utils/extras/wrappers/__init__.py +1 -0
  49. bear_utils/extras/wrappers/add_methods.py +100 -0
  50. bear_utils/extras/wrappers/string_io.py +46 -0
  51. bear_utils/files/__init__.py +6 -0
  52. bear_utils/files/file_handlers/__init__.py +5 -0
  53. bear_utils/files/file_handlers/_base_file_handler.py +107 -0
  54. bear_utils/files/file_handlers/file_handler_factory.py +280 -0
  55. bear_utils/files/file_handlers/json_file_handler.py +71 -0
  56. bear_utils/files/file_handlers/log_file_handler.py +40 -0
  57. bear_utils/files/file_handlers/toml_file_handler.py +76 -0
  58. bear_utils/files/file_handlers/txt_file_handler.py +76 -0
  59. bear_utils/files/file_handlers/yaml_file_handler.py +64 -0
  60. bear_utils/files/ignore_parser.py +293 -0
  61. bear_utils/graphics/__init__.py +6 -0
  62. bear_utils/graphics/bear_gradient.py +145 -0
  63. bear_utils/graphics/font/__init__.py +13 -0
  64. bear_utils/graphics/font/_raw_block_letters.py +463 -0
  65. bear_utils/graphics/font/_theme.py +31 -0
  66. bear_utils/graphics/font/_utils.py +220 -0
  67. bear_utils/graphics/font/block_font.py +192 -0
  68. bear_utils/graphics/font/glitch_font.py +63 -0
  69. bear_utils/graphics/image_helpers.py +45 -0
  70. bear_utils/gui/__init__.py +8 -0
  71. bear_utils/gui/gui_tools/__init__.py +10 -0
  72. bear_utils/gui/gui_tools/_settings.py +36 -0
  73. bear_utils/gui/gui_tools/_types.py +12 -0
  74. bear_utils/gui/gui_tools/qt_app.py +150 -0
  75. bear_utils/gui/gui_tools/qt_color_picker.py +130 -0
  76. bear_utils/gui/gui_tools/qt_file_handler.py +130 -0
  77. bear_utils/gui/gui_tools/qt_input_dialog.py +303 -0
  78. bear_utils/logger_manager/__init__.py +109 -0
  79. bear_utils/logger_manager/_common.py +63 -0
  80. bear_utils/logger_manager/_console_junk.py +135 -0
  81. bear_utils/logger_manager/_log_level.py +50 -0
  82. bear_utils/logger_manager/_styles.py +95 -0
  83. bear_utils/logger_manager/logger_protocol.py +42 -0
  84. bear_utils/logger_manager/loggers/__init__.py +1 -0
  85. bear_utils/logger_manager/loggers/_console.py +223 -0
  86. bear_utils/logger_manager/loggers/_level_sin.py +61 -0
  87. bear_utils/logger_manager/loggers/_logger.py +19 -0
  88. bear_utils/logger_manager/loggers/base_logger.py +244 -0
  89. bear_utils/logger_manager/loggers/base_logger.pyi +51 -0
  90. bear_utils/logger_manager/loggers/basic_logger/__init__.py +5 -0
  91. bear_utils/logger_manager/loggers/basic_logger/logger.py +80 -0
  92. bear_utils/logger_manager/loggers/basic_logger/logger.pyi +19 -0
  93. bear_utils/logger_manager/loggers/buffer_logger.py +57 -0
  94. bear_utils/logger_manager/loggers/console_logger.py +278 -0
  95. bear_utils/logger_manager/loggers/console_logger.pyi +50 -0
  96. bear_utils/logger_manager/loggers/fastapi_logger.py +333 -0
  97. bear_utils/logger_manager/loggers/file_logger.py +151 -0
  98. bear_utils/logger_manager/loggers/simple_logger.py +98 -0
  99. bear_utils/logger_manager/loggers/sub_logger.py +105 -0
  100. bear_utils/logger_manager/loggers/sub_logger.pyi +23 -0
  101. bear_utils/monitoring/__init__.py +13 -0
  102. bear_utils/monitoring/_common.py +28 -0
  103. bear_utils/monitoring/host_monitor.py +346 -0
  104. bear_utils/time/__init__.py +59 -0
  105. bear_utils-0.0.1.dist-info/METADATA +305 -0
  106. bear_utils-0.0.1.dist-info/RECORD +107 -0
  107. bear_utils-0.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,130 @@
1
+ """A module for file handling dialogs using PyQt6."""
2
+
3
+ from PyQt6.QtWidgets import QFileDialog
4
+
5
+ from .qt_app import QTApplication
6
+
7
+
8
+ class QTFileHandler(QTApplication):
9
+ """A singleton class to handle file selection dialogs using PyQt6."""
10
+
11
+ def select_file(self, caption: str = "Select File", directory: str = "", fil: str = "All Files (*)"):
12
+ """Shows a file selection dialog and returns the selected file path.
13
+
14
+ Args:
15
+ caption (str): The dialog window title
16
+ directory (str): The starting directory
17
+ filter (str): File filter pattern (e.g., "Images (*.png *.jpg);;Text files (*.txt)")
18
+
19
+ Returns:
20
+ str: Selected file path or empty string if canceled
21
+ """
22
+ file_path, _ = QFileDialog.getOpenFileName(None, caption, directory, fil)
23
+ return file_path
24
+
25
+ def select_files(self, caption: str = "Select Files", directory: str = "", fil: str = "All Files (*)"):
26
+ """Shows a file selection dialog that allows multiple selections.
27
+
28
+ Returns:
29
+ list: List of selected file paths
30
+ """
31
+ file_paths, _ = QFileDialog.getOpenFileNames(None, caption, directory, fil)
32
+ return file_paths
33
+
34
+ def select_directory(self, caption: str = "Select Directory", directory: str = ""):
35
+ """Shows a directory selection dialog.
36
+
37
+ Returns:
38
+ str: Selected directory path or empty string if canceled
39
+ """
40
+ return QFileDialog.getExistingDirectory(None, caption, directory)
41
+
42
+ def save_file_dialog(self, caption: str = "Save File", directory: str = "", fil: str = "All Files (*)"):
43
+ """Shows a save file dialog.
44
+
45
+ Returns:
46
+ str: Selected save file path or empty string if canceled
47
+ """
48
+ file_path, _ = QFileDialog.getSaveFileName(None, caption, directory, fil)
49
+ return file_path
50
+
51
+
52
+ def select_file(caption: str = "Select File", directory: str = "", fil: str = "All Files (*)"):
53
+ """Select a file using the QTApplication singleton instance.
54
+
55
+ Args:
56
+ caption (str): The dialog window title
57
+ directory (str): The starting directory
58
+ fil (str): File filter pattern (e.g., "Images (*.png *.jpg);;Text files (*.txt)")
59
+
60
+ Returns:
61
+ str: Selected file path or empty string if canceled
62
+ """
63
+ qt_app = QTFileHandler()
64
+ return qt_app.select_file(caption, directory, fil)
65
+
66
+
67
+ def select_files(caption: str = "Select Files", directory: str = "", fil: str = "All Files (*)"):
68
+ """Select multiple files using the QTApplication singleton instance.
69
+
70
+ Args:
71
+ caption (str): The dialog window title
72
+ directory (str): The starting directory
73
+ fil (str): File filter pattern (e.g., "Images (*.png *.jpg);;Text files (*.txt)")
74
+
75
+ Returns:
76
+ list: List of selected file paths
77
+ """
78
+ qt_app = QTFileHandler()
79
+ return qt_app.select_files(caption, directory, fil)
80
+
81
+
82
+ def select_directory(caption: str = "Select Directory", directory: str = ""):
83
+ """Select a directory using the QTApplication singleton instance.
84
+
85
+ Args:
86
+ caption (str): The dialog window title
87
+ directory (str): The starting directory
88
+
89
+ Returns:
90
+ str: Selected directory path or empty string if canceled
91
+ """
92
+ qt_app = QTFileHandler()
93
+ return qt_app.select_directory(caption, directory)
94
+
95
+
96
+ def save_file_dialog(caption: str = "Save File", directory: str = "", fil: str = "All Files (*)"):
97
+ """Show a save file dialog using the QTApplication singleton instance.
98
+
99
+ Args:
100
+ caption (str): The dialog window title
101
+ directory (str): The starting directory
102
+ fil (str): File filter pattern (e.g., "Images (*.png *.jpg);;Text files (*.txt)")
103
+
104
+ Returns:
105
+ str: Selected save file path or empty string if canceled
106
+ """
107
+ qt_app = QTFileHandler()
108
+ return qt_app.save_file_dialog(caption, directory, fil)
109
+
110
+
111
+ __all__ = [
112
+ "QTFileHandler",
113
+ "save_file_dialog",
114
+ "select_directory",
115
+ "select_file",
116
+ "select_files",
117
+ ]
118
+
119
+
120
+ # if __name__ == "__main__":
121
+ # # Example usage
122
+ # selected_file = select_file(
123
+ # "Select a file", filter="Python files (*.py);;All files (*)"
124
+ # )
125
+ # print(f"Selected file: {selected_file}")
126
+
127
+ # selected_files = select_files(
128
+ # "Select multiple files", filter="Images (*.png *.jpg);;All files (*)"
129
+ # )
130
+ # print(f"Selected files: {selected_files}")
@@ -0,0 +1,303 @@
1
+ """A module for input dialogs using PyQt6."""
2
+
3
+ from PyQt6.QtCore import Qt
4
+ from PyQt6.QtWidgets import QInputDialog, QLineEdit
5
+
6
+ from .qt_app import QTApplication
7
+
8
+
9
+ class InputDialog(QTApplication):
10
+ """A class for displaying various input dialogs using PyQt6."""
11
+
12
+ def get_text(
13
+ self,
14
+ title: str = "Input",
15
+ label: str = "Enter text:",
16
+ default: str = "",
17
+ echo_mode: QLineEdit.EchoMode = QLineEdit.EchoMode.Normal,
18
+ ) -> str | None:
19
+ """Shows a text input dialog.
20
+
21
+ Args:
22
+ title: Dialog window title
23
+ label: Text label above the input field
24
+ default: Default text in the input field
25
+ echo_mode: How text is displayed (Normal, Password, etc.)
26
+ input_mode: Type of text input (TextInput, IntInput, etc.)
27
+
28
+ Returns:
29
+ str: Entered text or None if canceled
30
+ """
31
+ try:
32
+ text, ok = QInputDialog.getText(
33
+ None,
34
+ title,
35
+ label,
36
+ echo_mode,
37
+ default,
38
+ Qt.WindowType.Dialog | Qt.WindowType.WindowCloseButtonHint,
39
+ )
40
+ return text if ok else None
41
+ except Exception as e:
42
+ self.console.error(f"Error in text input dialog: {e}")
43
+ return None
44
+
45
+ def get_password(
46
+ self,
47
+ title: str = "Password",
48
+ label: str = "Enter password:",
49
+ default: str = "",
50
+ ) -> str | None:
51
+ """Shows a password input dialog with masked text.
52
+
53
+ Args:
54
+ title: Dialog window title
55
+ label: Text label above the input field
56
+ default: Default text in the input field
57
+
58
+ Returns:
59
+ str: Entered password or None if canceled
60
+ """
61
+ return self.get_text(
62
+ title=title,
63
+ label=label,
64
+ default=default,
65
+ echo_mode=QLineEdit.EchoMode.Password,
66
+ )
67
+
68
+ def get_int(
69
+ self,
70
+ title: str = "Input",
71
+ label: str = "Enter number:",
72
+ default: int = 0,
73
+ min_value: int = -2147483647,
74
+ max_value: int = 2147483647,
75
+ step: int = 1,
76
+ ) -> int | None:
77
+ """Shows an integer input dialog with spinner.
78
+
79
+ Args:
80
+ title: Dialog window title
81
+ label: Text label above the input field
82
+ default: Default value
83
+ min_value: Minimum allowed value
84
+ max_value: Maximum allowed value
85
+ step: Step size for spinner buttons
86
+
87
+ Returns:
88
+ int: Entered integer or None if canceled
89
+ """
90
+ try:
91
+ value, ok = QInputDialog.getInt(
92
+ None,
93
+ title,
94
+ label,
95
+ default,
96
+ min_value,
97
+ max_value,
98
+ step,
99
+ Qt.WindowType.Dialog | Qt.WindowType.WindowCloseButtonHint,
100
+ )
101
+ return value if ok else None
102
+ except Exception as e:
103
+ self.console.error(f"Error in integer input dialog: {e}")
104
+ return None
105
+
106
+ def get_double(
107
+ self,
108
+ title: str = "Input",
109
+ label: str = "Enter number:",
110
+ default: float = 0.0,
111
+ min_value: float = -2147483647.0,
112
+ max_value: float = 2147483647.0,
113
+ decimals: int = 2,
114
+ ) -> float | None:
115
+ """Shows a floating-point input dialog with spinner.
116
+
117
+ Args:
118
+ title: Dialog window title
119
+ label: Text label above the input field
120
+ default: Default value
121
+ min_value: Minimum allowed value
122
+ max_value: Maximum allowed value
123
+ decimals: Number of decimal places to show
124
+
125
+ Returns:
126
+ float: Entered value or None if canceled
127
+ """
128
+ try:
129
+ value, ok = QInputDialog.getDouble(
130
+ None,
131
+ title,
132
+ label,
133
+ default,
134
+ min_value,
135
+ max_value,
136
+ decimals,
137
+ Qt.WindowType.Dialog | Qt.WindowType.WindowCloseButtonHint,
138
+ )
139
+ return value if ok else None
140
+ except Exception as e:
141
+ self.console.error(f"Error in double input dialog: {e}")
142
+ return None
143
+
144
+ def get_item(
145
+ self,
146
+ title: str = "Select",
147
+ label: str = "Select an item:",
148
+ items: list[str] | None = None,
149
+ current: int = 0,
150
+ editable: bool = False,
151
+ ) -> str | None:
152
+ """Shows a dropdown selection dialog.
153
+
154
+ Args:
155
+ title: Dialog window title
156
+ label: Text label above the dropdown
157
+ items: List of items to choose from
158
+ current: Index of the initially selected item
159
+ editable: Whether the text can be edited
160
+
161
+ Returns:
162
+ str: Selected or entered text, or None if canceled
163
+ """
164
+ if items is None:
165
+ items = ["Item 1", "Item 2", "Item 3"]
166
+
167
+ try:
168
+ item, ok = QInputDialog.getItem(
169
+ None,
170
+ title,
171
+ label,
172
+ items,
173
+ current,
174
+ editable,
175
+ Qt.WindowType.Dialog | Qt.WindowType.WindowCloseButtonHint,
176
+ )
177
+ return item if ok else None
178
+ except Exception as e:
179
+ self.console.error(f"Error in item selection dialog: {e}")
180
+ return None
181
+
182
+ def get_multiline_text(self, title: str = "Input", label: str = "Enter text:", default: str = "") -> str | None:
183
+ """Shows a multi-line text input dialog.
184
+
185
+ Args:
186
+ title: Dialog window title
187
+ label: Text label above the input field
188
+ default: Default text in the input field
189
+
190
+ Returns:
191
+ str: Entered text or None if canceled
192
+ """
193
+ try:
194
+ text, ok = QInputDialog.getMultiLineText(
195
+ None,
196
+ title,
197
+ label,
198
+ default,
199
+ Qt.WindowType.Dialog | Qt.WindowType.WindowCloseButtonHint,
200
+ )
201
+ return text if ok else None
202
+ except Exception as e:
203
+ self.console.error(f"Error in multiline text input dialog: {e}")
204
+ return None
205
+
206
+
207
+ def get_text(
208
+ title: str = "Input",
209
+ label: str = "Enter text:",
210
+ default: str = "",
211
+ echo_mode: QLineEdit.EchoMode = QLineEdit.EchoMode.Normal,
212
+ ) -> str | None:
213
+ """Displays a text input dialog and returns the text entered by the user."""
214
+ qt_app = InputDialog()
215
+ return qt_app.get_text(title, label, default, echo_mode)
216
+
217
+
218
+ def get_password(title: str = "Password", label: str = "Enter password:", default: str = "") -> str | None:
219
+ """Displays a password input dialog with masked text."""
220
+ qt_app = InputDialog()
221
+ return qt_app.get_password(title, label, default)
222
+
223
+
224
+ def get_api_key(
225
+ title: str = "API Key Required",
226
+ label: str = "Enter API key:",
227
+ default: str = "",
228
+ service_name: str = "",
229
+ ) -> str | None:
230
+ """Shows a secure input dialog optimized for entering API keys.
231
+
232
+ Args:
233
+ title: Dialog window title
234
+ label: Text label above the input field
235
+ default: Default API key (rarely used, but available)
236
+ service_name: Name of the service requiring the API key
237
+
238
+ Returns:
239
+ str: Entered API key or None if canceled
240
+ """
241
+ qt_app = InputDialog()
242
+
243
+ # Customize the label if service name is provided
244
+ if service_name:
245
+ label = f"Enter API key for {service_name}:"
246
+ title = f"{service_name} API Key"
247
+
248
+ return qt_app.get_text(title=title, label=label, default=default, echo_mode=QLineEdit.EchoMode.Password)
249
+
250
+
251
+ def get_int(
252
+ title: str = "Input",
253
+ label: str = "Enter number:",
254
+ default: int = 0,
255
+ min_value: int = -2147483647,
256
+ max_value: int = 2147483647,
257
+ step: int = 1,
258
+ ) -> int | None:
259
+ """Displays an integer input dialog with spinner buttons."""
260
+ qt_app = InputDialog()
261
+ return qt_app.get_int(title, label, default, min_value, max_value, step)
262
+
263
+
264
+ def get_double(
265
+ title: str = "Input",
266
+ label: str = "Enter number:",
267
+ default: float = 0.0,
268
+ min_value: float = -2147483647.0,
269
+ max_value: float = 2147483647.0,
270
+ decimals: int = 2,
271
+ ) -> float | None:
272
+ """Displays a floating-point input dialog with spinner buttons."""
273
+ qt_app = InputDialog()
274
+ return qt_app.get_double(title, label, default, min_value, max_value, decimals)
275
+
276
+
277
+ def get_item(
278
+ title: str = "Select",
279
+ label: str = "Select an item:",
280
+ items: list[str] | None = None,
281
+ current: int = 0,
282
+ editable: bool = False,
283
+ ) -> str | None:
284
+ """Displays a dropdown selection dialog."""
285
+ qt_app = InputDialog()
286
+ return qt_app.get_item(title, label, items, current, editable)
287
+
288
+
289
+ def get_multiline_text(title: str = "Input", label: str = "Enter text:", default: str = "") -> str | None:
290
+ """Displays a multi-line text input dialog."""
291
+ qt_app = InputDialog()
292
+ return qt_app.get_multiline_text(title, label, default)
293
+
294
+
295
+ if __name__ == "__main__":
296
+ # Example usage
297
+ api_key = get_api_key(
298
+ title="API Key Required",
299
+ label="Enter your API key:",
300
+ default="",
301
+ service_name="MyService",
302
+ )
303
+ print(f"API Key: {api_key}")
@@ -0,0 +1,109 @@
1
+ """Logging utilities for Bear Utils."""
2
+
3
+ from typing import Any
4
+
5
+ from bear_utils.logger_manager._common import VERBOSE_CONSOLE_FORMAT
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
8
+ from bear_utils.logger_manager.logger_protocol import AsyncLoggerProtocol, LoggerProtocol
9
+ from bear_utils.logger_manager.loggers._console import LogConsole
10
+ from bear_utils.logger_manager.loggers.base_logger import BaseLogger
11
+ from bear_utils.logger_manager.loggers.buffer_logger import BufferLogger
12
+ from bear_utils.logger_manager.loggers.console_logger import ConsoleLogger
13
+ from bear_utils.logger_manager.loggers.fastapi_logger import LoggingClient, LoggingServer
14
+ from bear_utils.logger_manager.loggers.file_logger import FileLogger
15
+ from bear_utils.logger_manager.loggers.simple_logger import SimpleLogger
16
+ from bear_utils.logger_manager.loggers.sub_logger import SubConsoleLogger
17
+
18
+
19
+ def get_logger(
20
+ console: bool = True,
21
+ file: bool = False,
22
+ queue_handler: bool = False,
23
+ buffering: bool = False,
24
+ **kwargs,
25
+ ) -> BaseLogger | ConsoleLogger | BufferError | FileLogger:
26
+ """Get a logger instance based on the specified parameters.
27
+
28
+ Args:
29
+ name (str): The name of the logger.
30
+ level (int): The logging level.
31
+ console (bool): Whether to enable console logging.
32
+ file (bool): Whether to enable file logging.
33
+ queue_handler (bool): Whether to use a queue handler.
34
+ buffering (bool): Whether to enable buffering.
35
+ style_disabled (bool): Whether to disable styling.
36
+ logger_mode (bool): Whether the logger is in logger mode.
37
+ **kwargs: Additional keyword arguments for customization.
38
+
39
+ Returns:
40
+ BaseLogger | ConsoleLogger | BufferLogger| FileLogger: An instance of the appropriate logger.
41
+ """
42
+ if (not console and not file) and buffering:
43
+ return BufferLogger(queue_handler=queue_handler, **kwargs)
44
+ if (console and file) or (console and buffering):
45
+ return ConsoleLogger(queue_handler=queue_handler, buffering=buffering, **kwargs)
46
+ if not console and not buffering and file:
47
+ return FileLogger(queue_handler=queue_handler, **kwargs)
48
+ return BaseLogger(**kwargs)
49
+
50
+
51
+ def get_console(namespace: str) -> tuple[BaseLogger, SubConsoleLogger]:
52
+ """Get a console logger and a sub-logger for a specific namespace.
53
+
54
+ Args:
55
+ namespace (str): The namespace for the sub-logger.
56
+
57
+ Returns:
58
+ tuple[BaseLogger, SubConsoleLogger]: A tuple containing the base logger and the sub-logger.
59
+ """
60
+ base_logger = BaseLogger.get_instance(init=True)
61
+ sub_logger = SubConsoleLogger(logger=base_logger, namespace=namespace)
62
+ return base_logger, sub_logger
63
+
64
+
65
+ def get_sub_logger(
66
+ logger: BaseLogger | ConsoleLogger | Any,
67
+ namespace: str,
68
+ ) -> SubConsoleLogger[BaseLogger | ConsoleLogger]:
69
+ """Get a sub-logger for a specific namespace.
70
+
71
+ Args:
72
+ logger (BaseLogger): The parent logger.
73
+ namespace (str): The namespace for the sub-logger.
74
+
75
+ Returns:
76
+ SubConsoleLogger: A sub-logger instance.
77
+ """
78
+ if not isinstance(logger, (BaseLogger | ConsoleLogger)):
79
+ raise TypeError("Expected logger to be an instance of BaseLogger or ConsoleLogger")
80
+
81
+ return SubConsoleLogger(logger=logger, namespace=namespace)
82
+
83
+
84
+ __all__ = [
85
+ "DEBUG",
86
+ "DEFAULT_THEME",
87
+ "ERROR",
88
+ "FAILURE",
89
+ "INFO",
90
+ "SUCCESS",
91
+ "VERBOSE",
92
+ "VERBOSE_CONSOLE_FORMAT",
93
+ "WARNING",
94
+ "AsyncLoggerProtocol",
95
+ "BaseLogger",
96
+ "BufferLogger",
97
+ "ConsoleLogger",
98
+ "FileLogger",
99
+ "LogConsole",
100
+ "LogLevel",
101
+ "LoggerProtocol",
102
+ "LoggingClient",
103
+ "LoggingServer",
104
+ "SimpleLogger",
105
+ "SubConsoleLogger",
106
+ "get_console",
107
+ "get_logger",
108
+ "get_sub_logger",
109
+ ]
@@ -0,0 +1,63 @@
1
+ import inspect
2
+ from types import TracebackType
3
+ from typing import Literal, Required, TypedDict
4
+
5
+
6
+ class ExecValues(TypedDict, total=True):
7
+ exc_type: Required[type[BaseException]]
8
+ exc_value: Required[BaseException]
9
+ exc_traceback: Required[TracebackType]
10
+
11
+
12
+ class StackLevelTracker:
13
+ STACK_LEVEL_OFFSET = 1
14
+
15
+ def __init__(self) -> None:
16
+ self.start_depth: int | None = None
17
+ self.end_depth: int | None = None
18
+
19
+ @property
20
+ def not_set(self) -> bool:
21
+ """Check if the start depth is not set."""
22
+ return self.start_depth is None
23
+
24
+ def record_start(self) -> None:
25
+ """Record the current stack depth as the start depth."""
26
+ self.start_depth = len(inspect.stack())
27
+
28
+ def record_end(self) -> int:
29
+ """Record the current stack depth as the end depth and then return the calculated stack level.
30
+
31
+ Returns:
32
+ int: The calculated stack level based on the difference between start and end depths.
33
+ """
34
+ self.end_depth = len(inspect.stack())
35
+ return self.calculate_stacklevel()
36
+
37
+ def calculate_stacklevel(self) -> int:
38
+ if self.start_depth is None or self.end_depth is None:
39
+ raise ValueError("Start and end depths must be recorded before calculating stack level.")
40
+ return self.end_depth - (self.start_depth + self.STACK_LEVEL_OFFSET)
41
+
42
+
43
+ VERBOSE_FORMAT = "%(asctime)s |%(levelname)s| {%(module)s|%(funcName)s|%(lineno)d} %(message)s"
44
+ VERBOSE_CONSOLE_FORMAT = "%(asctime)s <[{}]{}[/{}]> %(message)s"
45
+ SIMPLE_FORMAT = "%(message)s"
46
+ FIVE_MEGABYTES = 1024 * 1024 * 5
47
+
48
+ VERBOSE: Literal[5] = 5
49
+ SUCCESS: Literal[15] = 15
50
+ FAILURE: Literal[45] = 45
51
+
52
+
53
+ __all__ = [
54
+ "FAILURE",
55
+ "FIVE_MEGABYTES",
56
+ "SIMPLE_FORMAT",
57
+ "SUCCESS",
58
+ "VERBOSE",
59
+ "VERBOSE_CONSOLE_FORMAT",
60
+ "VERBOSE_FORMAT",
61
+ "ExecValues",
62
+ "StackLevelTracker",
63
+ ]