bear-utils 0.7.20__py3-none-any.whl → 0.7.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.
Files changed (79) hide show
  1. bear_utils/__init__.py +24 -1
  2. bear_utils/ai/__init__.py +5 -5
  3. bear_utils/ai/ai_helpers/__init__.py +24 -18
  4. bear_utils/ai/ai_helpers/_parsers.py +27 -21
  5. bear_utils/ai/ai_helpers/_types.py +2 -7
  6. bear_utils/cache/__init__.py +35 -23
  7. bear_utils/cli/__init__.py +13 -0
  8. bear_utils/cli/commands.py +14 -8
  9. bear_utils/cli/prompt_helpers.py +40 -34
  10. bear_utils/cli/shell/__init__.py +1 -0
  11. bear_utils/cli/shell/_base_command.py +17 -18
  12. bear_utils/cli/shell/_base_shell.py +37 -34
  13. bear_utils/config/__init__.py +4 -2
  14. bear_utils/config/config_manager.py +193 -56
  15. bear_utils/config/dir_manager.py +8 -3
  16. bear_utils/config/settings_manager.py +94 -171
  17. bear_utils/constants/__init__.py +2 -1
  18. bear_utils/constants/_exceptions.py +6 -1
  19. bear_utils/constants/date_related.py +2 -0
  20. bear_utils/constants/logger_protocol.py +28 -0
  21. bear_utils/constants/time_related.py +2 -0
  22. bear_utils/database/__init__.py +2 -0
  23. bear_utils/database/_db_manager.py +10 -11
  24. bear_utils/events/__init__.py +3 -1
  25. bear_utils/events/events_class.py +11 -11
  26. bear_utils/events/events_module.py +17 -8
  27. bear_utils/extras/__init__.py +8 -6
  28. bear_utils/extras/_async_helpers.py +2 -3
  29. bear_utils/extras/_tools.py +54 -52
  30. bear_utils/extras/platform_utils.py +5 -1
  31. bear_utils/extras/responses/__init__.py +1 -0
  32. bear_utils/extras/responses/function_response.py +301 -0
  33. bear_utils/extras/wrappers/__init__.py +1 -0
  34. bear_utils/extras/wrappers/add_methods.py +17 -15
  35. bear_utils/files/__init__.py +3 -1
  36. bear_utils/files/file_handlers/__init__.py +2 -0
  37. bear_utils/files/file_handlers/_base_file_handler.py +23 -3
  38. bear_utils/files/file_handlers/file_handler_factory.py +38 -38
  39. bear_utils/files/file_handlers/json_file_handler.py +49 -22
  40. bear_utils/files/file_handlers/log_file_handler.py +19 -12
  41. bear_utils/files/file_handlers/toml_file_handler.py +13 -5
  42. bear_utils/files/file_handlers/txt_file_handler.py +56 -14
  43. bear_utils/files/file_handlers/yaml_file_handler.py +19 -13
  44. bear_utils/files/ignore_parser.py +52 -57
  45. bear_utils/graphics/__init__.py +3 -1
  46. bear_utils/graphics/bear_gradient.py +17 -12
  47. bear_utils/graphics/image_helpers.py +11 -5
  48. bear_utils/gui/__init__.py +7 -2
  49. bear_utils/gui/gui_tools/__init__.py +9 -4
  50. bear_utils/gui/gui_tools/_settings.py +0 -1
  51. bear_utils/gui/gui_tools/qt_app.py +16 -11
  52. bear_utils/gui/gui_tools/qt_color_picker.py +24 -13
  53. bear_utils/gui/gui_tools/qt_file_handler.py +30 -38
  54. bear_utils/gui/gui_tools/qt_input_dialog.py +11 -14
  55. bear_utils/logging/__init__.py +6 -4
  56. bear_utils/logging/logger_manager/__init__.py +1 -0
  57. bear_utils/logging/logger_manager/_common.py +0 -1
  58. bear_utils/logging/logger_manager/_console_junk.py +15 -11
  59. bear_utils/logging/logger_manager/_styles.py +1 -2
  60. bear_utils/logging/logger_manager/loggers/__init__.py +1 -0
  61. bear_utils/logging/logger_manager/loggers/_base_logger.py +33 -33
  62. bear_utils/logging/logger_manager/loggers/_base_logger.pyi +6 -5
  63. bear_utils/logging/logger_manager/loggers/_buffer_logger.py +2 -3
  64. bear_utils/logging/logger_manager/loggers/_console_logger.py +54 -26
  65. bear_utils/logging/logger_manager/loggers/_console_logger.pyi +7 -21
  66. bear_utils/logging/logger_manager/loggers/_file_logger.py +20 -13
  67. bear_utils/logging/logger_manager/loggers/_level_sin.py +15 -15
  68. bear_utils/logging/logger_manager/loggers/_logger.py +4 -6
  69. bear_utils/logging/logger_manager/loggers/_sub_logger.py +16 -23
  70. bear_utils/logging/logger_manager/loggers/_sub_logger.pyi +4 -19
  71. bear_utils/logging/loggers.py +9 -13
  72. bear_utils/monitoring/__init__.py +7 -4
  73. bear_utils/monitoring/_common.py +28 -0
  74. bear_utils/monitoring/host_monitor.py +44 -48
  75. bear_utils/time/__init__.py +13 -6
  76. {bear_utils-0.7.20.dist-info → bear_utils-0.7.22.dist-info}/METADATA +50 -7
  77. bear_utils-0.7.22.dist-info/RECORD +83 -0
  78. bear_utils-0.7.20.dist-info/RECORD +0 -79
  79. {bear_utils-0.7.20.dist-info → bear_utils-0.7.22.dist-info}/WHEEL +0 -0
@@ -8,6 +8,12 @@ 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
+
11
17
  class FileHandler(ABC):
12
18
  """Abstract class for file handling with read, write, and present methods
13
19
 
@@ -19,6 +25,7 @@ class FileHandler(ABC):
19
25
  """
20
26
 
21
27
  valid_extensions: ClassVar[list[str]] = []
28
+ valid_types: ClassVar[tuple[type, ...]] = ()
22
29
 
23
30
  @classmethod
24
31
  def file_checker(cls, file_path: Path) -> bool:
@@ -33,7 +40,20 @@ class FileHandler(ABC):
33
40
  return file_path.suffix.lstrip(".") in cls.valid_extensions
34
41
 
35
42
  @classmethod
36
- def ValidateFileType(cls, method: Callable[P, R]) -> Callable[P, R]:
43
+ def check_data_type(cls, data: Any) -> Any:
44
+ """Check if the data is of the correct type.
45
+
46
+ Args:
47
+ data: Data to check the type of
48
+
49
+ Returns:
50
+ bool: True if the data is of the correct type, False otherwise
51
+ """
52
+ if not isinstance(data, cls.valid_types):
53
+ raise TypeError(f"Data must be one of {cls.valid_types}, got {type(data)}")
54
+
55
+ @classmethod
56
+ def ValidateFileType(cls, method: Callable[P, R]) -> Callable[P, R]: # noqa: N802 disable=invalid-name
37
57
  """Decorator to validate file type before executing a method.
38
58
 
39
59
  This decorator checks if the file is of the correct type before
@@ -50,9 +70,9 @@ class FileHandler(ABC):
50
70
  def wrapper(self: "FileHandler", file_path: Path, *args: Any, **kwargs: Any) -> R:
51
71
  if not self.file_checker(file_path):
52
72
  raise ValueError(f"Invalid file type. Expected {self.valid_extensions}")
53
- return method(self, file_path, *args, **kwargs) # type: ignore
73
+ return method(self, file_path, *args, **kwargs) # type: ignore[return-value]
54
74
 
55
- return cast(Callable[P, R], wrapper)
75
+ return cast("Callable[P, R]", wrapper)
56
76
 
57
77
  @abstractmethod
58
78
  def read_file(self, file_path: Path) -> dict[str, Any] | str:
@@ -1,8 +1,15 @@
1
- import warnings
1
+ """Factory for creating and managing file handlers based on file types."""
2
+
2
3
  from pathlib import Path
3
4
  from typing import Any, cast
5
+ import warnings
4
6
 
5
7
  from ._base_file_handler import FileHandler
8
+ from .json_file_handler import JsonFileHandler
9
+ from .log_file_handler import LogFileHandler
10
+ from .toml_file_handler import TomlFileHandler
11
+ from .txt_file_handler import TextFileHandler
12
+ from .yaml_file_handler import YamlFileHandler
6
13
 
7
14
 
8
15
  class FileHandlerFactory:
@@ -44,20 +51,14 @@ class FileHandlerFactory:
44
51
  This method imports and registers all standard FileHandler implementations.
45
52
  """
46
53
  try:
47
- from .json_file_handler import JsonFileHandler
48
- from .log_file_handler import LogFileHandler
49
- from .toml_file_handler import TomlFileHandler
50
- from .txt_file_handler import TextFileHandler
51
- from .yaml_file_handler import YamlFileHandler
52
-
53
- self.register_handler(JsonFileHandler)
54
- self.register_handler(TextFileHandler)
55
- self.register_handler(YamlFileHandler)
56
- self.register_handler(LogFileHandler)
57
- self.register_handler(TomlFileHandler)
54
+ self.register_handler(handler_class=JsonFileHandler)
55
+ self.register_handler(handler_class=TextFileHandler)
56
+ self.register_handler(handler_class=YamlFileHandler)
57
+ self.register_handler(handler_class=LogFileHandler)
58
+ self.register_handler(handler_class=TomlFileHandler)
58
59
 
59
60
  except ImportError as e:
60
- warnings.warn(f"Could not import all default handlers: {e}")
61
+ warnings.warn(f"Could not import all default handlers: {e}", UserWarning, stacklevel=2)
61
62
 
62
63
  def register_handler(self, handler_class: type[FileHandler]) -> None:
63
64
  """Register a handler class for its supported extensions.
@@ -91,25 +92,25 @@ class FileHandlerFactory:
91
92
  Raises:
92
93
  ValueError: If no file path is provided or stored, or if no handler exists for the file extension
93
94
  """
94
- path = file_path or self._file_path
95
- if not path:
95
+ path: Path | None = file_path or self._file_path
96
+ if path is None:
96
97
  raise ValueError("No file path provided or stored in factory")
97
98
 
98
- if file_path:
99
+ if file_path is not None:
99
100
  self._file_path = file_path
100
101
  self._handler_instance = None
101
102
 
102
- if self._handler_instance and not file_path:
103
+ if self._handler_instance and file_path is None:
103
104
  return self._handler_instance
104
105
 
105
- extension = path.suffix.lstrip(".")
106
- handler_class = self.get_handler_class(extension)
106
+ extension: str = path.suffix.lstrip(".")
107
+ handler_class: type[FileHandler] | None = self.get_handler_class(extension)
107
108
 
108
- if not handler_class:
109
+ if handler_class is None:
109
110
  raise ValueError(f"No handler registered for extension: {extension}")
110
111
 
111
112
  handler = handler_class()
112
- if not file_path: # Only cache if using the stored path
113
+ if file_path is None:
113
114
  self._handler_instance = handler
114
115
 
115
116
  return handler
@@ -136,37 +137,36 @@ class FileHandlerFactory:
136
137
  match operation:
137
138
  case "read":
138
139
  handler = self.get_handler_for_path(path)
139
- return handler.read_file(cast(Path, path), **kwargs)
140
+ return handler.read_file(cast("Path", path), **kwargs)
140
141
  case "write":
141
142
  if data is None:
142
143
  raise ValueError("Data required for write operation")
143
144
  handler = self.get_handler_for_path(path)
144
- return handler.write_file(cast(Path, path), data, **kwargs)
145
+ return handler.write_file(cast("Path", path), data, **kwargs)
145
146
  case "present":
146
147
  if data is None:
147
148
  raise ValueError("Data required for present operation")
148
149
  if path:
149
150
  handler: FileHandler = self.get_handler_for_path(path)
150
- else:
151
- if isinstance(data, dict):
152
- yaml_class = self.get_handler_class("yaml")
153
- if yaml_class:
154
- handler = yaml_class()
155
- else:
156
- raise ValueError("No handler available for dict data")
157
- elif isinstance(data, str):
158
- text_class = self.get_handler_class("txt")
159
- if text_class:
160
- handler = text_class()
161
- else:
162
- raise ValueError("No handler available for string data")
151
+ elif isinstance(data, dict):
152
+ yaml_class = self.get_handler_class("yaml")
153
+ if yaml_class:
154
+ handler = yaml_class()
155
+ else:
156
+ raise ValueError("No handler available for dict data")
157
+ elif isinstance(data, str):
158
+ text_class = self.get_handler_class("txt")
159
+ if text_class:
160
+ handler = text_class()
163
161
  else:
164
- raise ValueError(f"Cannot determine handler for data type: {type(data)}")
162
+ raise ValueError("No handler available for string data")
163
+ else:
164
+ raise ValueError(f"Cannot determine handler for data type: {type(data)}")
165
165
  return handler.present_file(data)
166
166
 
167
167
  case "info":
168
168
  handler = self.get_handler_for_path(path)
169
- return handler.get_file_info(cast(Path, path))
169
+ return handler.get_file_info(cast("Path", path))
170
170
  case _:
171
171
  raise ValueError(f"Invalid operation: {operation}")
172
172
 
@@ -1,44 +1,71 @@
1
+ """JsonFileHandler: A class for handling JSON files in Bear Utils."""
2
+
1
3
  import json
2
4
  from pathlib import Path
3
- from typing import Any, ClassVar
5
+ from typing import Any, ClassVar, overload
4
6
 
5
- from ._base_file_handler import FileHandler
7
+ from ._base_file_handler import FileHandler, check_data_type
6
8
 
7
9
 
8
10
  class JsonFileHandler(FileHandler):
9
11
  """Class for handling JSON files with read, write, and present methods"""
10
12
 
11
13
  valid_extensions: ClassVar[list[str]] = ["json"]
14
+ valid_types: ClassVar[tuple[type, ...]] = (dict, list, str)
12
15
 
13
16
  @FileHandler.ValidateFileType
14
17
  def read_file(self, file_path: Path, **kwargs) -> dict[str, Any]:
18
+ """Read a JSON file and return its content as a dictionary."""
15
19
  try:
16
- super().read_file(file_path)
17
- with open(file_path, "r", encoding="utf-8") as file:
20
+ super().read_file(file_path=file_path)
21
+ with open(file_path, encoding="utf-8") as file:
18
22
  return json.load(file, **kwargs)
19
23
  except Exception as e:
20
- raise ValueError(f"Error reading file: {e}")
24
+ raise ValueError(f"Error reading file: {e}") from e
25
+
26
+ @overload
27
+ def write_file(self, file_path: Path, data: str) -> None: ...
28
+
29
+ @overload
30
+ def write_file(self, file_path: Path, data: list[str]) -> None: ...
31
+
32
+ @overload
33
+ def write_file(self, file_path: Path, data: dict[str, Any], indent: int = 2, sort_keys: bool = False) -> None: ...
21
34
 
22
35
  @FileHandler.ValidateFileType
23
- def write_file(self, file_path: Path, data: dict[str, Any] | str, **kwargs) -> None:
36
+ def write_file(
37
+ self,
38
+ file_path: Path,
39
+ data: dict[str, Any] | str,
40
+ indent: int = 2,
41
+ sort_keys: bool = False,
42
+ **_,
43
+ ) -> None:
44
+ """Write data to a JSON file."""
24
45
  try:
25
- super().write_file(file_path, data)
26
- with open(file_path, "w", encoding="utf-8") as file:
27
- json.dump(
28
- data,
29
- file,
30
- indent=kwargs.pop("indent", 4),
31
- **kwargs,
32
- )
46
+ super().write_file(file_path=file_path, data=data)
47
+ check_data_type(data=data, valid_types=self.valid_types)
48
+ data = self.present_file(data, indent=indent, sort_keys=sort_keys)
49
+ file_path.write_text(data=data, encoding="utf-8")
33
50
  except Exception as e:
34
- raise ValueError(f"Error writing file: {e}")
51
+ raise ValueError(f"Error writing file: {e}") from e
52
+
53
+ @overload
54
+ def present_file(self, data: dict[str, Any], indent: int = 2, sort_keys: bool = False) -> str: ...
55
+
56
+ @overload
57
+ def present_file(self, data: str, **kwargs) -> str: ...
35
58
 
36
- def present_file(self, data: dict[str, Any] | str, **kwargs) -> str:
59
+ def present_file(self, data: dict[str, Any] | str, indent: int = 2, sort_keys: bool = False, **_) -> str:
60
+ """Present data as a JSON string."""
37
61
  try:
38
- return json.dumps(
39
- data,
40
- indent=kwargs.pop("indent", 4),
41
- **kwargs,
42
- )
62
+ if isinstance(data, list):
63
+ data = {"data": data}
64
+ elif isinstance(data, str):
65
+ try:
66
+ data = json.loads(data)
67
+ except json.JSONDecodeError as e:
68
+ raise ValueError(f"Invalid JSON string: {e}") from e
69
+ return json.dumps(data, indent=indent, sort_keys=sort_keys)
43
70
  except Exception as e:
44
- raise ValueError(f"Error presenting file: {e}")
71
+ raise ValueError(f"Error presenting file: {e}") from e
@@ -1,33 +1,40 @@
1
+ """Log File Handler Module"""
2
+
1
3
  from pathlib import Path
2
- from typing import Any, ClassVar
4
+ from typing import Any, ClassVar, cast
3
5
 
4
- from ._base_file_handler import FileHandler
6
+ from ._base_file_handler import FileHandler, check_data_type
5
7
 
6
8
 
7
9
  class LogFileHandler(FileHandler):
8
10
  """Class for handling .log files with read, write, and present methods"""
9
11
 
10
12
  valid_extensions: ClassVar[list[str]] = ["log"]
13
+ valid_types: ClassVar[tuple[type, ...]] = (str,)
11
14
 
12
15
  @FileHandler.ValidateFileType
13
16
  def read_file(self, file_path: Path) -> str:
17
+ """Read a log file and return its content as a string."""
14
18
  try:
15
19
  super().read_file(file_path)
16
- with open(file_path, "r", encoding="utf-8") as file:
20
+ with open(file_path, encoding="utf-8") as file:
17
21
  return file.read()
18
22
  except Exception as e:
19
- raise ValueError(f"Error reading file: {e}")
23
+ raise ValueError(f"Error reading file: {e}") from e
20
24
 
21
25
  @FileHandler.ValidateFileType
22
- def write_file(self, file_path: Path, data: dict[str, Any] | str, **kwargs) -> None:
26
+ def write_file(self, file_path: Path, data: dict[str, Any] | str, **_) -> None:
27
+ """Write data to a log file."""
23
28
  try:
24
- super().write_file(file_path, data)
25
- if not isinstance(data, str):
26
- raise ValueError("Data must be a string for log files")
29
+ super().write_file(file_path=file_path, data=data)
30
+ check_data_type(data=data, valid_types=self.valid_types)
27
31
  with open(file_path, "w", encoding="utf-8") as file:
28
- file.write(data)
32
+ file.write(cast("str", data))
29
33
  except Exception as e:
30
- raise ValueError(f"Error writing file: {e}")
34
+ raise ValueError(f"Error writing file: {e}") from e
31
35
 
32
- def present_file(self, data: dict[str, Any] | str, **kwargs) -> str:
33
- raise NotImplementedError("Presenting log files is not implemented. Not needed.")
36
+ def present_file(self, data: dict[str, Any] | list[str] | str, **_) -> str:
37
+ """Present data as a string."""
38
+ if isinstance(data, dict):
39
+ data = "\n".join(f"{key}: {value}" for key, value in data.items())
40
+ return str(data)
@@ -1,3 +1,5 @@
1
+ """TOML File Handler Module"""
2
+
1
3
  from dataclasses import dataclass
2
4
  from pathlib import Path
3
5
  from typing import Any, ClassVar
@@ -11,20 +13,23 @@ class TomlFileHandler(FileHandler):
11
13
  """Class for handling .toml files with read, write, and present methods"""
12
14
 
13
15
  valid_extensions: ClassVar[list[str]] = ["toml"]
16
+ valid_types: ClassVar[tuple[type, ...]] = (dict, str)
14
17
 
15
18
  @FileHandler.ValidateFileType
16
19
  def read_file(self, file_path: Path) -> dict:
20
+ """Read a TOML file and return its content as a dictionary."""
17
21
  try:
18
22
  super().read_file(file_path)
19
23
 
20
24
  return toml.load(file_path)
21
25
  except Exception as e:
22
- raise ValueError(f"Error reading file: {e}")
26
+ raise ValueError(f"Error reading file: {e}") from e
23
27
 
24
28
  @FileHandler.ValidateFileType
25
29
  def write_file(self, file_path: Path, data: dict[str, Any] | str, **kwargs) -> None:
30
+ """Write data to a TOML file."""
26
31
  try:
27
- super().write_file(file_path, data)
32
+ super().write_file(file_path=file_path, data=data)
28
33
 
29
34
  with open(file_path, "w", encoding="utf-8") as file:
30
35
  if isinstance(data, dict):
@@ -32,10 +37,12 @@ class TomlFileHandler(FileHandler):
32
37
  else:
33
38
  file.write(data)
34
39
  except Exception as e:
35
- raise ValueError(f"Error writing file: {e}")
40
+ raise ValueError(f"Error writing file: {e}") from e
36
41
 
37
- def present_file(self, data: dict[str, Any] | str, **kwargs) -> str:
38
- raise NotImplementedError("Presenting TOML files is not implemented. Not needed.")
42
+ def present_file(self, data: dict[str, Any] | str, **_) -> str:
43
+ """Present data as a string."""
44
+ # TODO: Actually implement this method to format TOML data nicely
45
+ return str(data)
39
46
 
40
47
 
41
48
  @dataclass
@@ -56,6 +63,7 @@ class PyProjectToml:
56
63
 
57
64
  @classmethod
58
65
  def from_dict(cls, data: dict[str, Any]) -> "PyProjectToml":
66
+ """Create a PyProjectToml instance from a dictionary."""
59
67
  data = data.get("project", {})
60
68
  authors: dict = data.get("authors", {})[0]
61
69
  return cls(
@@ -1,34 +1,76 @@
1
+ """Text File Handler Module for .txt files"""
2
+
3
+ import json
1
4
  from pathlib import Path
2
- from typing import Any, ClassVar
5
+ from typing import Any, ClassVar, overload
3
6
 
4
- from ._base_file_handler import FileHandler
7
+ from ._base_file_handler import FileHandler, check_data_type
5
8
 
6
9
 
7
10
  class TextFileHandler(FileHandler):
8
11
  """Class for handling .txt files with read, write, and present methods"""
9
12
 
10
13
  valid_extensions: ClassVar[list[str]] = ["txt"]
14
+ valid_types: ClassVar[tuple[type, ...]] = (str, dict, list)
11
15
 
12
16
  @FileHandler.ValidateFileType
13
17
  def read_file(self, file_path: Path) -> str:
18
+ """Read a text file and return its content as a string."""
14
19
  try:
15
20
  super().read_file(file_path)
16
- with open(file_path, "r", encoding="utf-8") as file:
21
+ with open(file_path, encoding="utf-8") as file:
17
22
  return file.read()
18
23
  except Exception as e:
19
- raise ValueError(f"Error reading file: {e}")
24
+ raise ValueError(f"Error reading file: {e}") from e
25
+
26
+ @overload
27
+ def write_file(self, file_path: Path, data: str) -> None: ...
28
+
29
+ @overload
30
+ def write_file(self, file_path: Path, data: list[str]) -> None: ...
31
+
32
+ @overload
33
+ def write_file(self, file_path: Path, data: dict[str, Any], indent: int = 2, sort_keys: bool = False) -> None: ...
20
34
 
21
35
  @FileHandler.ValidateFileType
22
- def write_file(self, file_path: Path, data: dict[str, Any] | str, **kwargs) -> None:
36
+ def write_file(
37
+ self,
38
+ file_path: Path,
39
+ data: dict[str, Any] | str,
40
+ indent: int = 2,
41
+ sort_keys: bool = False,
42
+ **_,
43
+ ) -> None:
44
+ """Write data to a text file."""
23
45
  try:
24
- super().write_file(file_path, data)
25
- if not isinstance(data, str):
26
- raise ValueError("Data must be a string for text files")
27
-
28
- with open(file_path, "w", encoding="utf-8") as file:
29
- file.write(data)
46
+ super().write_file(file_path=file_path, data=data)
47
+ check_data_type(data, self.valid_types)
48
+ if isinstance(data, dict):
49
+ data = json.dumps(data, indent=indent, sort_keys=sort_keys)
50
+ elif isinstance(data, list):
51
+ data = "\n".join(data)
52
+ file_path.write_text(data=data, encoding="utf-8")
30
53
  except Exception as e:
31
- raise ValueError(f"Error writing file: {e}")
54
+ raise ValueError(f"Error writing file: {e}") from e
55
+
56
+ def present_file(self, data: dict[str, Any] | list[str] | Any, **kwargs) -> str:
57
+ """Present data as a string.
58
+
59
+ This method converts the data to a string representation if it is a dictionary,
60
+
61
+ Args:
62
+ data (dict[str, Any] | str): Data to present
63
+ Returns:
64
+ str: String representation of the data
65
+ """
66
+ converted_data: str | None = None
67
+ if data is None:
68
+ raise ValueError("No data to present")
69
+ if isinstance(data, dict):
70
+ converted_data = json.dumps(data, indent=kwargs.get("indent", 2), sort_keys=kwargs.get("sort", False))
71
+ elif isinstance(data, list):
72
+ converted_data = "\n".join(data)
73
+ elif not isinstance(data, str):
74
+ raise TypeError("Data must be a string for text files")
32
75
 
33
- def present_file(self, data: dict[str, Any] | str, **kwargs) -> str:
34
- raise NotImplementedError("Presenting text files is not implemented. Not needed.")
76
+ return str(converted_data if isinstance(data, dict | list) else data)
@@ -1,3 +1,5 @@
1
+ """YamlFileHandler class for handling YAML files."""
2
+
1
3
  from pathlib import Path
2
4
  from typing import Any, ClassVar
3
5
 
@@ -10,6 +12,7 @@ class YamlFileHandler(FileHandler):
10
12
  """Class for handling .yaml/.yml files with read, write, and present methods"""
11
13
 
12
14
  valid_extensions: ClassVar[list[str]] = ["yaml", "yml"]
15
+ valid_types: ClassVar[tuple[type, ...]] = (dict, str)
13
16
 
14
17
  @FileHandler.ValidateFileType
15
18
  def unsafe_read_file(self, file_path: Path, **kwargs) -> dict[str, Any]:
@@ -26,32 +29,35 @@ class YamlFileHandler(FileHandler):
26
29
  Dictionary containing the parsed YAML data
27
30
  """
28
31
  try:
29
- super().read_file(file_path)
30
- with open(file_path, "r", encoding="utf-8") as file:
31
- return yaml.load(file, **kwargs)
32
+ super().read_file(file_path=file_path)
33
+ with open(file=file_path, encoding="utf-8") as file:
34
+ return yaml.load(stream=file, **kwargs) # noqa: S506 # Using unsafe loader intentionally
32
35
  except Exception as e:
33
- raise ValueError(f"Error reading file: {e}")
36
+ raise ValueError(f"Error reading file: {e}") from e
34
37
 
35
38
  @FileHandler.ValidateFileType
36
39
  def read_file(self, file_path: Path) -> dict[str, Any]:
40
+ """Read YAML file safely."""
37
41
  try:
38
- super().read_file(file_path)
39
- with open(file_path, "r", encoding="utf-8") as file:
40
- return yaml.safe_load(file)
42
+ super().read_file(file_path=file_path)
43
+ with open(file=file_path, encoding="utf-8") as file:
44
+ return yaml.safe_load(stream=file)
41
45
  except Exception as e:
42
- raise ValueError(f"Error reading file: {e}")
46
+ raise ValueError(f"Error reading file: {e}") from e
43
47
 
44
48
  @FileHandler.ValidateFileType
45
49
  def write_file(self, file_path: Path, data: dict[str, Any] | str, **kwargs) -> None:
50
+ """Write data to a YAML file."""
46
51
  try:
47
- super().write_file(file_path, data)
48
- with open(file_path, "w", encoding="utf-8") as file:
49
- yaml.dump(data, file, default_flow_style=False, sort_keys=False, **kwargs)
52
+ super().write_file(file_path=file_path, data=data)
53
+ with open(file=file_path, mode="w", encoding="utf-8") as file:
54
+ yaml.dump(data, stream=file, default_flow_style=False, sort_keys=False, **kwargs)
50
55
  except Exception as e:
51
- raise ValueError(f"Error writing file: {e}")
56
+ raise ValueError(f"Error writing file: {e}") from e
52
57
 
53
58
  def present_file(self, data: dict[str, Any] | str, **kwargs) -> str:
59
+ """Present data as a YAML string."""
54
60
  try:
55
61
  return yaml.dump(data, default_flow_style=False, sort_keys=False, **kwargs)
56
62
  except Exception as e:
57
- raise ValueError(f"Error presenting file: {e}")
63
+ raise ValueError(f"Error presenting file: {e}") from e