bear-utils 0.8.21__tar.gz → 0.8.23__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.21 → bear_utils-0.8.23}/.bumpversion.cfg +1 -1
- {bear_utils-0.8.21 → bear_utils-0.8.23}/PKG-INFO +2 -2
- {bear_utils-0.8.21 → bear_utils-0.8.23}/README.md +1 -1
- {bear_utils-0.8.21 → bear_utils-0.8.23}/pyproject.toml +1 -1
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/database/_db_manager.py +9 -16
- bear_utils-0.8.23/src/bear_utils/extras/_async_helpers.py +67 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/extras/responses/function_response.py +61 -33
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/gui/gui_tools/qt_app.py +1 -1
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_function_response.py +17 -2
- bear_utils-0.8.21/src/bear_utils/extras/_async_helpers.py +0 -38
- {bear_utils-0.8.21 → bear_utils-0.8.23}/.gitignore +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/.python-version +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/AGENTS.md +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/config/coverage.ini +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/config/default.toml +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/config/git-changelog.toml +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/config/pytest.ini +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/config/ruff.toml +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/config/vscode/launch.json +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/config/vscode/settings.json +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/config/vscode/tasks.json +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/directory_structure.txt +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/directory_structure.xml +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/maskfile.md +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/noxfile.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/__main__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/_internal/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/_internal/cli.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/_internal/debug.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/ai/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/ai/ai_helpers/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/ai/ai_helpers/_common.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/ai/ai_helpers/_config.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/ai/ai_helpers/_parsers.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/ai/ai_helpers/_types.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/cache/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/cli/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/cli/commands.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/cli/prompt_helpers.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/cli/shell/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/cli/shell/_base_command.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/cli/shell/_base_shell.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/cli/shell/_common.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/config/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/config/config_manager.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/config/dir_manager.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/config/settings_manager.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/constants/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/constants/_exceptions.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/constants/_lazy_typing.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/constants/date_related.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/constants/server.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/constants/time_related.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/database/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/events/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/events/events_class.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/events/events_module.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/extras/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/extras/_tools.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/extras/platform_utils.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/extras/responses/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/extras/wrappers/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/extras/wrappers/add_methods.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/_base_file_handler.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/file_handler_factory.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/json_file_handler.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/log_file_handler.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/toml_file_handler.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/txt_file_handler.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/yaml_file_handler.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/ignore_parser.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/graphics/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/graphics/bear_gradient.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/graphics/image_helpers.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/gui/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/gui/gui_tools/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/gui/gui_tools/_settings.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/gui/gui_tools/_types.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/gui/gui_tools/qt_color_picker.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/gui/gui_tools/qt_file_handler.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/gui/gui_tools/qt_input_dialog.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/_common.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/_console_junk.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/_log_level.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/_styles.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/logger_protocol.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/_level_sin.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/base_logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/base_logger.pyi +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/basic_logger/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/basic_logger/logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/basic_logger/logger.pyi +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/buffer_logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/console_logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/console_logger.pyi +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/fastapi_logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/file_logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/simple_logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/sub_logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/sub_logger.pyi +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/monitoring/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/monitoring/_common.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/monitoring/host_monitor.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/time/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/__init__.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_add_ord_suffix.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_clipboard.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_database_manager.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_default_shell.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_gradient.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_logger.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_platform_utils.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/tests/test_prompt_helpers.py +0 -0
- {bear_utils-0.8.21 → bear_utils-0.8.23}/uv.lock +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.23
|
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
|
@@ -24,7 +24,7 @@ Provides-Extra: gui
|
|
24
24
|
Requires-Dist: pyqt6>=6.9.0; extra == 'gui'
|
25
25
|
Description-Content-Type: text/markdown
|
26
26
|
|
27
|
-
# Bear Utils v# Bear Utils v0.8.
|
27
|
+
# Bear Utils v# Bear Utils v0.8.23
|
28
28
|
|
29
29
|
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
30
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Bear Utils v# Bear Utils v0.8.
|
1
|
+
# Bear Utils v# Bear Utils v0.8.23
|
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.23"
|
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"
|
@@ -32,42 +32,35 @@ class DatabaseManager:
|
|
32
32
|
raise ValueError("Base class is not set, failed to set base.")
|
33
33
|
return cls._base
|
34
34
|
|
35
|
-
def __init__(self, db_url: str | Path | None = None):
|
35
|
+
def __init__(self, db_url: str | Path | None = None, default_schema: str = "sqlite:///"):
|
36
36
|
if db_url is None or db_url == "":
|
37
37
|
raise ValueError("Database URL cannot be None or empty.")
|
38
|
-
if isinstance(db_url, str) and not db_url.startswith(
|
39
|
-
db_url = f"
|
38
|
+
if isinstance(db_url, str) and not db_url.startswith(default_schema):
|
39
|
+
db_url = f"{default_schema}{db_url}"
|
40
40
|
self.db_url: str = str(db_url)
|
41
41
|
self.engine: Engine = create_engine(self.db_url, echo=False)
|
42
42
|
base: DeclarativeMeta = DatabaseManager.get_base()
|
43
43
|
self.metadata: MetaData = base.metadata
|
44
|
-
self.SessionFactory = sessionmaker(bind=self.engine)
|
45
|
-
self.session = scoped_session(self.SessionFactory)
|
44
|
+
self.SessionFactory: sessionmaker[Session] = sessionmaker(bind=self.engine)
|
45
|
+
self.session: scoped_session[Session] = scoped_session(self.SessionFactory)
|
46
46
|
atexit.register(self.close_all)
|
47
47
|
self.create_tables()
|
48
48
|
|
49
49
|
def get_all_records(self, table_obj: type[TableType]) -> list[TableType]:
|
50
50
|
"""Get all records from a table."""
|
51
|
-
|
52
|
-
return session.query(table_obj).all()
|
51
|
+
return self.session().query(table_obj).all()
|
53
52
|
|
54
53
|
def count_records(self, table_obj: type[TableType]) -> int:
|
55
54
|
"""Count the number of records in a table."""
|
56
|
-
|
57
|
-
count: int = session.query(table_obj).count()
|
58
|
-
return count
|
55
|
+
return self.session().query(table_obj).count()
|
59
56
|
|
60
57
|
def get_records_by_var(self, table_obj: type[TableType], variable: str, value: str) -> list[TableType]:
|
61
58
|
"""Get records from a table by a specific variable."""
|
62
|
-
|
63
|
-
records: list[TableType] = session.query(table_obj).filter(getattr(table_obj, variable) == value).all()
|
64
|
-
return records
|
59
|
+
return self.session().query(table_obj).filter(getattr(table_obj, variable) == value).all()
|
65
60
|
|
66
61
|
def count_records_by_var(self, table_obj: type[TableType], variable: str, value: str) -> int:
|
67
62
|
"""Count the number of records in a table by a specific variable."""
|
68
|
-
|
69
|
-
count: int = session.query(table_obj).filter(getattr(table_obj, variable) == value).count()
|
70
|
-
return count
|
63
|
+
return self.session().query(table_obj).filter(getattr(table_obj, variable) == value).count()
|
71
64
|
|
72
65
|
@contextmanager
|
73
66
|
def open_session(self) -> Generator[Session, Any]:
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import asyncio
|
2
|
+
from asyncio import AbstractEventLoop, Task
|
3
|
+
from collections.abc import Callable
|
4
|
+
from contextlib import suppress
|
5
|
+
import inspect
|
6
|
+
|
7
|
+
from pydantic import BaseModel, Field
|
8
|
+
|
9
|
+
|
10
|
+
class AsyncResponseModel(BaseModel):
|
11
|
+
"""A model to handle asynchronous operations with a function and its arguments."""
|
12
|
+
|
13
|
+
loop: AbstractEventLoop | None = Field(default=None, description="The event loop to run the function in.")
|
14
|
+
task: Task | None = Field(default=None, description="The task created for the asynchronous function.")
|
15
|
+
before_loop: bool = Field(default=False, description="If the function was called from a running loop.")
|
16
|
+
|
17
|
+
model_config = {"arbitrary_types_allowed": True}
|
18
|
+
|
19
|
+
def conditional_run(self) -> None:
|
20
|
+
"""Run the event loop until the task is complete if not in a running loop."""
|
21
|
+
if self.loop and self.task and not self.before_loop:
|
22
|
+
self.loop.run_until_complete(self.task)
|
23
|
+
|
24
|
+
|
25
|
+
def is_async_function(func: Callable) -> bool:
|
26
|
+
"""Check if a function is asynchronous.
|
27
|
+
|
28
|
+
Args:
|
29
|
+
func (Callable): The function/method to check.
|
30
|
+
|
31
|
+
Returns:
|
32
|
+
bool: True if the function is asynchronous, False otherwise.
|
33
|
+
"""
|
34
|
+
return inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func) or inspect.isasyncgen(func)
|
35
|
+
|
36
|
+
|
37
|
+
def in_async_loop() -> bool:
|
38
|
+
"""Check if the current context is already in an async loop.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
bool: True if an async loop is running, False otherwise.
|
42
|
+
"""
|
43
|
+
loop: AbstractEventLoop | None = None
|
44
|
+
with suppress(RuntimeError):
|
45
|
+
loop = asyncio.get_running_loop()
|
46
|
+
return loop.is_running() if loop else False
|
47
|
+
|
48
|
+
|
49
|
+
def gimmie_async_loop() -> AbstractEventLoop:
|
50
|
+
"""Get the current event loop, creating one if it doesn't exist."""
|
51
|
+
if in_async_loop():
|
52
|
+
return asyncio.get_event_loop()
|
53
|
+
loop: AbstractEventLoop = asyncio.new_event_loop()
|
54
|
+
asyncio.set_event_loop(loop)
|
55
|
+
return loop
|
56
|
+
|
57
|
+
|
58
|
+
def create_async_task(
|
59
|
+
func: Callable,
|
60
|
+
*args,
|
61
|
+
**kwargs,
|
62
|
+
) -> AsyncResponseModel:
|
63
|
+
"""Create an asyncio task for a given function."""
|
64
|
+
before_loop: bool = in_async_loop()
|
65
|
+
loop: AbstractEventLoop = gimmie_async_loop()
|
66
|
+
task = loop.create_task(func(*args, **kwargs))
|
67
|
+
return AsyncResponseModel(loop=loop, task=task, before_loop=before_loop)
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/extras/responses/function_response.py
RENAMED
@@ -2,22 +2,24 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
-
from collections.abc import Callable
|
6
5
|
import json
|
7
6
|
from subprocess import CompletedProcess
|
8
7
|
from types import SimpleNamespace as Namespace
|
9
|
-
from typing import Any, Literal, Self, overload
|
8
|
+
from typing import TYPE_CHECKING, Any, Literal, Self, overload
|
10
9
|
|
11
10
|
from pydantic import BaseModel, Field, field_validator
|
12
11
|
|
13
|
-
from bear_utils.extras._async_helpers import
|
14
|
-
from bear_utils.logger_manager import (
|
15
|
-
AsyncLoggerProtocol,
|
16
|
-
LoggerProtocol,
|
17
|
-
)
|
12
|
+
from bear_utils.extras._async_helpers import AsyncResponseModel, create_async_task, is_async_function
|
18
13
|
|
19
|
-
|
20
|
-
|
14
|
+
# Pydantic will yell if we put this into a TYPE_CHECKING block.
|
15
|
+
from bear_utils.logger_manager import AsyncLoggerProtocol, LoggerProtocol # noqa: TC001
|
16
|
+
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
from collections.abc import Callable
|
19
|
+
|
20
|
+
|
21
|
+
SUCCESS: list[str] = ["name", "success", "number_of_tasks"]
|
22
|
+
FAILURE: list[str] = ["name", "number_of_tasks"]
|
21
23
|
|
22
24
|
|
23
25
|
class FunctionResponse(BaseModel):
|
@@ -240,6 +242,19 @@ class FunctionResponse(BaseModel):
|
|
240
242
|
self._add_error(error=result.stderr.strip() if result.stderr else "")
|
241
243
|
self.returncode = result.returncode
|
242
244
|
|
245
|
+
def _handle_content(self, content: list[str] | str | FunctionResponse | CompletedProcess | Any) -> None:
|
246
|
+
"""Handle different types of content and update the FunctionResponse."""
|
247
|
+
if isinstance(content, FunctionResponse):
|
248
|
+
self._handle_function_response(func_response=content)
|
249
|
+
elif isinstance(content, CompletedProcess):
|
250
|
+
self._handle_completed_process(result=content)
|
251
|
+
elif isinstance(content, (str | list)):
|
252
|
+
self._add_content(content=content)
|
253
|
+
else:
|
254
|
+
return
|
255
|
+
self.number_of_tasks += 1
|
256
|
+
|
257
|
+
@overload
|
243
258
|
def add(
|
244
259
|
self,
|
245
260
|
content: list[str] | str | FunctionResponse | CompletedProcess | None = None,
|
@@ -247,19 +262,36 @@ class FunctionResponse(BaseModel):
|
|
247
262
|
returncode: int | None = None,
|
248
263
|
log_output: bool = False,
|
249
264
|
extra: dict[str, Any] | None = None,
|
250
|
-
|
265
|
+
*,
|
266
|
+
to_dict: Literal[True],
|
267
|
+
) -> dict[str, Any]: ...
|
268
|
+
|
269
|
+
@overload
|
270
|
+
def add(
|
271
|
+
self,
|
272
|
+
content: list[str] | str | FunctionResponse | CompletedProcess | None = None,
|
273
|
+
error: str | list[str] | None = None,
|
274
|
+
returncode: int | None = None,
|
275
|
+
log_output: bool = False,
|
276
|
+
extra: dict[str, Any] | None = None,
|
277
|
+
*,
|
278
|
+
to_dict: Literal[False] = False,
|
279
|
+
) -> Self: ...
|
280
|
+
|
281
|
+
def add(
|
282
|
+
self,
|
283
|
+
content: list[str] | str | FunctionResponse | CompletedProcess | None = None,
|
284
|
+
error: str | list[str] | None = None,
|
285
|
+
returncode: int | None = None,
|
286
|
+
log_output: bool = False,
|
287
|
+
extra: dict[str, Any] | None = None,
|
288
|
+
*,
|
289
|
+
to_dict: bool = False,
|
290
|
+
) -> Self | dict[str, Any]:
|
251
291
|
"""Append additional content to the existing content."""
|
252
292
|
try:
|
253
293
|
if content is not None:
|
254
|
-
|
255
|
-
self._handle_function_response(func_response=content)
|
256
|
-
self.number_of_tasks += 1
|
257
|
-
elif isinstance(content, CompletedProcess):
|
258
|
-
self._handle_completed_process(result=content)
|
259
|
-
self.number_of_tasks += 1
|
260
|
-
elif isinstance(content, (str | list)) and content:
|
261
|
-
self._add_content(content=content)
|
262
|
-
self.number_of_tasks += 1
|
294
|
+
self._handle_content(content=content)
|
263
295
|
if error is not None and isinstance(error, (str | list)):
|
264
296
|
self._add_error(error=error)
|
265
297
|
if isinstance(returncode, int):
|
@@ -270,6 +302,8 @@ class FunctionResponse(BaseModel):
|
|
270
302
|
self._log_handling(content=content, error=error, logger=self.logger)
|
271
303
|
except Exception as e:
|
272
304
|
raise ValueError(f"Failed to add content: {e!s}") from e
|
305
|
+
if to_dict:
|
306
|
+
return self.done(to_dict=True)
|
273
307
|
return self
|
274
308
|
|
275
309
|
def _log_handling(
|
@@ -304,22 +338,16 @@ class FunctionResponse(BaseModel):
|
|
304
338
|
content = []
|
305
339
|
if not isinstance(error, (list | str)):
|
306
340
|
error = []
|
307
|
-
|
308
341
|
if not content and not error:
|
309
342
|
return
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
error=error,
|
317
|
-
info_func=logger.info,
|
318
|
-
error_func=logger.error,
|
319
|
-
)
|
343
|
+
res: AsyncResponseModel = create_async_task(
|
344
|
+
_log_messages,
|
345
|
+
content=content,
|
346
|
+
error=error,
|
347
|
+
info_func=logger.info,
|
348
|
+
error_func=logger.error,
|
320
349
|
)
|
321
|
-
|
322
|
-
loop.run_until_complete(task)
|
350
|
+
res.conditional_run()
|
323
351
|
|
324
352
|
@overload
|
325
353
|
def done(self, to_dict: Literal[True], suppress: list[str] | None = None) -> dict[str, Any]: ...
|
@@ -352,7 +380,7 @@ class FunctionResponse(BaseModel):
|
|
352
380
|
add("name", self.name, bool(self.name))
|
353
381
|
add("success", self.success)
|
354
382
|
add("returncode", self.returncode, self.returncode > 0)
|
355
|
-
add("number_of_tasks", self.number_of_tasks, self.number_of_tasks > 0)
|
383
|
+
add("number_of_tasks", self.number_of_tasks, (self.number_of_tasks > 0 and not self.success))
|
356
384
|
add("content", self.content, bool(self.content))
|
357
385
|
add("error", self.error, bool(self.error))
|
358
386
|
result.update(self.extra)
|
@@ -36,7 +36,7 @@ class QTApplication(QObject):
|
|
36
36
|
self.app.setOrganizationDomain(org_domain)
|
37
37
|
else:
|
38
38
|
self.app = QApplication.instance()
|
39
|
-
self.console = ConsoleLogger.get_instance(init=True, name=app_name, level=VERBOSE)
|
39
|
+
self.console: ConsoleLogger = ConsoleLogger.get_instance(init=True, name=app_name, level=VERBOSE)
|
40
40
|
atexit.register(self.cleanup)
|
41
41
|
|
42
42
|
def _default_exit_shortcuts(self) -> None:
|
@@ -635,15 +635,30 @@ class TestComplexScenarios:
|
|
635
635
|
response.successful("Cleared 5 completed tasks")
|
636
636
|
response.add(content="Operation summary generated")
|
637
637
|
|
638
|
-
# Get dict format suitable for MCP
|
639
638
|
result = response.done(to_dict=True, suppress=SUCCESS)
|
640
639
|
|
641
640
|
# SUCCESS suppresses "name" and "success", so check what actually remains
|
642
641
|
assert "content" in result
|
643
|
-
assert "number_of_tasks" in result
|
642
|
+
assert "number_of_tasks" not in result # This will only be here in failures
|
644
643
|
assert "name" not in result
|
645
644
|
assert "success" not in result
|
646
645
|
|
646
|
+
def test_mcp_server_response_format_error(self):
|
647
|
+
"""Test formatting response for MCP server consumption."""
|
648
|
+
response = FunctionResponse(name="clear_tasks_error")
|
649
|
+
|
650
|
+
# Add some successful operations
|
651
|
+
response.fail("Failed to clear tasks")
|
652
|
+
response.add(content="Error summary generated")
|
653
|
+
|
654
|
+
result = response.done(to_dict=True, suppress=FAILURE)
|
655
|
+
|
656
|
+
# SUCCESS suppresses "name" and "success", so check what actually remains
|
657
|
+
assert "content" in result
|
658
|
+
assert "number_of_tasks" in result
|
659
|
+
assert "name" not in result
|
660
|
+
assert "success" in result
|
661
|
+
|
647
662
|
def test_error_handling_in_add_method(self):
|
648
663
|
"""Test error handling within add method."""
|
649
664
|
response = FunctionResponse()
|
@@ -1,38 +0,0 @@
|
|
1
|
-
import asyncio
|
2
|
-
from asyncio import AbstractEventLoop
|
3
|
-
from collections.abc import Callable
|
4
|
-
from contextlib import suppress
|
5
|
-
import inspect
|
6
|
-
|
7
|
-
|
8
|
-
def is_async_function(func: Callable) -> bool:
|
9
|
-
"""Check if a function is asynchronous.
|
10
|
-
|
11
|
-
Args:
|
12
|
-
func (Callable): The function/method to check.
|
13
|
-
|
14
|
-
Returns:
|
15
|
-
bool: True if the function is asynchronous, False otherwise.
|
16
|
-
"""
|
17
|
-
return inspect.iscoroutinefunction(func) or inspect.isasyncgenfunction(func) or inspect.isasyncgen(func)
|
18
|
-
|
19
|
-
|
20
|
-
def in_async_loop() -> bool:
|
21
|
-
"""Check if the current context is already in an async loop.
|
22
|
-
|
23
|
-
Returns:
|
24
|
-
bool: True if an async loop is running, False otherwise.
|
25
|
-
"""
|
26
|
-
loop: AbstractEventLoop | None = None
|
27
|
-
with suppress(RuntimeError):
|
28
|
-
loop = asyncio.get_running_loop()
|
29
|
-
return loop.is_running() if loop else False
|
30
|
-
|
31
|
-
|
32
|
-
def gimmie_async_loop() -> AbstractEventLoop:
|
33
|
-
"""Get the current event loop, creating one if it doesn't exist."""
|
34
|
-
if in_async_loop():
|
35
|
-
return asyncio.get_event_loop()
|
36
|
-
loop: AbstractEventLoop = asyncio.new_event_loop()
|
37
|
-
asyncio.set_event_loop(loop)
|
38
|
-
return loop
|
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
|
File without changes
|
File without changes
|
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/_base_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/file_handler_factory.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/json_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/log_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/toml_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/files/file_handlers/txt_file_handler.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/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
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/base_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/base_logger.pyi
RENAMED
File without changes
|
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/basic_logger/logger.py
RENAMED
File without changes
|
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/buffer_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/console_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/console_logger.pyi
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/fastapi_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/file_logger.py
RENAMED
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/src/bear_utils/logger_manager/loggers/simple_logger.py
RENAMED
File without changes
|
File without changes
|
{bear_utils-0.8.21 → bear_utils-0.8.23}/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
|
File without changes
|