bear-utils 0.7.21__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.
- bear_utils/__init__.py +24 -1
- bear_utils/ai/__init__.py +5 -5
- bear_utils/ai/ai_helpers/__init__.py +24 -18
- bear_utils/ai/ai_helpers/_parsers.py +27 -21
- bear_utils/ai/ai_helpers/_types.py +2 -7
- bear_utils/cache/__init__.py +35 -23
- bear_utils/cli/__init__.py +13 -0
- bear_utils/cli/commands.py +14 -8
- bear_utils/cli/prompt_helpers.py +40 -34
- bear_utils/cli/shell/__init__.py +1 -0
- bear_utils/cli/shell/_base_command.py +17 -18
- bear_utils/cli/shell/_base_shell.py +37 -34
- bear_utils/config/__init__.py +4 -2
- bear_utils/config/config_manager.py +193 -56
- bear_utils/config/dir_manager.py +8 -3
- bear_utils/config/settings_manager.py +94 -171
- bear_utils/constants/__init__.py +2 -1
- bear_utils/constants/_exceptions.py +6 -1
- bear_utils/constants/date_related.py +2 -0
- bear_utils/constants/logger_protocol.py +28 -0
- bear_utils/constants/time_related.py +2 -0
- bear_utils/database/__init__.py +2 -0
- bear_utils/database/_db_manager.py +10 -11
- bear_utils/events/__init__.py +3 -1
- bear_utils/events/events_class.py +11 -11
- bear_utils/events/events_module.py +17 -8
- bear_utils/extras/__init__.py +8 -6
- bear_utils/extras/_async_helpers.py +2 -3
- bear_utils/extras/_tools.py +54 -52
- bear_utils/extras/platform_utils.py +5 -1
- bear_utils/extras/responses/__init__.py +1 -0
- bear_utils/extras/responses/function_response.py +301 -0
- bear_utils/extras/wrappers/__init__.py +1 -0
- bear_utils/extras/wrappers/add_methods.py +17 -15
- bear_utils/files/__init__.py +3 -1
- bear_utils/files/file_handlers/__init__.py +2 -0
- bear_utils/files/file_handlers/_base_file_handler.py +23 -3
- bear_utils/files/file_handlers/file_handler_factory.py +38 -38
- bear_utils/files/file_handlers/json_file_handler.py +49 -22
- bear_utils/files/file_handlers/log_file_handler.py +19 -12
- bear_utils/files/file_handlers/toml_file_handler.py +13 -5
- bear_utils/files/file_handlers/txt_file_handler.py +56 -14
- bear_utils/files/file_handlers/yaml_file_handler.py +19 -13
- bear_utils/files/ignore_parser.py +52 -57
- bear_utils/graphics/__init__.py +3 -1
- bear_utils/graphics/bear_gradient.py +17 -12
- bear_utils/graphics/image_helpers.py +11 -5
- bear_utils/gui/__init__.py +3 -1
- bear_utils/gui/gui_tools/__init__.py +3 -1
- bear_utils/gui/gui_tools/_settings.py +0 -1
- bear_utils/gui/gui_tools/qt_app.py +16 -11
- bear_utils/gui/gui_tools/qt_color_picker.py +24 -13
- bear_utils/gui/gui_tools/qt_file_handler.py +30 -38
- bear_utils/gui/gui_tools/qt_input_dialog.py +11 -14
- bear_utils/logging/__init__.py +6 -4
- bear_utils/logging/logger_manager/__init__.py +1 -0
- bear_utils/logging/logger_manager/_common.py +0 -1
- bear_utils/logging/logger_manager/_console_junk.py +15 -11
- bear_utils/logging/logger_manager/_styles.py +1 -2
- bear_utils/logging/logger_manager/loggers/__init__.py +1 -0
- bear_utils/logging/logger_manager/loggers/_base_logger.py +33 -33
- bear_utils/logging/logger_manager/loggers/_base_logger.pyi +6 -5
- bear_utils/logging/logger_manager/loggers/_buffer_logger.py +2 -3
- bear_utils/logging/logger_manager/loggers/_console_logger.py +54 -26
- bear_utils/logging/logger_manager/loggers/_console_logger.pyi +7 -21
- bear_utils/logging/logger_manager/loggers/_file_logger.py +20 -13
- bear_utils/logging/logger_manager/loggers/_level_sin.py +15 -15
- bear_utils/logging/logger_manager/loggers/_logger.py +4 -6
- bear_utils/logging/logger_manager/loggers/_sub_logger.py +16 -23
- bear_utils/logging/logger_manager/loggers/_sub_logger.pyi +4 -19
- bear_utils/logging/loggers.py +9 -13
- bear_utils/monitoring/__init__.py +7 -4
- bear_utils/monitoring/_common.py +28 -0
- bear_utils/monitoring/host_monitor.py +44 -48
- bear_utils/time/__init__.py +13 -6
- {bear_utils-0.7.21.dist-info → bear_utils-0.7.22.dist-info}/METADATA +50 -6
- bear_utils-0.7.22.dist-info/RECORD +83 -0
- bear_utils-0.7.21.dist-info/RECORD +0 -79
- {bear_utils-0.7.21.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
|
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
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
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
|
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
|
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
|
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
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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(
|
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,
|
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(
|
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
|
-
|
27
|
-
|
28
|
-
|
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, **
|
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
|
-
|
39
|
-
data
|
40
|
-
|
41
|
-
|
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,
|
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, **
|
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
|
-
|
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, **
|
33
|
-
|
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, **
|
38
|
-
|
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,
|
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(
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
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,
|
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,
|
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
|