bear-utils 0.8.23__tar.gz → 0.8.24__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.
- {bear_utils-0.8.23 → bear_utils-0.8.24}/.bumpversion.cfg +1 -1
- {bear_utils-0.8.23 → bear_utils-0.8.24}/PKG-INFO +3 -2
- {bear_utils-0.8.23 → bear_utils-0.8.24}/README.md +1 -1
- {bear_utils-0.8.23 → bear_utils-0.8.24}/pyproject.toml +2 -1
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/prompt_helpers.py +25 -18
- bear_utils-0.8.24/src/bear_utils/cli/typer_bridge.py +90 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_function_response.py +7 -7
- {bear_utils-0.8.23 → bear_utils-0.8.24}/uv.lock +34 -1
- {bear_utils-0.8.23 → bear_utils-0.8.24}/.gitignore +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/.python-version +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/AGENTS.md +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/config/coverage.ini +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/config/default.toml +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/config/git-changelog.toml +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/config/pytest.ini +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/config/ruff.toml +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/config/vscode/launch.json +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/config/vscode/settings.json +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/config/vscode/tasks.json +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/directory_structure.txt +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/directory_structure.xml +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/maskfile.md +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/noxfile.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/__main__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/_internal/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/_internal/cli.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/_internal/debug.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/_common.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/_config.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/_parsers.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/ai/ai_helpers/_types.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cache/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/commands.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/shell/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/shell/_base_command.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/shell/_base_shell.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/cli/shell/_common.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/config/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/config/config_manager.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/config/dir_manager.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/config/settings_manager.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/_exceptions.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/_lazy_typing.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/date_related.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/server.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/constants/time_related.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/database/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/database/_db_manager.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/events/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/events/events_class.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/events/events_module.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/_async_helpers.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/_tools.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/platform_utils.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/responses/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/responses/function_response.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/wrappers/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/wrappers/add_methods.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/_base_file_handler.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/file_handler_factory.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/json_file_handler.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/log_file_handler.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/toml_file_handler.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/txt_file_handler.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/yaml_file_handler.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/ignore_parser.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/graphics/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/graphics/bear_gradient.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/graphics/image_helpers.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/_settings.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/_types.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/qt_app.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/qt_color_picker.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/qt_file_handler.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/gui/gui_tools/qt_input_dialog.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/_common.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/_console_junk.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/_log_level.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/_styles.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/logger_protocol.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/_level_sin.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/base_logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/base_logger.pyi +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/basic_logger/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/basic_logger/logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/basic_logger/logger.pyi +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/buffer_logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/console_logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/console_logger.pyi +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/fastapi_logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/file_logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/simple_logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/sub_logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/sub_logger.pyi +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/monitoring/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/monitoring/_common.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/monitoring/host_monitor.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/time/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/__init__.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_add_ord_suffix.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_clipboard.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_database_manager.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_default_shell.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_gradient.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_logger.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_platform_utils.py +0 -0
- {bear_utils-0.8.23 → bear_utils-0.8.24}/tests/test_prompt_helpers.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bear-utils
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.24
|
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
|
@@ -19,12 +19,13 @@ Requires-Dist: singleton-base>=1.0.5
|
|
19
19
|
Requires-Dist: sqlalchemy<3.0.0,>=2.0.40
|
20
20
|
Requires-Dist: tinydb>=4.8.2
|
21
21
|
Requires-Dist: toml>=0.10.2
|
22
|
+
Requires-Dist: typer>=0.16.0
|
22
23
|
Requires-Dist: uvicorn>=0.35.0
|
23
24
|
Provides-Extra: gui
|
24
25
|
Requires-Dist: pyqt6>=6.9.0; extra == 'gui'
|
25
26
|
Description-Content-Type: text/markdown
|
26
27
|
|
27
|
-
# Bear Utils v# Bear Utils v0.8.
|
28
|
+
# Bear Utils v# Bear Utils v0.8.24
|
28
29
|
|
29
30
|
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.
|
30
31
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Bear Utils v# Bear Utils v0.8.
|
1
|
+
# Bear Utils v# Bear Utils v0.8.24
|
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
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "bear-utils"
|
3
|
-
version = "0.8.
|
3
|
+
version = "0.8.24"
|
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"
|
@@ -22,6 +22,7 @@ dependencies = [
|
|
22
22
|
"fastapi>=0.116.0",
|
23
23
|
"uvicorn>=0.35.0",
|
24
24
|
"bear-epoch-time>=1.1.1",
|
25
|
+
"typer>=0.16.0",
|
25
26
|
]
|
26
27
|
|
27
28
|
[project.optional-dependencies]
|
@@ -10,7 +10,11 @@ from bear_utils.constants._exceptions import UserCancelledError
|
|
10
10
|
from bear_utils.constants._lazy_typing import OptBool, OptFloat, OptInt, OptStr
|
11
11
|
from bear_utils.logger_manager import get_console
|
12
12
|
|
13
|
-
|
13
|
+
|
14
|
+
def _parse_exit(value: str) -> bool:
|
15
|
+
"""Parse a string into a boolean indicating if the user wants to exit."""
|
16
|
+
lower_value: str = value.lower().strip()
|
17
|
+
return lower_value in ("exit", "quit", "q")
|
14
18
|
|
15
19
|
|
16
20
|
def _parse_bool(value: str) -> bool:
|
@@ -70,7 +74,7 @@ def ask_question(question: str, expected_type: type, default: Any = None) -> Any
|
|
70
74
|
UserCancelledError: If the user cancels input with Ctrl+C
|
71
75
|
ValueError: If an unsupported type is specified
|
72
76
|
"""
|
73
|
-
console,
|
77
|
+
console, _ = get_console("prompt_helpers.py")
|
74
78
|
|
75
79
|
try:
|
76
80
|
while True:
|
@@ -80,14 +84,14 @@ def ask_question(question: str, expected_type: type, default: Any = None) -> Any
|
|
80
84
|
if not response:
|
81
85
|
if default is not None:
|
82
86
|
return default
|
83
|
-
|
87
|
+
console.error("Input required. Please enter a value.")
|
84
88
|
continue
|
85
89
|
try:
|
86
90
|
result: str | int | float | bool = _convert_value(response, expected_type)
|
87
|
-
|
91
|
+
console.verbose(f"{expected_type.__name__} detected")
|
88
92
|
return result
|
89
93
|
except ValueError as e:
|
90
|
-
|
94
|
+
console.error(f"Invalid input: {e}. Please enter a valid {expected_type.__name__}.")
|
91
95
|
|
92
96
|
except KeyboardInterrupt:
|
93
97
|
raise UserCancelledError("User cancelled input") from None
|
@@ -108,15 +112,13 @@ def ask_yes_no(question: str, default: bool | None = None) -> bool | None:
|
|
108
112
|
try:
|
109
113
|
while True:
|
110
114
|
console.print(question)
|
111
|
-
response = prompt("> ").strip().lower()
|
112
|
-
|
115
|
+
response: str = prompt("> ").strip().lower()
|
113
116
|
if not response:
|
114
117
|
if default is not None:
|
115
118
|
return default
|
116
119
|
sub.error("Please enter 'yes', 'no', or 'exit'.")
|
117
120
|
continue
|
118
|
-
|
119
|
-
if response in ("exit", "quit"):
|
121
|
+
if _parse_exit(response):
|
120
122
|
return None
|
121
123
|
try:
|
122
124
|
return _parse_bool(response)
|
@@ -128,7 +130,10 @@ def ask_yes_no(question: str, default: bool | None = None) -> bool | None:
|
|
128
130
|
|
129
131
|
|
130
132
|
def restricted_prompt(
|
131
|
-
question: str,
|
133
|
+
question: str,
|
134
|
+
valid_options: list[str],
|
135
|
+
exit_command: str = "exit",
|
136
|
+
case_sensitive: bool = False,
|
132
137
|
) -> str | None:
|
133
138
|
"""Continuously prompt the user until they provide a valid response or exit.
|
134
139
|
|
@@ -141,12 +146,12 @@ def restricted_prompt(
|
|
141
146
|
Returns:
|
142
147
|
The user's response or None if they chose to exit
|
143
148
|
"""
|
144
|
-
console,
|
145
|
-
completer_options = [*valid_options, exit_command]
|
149
|
+
console, _ = get_console("prompt_helpers.py")
|
150
|
+
completer_options: list[str] = [*valid_options, exit_command]
|
146
151
|
completer = WordCompleter(completer_options)
|
147
152
|
|
148
|
-
comparison_options = valid_options if case_sensitive else [opt.lower() for opt in valid_options]
|
149
|
-
comparison_exit = exit_command if case_sensitive else exit_command.lower()
|
153
|
+
comparison_options: list[str] = valid_options if case_sensitive else [opt.lower() for opt in valid_options]
|
154
|
+
comparison_exit: str = exit_command if case_sensitive else exit_command.lower()
|
150
155
|
|
151
156
|
class OptionValidator(Validator):
|
152
157
|
def validate(self, document: Any) -> None:
|
@@ -162,11 +167,14 @@ def restricted_prompt(
|
|
162
167
|
while True:
|
163
168
|
console.print(question)
|
164
169
|
response: str = prompt(
|
165
|
-
"> ",
|
170
|
+
"> ",
|
171
|
+
completer=completer,
|
172
|
+
validator=OptionValidator(),
|
173
|
+
complete_while_typing=True,
|
166
174
|
).strip()
|
167
175
|
comparison_response: str = response if case_sensitive else response.lower()
|
168
176
|
if not response:
|
169
|
-
|
177
|
+
console.error("Please enter a valid option or 'exit'.")
|
170
178
|
continue
|
171
179
|
if comparison_response == comparison_exit:
|
172
180
|
return None
|
@@ -175,7 +183,6 @@ def restricted_prompt(
|
|
175
183
|
idx: int = comparison_options.index(comparison_response)
|
176
184
|
return valid_options[idx]
|
177
185
|
return response
|
178
|
-
|
179
186
|
except KeyboardInterrupt:
|
180
|
-
|
187
|
+
console.warning("KeyboardInterrupt: Exiting the prompt.")
|
181
188
|
return None
|
@@ -0,0 +1,90 @@
|
|
1
|
+
"""A simple bridge for augmenting Typer with alias support and command execution for interactive use."""
|
2
|
+
|
3
|
+
from collections.abc import Callable
|
4
|
+
import shlex
|
5
|
+
from typing import Any, TypedDict
|
6
|
+
|
7
|
+
from rich.console import Console
|
8
|
+
from singleton_base import SingletonBase
|
9
|
+
from typer import Exit, Typer
|
10
|
+
from typer.models import CommandInfo
|
11
|
+
|
12
|
+
from bear_utils.logger_manager import AsyncLoggerProtocol, LoggerProtocol
|
13
|
+
|
14
|
+
|
15
|
+
class CommandMeta(TypedDict):
|
16
|
+
"""Metadata for a Typer command."""
|
17
|
+
|
18
|
+
name: str
|
19
|
+
help: str
|
20
|
+
hidden: bool
|
21
|
+
|
22
|
+
|
23
|
+
def get_command_meta(command: CommandInfo) -> CommandMeta:
|
24
|
+
"""Extract metadata from a Typer command."""
|
25
|
+
return {
|
26
|
+
"name": command.name or (command.callback.__name__ if command.callback else "unknown"),
|
27
|
+
"help": (command.callback.__doc__ if command.callback else None) or "No description available",
|
28
|
+
"hidden": command.hidden,
|
29
|
+
}
|
30
|
+
|
31
|
+
|
32
|
+
# TODO: Add support for usage statements for a more robust help system
|
33
|
+
|
34
|
+
|
35
|
+
class TyperBridge(SingletonBase):
|
36
|
+
"""Simple bridge for Typer command execution."""
|
37
|
+
|
38
|
+
def __init__(self, typer_app: Typer, console: AsyncLoggerProtocol | LoggerProtocol | Console) -> None:
|
39
|
+
"""Initialize the TyperBridge with a Typer app instance."""
|
40
|
+
self.app: Typer = typer_app
|
41
|
+
self.console: AsyncLoggerProtocol | LoggerProtocol | Console = console or Console()
|
42
|
+
self.command_meta: dict[str, CommandMeta] = {}
|
43
|
+
|
44
|
+
def metadata(self, *alias_names: str) -> Callable[..., Callable[..., Any]]:
|
45
|
+
"""Register aliases as hidden Typer commands."""
|
46
|
+
|
47
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
48
|
+
for alias in alias_names:
|
49
|
+
self.app.command(name=alias, hidden=True)(func)
|
50
|
+
return func
|
51
|
+
|
52
|
+
return decorator
|
53
|
+
|
54
|
+
def execute_command(self, command_string: str) -> bool:
|
55
|
+
"""Execute command via Typer. Return True if successful."""
|
56
|
+
try:
|
57
|
+
parts: list[str] = shlex.split(command_string.strip())
|
58
|
+
if not parts:
|
59
|
+
return False
|
60
|
+
self.app(parts, standalone_mode=False)
|
61
|
+
return True
|
62
|
+
except Exit:
|
63
|
+
return True
|
64
|
+
except Exception as e:
|
65
|
+
if isinstance(self.console, Console):
|
66
|
+
self.console.print(f"[red]Error executing command: {e}[/red]")
|
67
|
+
else:
|
68
|
+
self.console.error(f"Error executing command: {e}", exc_info=True)
|
69
|
+
return False
|
70
|
+
|
71
|
+
def bootstrap_command_meta(self) -> None:
|
72
|
+
"""Bootstrap command metadata from the Typer app."""
|
73
|
+
if not self.command_meta:
|
74
|
+
for cmd in self.app.registered_commands:
|
75
|
+
cmd_meta: CommandMeta = get_command_meta(command=cmd)
|
76
|
+
self.command_meta[cmd_meta["name"]] = cmd_meta
|
77
|
+
|
78
|
+
def get_all_command_info(self, show_hidden: bool = False) -> dict[str, CommandMeta]:
|
79
|
+
"""Get all command information from the Typer app."""
|
80
|
+
if not self.command_meta:
|
81
|
+
self.bootstrap_command_meta()
|
82
|
+
if not show_hidden:
|
83
|
+
return {name: meta for name, meta in self.command_meta.items() if not meta["hidden"]}
|
84
|
+
return self.command_meta
|
85
|
+
|
86
|
+
def get_command_info(self, command_name: str) -> CommandMeta | None:
|
87
|
+
"""Get metadata for a specific command."""
|
88
|
+
if not self.command_meta:
|
89
|
+
self.bootstrap_command_meta()
|
90
|
+
return self.command_meta.get(command_name)
|
@@ -597,11 +597,11 @@ class TestConstants:
|
|
597
597
|
|
598
598
|
def test_success_constant(self):
|
599
599
|
"""Test SUCCESS constant values."""
|
600
|
-
assert SUCCESS == ["name", "success"]
|
600
|
+
assert SUCCESS == ["name", "success", "number_of_tasks"]
|
601
601
|
|
602
602
|
def test_failure_constant(self):
|
603
603
|
"""Test FAILURE constant values."""
|
604
|
-
assert FAILURE == ["name"]
|
604
|
+
assert FAILURE == ["name", "number_of_tasks"]
|
605
605
|
|
606
606
|
|
607
607
|
class TestComplexScenarios:
|
@@ -635,13 +635,13 @@ class TestComplexScenarios:
|
|
635
635
|
response.successful("Cleared 5 completed tasks")
|
636
636
|
response.add(content="Operation summary generated")
|
637
637
|
|
638
|
-
result = response.done(to_dict=True
|
638
|
+
result = response.done(to_dict=True)
|
639
639
|
|
640
640
|
# SUCCESS suppresses "name" and "success", so check what actually remains
|
641
641
|
assert "content" in result
|
642
642
|
assert "number_of_tasks" not in result # This will only be here in failures
|
643
|
-
assert "name"
|
644
|
-
assert "success"
|
643
|
+
assert "name" in result
|
644
|
+
assert "success" in result
|
645
645
|
|
646
646
|
def test_mcp_server_response_format_error(self):
|
647
647
|
"""Test formatting response for MCP server consumption."""
|
@@ -651,12 +651,12 @@ class TestComplexScenarios:
|
|
651
651
|
response.fail("Failed to clear tasks")
|
652
652
|
response.add(content="Error summary generated")
|
653
653
|
|
654
|
-
result = response.done(to_dict=True
|
654
|
+
result = response.done(to_dict=True)
|
655
655
|
|
656
656
|
# SUCCESS suppresses "name" and "success", so check what actually remains
|
657
657
|
assert "content" in result
|
658
658
|
assert "number_of_tasks" in result
|
659
|
-
assert "name"
|
659
|
+
assert "name" in result
|
660
660
|
assert "success" in result
|
661
661
|
|
662
662
|
def test_error_handling_in_add_method(self):
|
@@ -73,7 +73,7 @@ wheels = [
|
|
73
73
|
|
74
74
|
[[package]]
|
75
75
|
name = "bear-utils"
|
76
|
-
version = "0.8.
|
76
|
+
version = "0.8.23"
|
77
77
|
source = { editable = "." }
|
78
78
|
dependencies = [
|
79
79
|
{ name = "bear-epoch-time" },
|
@@ -91,9 +91,15 @@ dependencies = [
|
|
91
91
|
{ name = "sqlalchemy" },
|
92
92
|
{ name = "tinydb" },
|
93
93
|
{ name = "toml" },
|
94
|
+
{ name = "typer" },
|
94
95
|
{ name = "uvicorn" },
|
95
96
|
]
|
96
97
|
|
98
|
+
[package.optional-dependencies]
|
99
|
+
gui = [
|
100
|
+
{ name = "pyqt6" },
|
101
|
+
]
|
102
|
+
|
97
103
|
[package.dev-dependencies]
|
98
104
|
ci = [
|
99
105
|
{ name = "bump2version" },
|
@@ -129,14 +135,17 @@ requires-dist = [
|
|
129
135
|
{ name = "prompt-toolkit", specifier = ">=3.0.51,<4.0.0" },
|
130
136
|
{ name = "pydantic", specifier = ">=2.11.5" },
|
131
137
|
{ name = "pyglm", specifier = ">=2.8.2,<3.0.0" },
|
138
|
+
{ name = "pyqt6", marker = "extra == 'gui'", specifier = ">=6.9.0" },
|
132
139
|
{ name = "pyyaml", specifier = ">=6.0.2" },
|
133
140
|
{ name = "rich", specifier = ">=14.0.0,<15.0.0" },
|
134
141
|
{ name = "singleton-base", specifier = ">=1.0.5" },
|
135
142
|
{ name = "sqlalchemy", specifier = ">=2.0.40,<3.0.0" },
|
136
143
|
{ name = "tinydb", specifier = ">=4.8.2" },
|
137
144
|
{ name = "toml", specifier = ">=0.10.2" },
|
145
|
+
{ name = "typer", specifier = ">=0.16.0" },
|
138
146
|
{ name = "uvicorn", specifier = ">=0.35.0" },
|
139
147
|
]
|
148
|
+
provides-extras = ["gui"]
|
140
149
|
|
141
150
|
[package.metadata.requires-dev]
|
142
151
|
ci = [
|
@@ -1252,6 +1261,15 @@ wheels = [
|
|
1252
1261
|
{ url = "https://files.pythonhosted.org/packages/a6/24/4d91e05817e92e3a61c8a21e08fd0f390f5301f1c448b137c57c4bc6e543/semver-3.0.4-py3-none-any.whl", hash = "sha256:9c824d87ba7f7ab4a1890799cec8596f15c1241cb473404ea1cb0c55e4b04746", size = 17912, upload_time = "2025-01-24T13:19:24.949Z" },
|
1253
1262
|
]
|
1254
1263
|
|
1264
|
+
[[package]]
|
1265
|
+
name = "shellingham"
|
1266
|
+
version = "1.5.4"
|
1267
|
+
source = { registry = "https://pypi.org/simple/" }
|
1268
|
+
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload_time = "2023-10-24T04:13:40.426Z" }
|
1269
|
+
wheels = [
|
1270
|
+
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload_time = "2023-10-24T04:13:38.866Z" },
|
1271
|
+
]
|
1272
|
+
|
1255
1273
|
[[package]]
|
1256
1274
|
name = "singleton-base"
|
1257
1275
|
version = "1.0.8"
|
@@ -1370,6 +1388,21 @@ wheels = [
|
|
1370
1388
|
{ url = "https://files.pythonhosted.org/packages/56/56/2d2a9139ec6c470836fc2b8c0a5c3c781d50b22c90c47c3f578902080a83/type_lens-0.2.3-py3-none-any.whl", hash = "sha256:3c1850545de595a5d5df4e24f8ea8c0f6d9aff42317ca9941d5069b9ad5c1e2e", size = 14288, upload_time = "2024-10-02T14:52:43.191Z" },
|
1371
1389
|
]
|
1372
1390
|
|
1391
|
+
[[package]]
|
1392
|
+
name = "typer"
|
1393
|
+
version = "0.16.0"
|
1394
|
+
source = { registry = "https://pypi.org/simple/" }
|
1395
|
+
dependencies = [
|
1396
|
+
{ name = "click" },
|
1397
|
+
{ name = "rich" },
|
1398
|
+
{ name = "shellingham" },
|
1399
|
+
{ name = "typing-extensions" },
|
1400
|
+
]
|
1401
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c5/8c/7d682431efca5fd290017663ea4588bf6f2c6aad085c7f108c5dbc316e70/typer-0.16.0.tar.gz", hash = "sha256:af377ffaee1dbe37ae9440cb4e8f11686ea5ce4e9bae01b84ae7c63b87f1dd3b", size = 102625, upload_time = "2025-05-26T14:30:31.824Z" }
|
1402
|
+
wheels = [
|
1403
|
+
{ url = "https://files.pythonhosted.org/packages/76/42/3efaf858001d2c2913de7f354563e3a3a2f0decae3efe98427125a8f441e/typer-0.16.0-py3-none-any.whl", hash = "sha256:1f79bed11d4d02d4310e3c1b7ba594183bcedb0ac73b27a9e5f28f6fb5b98855", size = 46317, upload_time = "2025-05-26T14:30:30.523Z" },
|
1404
|
+
]
|
1405
|
+
|
1373
1406
|
[[package]]
|
1374
1407
|
name = "types-markdown"
|
1375
1408
|
version = "3.8.0.20250708"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/extras/responses/function_response.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/_base_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/file_handler_factory.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/json_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/log_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/toml_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/txt_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/files/file_handlers/yaml_file_handler.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/base_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/base_logger.pyi
RENAMED
File without changes
|
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/basic_logger/logger.py
RENAMED
File without changes
|
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/buffer_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/console_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/console_logger.pyi
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/fastapi_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/file_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/simple_logger.py
RENAMED
File without changes
|
File without changes
|
{bear_utils-0.8.23 → bear_utils-0.8.24}/src/bear_utils/logger_manager/loggers/sub_logger.pyi
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|