bear-utils 0.7.23__py3-none-any.whl → 0.7.24__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.
@@ -5,12 +5,11 @@ from __future__ import annotations
5
5
  from io import StringIO
6
6
  import json
7
7
  from subprocess import CompletedProcess
8
- from typing import TYPE_CHECKING, Any, Literal, Self, overload
8
+ from typing import Any, Literal, Self, overload
9
9
 
10
10
  from pydantic import BaseModel, Field, field_validator
11
11
 
12
- if TYPE_CHECKING:
13
- from bear_utils.constants.logger_protocol import LoggerProtocol
12
+ from bear_utils.constants.logger_protocol import LoggerProtocol # noqa: TC001 # DO NOT PUT INTO A TYPE_CHECKING BLOCK
14
13
 
15
14
  SUCCESS: list[str] = ["name", "success"]
16
15
  FAILURE: list[str] = ["name"]
@@ -130,6 +129,25 @@ class FunctionResponse(BaseModel):
130
129
  """Check if the response indicates success."""
131
130
  return self.returncode == 0
132
131
 
132
+ def sub_task(
133
+ self,
134
+ name: str = "",
135
+ content: str | list[str] = "",
136
+ error: str | list[str] = "",
137
+ extra: dict[str, Any] | None = None,
138
+ returncode: int | None = None,
139
+ log_output: bool = False,
140
+ ) -> None:
141
+ """Add a sub-task response to the FunctionResponse."""
142
+ func_response: FunctionResponse = FunctionResponse(name=name, logger=self.logger).add(
143
+ content=content,
144
+ error=error,
145
+ returncode=returncode or self.returncode,
146
+ log_output=log_output,
147
+ extra=extra,
148
+ )
149
+ self.add(content=func_response)
150
+
133
151
  def successful(
134
152
  self,
135
153
  content: str | list[str] | CompletedProcess,
@@ -184,6 +202,19 @@ class FunctionResponse(BaseModel):
184
202
  except Exception as e:
185
203
  raise ValueError(f"Failed to add content: {e!s}") from e
186
204
 
205
+ def _handle_function_response(self, func_response: FunctionResponse) -> None:
206
+ """Handle a FunctionResponse object and update the current response."""
207
+ if func_response.extra:
208
+ self.extra.update(func_response.extra)
209
+ self._add_to_error(error=func_response.error, name=func_response.name)
210
+ self._add_to_content(content=func_response.content, name=func_response.name)
211
+
212
+ def _handle_completed_process(self, result: CompletedProcess[str]) -> None:
213
+ """Handle a CompletedProcess object and update the FunctionResponse."""
214
+ self._add_to_content(content=result.stdout.strip() if result.stdout else "")
215
+ self._add_to_error(error=result.stderr.strip() if result.stderr else "")
216
+ self.returncode = result.returncode
217
+
187
218
  def add(
188
219
  self,
189
220
  content: list[str] | str | FunctionResponse | CompletedProcess | None = None,
@@ -205,45 +236,49 @@ class FunctionResponse(BaseModel):
205
236
  Self: The updated FunctionResponse instance.
206
237
  """
207
238
  try:
208
- if isinstance(content, FunctionResponse):
209
- if content.extra:
210
- self.extra.update(content.extra)
211
- self._add_to_error(error=content.error, name=content.name)
212
- self._add_to_content(content=content.content, name=content.name)
213
- self.number_of_tasks += 1
214
- elif isinstance(content, CompletedProcess):
215
- result: CompletedProcess[str] = content
216
- self._add_to_content(content=result.stdout.strip() if result.stdout else "")
217
- self._add_to_error(error=result.stderr.strip() if result.stderr else "")
218
- self.returncode = result.returncode
219
- self.number_of_tasks += 1
220
- elif isinstance(content, str | list):
221
- self._add_to_content(content=content)
222
- self.number_of_tasks += 1
223
- if isinstance(error, str | list):
224
- self._add_to_error(error=error)
225
- if returncode is not None:
226
- self.returncode = returncode
227
- if extra is not None and isinstance(extra, dict):
228
- self.extra.update(extra)
229
- if log_output and self.logger is not None:
230
- if content is not None and error is None:
231
- if isinstance(content, list):
232
- for item in content:
233
- self.logger.info(message=f"{self.name}: {item}" if self.name else item)
234
- elif isinstance(content, str):
235
- self.logger.info(message=f"{self.name}: {content}" if self.name else content)
236
- elif error is not None and content is None:
237
- if isinstance(error, list):
238
- for err in error:
239
- self.logger.error(message=f"{self.name}: {err}" if self.name else err)
240
- elif isinstance(error, str):
241
- self.logger.error(message=f"{self.name}: {error}" if self.name else error)
242
-
239
+ match content:
240
+ case FunctionResponse():
241
+ self._handle_function_response(func_response=content)
242
+ self.number_of_tasks += 1
243
+ case CompletedProcess():
244
+ self._handle_completed_process(result=content)
245
+ self.number_of_tasks += 1
246
+ case str() | list() if content:
247
+ self._add_to_content(content=content)
248
+ self.number_of_tasks += 1
249
+ case None:
250
+ content = None
251
+ case _:
252
+ content = None
253
+ self._add_to_error(error=error) if isinstance(error, (str | list)) else None
254
+ self.returncode = returncode if returncode is not None else self.returncode
255
+ self.extra.update(extra) if isinstance(extra, dict) else None
256
+ if log_output and self.logger is not None and (content is not None or error is not None):
257
+ self._log_handling(content=content, error=error, logger=self.logger)
243
258
  except Exception as e:
244
259
  raise ValueError(f"Failed to add content: {e!s}") from e
245
260
  return self
246
261
 
262
+ def _log_handling(
263
+ self,
264
+ content: list[str] | str | FunctionResponse | CompletedProcess | None,
265
+ error: str | list[str] | None,
266
+ logger: LoggerProtocol,
267
+ ) -> None:
268
+ """Log the content and error messages if they exist."""
269
+ if content is not None and error is None:
270
+ if isinstance(content, list):
271
+ for item in content:
272
+ logger.info(message=f"{self.name}: {item}" if self.name else item)
273
+ elif isinstance(content, str):
274
+ logger.info(message=f"{self.name}: {content}" if self.name else content)
275
+ elif error is not None and content is None:
276
+ if isinstance(error, list):
277
+ for err in error:
278
+ logger.error(message=f"{self.name}: {err}" if self.name else err)
279
+ elif isinstance(error, str):
280
+ logger.error(message=f"{self.name}: {error}" if self.name else error)
281
+
247
282
  @overload
248
283
  def done(self, to_dict: Literal[True], suppress: list[str] | None = None) -> dict[str, Any]: ...
249
284
 
@@ -8,12 +8,6 @@ P = ParamSpec("P")
8
8
  R = TypeVar("R")
9
9
 
10
10
 
11
- def check_data_type(data: dict[str, Any] | str, valid_types: tuple[type, ...]) -> None:
12
- """Check if the data is of a valid type for text files."""
13
- if not isinstance(data, valid_types):
14
- raise TypeError(f"Data must be one of {valid_types}, got {type(data)}")
15
-
16
-
17
11
  class FileHandler(ABC):
18
12
  """Abstract class for file handling with read, write, and present methods
19
13
 
@@ -40,7 +34,7 @@ class FileHandler(ABC):
40
34
  return file_path.suffix.lstrip(".") in cls.valid_extensions
41
35
 
42
36
  @classmethod
43
- def check_data_type(cls, data: Any) -> Any:
37
+ def check_data_type(cls, data: dict[str, Any] | str, valid_types: tuple[type, ...]) -> None:
44
38
  """Check if the data is of the correct type.
45
39
 
46
40
  Args:
@@ -49,8 +43,8 @@ class FileHandler(ABC):
49
43
  Returns:
50
44
  bool: True if the data is of the correct type, False otherwise
51
45
  """
52
- if not isinstance(data, cls.valid_types):
53
- raise TypeError(f"Data must be one of {cls.valid_types}, got {type(data)}")
46
+ if not isinstance(data, valid_types):
47
+ raise TypeError(f"Data must be one of {valid_types}, got {type(data)}")
54
48
 
55
49
  @classmethod
56
50
  def ValidateFileType(cls, method: Callable[P, R]) -> Callable[P, R]: # noqa: N802 disable=invalid-name
@@ -4,7 +4,7 @@ import json
4
4
  from pathlib import Path
5
5
  from typing import Any, ClassVar, overload
6
6
 
7
- from ._base_file_handler import FileHandler, check_data_type
7
+ from ._base_file_handler import FileHandler
8
8
 
9
9
 
10
10
  class JsonFileHandler(FileHandler):
@@ -44,7 +44,7 @@ class JsonFileHandler(FileHandler):
44
44
  """Write data to a JSON file."""
45
45
  try:
46
46
  super().write_file(file_path=file_path, data=data)
47
- check_data_type(data=data, valid_types=self.valid_types)
47
+ self.check_data_type(data=data, valid_types=self.valid_types)
48
48
  data = self.present_file(data, indent=indent, sort_keys=sort_keys)
49
49
  file_path.write_text(data=data, encoding="utf-8")
50
50
  except Exception as e:
@@ -3,7 +3,7 @@
3
3
  from pathlib import Path
4
4
  from typing import Any, ClassVar, cast
5
5
 
6
- from ._base_file_handler import FileHandler, check_data_type
6
+ from ._base_file_handler import FileHandler
7
7
 
8
8
 
9
9
  class LogFileHandler(FileHandler):
@@ -27,7 +27,7 @@ class LogFileHandler(FileHandler):
27
27
  """Write data to a log file."""
28
28
  try:
29
29
  super().write_file(file_path=file_path, data=data)
30
- check_data_type(data=data, valid_types=self.valid_types)
30
+ self.check_data_type(data=data, valid_types=self.valid_types)
31
31
  with open(file_path, "w", encoding="utf-8") as file:
32
32
  file.write(cast("str", data))
33
33
  except Exception as e:
@@ -30,7 +30,7 @@ class TomlFileHandler(FileHandler):
30
30
  """Write data to a TOML file."""
31
31
  try:
32
32
  super().write_file(file_path=file_path, data=data)
33
-
33
+ self.check_data_type(data=data, valid_types=self.valid_types)
34
34
  with open(file_path, "w", encoding="utf-8") as file:
35
35
  if isinstance(data, dict):
36
36
  toml.dump(data, file, **kwargs)
@@ -4,7 +4,7 @@ import json
4
4
  from pathlib import Path
5
5
  from typing import Any, ClassVar, overload
6
6
 
7
- from ._base_file_handler import FileHandler, check_data_type
7
+ from ._base_file_handler import FileHandler
8
8
 
9
9
 
10
10
  class TextFileHandler(FileHandler):
@@ -44,7 +44,7 @@ class TextFileHandler(FileHandler):
44
44
  """Write data to a text file."""
45
45
  try:
46
46
  super().write_file(file_path=file_path, data=data)
47
- check_data_type(data, self.valid_types)
47
+ self.check_data_type(data=data, valid_types=self.valid_types)
48
48
  if isinstance(data, dict):
49
49
  data = json.dumps(data, indent=indent, sort_keys=sort_keys)
50
50
  elif isinstance(data, list):
@@ -50,6 +50,7 @@ class YamlFileHandler(FileHandler):
50
50
  """Write data to a YAML file."""
51
51
  try:
52
52
  super().write_file(file_path=file_path, data=data)
53
+ self.check_data_type(data=data, valid_types=self.valid_types)
53
54
  with open(file=file_path, mode="w", encoding="utf-8") as file:
54
55
  yaml.dump(data, stream=file, default_flow_style=False, sort_keys=False, **kwargs)
55
56
  except Exception as e:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bear-utils
3
- Version: 0.7.23
3
+ Version: 0.7.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
@@ -20,7 +20,7 @@ Requires-Dist: tinydb>=4.8.2
20
20
  Requires-Dist: toml>=0.10.2
21
21
  Description-Content-Type: text/markdown
22
22
 
23
- # Bear Utils v# Bear Utils v0.7.23
23
+ # Bear Utils v# Bear Utils v0.7.24
24
24
 
25
25
  Personal set of tools and utilities for Python projects, focusing on modularity and ease of use. This library includes components for caching, database management, logging, time handling, file operations, CLI prompts, image processing, clipboard interaction, gradient utilities, event systems, and async helpers.
26
26
 
@@ -33,19 +33,19 @@ bear_utils/extras/_async_helpers.py,sha256=cxq5d24NHkECmZqTVXEazv6K-XUa7skFnX6KQ
33
33
  bear_utils/extras/_tools.py,sha256=OYzGEEp7F6H6qr1t1QlnHTnAb4JaH6p9_maSXeDGFFM,7681
34
34
  bear_utils/extras/platform_utils.py,sha256=Ai7ow7S-_cKb5zFwFh8dkC8xmbMJFy-0_-w3NCERdEw,1362
35
35
  bear_utils/extras/responses/__init__.py,sha256=U5InC9ec9OI-f_eCi78z8UJsqtgEA5PGBvu94yvgjnA,89
36
- bear_utils/extras/responses/function_response.py,sha256=GJCYKKXC5U4lT-mBf40YZj5a6UIS3bCRn0qzwM-CETM,12718
36
+ bear_utils/extras/responses/function_response.py,sha256=Zis2uIpGp3Mqbpc0RszyNt2JSsLAiGmWKhgezLjzUTM,14153
37
37
  bear_utils/extras/wrappers/__init__.py,sha256=crh4sKOLvuhNMVX5bJYjCFWtXtH7G47UgNPOHq3HXTk,43
38
38
  bear_utils/extras/wrappers/add_methods.py,sha256=z2XZG2ZoYOB1MaGiLli4NRyyTeRgBy7tuYsiy8mTa9s,4422
39
39
  bear_utils/files/__init__.py,sha256=mIdnFSXoDE64ElM43bN2m6KuafURnN82ki0pdqN8q2o,201
40
40
  bear_utils/files/ignore_parser.py,sha256=ipBqUH5ndipPSq27TEsGDa7Sqq53KrczGLZcnNbW9F0,10951
41
41
  bear_utils/files/file_handlers/__init__.py,sha256=VF2IlWNr3UqeSvsbh3YCbLw9cLmlyf64mfeOKuhBdvk,136
42
- bear_utils/files/file_handlers/_base_file_handler.py,sha256=2Df8s-tNfsiQtV_pVFaDJe2sS_edPHbeR9nd5htgfwg,3960
42
+ bear_utils/files/file_handlers/_base_file_handler.py,sha256=Fl45rAuKSY-fVYBP-7o7op6thXlX8FLQbgwXEt4gDLQ,3726
43
43
  bear_utils/files/file_handlers/file_handler_factory.py,sha256=fDo2UcWp5-pOMtVWKCTuz-Fw4qSIB9fg5FgNRoYR6g4,9931
44
- bear_utils/files/file_handlers/json_file_handler.py,sha256=DvGvQ8D4ZFHHswtQQXmuiEY9HMkFA0o68u0_ekwwiXQ,2668
45
- bear_utils/files/file_handlers/log_file_handler.py,sha256=5yqW-ASikC4T0QPA7dkk-Putc8bEkg2MmZr_RCzYAvI,1542
46
- bear_utils/files/file_handlers/toml_file_handler.py,sha256=7eXDJBF-376NPe1uq7_Rsy3mER0sbbD5mBahvPJGsm8,2626
47
- bear_utils/files/file_handlers/txt_file_handler.py,sha256=24PLekOUUh_4XHGHCN61xqlubcM1GWgP6vonzEfoqac,2757
48
- bear_utils/files/file_handlers/yaml_file_handler.py,sha256=B1R-HgQYkqM93EoXDm0nYPje23KnvEI_sGxpX7lk2OM,2462
44
+ bear_utils/files/file_handlers/json_file_handler.py,sha256=0kqsG5QErFc3TVbK1I1l4cdzSxT6mYCmNuaW7hpFkYo,2656
45
+ bear_utils/files/file_handlers/log_file_handler.py,sha256=RDPOqNE_ujL_IIjfqi89LXNd4jntQ0bYTGqRVKiC_CU,1530
46
+ bear_utils/files/file_handlers/toml_file_handler.py,sha256=YPC9Y8DEURS9v3lV_iEng6URP0q97oDD74qdN870rhg,2699
47
+ bear_utils/files/file_handlers/txt_file_handler.py,sha256=Et_AxKEb3_XpVfJYIIsrHqvmDNXzzrBjmn2Ost3bPPA,2762
48
+ bear_utils/files/file_handlers/yaml_file_handler.py,sha256=iUFAJle2t67SX6uvPiutjdO8gSU1q_rXrZXH9MnJygg,2536
49
49
  bear_utils/graphics/__init__.py,sha256=uR_NFKfskJGDPT0PGiw38rRniV945H67fvDALxUTnVw,268
50
50
  bear_utils/graphics/bear_gradient.py,sha256=36B9hjU_qDjdgZaVcRl4jE3uQyU8k8G_MiORFzaendE,5272
51
51
  bear_utils/graphics/image_helpers.py,sha256=AaDQm6uunIdVkcMSXmoiaNQ68zRQQJ6bbhoApk6GSKU,1649
@@ -78,6 +78,6 @@ bear_utils/monitoring/__init__.py,sha256=9DKNIWTp_voLnaWgiP-wJ-o_N0hYixo-MzjUmg8
78
78
  bear_utils/monitoring/_common.py,sha256=LYQFxgTP9fk0cH71IQTuGwBYYPWCqHP_mMRNecoD76M,657
79
79
  bear_utils/monitoring/host_monitor.py,sha256=iawDGJWvByUnTanJvgiZMlqSJr3JpEWJdgA99A-fol0,13214
80
80
  bear_utils/time/__init__.py,sha256=d9Ovv-Dlx5NWgnOl1hY-evznVm9hboS6ypNp1wDFxQQ,934
81
- bear_utils-0.7.23.dist-info/METADATA,sha256=wXfmG9H_JcV1OF_uoLJNbIxnVQ2ieEoMXUPa450yTtM,8629
82
- bear_utils-0.7.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
- bear_utils-0.7.23.dist-info/RECORD,,
81
+ bear_utils-0.7.24.dist-info/METADATA,sha256=UMrwess6tydfPIEh_rszg6XqQ_YyF9VYHgyVpRAn864,8629
82
+ bear_utils-0.7.24.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
+ bear_utils-0.7.24.dist-info/RECORD,,