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,151 @@
1
+ """A file-based logger that writes log messages to files, styling is disabled as it is not applicable for file output."""
2
+
3
+ from logging import DEBUG
4
+ from pathlib import Path
5
+ from typing import TYPE_CHECKING, Any, override
6
+
7
+ from rich.theme import Theme
8
+
9
+ from bear_utils.logger_manager._common import FIVE_MEGABYTES
10
+
11
+ from .console_logger import ConsoleLogger
12
+ from .sub_logger import SubConsoleLogger
13
+
14
+ if TYPE_CHECKING:
15
+ from bear_utils.logger_manager._styles import LoggerExtraInfo
16
+
17
+
18
+ class FileLogger(ConsoleLogger):
19
+ """A file-based logger that writes styled log messages to files.
20
+
21
+ Combines Python's logging framework with Rich console styling, but outputs
22
+ to files instead of console. Supports file rotation, custom formatting,
23
+ and maintains the same interface as other loggers.
24
+
25
+ Features:
26
+ - File logging with rotation
27
+ - Rich-style method generation (info, error, debug, etc.)
28
+ - Consistent print() interface
29
+ - Exception tracebacks in file format
30
+ - JSON logging support
31
+
32
+ Example:
33
+ logger = FileLogger.get_instance(
34
+ init=True,
35
+ name="FileLogger",
36
+ file_path="app.log",
37
+ max_bytes=10*1024*1024,
38
+ backup_count=5
39
+ )
40
+ logger.info("This goes to the file")
41
+ logger.error("This error is logged to file")
42
+ """
43
+
44
+ def __init__(
45
+ self,
46
+ theme: Theme | None = None,
47
+ name: str = "FileLogger",
48
+ level: int = DEBUG,
49
+ file_path: str = "app.log",
50
+ max_bytes: int = FIVE_MEGABYTES,
51
+ backup_count: int = 5,
52
+ *_,
53
+ **kwargs,
54
+ ) -> None:
55
+ """Initialize the FileLogger with file path and rotation settings."""
56
+ self.file_path = Path(file_path) # TODO: Add more options for filename patterns (timestamps, process IDs, etc.)
57
+ self.max_bytes = max_bytes
58
+ self.backup_count = backup_count
59
+ ConsoleLogger.__init__(
60
+ self,
61
+ name=name,
62
+ level=level,
63
+ file=True,
64
+ console=False,
65
+ queue_handler=kwargs.pop("queue_handler", False),
66
+ file_path=self.file_path,
67
+ max_bytes=self.max_bytes,
68
+ backup_count=self.backup_count,
69
+ theme=theme,
70
+ style_disabled=True,
71
+ logger_mode=True,
72
+ **kwargs,
73
+ )
74
+
75
+ @override
76
+ def get_sub_logger(self, namespace: str, **kwargs: Any) -> SubConsoleLogger:
77
+ return SubConsoleLogger(self, namespace, **kwargs)
78
+
79
+ @override
80
+ def replacement_method(self, msg: object, *args, **kwargs) -> None:
81
+ """Handle logging method calls with proper file logging integration."""
82
+ extra: LoggerExtraInfo = kwargs.pop("injected_extra")
83
+ if kwargs.get("extra"):
84
+ extra.update(kwargs.pop("extra"))
85
+ if extra.get("namespace"):
86
+ msg = f"<{extra.get('namespace')}> {msg}"
87
+ if self.stack_tracker.not_set:
88
+ self.stack_tracker.record_start()
89
+
90
+ self.log(
91
+ extra.get("log_level", DEBUG),
92
+ msg,
93
+ extra,
94
+ *args,
95
+ **kwargs,
96
+ )
97
+
98
+ def print(
99
+ self,
100
+ msg: object,
101
+ _: str = "\n",
102
+ exc_info: str | None = None,
103
+ extra: dict | None = None,
104
+ *__,
105
+ **___,
106
+ ) -> None:
107
+ """Print a message to the file with proper formatting.
108
+
109
+ Maintains the same interface as other loggers but writes to file instead of console.
110
+ """
111
+ try:
112
+ # For file logging, we want to use the logging system rather than direct file writing
113
+ # This ensures proper formatting, rotation, etc.
114
+ if exc_info is not None:
115
+ # Log with exception info
116
+ self.error(f"{msg}", exc_info=exc_info, extra=extra)
117
+ else:
118
+ # Regular info log
119
+ self.info(f"{msg}", extra=extra)
120
+
121
+ if extra:
122
+ # Log extra data as JSON-like format
123
+ self.info(f"Extra data: {extra}")
124
+ except Exception as e:
125
+ print(f"FileLogger: Failed to write to log file. Message: {msg}, Error: {e}")
126
+
127
+ def get_file_size(self) -> int:
128
+ """Get current size of the log file in bytes."""
129
+ if self.file_path.exists():
130
+ return self.file_path.stat().st_size
131
+ return 0
132
+
133
+ def get_file_path(self) -> Path:
134
+ """Get the current log file path."""
135
+ return self.file_path
136
+
137
+ def rotate_file(self) -> None:
138
+ """Manually trigger file rotation."""
139
+ if hasattr(self.file_handler, "doRollover"):
140
+ self.file_handler.doRollover()
141
+
142
+ @override
143
+ def exit(self) -> None:
144
+ """Clean up file resources."""
145
+ if hasattr(self, "file_handler"):
146
+ self.file_handler.flush()
147
+ self.file_handler.close()
148
+ self.removeHandler(self.file_handler)
149
+
150
+ # Call parent exit
151
+ super().exit()
@@ -0,0 +1,98 @@
1
+ """Simple logger implementation with log levels and timestamped output."""
2
+
3
+ from io import StringIO
4
+ from typing import TextIO
5
+
6
+ from bear_utils.constants import STDERR, STDOUT, NullFile
7
+ from bear_utils.logger_manager._log_level import LogLevel
8
+ from bear_utils.time import EpochTimestamp
9
+
10
+ VERBOSE: LogLevel = LogLevel.VERBOSE
11
+ DEBUG: LogLevel = LogLevel.DEBUG
12
+ INFO: LogLevel = LogLevel.INFO
13
+ WARNING: LogLevel = LogLevel.WARNING
14
+ ERROR: LogLevel = LogLevel.ERROR
15
+ SUCCESS: LogLevel = LogLevel.SUCCESS
16
+ FAILURE: LogLevel = LogLevel.FAILURE
17
+
18
+
19
+ class SimpleLogger[T: TextIO]:
20
+ """A simple logger that writes messages to stdout, stderr, or StringIO with a timestamp."""
21
+
22
+ def __init__(self, level: str | int | LogLevel = "DEBUG", file: T = STDERR) -> None:
23
+ """Initialize the logger with a minimum log level and output file."""
24
+ self.level: LogLevel = LogLevel.get(level, default=DEBUG)
25
+ self.file: T = file # Can be STDOUT, STDERR, StringIO, NullFile or other TextIO Objects
26
+ self.buffer: list[str] = []
27
+
28
+ def print(self, msg: object, end: str = "\n") -> None:
29
+ """Print the message to the specified file with an optional end character."""
30
+ print(msg, end=end, file=self.file)
31
+
32
+ def _log(self, level: LogLevel, msg: object, end: str = "\n", *args, **kwargs) -> None:
33
+ timestamp: str = EpochTimestamp.now().to_string()
34
+ try:
35
+ self.buffer.append(f"[{timestamp}] {level.value}: {msg}")
36
+ if args:
37
+ self.buffer.append(" ".join(str(arg) for arg in args))
38
+ if kwargs:
39
+ for key, value in kwargs.items():
40
+ self.buffer.append(f"{key}={value}")
41
+ self.print(f"{end}".join(self.buffer))
42
+ except Exception as e:
43
+ self.print(f"[{timestamp}] {level.value}: {msg} - Error: {e}")
44
+ finally:
45
+ self.buffer.clear()
46
+
47
+ def log(self, level: LogLevel, msg: object, *args, **kwargs) -> None:
48
+ """Log a message at the specified level."""
49
+ if level.value >= self.level.value:
50
+ self._log(level, msg, *args, **kwargs)
51
+
52
+ def verbose(self, msg: object, *args, **kwargs) -> None:
53
+ """Alias for debug level logging."""
54
+ self.log(VERBOSE, msg, *args, **kwargs)
55
+
56
+ def debug(self, msg: object, *args, **kwargs) -> None:
57
+ """Log a debug message."""
58
+ self.log(DEBUG, msg, *args, **kwargs)
59
+
60
+ def info(self, msg: object, *args, **kwargs) -> None:
61
+ """Log an info message."""
62
+ self.log(INFO, msg, *args, **kwargs)
63
+
64
+ def warning(self, msg: object, *args, **kwargs) -> None:
65
+ """Log a warning message."""
66
+ self.log(WARNING, msg, *args, **kwargs)
67
+
68
+ def error(self, msg: object, *args, **kwargs) -> None:
69
+ """Log an error message."""
70
+ self.log(ERROR, msg, *args, **kwargs)
71
+
72
+ def success(self, msg: object, *args, **kwargs) -> None:
73
+ """Log a success message."""
74
+ self.log(SUCCESS, msg, *args, **kwargs)
75
+
76
+ def failure(self, msg: object, *args, **kwargs) -> None:
77
+ """Log a failure message."""
78
+ self.log(FAILURE, msg, *args, **kwargs)
79
+
80
+
81
+ __all__ = [
82
+ "STDERR",
83
+ "STDOUT",
84
+ "LogLevel",
85
+ "NullFile",
86
+ "SimpleLogger",
87
+ ]
88
+
89
+ # Example usage:
90
+ if __name__ == "__main__":
91
+ logger = SimpleLogger(file=StringIO())
92
+ logger_two = SimpleLogger(level="INFO", file=NullFile())
93
+ logger.info(msg="This is an info message")
94
+ logger_two.info(msg="This is an info message")
95
+
96
+ value = logger.file
97
+ print(value.getvalue()) # Print the captured log messages from StringIO
98
+ # print(logger_two.file.getvalue()) # should throw a typing error since it is not a StringIO
@@ -0,0 +1,105 @@
1
+ """Logger adapter module that provides an alternative to SubLogger using standard library tools.
2
+
3
+ This implementation shows how to use LoggerAdapter and contextvars to maintain
4
+ all the functionality of the existing SubLogger while reducing complexity.
5
+ """
6
+
7
+ from functools import partial
8
+ from typing import TYPE_CHECKING, Any, Generic, TypeVar
9
+
10
+ from rich.text import Text
11
+
12
+ from bear_utils.logger_manager._styles import LOGGER_METHODS, LoggerExtraInfo
13
+
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]): # noqa: UP046
23
+ """SubConsoleLogger: A logger that wraps another logger with a namespace."""
24
+
25
+ def __init__(self, logger: _T, namespace: str, **kwargs) -> None:
26
+ """Initialize the SubConsoleLogger with a logger and a namespace.
27
+
28
+ Args:
29
+ logger: The underlying logger to wrap with a namespace.
30
+ namespace: The namespace to prefix log messages with
31
+ """
32
+ self.logger: _T = logger
33
+ self._level: int = kwargs.get("level") or logger._level
34
+ self.logger_mode = logger.logger_mode
35
+ self.namespace: str = namespace
36
+ self._setup(self.namespace)
37
+
38
+ def print(
39
+ self,
40
+ msg: object,
41
+ end: str = "\n",
42
+ exc_info: Any = None,
43
+ extra: dict | None = None,
44
+ *_,
45
+ **kwargs,
46
+ ) -> None | str:
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
+ """Set the logging level for the logger.
55
+
56
+ This method allows changing the logging level dynamically.
57
+ """
58
+ self._level = level
59
+
60
+ def filter_by_level(self, level: int | str) -> bool:
61
+ """Filter method to determine if a message should be logged based on its level.
62
+
63
+ This method checks if the provided level is greater than or equal to the logger's level.
64
+
65
+ Args:
66
+ level (int | str): The logging level of the message.
67
+
68
+ Returns:
69
+ bool: True if the message should be logged, False otherwise.
70
+ """
71
+ level = check_level(level)
72
+ return level >= self._level
73
+
74
+ def trigger_buffer_flush(self) -> str | Text:
75
+ """Flush buffered messages to console output.
76
+
77
+ This method is used to ensure that any buffered log messages are printed
78
+ to the console, similar to the SubLogger's buffer flush functionality.
79
+ """
80
+ return self.logger.trigger_buffer_flush()
81
+
82
+ def _setup(self, name: str) -> None:
83
+ """Get an attribute from the logger.
84
+
85
+ This allows for accessing logger methods directly.
86
+ """
87
+ filter_func = self.filter_by_level
88
+ for style_name, og_extra in LOGGER_METHODS.items():
89
+ extra: LoggerExtraInfo = og_extra.copy()
90
+
91
+ extra["namespace"] = name
92
+
93
+ setattr(
94
+ self,
95
+ style_name,
96
+ partial(
97
+ self.logger.replacement_method,
98
+ injected_extra=extra,
99
+ filter_func=filter_func,
100
+ ),
101
+ )
102
+
103
+ def __repr__(self) -> str:
104
+ """String representation of the adapter."""
105
+ return f"ConsoleAdapter(namespace={self.namespace}"
@@ -0,0 +1,23 @@
1
+ from typing import Any, Generic, TypeVar
2
+
3
+ from .base_logger import BaseLogger
4
+
5
+ _T = TypeVar("_T", bound=BaseLogger)
6
+
7
+ class SubConsoleLogger(Generic[_T]): # noqa: UP046
8
+ logger: _T
9
+ _level: int
10
+ namespace: str
11
+ extra: dict[str, Any] | None
12
+ log_level: int
13
+ # fmt: off
14
+ def __init__(self, logger: _T, namespace: str, **kwargs) -> None: ...
15
+ def set_sub_level(self, level: int) -> None: ...
16
+ def success(self, msg: object, *args, **kwargs) -> None: ...
17
+ def failure(self, msg: object, *args, **kwargs) -> None: ...
18
+ def verbose(self, msg: object, *args, **kwargs) -> None: ...
19
+ def info(self, msg: object, *args, **kwargs) -> None: ...
20
+ def debug(self, msg: object, *args, **kwargs) -> None: ...
21
+ def warning(self, msg: object, *args, **kwargs) -> None: ...
22
+ def error(self, msg: object, *args, **kwargs) -> None: ...
23
+ def print(self, msg: object, end: str = "\n", exc_info: Any = None, extra: dict | None = None, *args, **kwargs: Any) -> None | str: ...
@@ -0,0 +1,13 @@
1
+ """A module for monitoring host system metrics."""
2
+
3
+ from ._common import CPU, DISK, GPU, MEM, TaskChoice
4
+ from .host_monitor import HostMonitor
5
+
6
+ __all__ = [
7
+ "CPU",
8
+ "DISK",
9
+ "GPU",
10
+ "MEM",
11
+ "HostMonitor",
12
+ "TaskChoice",
13
+ ]
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from enum import StrEnum
4
+ from typing import Literal
5
+
6
+
7
+ class TaskChoice(StrEnum):
8
+ """Enum for task choices."""
9
+
10
+ CPU = "cpu"
11
+ MEM = "mem"
12
+ DISK = "disk"
13
+ GPU = "gpu"
14
+
15
+
16
+ CPU = TaskChoice.CPU
17
+ MEM = TaskChoice.MEM
18
+ DISK = TaskChoice.DISK
19
+ GPU = TaskChoice.GPU
20
+
21
+ CPU_MEM: tuple[Literal[TaskChoice.CPU], Literal[TaskChoice.MEM]] = (CPU, MEM)
22
+ CPU_MEM_GPU: tuple[Literal[TaskChoice.CPU], Literal[TaskChoice.MEM], Literal[TaskChoice.GPU]] = (CPU, MEM, GPU)
23
+ ALL_TASKS: tuple[
24
+ Literal[TaskChoice.CPU],
25
+ Literal[TaskChoice.MEM],
26
+ Literal[TaskChoice.DISK],
27
+ Literal[TaskChoice.GPU],
28
+ ] = (CPU, MEM, DISK, GPU)