bear-utils 0.8.21__py3-none-any.whl → 0.8.22__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bear_utils/extras/_async_helpers.py +30 -1
- bear_utils/extras/responses/function_response.py +58 -29
- bear_utils/gui/gui_tools/qt_app.py +1 -1
- {bear_utils-0.8.21.dist-info → bear_utils-0.8.22.dist-info}/METADATA +2 -2
- {bear_utils-0.8.21.dist-info → bear_utils-0.8.22.dist-info}/RECORD +6 -6
- {bear_utils-0.8.21.dist-info → bear_utils-0.8.22.dist-info}/WHEEL +0 -0
@@ -1,9 +1,26 @@
|
|
1
1
|
import asyncio
|
2
|
-
from asyncio import AbstractEventLoop
|
2
|
+
from asyncio import AbstractEventLoop, Task
|
3
3
|
from collections.abc import Callable
|
4
4
|
from contextlib import suppress
|
5
5
|
import inspect
|
6
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
|
+
|
7
24
|
|
8
25
|
def is_async_function(func: Callable) -> bool:
|
9
26
|
"""Check if a function is asynchronous.
|
@@ -36,3 +53,15 @@ def gimmie_async_loop() -> AbstractEventLoop:
|
|
36
53
|
loop: AbstractEventLoop = asyncio.new_event_loop()
|
37
54
|
asyncio.set_event_loop(loop)
|
38
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)
|
@@ -2,23 +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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
)
|
12
|
+
from bear_utils.extras._async_helpers import AsyncResponseModel, create_async_task, is_async_function
|
13
|
+
|
14
|
+
# Pydantic will yell if we put this into a TYPE_CHECKING block.
|
15
|
+
from bear_utils.logger_manager import AsyncLoggerProtocol, LoggerProtocol # noqa: TC001
|
18
16
|
|
19
17
|
SUCCESS: list[str] = ["name", "success"]
|
20
18
|
FAILURE: list[str] = ["name"]
|
21
19
|
|
20
|
+
if TYPE_CHECKING:
|
21
|
+
from collections.abc import Callable
|
22
|
+
|
22
23
|
|
23
24
|
class FunctionResponse(BaseModel):
|
24
25
|
"""A class to represent the response of a function call, including success status, content, and error messages."""
|
@@ -240,6 +241,19 @@ class FunctionResponse(BaseModel):
|
|
240
241
|
self._add_error(error=result.stderr.strip() if result.stderr else "")
|
241
242
|
self.returncode = result.returncode
|
242
243
|
|
244
|
+
def _handle_content(self, content: list[str] | str | FunctionResponse | CompletedProcess | Any) -> None:
|
245
|
+
"""Handle different types of content and update the FunctionResponse."""
|
246
|
+
if isinstance(content, FunctionResponse):
|
247
|
+
self._handle_function_response(func_response=content)
|
248
|
+
elif isinstance(content, CompletedProcess):
|
249
|
+
self._handle_completed_process(result=content)
|
250
|
+
elif isinstance(content, (str | list)):
|
251
|
+
self._add_content(content=content)
|
252
|
+
else:
|
253
|
+
return
|
254
|
+
self.number_of_tasks += 1
|
255
|
+
|
256
|
+
@overload
|
243
257
|
def add(
|
244
258
|
self,
|
245
259
|
content: list[str] | str | FunctionResponse | CompletedProcess | None = None,
|
@@ -247,19 +261,36 @@ class FunctionResponse(BaseModel):
|
|
247
261
|
returncode: int | None = None,
|
248
262
|
log_output: bool = False,
|
249
263
|
extra: dict[str, Any] | None = None,
|
250
|
-
|
264
|
+
*,
|
265
|
+
to_dict: Literal[True],
|
266
|
+
) -> dict[str, Any]: ...
|
267
|
+
|
268
|
+
@overload
|
269
|
+
def add(
|
270
|
+
self,
|
271
|
+
content: list[str] | str | FunctionResponse | CompletedProcess | None = None,
|
272
|
+
error: str | list[str] | None = None,
|
273
|
+
returncode: int | None = None,
|
274
|
+
log_output: bool = False,
|
275
|
+
extra: dict[str, Any] | None = None,
|
276
|
+
*,
|
277
|
+
to_dict: Literal[False] = False,
|
278
|
+
) -> Self: ...
|
279
|
+
|
280
|
+
def add(
|
281
|
+
self,
|
282
|
+
content: list[str] | str | FunctionResponse | CompletedProcess | None = None,
|
283
|
+
error: str | list[str] | None = None,
|
284
|
+
returncode: int | None = None,
|
285
|
+
log_output: bool = False,
|
286
|
+
extra: dict[str, Any] | None = None,
|
287
|
+
*,
|
288
|
+
to_dict: bool = False,
|
289
|
+
) -> Self | dict[str, Any]:
|
251
290
|
"""Append additional content to the existing content."""
|
252
291
|
try:
|
253
292
|
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
|
293
|
+
self._handle_content(content=content)
|
263
294
|
if error is not None and isinstance(error, (str | list)):
|
264
295
|
self._add_error(error=error)
|
265
296
|
if isinstance(returncode, int):
|
@@ -270,6 +301,8 @@ class FunctionResponse(BaseModel):
|
|
270
301
|
self._log_handling(content=content, error=error, logger=self.logger)
|
271
302
|
except Exception as e:
|
272
303
|
raise ValueError(f"Failed to add content: {e!s}") from e
|
304
|
+
if to_dict:
|
305
|
+
return self.done(to_dict=True)
|
273
306
|
return self
|
274
307
|
|
275
308
|
def _log_handling(
|
@@ -308,18 +341,14 @@ class FunctionResponse(BaseModel):
|
|
308
341
|
if not content and not error:
|
309
342
|
return
|
310
343
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
info_func=logger.info,
|
318
|
-
error_func=logger.error,
|
319
|
-
)
|
344
|
+
res: AsyncResponseModel = create_async_task(
|
345
|
+
_log_messages,
|
346
|
+
content=content,
|
347
|
+
error=error,
|
348
|
+
info_func=logger.info,
|
349
|
+
error_func=logger.error,
|
320
350
|
)
|
321
|
-
|
322
|
-
loop.run_until_complete(task)
|
351
|
+
res.conditional_run()
|
323
352
|
|
324
353
|
@overload
|
325
354
|
def done(self, to_dict: Literal[True], suppress: list[str] | None = None) -> dict[str, Any]: ...
|
@@ -352,7 +381,7 @@ class FunctionResponse(BaseModel):
|
|
352
381
|
add("name", self.name, bool(self.name))
|
353
382
|
add("success", self.success)
|
354
383
|
add("returncode", self.returncode, self.returncode > 0)
|
355
|
-
add("number_of_tasks", self.number_of_tasks, self.number_of_tasks > 0)
|
384
|
+
add("number_of_tasks", self.number_of_tasks, (self.number_of_tasks > 0 and not self.success))
|
356
385
|
add("content", self.content, bool(self.content))
|
357
386
|
add("error", self.error, bool(self.error))
|
358
387
|
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:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: bear-utils
|
3
|
-
Version: 0.8.
|
3
|
+
Version: 0.8.22
|
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.22
|
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
|
|
@@ -33,11 +33,11 @@ bear_utils/events/__init__.py,sha256=EFqmuzhaEYK9kjkGlrM7bjdjPwFEDbKn6RjJKfIBEJY
|
|
33
33
|
bear_utils/events/events_class.py,sha256=vPDjWrbut8L3TFn7byyYFZpWYM5ADIqtW2Aeh-qtKfQ,1632
|
34
34
|
bear_utils/events/events_module.py,sha256=DkQsDZ5WzM1MH1Msg7mRny40a17Jl31CXbpw4-pICxc,2349
|
35
35
|
bear_utils/extras/__init__.py,sha256=szflSapj7aGFc2j5sTitQFccXu-6_UdGG-eYuv6zVJI,607
|
36
|
-
bear_utils/extras/_async_helpers.py,sha256=
|
36
|
+
bear_utils/extras/_async_helpers.py,sha256=4buULZZkR0n2z13Q8_FK59dMchj0Mup03YBaqSCbveQ,2291
|
37
37
|
bear_utils/extras/_tools.py,sha256=-rOH_-0pRd7_-o_l2QqByYBB2yhbXcTC8fuQf4Sx-eg,7671
|
38
38
|
bear_utils/extras/platform_utils.py,sha256=Ai7ow7S-_cKb5zFwFh8dkC8xmbMJFy-0_-w3NCERdEw,1362
|
39
39
|
bear_utils/extras/responses/__init__.py,sha256=XbE4VKemrKRwx9E5jqy__OiM_AAjA58ebnqQ2hytnT0,225
|
40
|
-
bear_utils/extras/responses/function_response.py,sha256=
|
40
|
+
bear_utils/extras/responses/function_response.py,sha256=VFhYGoSXbqdkgeBWYdmHcHcJ2Z0BFg_Y8EmdkaF442Y,16220
|
41
41
|
bear_utils/extras/wrappers/__init__.py,sha256=crh4sKOLvuhNMVX5bJYjCFWtXtH7G47UgNPOHq3HXTk,43
|
42
42
|
bear_utils/extras/wrappers/add_methods.py,sha256=z2XZG2ZoYOB1MaGiLli4NRyyTeRgBy7tuYsiy8mTa9s,4422
|
43
43
|
bear_utils/files/__init__.py,sha256=mIdnFSXoDE64ElM43bN2m6KuafURnN82ki0pdqN8q2o,201
|
@@ -57,7 +57,7 @@ bear_utils/gui/__init__.py,sha256=z_rOZ-nN3E6Hd_nGD6SdDVqE61VUk2OOogI6U89nkkA,34
|
|
57
57
|
bear_utils/gui/gui_tools/__init__.py,sha256=cD6cKxU1cmKDVaBRT8KsqsCbulf6TUNAmVr50XGPpo8,446
|
58
58
|
bear_utils/gui/gui_tools/_settings.py,sha256=xSQ7I-axAifZNvEw_28mnFBFYIJd4xFuDpycFFQLib0,1201
|
59
59
|
bear_utils/gui/gui_tools/_types.py,sha256=krguJ-ccALKeUHz9auh_iyOCzeAuerOYcuhWW8jjJQ0,248
|
60
|
-
bear_utils/gui/gui_tools/qt_app.py,sha256=
|
60
|
+
bear_utils/gui/gui_tools/qt_app.py,sha256=sxBLcPgy2r9coFqjN3wnCoFKdyvCWvHIJfb64NHtJOs,5880
|
61
61
|
bear_utils/gui/gui_tools/qt_color_picker.py,sha256=5NtLiBHk5r9Goma_oiymriH49D_JGIk844p4Hsi51io,4744
|
62
62
|
bear_utils/gui/gui_tools/qt_file_handler.py,sha256=FgWdS-9WnjVuyGIC8V30ByDCBeJGZKGc8KRTy34SFfI,4404
|
63
63
|
bear_utils/gui/gui_tools/qt_input_dialog.py,sha256=5KaCM9q8kmoy-Fd0j1FbXIVrLlE7W47NEGdhsWtvKwQ,9281
|
@@ -86,6 +86,6 @@ bear_utils/monitoring/__init__.py,sha256=9DKNIWTp_voLnaWgiP-wJ-o_N0hYixo-MzjUmg8
|
|
86
86
|
bear_utils/monitoring/_common.py,sha256=LYQFxgTP9fk0cH71IQTuGwBYYPWCqHP_mMRNecoD76M,657
|
87
87
|
bear_utils/monitoring/host_monitor.py,sha256=e0TYRJw9iDj5Ga6y3ck1TBFEeH42Cax5mQYaNU8yams,13241
|
88
88
|
bear_utils/time/__init__.py,sha256=VctjJG17SyEHAFXytI1sZrOrq7zm3hVenIDOJFdaMN0,1424
|
89
|
-
bear_utils-0.8.
|
90
|
-
bear_utils-0.8.
|
91
|
-
bear_utils-0.8.
|
89
|
+
bear_utils-0.8.22.dist-info/METADATA,sha256=bI6HOJaPlDs8ml0B_n-if7qmczI67TKOllT-6JkXXhY,8763
|
90
|
+
bear_utils-0.8.22.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
91
|
+
bear_utils-0.8.22.dist-info/RECORD,,
|
File without changes
|