bear-utils 0.7.23__tar.gz → 0.8.0__tar.gz

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 (113) hide show
  1. {bear_utils-0.7.23 → bear_utils-0.8.0}/.bumpversion.cfg +1 -1
  2. {bear_utils-0.7.23 → bear_utils-0.8.0}/PKG-INFO +2 -2
  3. {bear_utils-0.7.23 → bear_utils-0.8.0}/README.md +1 -1
  4. {bear_utils-0.7.23 → bear_utils-0.8.0}/config/vscode/launch.json +1 -12
  5. {bear_utils-0.7.23 → bear_utils-0.8.0}/noxfile.py +17 -0
  6. {bear_utils-0.7.23 → bear_utils-0.8.0}/pyproject.toml +3 -1
  7. bear_utils-0.8.0/src/bear_utils/__init__.py +38 -0
  8. bear_utils-0.8.0/src/bear_utils/__main__.py +14 -0
  9. bear_utils-0.8.0/src/bear_utils/_internal/cli.py +73 -0
  10. bear_utils-0.8.0/src/bear_utils/_internal/debug.py +159 -0
  11. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/ai/ai_helpers/__init__.py +1 -1
  12. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/ai/ai_helpers/_types.py +1 -1
  13. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/cli/prompt_helpers.py +1 -1
  14. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/cli/shell/_base_shell.py +1 -1
  15. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/config/settings_manager.py +9 -8
  16. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/extras/_tools.py +2 -2
  17. bear_utils-0.8.0/src/bear_utils/extras/responses/__init__.py +9 -0
  18. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/extras/responses/function_response.py +73 -38
  19. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/file_handlers/_base_file_handler.py +3 -9
  20. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/file_handlers/json_file_handler.py +2 -2
  21. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/file_handlers/log_file_handler.py +2 -2
  22. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/file_handlers/toml_file_handler.py +1 -1
  23. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/file_handlers/txt_file_handler.py +2 -2
  24. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/file_handlers/yaml_file_handler.py +1 -0
  25. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/ignore_parser.py +1 -1
  26. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/gui/gui_tools/qt_app.py +1 -1
  27. bear_utils-0.7.23/src/bear_utils/logging/loggers.py → bear_utils-0.8.0/src/bear_utils/logger_manager/__init__.py +22 -6
  28. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_base_logger.py +2 -2
  29. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_base_logger.pyi +1 -1
  30. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_console_logger.py +2 -2
  31. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_console_logger.pyi +1 -1
  32. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_file_logger.py +2 -2
  33. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_sub_logger.py +1 -1
  34. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/monitoring/host_monitor.py +1 -1
  35. bear_utils-0.8.0/tests/__init__.py +0 -0
  36. bear_utils-0.8.0/tests/test_function_response.py +560 -0
  37. {bear_utils-0.7.23 → bear_utils-0.8.0}/tests/test_logger.py +2 -2
  38. {bear_utils-0.7.23 → bear_utils-0.8.0}/uv.lock +61 -1
  39. bear_utils-0.7.23/src/bear_utils/__init__.py +0 -38
  40. bear_utils-0.7.23/src/bear_utils/extras/responses/__init__.py +0 -1
  41. bear_utils-0.7.23/src/bear_utils/logging/__init__.py +0 -27
  42. bear_utils-0.7.23/src/bear_utils/logging/logger_manager/__init__.py +0 -1
  43. {bear_utils-0.7.23 → bear_utils-0.8.0}/.gitignore +0 -0
  44. {bear_utils-0.7.23 → bear_utils-0.8.0}/.python-version +0 -0
  45. {bear_utils-0.7.23 → bear_utils-0.8.0}/AGENTS.md +0 -0
  46. {bear_utils-0.7.23 → bear_utils-0.8.0}/config/coverage.ini +0 -0
  47. {bear_utils-0.7.23 → bear_utils-0.8.0}/config/default.toml +0 -0
  48. {bear_utils-0.7.23 → bear_utils-0.8.0}/config/git-changelog.toml +0 -0
  49. {bear_utils-0.7.23 → bear_utils-0.8.0}/config/pytest.ini +0 -0
  50. {bear_utils-0.7.23 → bear_utils-0.8.0}/config/ruff.toml +0 -0
  51. {bear_utils-0.7.23 → bear_utils-0.8.0}/config/vscode/settings.json +0 -0
  52. {bear_utils-0.7.23 → bear_utils-0.8.0}/config/vscode/tasks.json +0 -0
  53. {bear_utils-0.7.23 → bear_utils-0.8.0}/directory_structure.txt +0 -0
  54. {bear_utils-0.7.23 → bear_utils-0.8.0}/maskfile.md +0 -0
  55. {bear_utils-0.7.23/tests → bear_utils-0.8.0/src/bear_utils/_internal}/__init__.py +0 -0
  56. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/ai/__init__.py +0 -0
  57. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/ai/ai_helpers/_common.py +0 -0
  58. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/ai/ai_helpers/_config.py +0 -0
  59. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/ai/ai_helpers/_parsers.py +0 -0
  60. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/cache/__init__.py +0 -0
  61. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/cli/__init__.py +0 -0
  62. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/cli/commands.py +0 -0
  63. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/cli/shell/__init__.py +0 -0
  64. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/cli/shell/_base_command.py +0 -0
  65. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/cli/shell/_common.py +0 -0
  66. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/config/__init__.py +0 -0
  67. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/config/config_manager.py +0 -0
  68. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/config/dir_manager.py +0 -0
  69. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/constants/__init__.py +0 -0
  70. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/constants/_exceptions.py +0 -0
  71. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/constants/_lazy_typing.py +0 -0
  72. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/constants/date_related.py +0 -0
  73. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/constants/logger_protocol.py +0 -0
  74. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/constants/time_related.py +0 -0
  75. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/database/__init__.py +0 -0
  76. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/database/_db_manager.py +0 -0
  77. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/events/__init__.py +0 -0
  78. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/events/events_class.py +0 -0
  79. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/events/events_module.py +0 -0
  80. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/extras/__init__.py +0 -0
  81. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/extras/_async_helpers.py +0 -0
  82. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/extras/platform_utils.py +0 -0
  83. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/extras/wrappers/__init__.py +0 -0
  84. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/extras/wrappers/add_methods.py +0 -0
  85. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/__init__.py +0 -0
  86. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/file_handlers/__init__.py +0 -0
  87. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/files/file_handlers/file_handler_factory.py +0 -0
  88. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/graphics/__init__.py +0 -0
  89. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/graphics/bear_gradient.py +0 -0
  90. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/graphics/image_helpers.py +0 -0
  91. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/gui/__init__.py +0 -0
  92. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/gui/gui_tools/__init__.py +0 -0
  93. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/gui/gui_tools/_settings.py +0 -0
  94. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/gui/gui_tools/_types.py +0 -0
  95. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/gui/gui_tools/qt_color_picker.py +0 -0
  96. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/gui/gui_tools/qt_file_handler.py +0 -0
  97. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/gui/gui_tools/qt_input_dialog.py +0 -0
  98. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/_common.py +0 -0
  99. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/_console_junk.py +0 -0
  100. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/_styles.py +0 -0
  101. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/__init__.py +0 -0
  102. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_buffer_logger.py +0 -0
  103. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_level_sin.py +0 -0
  104. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_logger.py +0 -0
  105. {bear_utils-0.7.23/src/bear_utils/logging → bear_utils-0.8.0/src/bear_utils}/logger_manager/loggers/_sub_logger.pyi +0 -0
  106. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/monitoring/__init__.py +0 -0
  107. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/monitoring/_common.py +0 -0
  108. {bear_utils-0.7.23 → bear_utils-0.8.0}/src/bear_utils/time/__init__.py +0 -0
  109. {bear_utils-0.7.23 → bear_utils-0.8.0}/tests/test_add_ord_suffix.py +0 -0
  110. {bear_utils-0.7.23 → bear_utils-0.8.0}/tests/test_clipboard.py +0 -0
  111. {bear_utils-0.7.23 → bear_utils-0.8.0}/tests/test_default_shell.py +0 -0
  112. {bear_utils-0.7.23 → bear_utils-0.8.0}/tests/test_gradient.py +0 -0
  113. {bear_utils-0.7.23 → bear_utils-0.8.0}/tests/test_platform_utils.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 0.7.23
2
+ current_version = 0.8.0
3
3
 
4
4
  [bumpversion:file:pyproject.toml]
5
5
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bear-utils
3
- Version: 0.7.23
3
+ Version: 0.8.0
4
4
  Summary: Various utilities for Bear programmers, including a rich logging utility, a disk cache, and a SQLite database wrapper amongst other things.
5
5
  Author-email: chaz <bright.lid5647@fastmail.com>
6
6
  Requires-Python: >=3.12
@@ -20,7 +20,7 @@ Requires-Dist: tinydb>=4.8.2
20
20
  Requires-Dist: toml>=0.10.2
21
21
  Description-Content-Type: text/markdown
22
22
 
23
- # Bear Utils v# Bear Utils v0.7.23
23
+ # Bear Utils v# Bear Utils v0.8.0
24
24
 
25
25
  Personal set of tools and utilities for Python projects, focusing on modularity and ease of use. This library includes components for caching, database management, logging, time handling, file operations, CLI prompts, image processing, clipboard interaction, gradient utilities, event systems, and async helpers.
26
26
 
@@ -1,4 +1,4 @@
1
- # Bear Utils v# Bear Utils v0.7.23
1
+ # Bear Utils v# Bear Utils v0.8.0
2
2
 
3
3
  Personal set of tools and utilities for Python projects, focusing on modularity and ease of use. This library includes components for caching, database management, logging, time handling, file operations, CLI prompts, image processing, clipboard interaction, gradient utilities, event systems, and async helpers.
4
4
 
@@ -14,22 +14,11 @@
14
14
  "name": "run",
15
15
  "type": "debugpy",
16
16
  "request": "launch",
17
- "module": "trackdo",
17
+ "module": "bear-utils",
18
18
  "console": "integratedTerminal",
19
19
  "justMyCode": false,
20
20
  "args": "${command:pickArgs}"
21
21
  },
22
- {
23
- "name": "docs",
24
- "type": "debugpy",
25
- "request": "launch",
26
- "module": "mkdocs",
27
- "justMyCode": false,
28
- "args": [
29
- "serve",
30
- "-v"
31
- ]
32
- },
33
22
  {
34
23
  "name": "test",
35
24
  "type": "debugpy",
@@ -5,6 +5,23 @@ import nox
5
5
  PYTHON_VERSIONS: list[str] = ["3.13", "3.14"]
6
6
 
7
7
 
8
+ @nox.session(venv_backend="uv", tags=["lint", "fix"])
9
+ def coverage(session: nox.Session) -> None:
10
+ """Run tests with coverage reporting."""
11
+ session.install("coverage[toml]", "pytest", "pytest-cov")
12
+ session.install("-e", ".")
13
+
14
+ session.run(
15
+ "pytest",
16
+ "--cov=src",
17
+ "--cov-report=term-missing",
18
+ "--cov-report=html",
19
+ "--cov-config=config/coverage.ini",
20
+ )
21
+
22
+ session.log("Coverage HTML report generated in htmlcov/")
23
+
24
+
8
25
  @nox.session(venv_backend="uv", tags=["lint"])
9
26
  def ruff_check(session: nox.Session) -> None:
10
27
  """Run ruff linting and formatting checks (CI-friendly, no changes)."""
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "bear-utils"
3
- version = "0.7.23"
3
+ version = "0.8.0"
4
4
  description = "Various utilities for Bear programmers, including a rich logging utility, a disk cache, and a SQLite database wrapper amongst other things."
5
5
  authors = [{ name = "chaz", email = "bright.lid5647@fastmail.com" }]
6
6
  readme = "README.md"
@@ -29,9 +29,11 @@ build-backend = "hatchling.build"
29
29
  [dependency-groups]
30
30
  dev = [
31
31
  "bump2version>=1.0.1",
32
+ "coverage>=7.9.2",
32
33
  "isort>=6.0.1",
33
34
  "nox>=2025.5.1",
34
35
  "pytest>=8.3.5",
36
+ "pytest-cov>=6.2.1",
35
37
  "twine>=6.1.0",
36
38
  ]
37
39
  gui = [
@@ -0,0 +1,38 @@
1
+ """A module for Bear Utils, providing various utilities and tools."""
2
+
3
+ from bear_epoch_time import EpochTimestamp, TimeTools
4
+
5
+ from bear_utils.cache import CacheWrapper, cache, cache_factory
6
+ from bear_utils.config.settings_manager import SettingsManager, get_settings_manager
7
+ from bear_utils.constants.date_related import DATE_FORMAT, DATE_TIME_FORMAT
8
+ from bear_utils.database import DatabaseManager
9
+ from bear_utils.events import Events
10
+ from bear_utils.extras.responses import FAILURE, SUCCESS, FunctionResponse
11
+ from bear_utils.files.file_handlers.file_handler_factory import FileHandlerFactory
12
+ from bear_utils.logger_manager import BaseLogger, BufferLogger, ConsoleLogger, FileLogger
13
+ from bear_utils.logger_manager._common import VERBOSE_CONSOLE_FORMAT
14
+ from bear_utils.logger_manager._styles import VERBOSE
15
+
16
+ __all__ = [
17
+ "DATE_FORMAT",
18
+ "DATE_TIME_FORMAT",
19
+ "FAILURE",
20
+ "SUCCESS",
21
+ "VERBOSE",
22
+ "VERBOSE_CONSOLE_FORMAT",
23
+ "BaseLogger",
24
+ "BufferLogger",
25
+ "CacheWrapper",
26
+ "ConsoleLogger",
27
+ "DatabaseManager",
28
+ "EpochTimestamp",
29
+ "Events",
30
+ "FileHandlerFactory",
31
+ "FileLogger",
32
+ "FunctionResponse",
33
+ "SettingsManager",
34
+ "TimeTools",
35
+ "cache",
36
+ "cache_factory",
37
+ "get_settings_manager",
38
+ ]
@@ -0,0 +1,14 @@
1
+ """Entry-point module, in case you use `python -m bear_utils`.
2
+
3
+ Why does this file exist, and why `__main__`? For more info, read:
4
+
5
+ - https://www.python.org/dev/peps/pep-0338/
6
+ - https://docs.python.org/3/using/cmdline.html#cmdoption-m
7
+ """
8
+
9
+ import sys
10
+
11
+ from bear_utils._internal.cli import main
12
+
13
+ if __name__ == "__main__":
14
+ sys.exit(main(sys.argv[1:]))
@@ -0,0 +1,73 @@
1
+ # Why does this file exist, and why not put this in `__main__`?
2
+ #
3
+ # You might be tempted to import things from `__main__` later,
4
+ # but that will cause problems: the code will get executed twice:
5
+ #
6
+ # - When you run `python -m bear_utils` python will execute
7
+ # `__main__.py` as a script. That means there won't be any
8
+ # `bear_utils.__main__` in `sys.modules`.
9
+ # - When you import `__main__` it will get executed again (as a module) because
10
+ # there's no `bear_utils.__main__` in `sys.modules`.
11
+ from __future__ import annotations
12
+
13
+ from argparse import Action, ArgumentParser, Namespace
14
+ import sys
15
+ from typing import Any
16
+
17
+ from bear_utils._internal import debug
18
+
19
+
20
+ class _DebugInfo(Action):
21
+ def __init__(self, nargs: int | str | None = 0, **kwargs: Any) -> None:
22
+ super().__init__(nargs=nargs, **kwargs)
23
+
24
+ def __call__(self, *_: Any, **__: Any) -> None:
25
+ debug._print_debug_info()
26
+ sys.exit(0)
27
+
28
+
29
+ class _About(Action):
30
+ def __init__(self, nargs: int | str | None = 0, **kwargs: Any) -> None:
31
+ super().__init__(nargs=nargs, **kwargs)
32
+
33
+ def __call__(self, *_: Any, **__: Any) -> None:
34
+ print(debug._get_package_info())
35
+ sys.exit(0)
36
+
37
+
38
+ def get_parser() -> ArgumentParser:
39
+ name: str = debug._get_name()
40
+ version: str = f"{name} v{debug._get_version()}"
41
+ parser = ArgumentParser(description=name.capitalize(), prog=name, exit_on_error=False)
42
+ parser.add_argument("-V", "--version", action="version", version=version)
43
+ parser.add_argument("--about", action=_About, help="Print information about the package")
44
+ parser.add_argument("--debug_info", action=_DebugInfo, help="Print debug information")
45
+ return parser
46
+
47
+
48
+ def main(args: list[str] | None = None) -> int:
49
+ """Main entry point for the CLI.
50
+
51
+ This function is called when the CLI is executed. It can be used to
52
+ initialize the CLI, parse arguments, and execute commands.
53
+
54
+ Args:
55
+ args (list[str] | None): A list of command-line arguments. If None, uses sys.argv[1:].
56
+
57
+ Returns:
58
+ int: Exit code of the CLI execution. 0 for success, non-zero for failure.
59
+ """
60
+ if args is None:
61
+ args = sys.argv[1:]
62
+ try:
63
+ parser: ArgumentParser = get_parser()
64
+ opts: Namespace = parser.parse_args(args)
65
+ print(opts)
66
+ except Exception as e:
67
+ print(f"Error initializing CLI: {e}", file=sys.stderr)
68
+ return 1
69
+ return 0
70
+
71
+
72
+ if __name__ == "__main__":
73
+ main()
@@ -0,0 +1,159 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import importlib.metadata
5
+ from importlib.metadata import PackageNotFoundError, metadata, version
6
+ import os
7
+ import platform
8
+ import sys
9
+
10
+ __PACKAGE_NAME__ = "bear-utils"
11
+
12
+
13
+ @dataclass
14
+ class _Package:
15
+ """Dataclass to store package information."""
16
+
17
+ name: str = __PACKAGE_NAME__
18
+ """Package name."""
19
+ version: str = "0.0.0"
20
+ """Package version."""
21
+ description: str = "No description available."
22
+ """Package description."""
23
+
24
+ def __str__(self) -> str:
25
+ """String representation of the package information."""
26
+ return f"{self.name} v{self.version}: {self.description}"
27
+
28
+
29
+ @dataclass
30
+ class _Variable:
31
+ """Dataclass describing an environment variable."""
32
+
33
+ name: str
34
+ """Variable name."""
35
+ value: str
36
+ """Variable value."""
37
+
38
+
39
+ @dataclass
40
+ class _Environment:
41
+ """Dataclass to store environment information."""
42
+
43
+ interpreter_name: str
44
+ """Python interpreter name."""
45
+ interpreter_version: str
46
+ """Python interpreter version."""
47
+ interpreter_path: str
48
+ """Path to Python executable."""
49
+ platform: str
50
+ """Operating System."""
51
+ packages: list[_Package]
52
+ """Installed packages."""
53
+ variables: list[_Variable]
54
+ """Environment variables."""
55
+
56
+
57
+ def _interpreter_name_version() -> tuple[str, str]:
58
+ if hasattr(sys, "implementation"):
59
+ impl = sys.implementation.version
60
+ version = f"{impl.major}.{impl.minor}.{impl.micro}"
61
+ kind = impl.releaselevel
62
+ if kind != "final":
63
+ version += kind[0] + str(impl.serial)
64
+ return sys.implementation.name, version
65
+ return "", "0.0.0"
66
+
67
+
68
+ def _get_package_info(dist: str = __PACKAGE_NAME__) -> _Package:
69
+ try:
70
+ return _Package(
71
+ name=dist,
72
+ version=version(dist),
73
+ description=metadata(dist)["Summary"],
74
+ )
75
+ except PackageNotFoundError:
76
+ return _Package(name=dist)
77
+
78
+
79
+ def _get_name(dist: str = __PACKAGE_NAME__) -> str:
80
+ """Get name of the given distribution.
81
+
82
+ Parameters:
83
+ dist: A distribution name.
84
+
85
+ Returns:
86
+ A package name.
87
+ """
88
+ return _get_package_info(dist).name
89
+
90
+
91
+ def _get_version(dist: str = __PACKAGE_NAME__) -> str:
92
+ """Get version of the given distribution.
93
+
94
+ Parameters:
95
+ dist: A distribution name.
96
+
97
+ Returns:
98
+ A version number.
99
+ """
100
+ return _get_package_info(dist).version
101
+
102
+
103
+ def _get_description(dist: str = __PACKAGE_NAME__) -> str:
104
+ """Get description of the given distribution.
105
+
106
+ Parameters:
107
+ dist: A distribution name.
108
+
109
+ Returns:
110
+ A description string.
111
+ """
112
+ return _get_package_info(dist).description
113
+
114
+
115
+ def _get_debug_info() -> _Environment:
116
+ """Get debug/environment information.
117
+
118
+ Returns:
119
+ Environment information.
120
+ """
121
+ py_name, py_version = _interpreter_name_version()
122
+ packages: list[str] = [__PACKAGE_NAME__]
123
+ variables: list[str] = [
124
+ "PYTHONPATH",
125
+ *[var for var in os.environ if var.startswith(__PACKAGE_NAME__.replace("-", "_"))],
126
+ ]
127
+ return _Environment(
128
+ interpreter_name=py_name,
129
+ interpreter_version=py_version,
130
+ interpreter_path=sys.executable,
131
+ platform=platform.platform(),
132
+ variables=[_Variable(var, val) for var in variables if (val := os.getenv(var))],
133
+ packages=[_Package(pkg, _get_version(pkg)) for pkg in packages],
134
+ )
135
+
136
+
137
+ def get_installed_packages() -> list[_Package]:
138
+ """Get all installed packages in current environment"""
139
+ packages = []
140
+ for dist in importlib.metadata.distributions():
141
+ packages.append({"name": dist.metadata["Name"], "version": dist.version})
142
+ return packages
143
+
144
+
145
+ def _print_debug_info() -> None:
146
+ """Print debug/environment information."""
147
+ info: _Environment = _get_debug_info()
148
+ print(f"- __System__: {info.platform}")
149
+ print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})")
150
+ print("- __Environment variables__:")
151
+ for var in info.variables:
152
+ print(f" - `{var.name}`: `{var.value}`")
153
+ print("- __Installed packages__:")
154
+ for pkg in info.packages:
155
+ print(f" - `{pkg.name}` v{pkg.version}")
156
+
157
+
158
+ if __name__ == "__main__":
159
+ _print_debug_info()
@@ -5,7 +5,7 @@ from typing import Any, cast
5
5
 
6
6
  from rich.markdown import Markdown
7
7
 
8
- from bear_utils.logging import BaseLogger
8
+ from bear_utils.logger_manager import BaseLogger
9
9
 
10
10
  from ._common import GPT_4_1_NANO, PRODUCTION_MODE, TESTING_MODE, AIModel, EnvironmentMode
11
11
  from ._config import AIEndpointConfig
@@ -1,6 +1,6 @@
1
1
  from abc import ABC, abstractmethod
2
2
 
3
- from bear_utils.logging import BaseLogger
3
+ from bear_utils.logger_manager import BaseLogger
4
4
 
5
5
 
6
6
  class ResponseParser[T_Response](ABC):
@@ -8,7 +8,7 @@ from prompt_toolkit.validation import ValidationError, Validator
8
8
 
9
9
  from bear_utils.constants._exceptions import UserCancelledError
10
10
  from bear_utils.constants._lazy_typing import LitBool, LitFloat, LitInt, LitStr, OptBool, OptFloat, OptInt, OptStr
11
- from bear_utils.logging.loggers import get_console
11
+ from bear_utils.logger_manager import get_console
12
12
 
13
13
  # TODO: Overhaul this trash, it is written like absolute garbage.
14
14
 
@@ -14,7 +14,7 @@ from subprocess import CompletedProcess
14
14
  from typing import Self, override
15
15
 
16
16
  from bear_utils.constants.logger_protocol import LoggerProtocol
17
- from bear_utils.logging import VERBOSE, BaseLogger, SubConsoleLogger
17
+ from bear_utils.logger_manager import VERBOSE, BaseLogger, SubConsoleLogger
18
18
 
19
19
  from ._base_command import BaseShellCommand
20
20
  from ._common import DEFAULT_SHELL
@@ -9,11 +9,11 @@ from typing import Any, Self
9
9
  from tinydb import Query, TinyDB
10
10
 
11
11
 
12
- def get_bear_config_path() -> Path:
13
- """Get the path to the bear configuration path"""
14
- path: Path = Path.home() / ".config" / "bear_utils"
15
- path.mkdir(parents=True, exist_ok=True)
16
- return path
12
+ def get_config_folder() -> Path:
13
+ """Get the path to the bear configuration directory."""
14
+ config_path: Path = Path.home() / ".bear_utils"
15
+ config_path.mkdir(parents=True, exist_ok=True)
16
+ return config_path
17
17
 
18
18
 
19
19
  class SettingsManager:
@@ -21,11 +21,12 @@ class SettingsManager:
21
21
 
22
22
  __slots__ = ("cache", "db", "file_path", "settings_name")
23
23
 
24
- def __init__(self, settings_name: str) -> None:
24
+ def __init__(self, settings_name: str, folder_path: str | Path | None = None) -> None:
25
25
  """Initialize the SettingsManager with a specific settings name."""
26
- self.settings_name = settings_name
26
+ self.settings_name: str = settings_name
27
27
  self.cache: dict[str, Any] = {}
28
- self.file_path: Path = get_bear_config_path() / f"{settings_name}.json"
28
+ file_name: str = f"{settings_name}.json"
29
+ self.file_path: Path = Path(folder_path) / file_name if folder_path else get_config_folder() / file_name
29
30
  self.db: TinyDB = TinyDB(self.file_path, indent=4, ensure_ascii=False)
30
31
 
31
32
  atexit.register(self.close)
@@ -8,7 +8,7 @@ from typing import TYPE_CHECKING
8
8
  from bear_utils.cli.shell._base_command import BaseShellCommand as ShellCommand
9
9
  from bear_utils.cli.shell._base_shell import AsyncShellSession
10
10
  from bear_utils.extras.platform_utils import OS, get_platform
11
- from bear_utils.logging.logger_manager.loggers._base_logger import BaseLogger
11
+ from bear_utils.logger_manager.loggers._base_logger import BaseLogger
12
12
 
13
13
  if TYPE_CHECKING:
14
14
  from subprocess import CompletedProcess
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
17
17
  class TextHelper:
18
18
  @cached_property
19
19
  def local_console(self) -> BaseLogger:
20
- from bear_utils.logging.loggers import BaseLogger # noqa: PLC0415
20
+ from bear_utils.logger_manager import BaseLogger # noqa: PLC0415
21
21
 
22
22
  init: bool = not BaseLogger.has_instance()
23
23
  return BaseLogger.get_instance(init=init)
@@ -0,0 +1,9 @@
1
+ """A module for handling responses for functions, methods, and classes in Bear Utils."""
2
+
3
+ from .function_response import FAILURE, SUCCESS, FunctionResponse
4
+
5
+ __all__ = [
6
+ "FAILURE",
7
+ "SUCCESS",
8
+ "FunctionResponse",
9
+ ]
@@ -5,12 +5,11 @@ from __future__ import annotations
5
5
  from io import StringIO
6
6
  import json
7
7
  from subprocess import CompletedProcess
8
- from typing import TYPE_CHECKING, Any, Literal, Self, overload
8
+ from typing import Any, Literal, Self, overload
9
9
 
10
10
  from pydantic import BaseModel, Field, field_validator
11
11
 
12
- if TYPE_CHECKING:
13
- from bear_utils.constants.logger_protocol import LoggerProtocol
12
+ from bear_utils.constants.logger_protocol import LoggerProtocol # noqa: TC001 # DO NOT PUT INTO A TYPE_CHECKING BLOCK
14
13
 
15
14
  SUCCESS: list[str] = ["name", "success"]
16
15
  FAILURE: list[str] = ["name"]
@@ -130,6 +129,25 @@ class FunctionResponse(BaseModel):
130
129
  """Check if the response indicates success."""
131
130
  return self.returncode == 0
132
131
 
132
+ def sub_task(
133
+ self,
134
+ name: str = "",
135
+ content: str | list[str] = "",
136
+ error: str | list[str] = "",
137
+ extra: dict[str, Any] | None = None,
138
+ returncode: int | None = None,
139
+ log_output: bool = False,
140
+ ) -> None:
141
+ """Add a sub-task response to the FunctionResponse."""
142
+ func_response: FunctionResponse = FunctionResponse(name=name, logger=self.logger).add(
143
+ content=content,
144
+ error=error,
145
+ returncode=returncode or self.returncode,
146
+ log_output=log_output,
147
+ extra=extra,
148
+ )
149
+ self.add(content=func_response)
150
+
133
151
  def successful(
134
152
  self,
135
153
  content: str | list[str] | CompletedProcess,
@@ -184,6 +202,19 @@ class FunctionResponse(BaseModel):
184
202
  except Exception as e:
185
203
  raise ValueError(f"Failed to add content: {e!s}") from e
186
204
 
205
+ def _handle_function_response(self, func_response: FunctionResponse) -> None:
206
+ """Handle a FunctionResponse object and update the current response."""
207
+ if func_response.extra:
208
+ self.extra.update(func_response.extra)
209
+ self._add_to_error(error=func_response.error, name=func_response.name)
210
+ self._add_to_content(content=func_response.content, name=func_response.name)
211
+
212
+ def _handle_completed_process(self, result: CompletedProcess[str]) -> None:
213
+ """Handle a CompletedProcess object and update the FunctionResponse."""
214
+ self._add_to_content(content=result.stdout.strip() if result.stdout else "")
215
+ self._add_to_error(error=result.stderr.strip() if result.stderr else "")
216
+ self.returncode = result.returncode
217
+
187
218
  def add(
188
219
  self,
189
220
  content: list[str] | str | FunctionResponse | CompletedProcess | None = None,
@@ -205,45 +236,49 @@ class FunctionResponse(BaseModel):
205
236
  Self: The updated FunctionResponse instance.
206
237
  """
207
238
  try:
208
- if isinstance(content, FunctionResponse):
209
- if content.extra:
210
- self.extra.update(content.extra)
211
- self._add_to_error(error=content.error, name=content.name)
212
- self._add_to_content(content=content.content, name=content.name)
213
- self.number_of_tasks += 1
214
- elif isinstance(content, CompletedProcess):
215
- result: CompletedProcess[str] = content
216
- self._add_to_content(content=result.stdout.strip() if result.stdout else "")
217
- self._add_to_error(error=result.stderr.strip() if result.stderr else "")
218
- self.returncode = result.returncode
219
- self.number_of_tasks += 1
220
- elif isinstance(content, str | list):
221
- self._add_to_content(content=content)
222
- self.number_of_tasks += 1
223
- if isinstance(error, str | list):
224
- self._add_to_error(error=error)
225
- if returncode is not None:
226
- self.returncode = returncode
227
- if extra is not None and isinstance(extra, dict):
228
- self.extra.update(extra)
229
- if log_output and self.logger is not None:
230
- if content is not None and error is None:
231
- if isinstance(content, list):
232
- for item in content:
233
- self.logger.info(message=f"{self.name}: {item}" if self.name else item)
234
- elif isinstance(content, str):
235
- self.logger.info(message=f"{self.name}: {content}" if self.name else content)
236
- elif error is not None and content is None:
237
- if isinstance(error, list):
238
- for err in error:
239
- self.logger.error(message=f"{self.name}: {err}" if self.name else err)
240
- elif isinstance(error, str):
241
- self.logger.error(message=f"{self.name}: {error}" if self.name else error)
242
-
239
+ match content:
240
+ case FunctionResponse():
241
+ self._handle_function_response(func_response=content)
242
+ self.number_of_tasks += 1
243
+ case CompletedProcess():
244
+ self._handle_completed_process(result=content)
245
+ self.number_of_tasks += 1
246
+ case str() | list() if content:
247
+ self._add_to_content(content=content)
248
+ self.number_of_tasks += 1
249
+ case None:
250
+ content = None
251
+ case _:
252
+ content = None
253
+ self._add_to_error(error=error) if isinstance(error, (str | list)) else None
254
+ self.returncode = returncode if returncode is not None else self.returncode
255
+ self.extra.update(extra) if isinstance(extra, dict) else None
256
+ if log_output and self.logger is not None and (content is not None or error is not None):
257
+ self._log_handling(content=content, error=error, logger=self.logger)
243
258
  except Exception as e:
244
259
  raise ValueError(f"Failed to add content: {e!s}") from e
245
260
  return self
246
261
 
262
+ def _log_handling(
263
+ self,
264
+ content: list[str] | str | FunctionResponse | CompletedProcess | None,
265
+ error: str | list[str] | None,
266
+ logger: LoggerProtocol,
267
+ ) -> None:
268
+ """Log the content and error messages if they exist."""
269
+ if content is not None and error is None:
270
+ if isinstance(content, list):
271
+ for item in content:
272
+ logger.info(message=f"{self.name}: {item}" if self.name else item)
273
+ elif isinstance(content, str):
274
+ logger.info(message=f"{self.name}: {content}" if self.name else content)
275
+ elif error is not None and content is None:
276
+ if isinstance(error, list):
277
+ for err in error:
278
+ logger.error(message=f"{self.name}: {err}" if self.name else err)
279
+ elif isinstance(error, str):
280
+ logger.error(message=f"{self.name}: {error}" if self.name else error)
281
+
247
282
  @overload
248
283
  def done(self, to_dict: Literal[True], suppress: list[str] | None = None) -> dict[str, Any]: ...
249
284
 
@@ -8,12 +8,6 @@ P = ParamSpec("P")
8
8
  R = TypeVar("R")
9
9
 
10
10
 
11
- def check_data_type(data: dict[str, Any] | str, valid_types: tuple[type, ...]) -> None:
12
- """Check if the data is of a valid type for text files."""
13
- if not isinstance(data, valid_types):
14
- raise TypeError(f"Data must be one of {valid_types}, got {type(data)}")
15
-
16
-
17
11
  class FileHandler(ABC):
18
12
  """Abstract class for file handling with read, write, and present methods
19
13
 
@@ -40,7 +34,7 @@ class FileHandler(ABC):
40
34
  return file_path.suffix.lstrip(".") in cls.valid_extensions
41
35
 
42
36
  @classmethod
43
- def check_data_type(cls, data: Any) -> Any:
37
+ def check_data_type(cls, data: dict[str, Any] | str, valid_types: tuple[type, ...]) -> None:
44
38
  """Check if the data is of the correct type.
45
39
 
46
40
  Args:
@@ -49,8 +43,8 @@ class FileHandler(ABC):
49
43
  Returns:
50
44
  bool: True if the data is of the correct type, False otherwise
51
45
  """
52
- if not isinstance(data, cls.valid_types):
53
- raise TypeError(f"Data must be one of {cls.valid_types}, got {type(data)}")
46
+ if not isinstance(data, valid_types):
47
+ raise TypeError(f"Data must be one of {valid_types}, got {type(data)}")
54
48
 
55
49
  @classmethod
56
50
  def ValidateFileType(cls, method: Callable[P, R]) -> Callable[P, R]: # noqa: N802 disable=invalid-name