bear-utils 0.8.19__tar.gz → 0.8.20__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 (118) hide show
  1. {bear_utils-0.8.19 → bear_utils-0.8.20}/.bumpversion.cfg +1 -1
  2. {bear_utils-0.8.19 → bear_utils-0.8.20}/PKG-INFO +2 -2
  3. {bear_utils-0.8.19 → bear_utils-0.8.20}/README.md +1 -1
  4. {bear_utils-0.8.19 → bear_utils-0.8.20}/pyproject.toml +3 -1
  5. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/events/events_module.py +1 -1
  6. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/extras/_async_helpers.py +11 -6
  7. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/extras/responses/function_response.py +17 -52
  8. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_function_response.py +71 -3
  9. {bear_utils-0.8.19 → bear_utils-0.8.20}/uv.lock +15 -1
  10. {bear_utils-0.8.19 → bear_utils-0.8.20}/.gitignore +0 -0
  11. {bear_utils-0.8.19 → bear_utils-0.8.20}/.python-version +0 -0
  12. {bear_utils-0.8.19 → bear_utils-0.8.20}/AGENTS.md +0 -0
  13. {bear_utils-0.8.19 → bear_utils-0.8.20}/config/coverage.ini +0 -0
  14. {bear_utils-0.8.19 → bear_utils-0.8.20}/config/default.toml +0 -0
  15. {bear_utils-0.8.19 → bear_utils-0.8.20}/config/git-changelog.toml +0 -0
  16. {bear_utils-0.8.19 → bear_utils-0.8.20}/config/pytest.ini +0 -0
  17. {bear_utils-0.8.19 → bear_utils-0.8.20}/config/ruff.toml +0 -0
  18. {bear_utils-0.8.19 → bear_utils-0.8.20}/config/vscode/launch.json +0 -0
  19. {bear_utils-0.8.19 → bear_utils-0.8.20}/config/vscode/settings.json +0 -0
  20. {bear_utils-0.8.19 → bear_utils-0.8.20}/config/vscode/tasks.json +0 -0
  21. {bear_utils-0.8.19 → bear_utils-0.8.20}/directory_structure.txt +0 -0
  22. {bear_utils-0.8.19 → bear_utils-0.8.20}/directory_structure.xml +0 -0
  23. {bear_utils-0.8.19 → bear_utils-0.8.20}/maskfile.md +0 -0
  24. {bear_utils-0.8.19 → bear_utils-0.8.20}/noxfile.py +0 -0
  25. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/__init__.py +0 -0
  26. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/__main__.py +0 -0
  27. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/_internal/__init__.py +0 -0
  28. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/_internal/cli.py +0 -0
  29. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/_internal/debug.py +0 -0
  30. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/ai/__init__.py +0 -0
  31. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/ai/ai_helpers/__init__.py +0 -0
  32. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/ai/ai_helpers/_common.py +0 -0
  33. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/ai/ai_helpers/_config.py +0 -0
  34. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/ai/ai_helpers/_parsers.py +0 -0
  35. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/ai/ai_helpers/_types.py +0 -0
  36. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/cache/__init__.py +0 -0
  37. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/cli/__init__.py +0 -0
  38. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/cli/commands.py +0 -0
  39. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/cli/prompt_helpers.py +0 -0
  40. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/cli/shell/__init__.py +0 -0
  41. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/cli/shell/_base_command.py +0 -0
  42. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/cli/shell/_base_shell.py +0 -0
  43. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/cli/shell/_common.py +0 -0
  44. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/config/__init__.py +0 -0
  45. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/config/config_manager.py +0 -0
  46. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/config/dir_manager.py +0 -0
  47. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/config/settings_manager.py +0 -0
  48. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/constants/__init__.py +0 -0
  49. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/constants/_exceptions.py +0 -0
  50. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/constants/_lazy_typing.py +0 -0
  51. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/constants/date_related.py +0 -0
  52. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/constants/server.py +0 -0
  53. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/constants/time_related.py +0 -0
  54. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/database/__init__.py +0 -0
  55. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/database/_db_manager.py +0 -0
  56. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/events/__init__.py +0 -0
  57. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/events/events_class.py +0 -0
  58. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/extras/__init__.py +0 -0
  59. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/extras/_tools.py +0 -0
  60. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/extras/platform_utils.py +0 -0
  61. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/extras/responses/__init__.py +0 -0
  62. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/extras/wrappers/__init__.py +0 -0
  63. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/extras/wrappers/add_methods.py +0 -0
  64. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/__init__.py +0 -0
  65. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/file_handlers/__init__.py +0 -0
  66. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/file_handlers/_base_file_handler.py +0 -0
  67. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/file_handlers/file_handler_factory.py +0 -0
  68. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/file_handlers/json_file_handler.py +0 -0
  69. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/file_handlers/log_file_handler.py +0 -0
  70. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/file_handlers/toml_file_handler.py +0 -0
  71. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/file_handlers/txt_file_handler.py +0 -0
  72. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/file_handlers/yaml_file_handler.py +0 -0
  73. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/files/ignore_parser.py +0 -0
  74. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/graphics/__init__.py +0 -0
  75. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/graphics/bear_gradient.py +0 -0
  76. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/graphics/image_helpers.py +0 -0
  77. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/gui/__init__.py +0 -0
  78. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/gui/gui_tools/__init__.py +0 -0
  79. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/gui/gui_tools/_settings.py +0 -0
  80. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/gui/gui_tools/_types.py +0 -0
  81. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/gui/gui_tools/qt_app.py +0 -0
  82. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/gui/gui_tools/qt_color_picker.py +0 -0
  83. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/gui/gui_tools/qt_file_handler.py +0 -0
  84. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/gui/gui_tools/qt_input_dialog.py +0 -0
  85. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/__init__.py +0 -0
  86. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/_common.py +0 -0
  87. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/_console_junk.py +0 -0
  88. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/_log_level.py +0 -0
  89. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/_styles.py +0 -0
  90. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/logger_protocol.py +0 -0
  91. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/__init__.py +0 -0
  92. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/_level_sin.py +0 -0
  93. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/base_logger.py +0 -0
  94. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/base_logger.pyi +0 -0
  95. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/basic_logger/__init__.py +0 -0
  96. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/basic_logger/logger.py +0 -0
  97. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/basic_logger/logger.pyi +0 -0
  98. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/buffer_logger.py +0 -0
  99. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/console_logger.py +0 -0
  100. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/console_logger.pyi +0 -0
  101. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/fastapi_logger.py +0 -0
  102. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/file_logger.py +0 -0
  103. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/simple_logger.py +0 -0
  104. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/sub_logger.py +0 -0
  105. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/logger_manager/loggers/sub_logger.pyi +0 -0
  106. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/monitoring/__init__.py +0 -0
  107. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/monitoring/_common.py +0 -0
  108. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/monitoring/host_monitor.py +0 -0
  109. {bear_utils-0.8.19 → bear_utils-0.8.20}/src/bear_utils/time/__init__.py +0 -0
  110. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/__init__.py +0 -0
  111. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_add_ord_suffix.py +0 -0
  112. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_clipboard.py +0 -0
  113. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_database_manager.py +0 -0
  114. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_default_shell.py +0 -0
  115. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_gradient.py +0 -0
  116. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_logger.py +0 -0
  117. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_platform_utils.py +0 -0
  118. {bear_utils-0.8.19 → bear_utils-0.8.20}/tests/test_prompt_helpers.py +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 0.8.19
2
+ current_version = 0.8.20
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.8.19
3
+ Version: 0.8.20
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
@@ -22,7 +22,7 @@ Requires-Dist: toml>=0.10.2
22
22
  Requires-Dist: uvicorn>=0.35.0
23
23
  Description-Content-Type: text/markdown
24
24
 
25
- # Bear Utils v# Bear Utils v0.8.19
25
+ # Bear Utils v# Bear Utils v0.8.20
26
26
 
27
27
  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.
28
28
 
@@ -1,4 +1,4 @@
1
- # Bear Utils v# Bear Utils v0.8.19
1
+ # Bear Utils v# Bear Utils v0.8.20
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.19"
3
+ version = "0.8.20"
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"
@@ -33,6 +33,7 @@ maintain = [
33
33
  "git-changelog>=2.5",
34
34
  "yore>=0.3.3",
35
35
  "griffe>=1.7.3",
36
+ "pytest-asyncio>=1.0.0",
36
37
  ]
37
38
  ci = [
38
39
  "ruff>=0.4",
@@ -58,6 +59,7 @@ default-groups = ["maintain", "ci"]
58
59
  testpaths = ["tests"]
59
60
  pythonpath = ["src"]
60
61
  addopts = "-v"
62
+ asyncio_mode = "auto"
61
63
  markers = [
62
64
  "visual: marks tests as visual verification tests (deselect with '-m \"not visual\"')"
63
65
  ]
@@ -50,7 +50,7 @@ def dispatch_event(name: str, *args, **kwargs) -> Any | None:
50
50
  results: list[Any] = []
51
51
  for func in _event_registry.get(name, []):
52
52
  if is_async_function(func):
53
- result: Any = asyncio.run(func(*args, **kwargs))
53
+ result: Any = asyncio.run(func(*args, **kwargs)) # FIXME: This will crash if called from an async context
54
54
  else:
55
55
  result: Any = func(*args, **kwargs)
56
56
  results.append(result)
@@ -1,6 +1,7 @@
1
1
  import asyncio
2
- from asyncio import AbstractEventLoop, get_event_loop
2
+ from asyncio import AbstractEventLoop
3
3
  from collections.abc import Callable
4
+ from contextlib import suppress
4
5
  import inspect
5
6
 
6
7
 
@@ -17,11 +18,15 @@ def is_async_function(func: Callable) -> bool:
17
18
 
18
19
 
19
20
  def in_async_loop() -> bool:
20
- """Check if the current context is already in an async loop."""
21
- try:
22
- return get_event_loop().is_running()
23
- except RuntimeError:
24
- return False
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
25
30
 
26
31
 
27
32
  def gimmie_async_loop() -> AbstractEventLoop:
@@ -2,7 +2,6 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import asyncio
6
5
  from collections.abc import Callable
7
6
  import json
8
7
  from subprocess import CompletedProcess
@@ -251,19 +250,19 @@ class FunctionResponse(BaseModel):
251
250
  ) -> Self:
252
251
  """Append additional content to the existing content."""
253
252
  try:
254
- if isinstance(content, FunctionResponse | CompletedProcess | str | list) and isinstance(
255
- error, (str | list)
256
- ):
257
- match content:
258
- case FunctionResponse():
259
- self._handle_function_response(func_response=content)
260
- case CompletedProcess():
261
- self._handle_completed_process(result=content)
262
- case str() | list() if content:
263
- self._add_content(content=content)
264
- if isinstance(error, (str | list)):
265
- self._add_error(error=error)
266
- if returncode is not None:
253
+ if content is not None:
254
+ if isinstance(content, FunctionResponse):
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
263
+ if error is not None and isinstance(error, (str | list)):
264
+ self._add_error(error=error)
265
+ if isinstance(returncode, int):
267
266
  self.returncode = returncode
268
267
  if isinstance(extra, dict):
269
268
  self.extra.update(extra)
@@ -283,10 +282,12 @@ class FunctionResponse(BaseModel):
283
282
 
284
283
  async def loop_logging(messages: list[str], func: Callable) -> None:
285
284
  for msg in messages:
285
+ if msg == "":
286
+ continue
286
287
  if is_async_function(func):
287
- await func(f"{self.name}: {msg}" if self.name else msg)
288
+ await func(f"{msg}")
288
289
  else:
289
- func(f"{self.name}: {msg}" if self.name else msg)
290
+ func(f"{msg}")
290
291
 
291
292
  async def _log_messages(
292
293
  content: str | list[str], error: str | list[str], info_func: Callable, error_func: Callable
@@ -375,39 +376,3 @@ def fail(
375
376
  ) -> FunctionResponse:
376
377
  """Create a failed FunctionResponse."""
377
378
  return FunctionResponse().fail(content=content, error=error, returncode=returncode, **kwargs)
378
-
379
-
380
- async def testing_already_in_loop() -> None:
381
- """Test function to check if already in an event loop."""
382
- # from rich import inspect
383
-
384
- response = FunctionResponse(name="example_function", logger=SimpleLogger())
385
- try:
386
- response.add(content=["Test content", "test 1234"], error="Test error", log_output=True)
387
- response.done(to_dict=True)
388
- except RuntimeError as e:
389
- print(f"Already in an event loop: {e}")
390
- else:
391
- print("Successfully added content and error without issues.")
392
- # inspect(response)
393
-
394
-
395
- def testing_not_in_loop() -> None:
396
- """Test function to check if not in an event loop."""
397
- response = FunctionResponse(name="example_function", logger=SimpleLogger())
398
- try:
399
- response.add(content=["Test content", "test 1234"], error="Test error", log_output=True)
400
- response.done(to_dict=True)
401
- except RuntimeError as e:
402
- print(f"Not in an event loop: {e}")
403
- else:
404
- print("Successfully added content and error without issues.")
405
-
406
-
407
- if __name__ == "__main__":
408
- # Example usage
409
-
410
- from bear_utils.logger_manager import SimpleLogger
411
-
412
- asyncio.run(testing_already_in_loop())
413
- testing_not_in_loop()
@@ -13,6 +13,42 @@ from bear_utils.extras.responses.function_response import (
13
13
  fail,
14
14
  success,
15
15
  )
16
+ from bear_utils.logger_manager import SimpleLogger as AsyncLogger
17
+
18
+
19
+ class MockLogger:
20
+ def __init__(self):
21
+ """Initialize mock logger."""
22
+ self.messages = []
23
+
24
+ def log(self, level: str, msg: object, *args, **kwargs) -> None:
25
+ """Mock logging method."""
26
+ msg = f"{level.upper()}: {msg}"
27
+ if args:
28
+ msg = f"{msg} {' '.join(map(str, args))}"
29
+ if kwargs:
30
+ msg = f"{msg} {kwargs}"
31
+ self.messages.append(msg)
32
+
33
+ def info(self, msg: object, *args, **kwargs) -> None:
34
+ """Mock info logging method."""
35
+ self.log("info", msg, *args, **kwargs)
36
+
37
+ def error(self, msg: object, *args, **kwargs) -> None:
38
+ """Mock error logging method."""
39
+ self.log("error", msg, *args, **kwargs)
40
+
41
+ def debug(self, msg: object, *args, **kwargs) -> None:
42
+ """Mock debug logging method."""
43
+ self.log("debug", msg, *args, **kwargs)
44
+
45
+ def warning(self, msg: object, *args, **kwargs) -> None:
46
+ """Mock warning logging method."""
47
+ self.log("warning", msg, *args, **kwargs)
48
+
49
+ def get_messages(self) -> list[str]:
50
+ """Retrieve all logged messages."""
51
+ return self.messages
16
52
 
17
53
 
18
54
  class TestFunctionResponseCreation:
@@ -166,8 +202,22 @@ class TestFluentAPI:
166
202
 
167
203
  def test_successful_method(self):
168
204
  """Test successful() method."""
169
- response = FunctionResponse(name="test")
170
- result = response.successful("operation completed")
205
+ mock_logger = MockLogger()
206
+ response = FunctionResponse(name="test", logger=mock_logger)
207
+ result = response.successful("operation completed", log_output=True)
208
+
209
+ assert result is response # Returns self for chaining
210
+ assert response.content == ["operation completed"]
211
+ assert response.returncode == 0
212
+ assert response.success is True
213
+ messages = mock_logger.get_messages()
214
+ assert len(messages) == 1
215
+ assert "INFO: operation completed" in messages
216
+
217
+ async def test_async_successful_method(self):
218
+ """Test successful() method."""
219
+ response = FunctionResponse(name="test", logger=AsyncLogger())
220
+ result = response.successful("operation completed", log_output=True)
171
221
 
172
222
  assert result is response # Returns self for chaining
173
223
  assert response.content == ["operation completed"]
@@ -192,7 +242,7 @@ class TestFluentAPI:
192
242
  .add(content="second operation")
193
243
  .add(error="minor warning")
194
244
  )
195
-
245
+ print(response)
196
246
  assert response.content == ["first operation", "second operation"]
197
247
  assert response.error == ["minor warning"]
198
248
  assert response.returncode == 0
@@ -296,6 +346,24 @@ class TestSubTaskMethod:
296
346
  # Should still add content, just without name prefix
297
347
  assert "content without name" in main_response.content
298
348
 
349
+ def test_async_sub_task(self):
350
+ """Test sub_task with async logger."""
351
+ main_response = FunctionResponse(name="main_task", logger=AsyncLogger())
352
+ main_response.sub_task(name="async_subtask", content="async content")
353
+
354
+ assert main_response.number_of_tasks == 1
355
+ assert main_response.content == ["async_subtask: async content"]
356
+ assert isinstance(main_response.logger, AsyncLogger)
357
+
358
+ async def test_async_sub_task_with_async_logger(self):
359
+ """Test sub_task with async logger with an async context."""
360
+ main_response = FunctionResponse(name="main_task", logger=AsyncLogger())
361
+ main_response.sub_task(name="async_subtask", content="async content")
362
+
363
+ assert main_response.number_of_tasks == 1
364
+ assert main_response.content == ["async_subtask: async content"]
365
+ assert isinstance(main_response.logger, AsyncLogger)
366
+
299
367
 
300
368
  class TestAddMethod:
301
369
  """Test the complex add() method."""
@@ -73,7 +73,7 @@ wheels = [
73
73
 
74
74
  [[package]]
75
75
  name = "bear-utils"
76
- version = "0.8.7"
76
+ version = "0.8.19"
77
77
  source = { editable = "." }
78
78
  dependencies = [
79
79
  { name = "bear-epoch-time" },
@@ -114,6 +114,7 @@ gui = [
114
114
  maintain = [
115
115
  { name = "git-changelog" },
116
116
  { name = "griffe" },
117
+ { name = "pytest-asyncio" },
117
118
  { name = "yore" },
118
119
  ]
119
120
 
@@ -155,6 +156,7 @@ gui = [{ name = "pyqt6", specifier = ">=6.9.0" }]
155
156
  maintain = [
156
157
  { name = "git-changelog", specifier = ">=2.5" },
157
158
  { name = "griffe", specifier = ">=1.7.3" },
159
+ { name = "pytest-asyncio", specifier = ">=1.0.0" },
158
160
  { name = "yore", specifier = ">=0.3.3" },
159
161
  ]
160
162
 
@@ -1033,6 +1035,18 @@ wheels = [
1033
1035
  { url = "https://files.pythonhosted.org/packages/2f/de/afa024cbe022b1b318a3d224125aa24939e99b4ff6f22e0ba639a2eaee47/pytest-8.4.0-py3-none-any.whl", hash = "sha256:f40f825768ad76c0977cbacdf1fd37c6f7a468e460ea6a0636078f8972d4517e", size = 363797, upload_time = "2025-06-02T17:36:27.859Z" },
1034
1036
  ]
1035
1037
 
1038
+ [[package]]
1039
+ name = "pytest-asyncio"
1040
+ version = "1.0.0"
1041
+ source = { registry = "https://pypi.org/simple/" }
1042
+ dependencies = [
1043
+ { name = "pytest" },
1044
+ ]
1045
+ sdist = { url = "https://files.pythonhosted.org/packages/d0/d4/14f53324cb1a6381bef29d698987625d80052bb33932d8e7cbf9b337b17c/pytest_asyncio-1.0.0.tar.gz", hash = "sha256:d15463d13f4456e1ead2594520216b225a16f781e144f8fdf6c5bb4667c48b3f", size = 46960, upload_time = "2025-05-26T04:54:40.484Z" }
1046
+ wheels = [
1047
+ { url = "https://files.pythonhosted.org/packages/30/05/ce271016e351fddc8399e546f6e23761967ee09c8c568bbfbecb0c150171/pytest_asyncio-1.0.0-py3-none-any.whl", hash = "sha256:4f024da9f1ef945e680dc68610b52550e36590a67fd31bb3b4943979a1f90ef3", size = 15976, upload_time = "2025-05-26T04:54:39.035Z" },
1048
+ ]
1049
+
1036
1050
  [[package]]
1037
1051
  name = "pytest-cov"
1038
1052
  version = "6.2.1"
File without changes
File without changes
File without changes
File without changes
File without changes