fishertools 0.2.1__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.
- fishertools/__init__.py +82 -0
- fishertools/config/__init__.py +24 -0
- fishertools/config/manager.py +247 -0
- fishertools/config/models.py +96 -0
- fishertools/config/parser.py +265 -0
- fishertools/decorators.py +93 -0
- fishertools/documentation/__init__.py +38 -0
- fishertools/documentation/api.py +242 -0
- fishertools/documentation/generator.py +502 -0
- fishertools/documentation/models.py +126 -0
- fishertools/documentation/visual.py +583 -0
- fishertools/errors/__init__.py +29 -0
- fishertools/errors/exceptions.py +191 -0
- fishertools/errors/explainer.py +303 -0
- fishertools/errors/formatters.py +386 -0
- fishertools/errors/models.py +228 -0
- fishertools/errors/patterns.py +119 -0
- fishertools/errors/recovery.py +467 -0
- fishertools/examples/__init__.py +22 -0
- fishertools/examples/models.py +118 -0
- fishertools/examples/repository.py +770 -0
- fishertools/helpers.py +116 -0
- fishertools/integration.py +451 -0
- fishertools/learn/__init__.py +18 -0
- fishertools/learn/examples.py +550 -0
- fishertools/learn/tips.py +281 -0
- fishertools/learning/__init__.py +32 -0
- fishertools/learning/core.py +349 -0
- fishertools/learning/models.py +112 -0
- fishertools/learning/progress.py +314 -0
- fishertools/learning/session.py +500 -0
- fishertools/learning/tutorial.py +626 -0
- fishertools/legacy/__init__.py +76 -0
- fishertools/legacy/deprecated.py +261 -0
- fishertools/legacy/deprecation.py +149 -0
- fishertools/safe/__init__.py +16 -0
- fishertools/safe/collections.py +242 -0
- fishertools/safe/files.py +240 -0
- fishertools/safe/strings.py +15 -0
- fishertools/utils.py +57 -0
- fishertools-0.2.1.dist-info/METADATA +256 -0
- fishertools-0.2.1.dist-info/RECORD +81 -0
- fishertools-0.2.1.dist-info/WHEEL +5 -0
- fishertools-0.2.1.dist-info/licenses/LICENSE +21 -0
- fishertools-0.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +6 -0
- tests/conftest.py +25 -0
- tests/test_config/__init__.py +3 -0
- tests/test_config/test_basic_config.py +57 -0
- tests/test_config/test_config_error_handling.py +287 -0
- tests/test_config/test_config_properties.py +435 -0
- tests/test_documentation/__init__.py +3 -0
- tests/test_documentation/test_documentation_properties.py +253 -0
- tests/test_documentation/test_visual_documentation_properties.py +444 -0
- tests/test_errors/__init__.py +3 -0
- tests/test_errors/test_api.py +301 -0
- tests/test_errors/test_error_handling.py +354 -0
- tests/test_errors/test_explainer.py +173 -0
- tests/test_errors/test_formatters.py +338 -0
- tests/test_errors/test_models.py +248 -0
- tests/test_errors/test_patterns.py +270 -0
- tests/test_examples/__init__.py +3 -0
- tests/test_examples/test_example_repository_properties.py +204 -0
- tests/test_examples/test_specific_examples.py +303 -0
- tests/test_integration.py +298 -0
- tests/test_integration_enhancements.py +462 -0
- tests/test_learn/__init__.py +3 -0
- tests/test_learn/test_examples.py +221 -0
- tests/test_learn/test_tips.py +285 -0
- tests/test_learning/__init__.py +3 -0
- tests/test_learning/test_interactive_learning_properties.py +337 -0
- tests/test_learning/test_learning_system_properties.py +194 -0
- tests/test_learning/test_progress_tracking_properties.py +279 -0
- tests/test_legacy/__init__.py +3 -0
- tests/test_legacy/test_backward_compatibility.py +236 -0
- tests/test_legacy/test_deprecation_warnings.py +208 -0
- tests/test_safe/__init__.py +3 -0
- tests/test_safe/test_collections_properties.py +189 -0
- tests/test_safe/test_files.py +104 -0
- tests/test_structure.py +58 -0
- tests/test_structure_enhancements.py +115 -0
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Safe collection operations for beginners.
|
|
3
|
+
|
|
4
|
+
This module provides safe versions of common collection operations
|
|
5
|
+
that prevent typical mistakes and provide helpful error messages.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Optional, Union, List, Dict, Tuple
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def safe_get(collection: Union[List, Tuple, Dict, str], index: Union[int, str], default: Any = None) -> Any:
|
|
12
|
+
"""
|
|
13
|
+
Safely get an element from a collection by index or key.
|
|
14
|
+
|
|
15
|
+
Предотвращает ошибки IndexError и KeyError, возвращая значение по умолчанию
|
|
16
|
+
вместо исключения. Подходит для списков, кортежей, словарей и строк.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
collection: Коллекция (список, кортеж, словарь или строка)
|
|
20
|
+
index: Индекс (для списков/кортежей/строк) или ключ (для словарей)
|
|
21
|
+
default: Значение по умолчанию, если элемент не найден
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Элемент коллекции или значение по умолчанию
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
SafeUtilityError: If collection is None or unsupported type
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
>>> safe_get([1, 2, 3], 1)
|
|
31
|
+
2
|
|
32
|
+
>>> safe_get([1, 2, 3], 10, "не найдено")
|
|
33
|
+
'не найдено'
|
|
34
|
+
>>> safe_get({"name": "Иван"}, "name")
|
|
35
|
+
'Иван'
|
|
36
|
+
>>> safe_get({"name": "Иван"}, "age", 0)
|
|
37
|
+
0
|
|
38
|
+
"""
|
|
39
|
+
from ..errors.exceptions import SafeUtilityError
|
|
40
|
+
|
|
41
|
+
if collection is None:
|
|
42
|
+
raise SafeUtilityError("Коллекция не может быть None. Передайте список, кортеж, словарь или строку.",
|
|
43
|
+
utility_name="safe_get")
|
|
44
|
+
|
|
45
|
+
# Для словарей используем get()
|
|
46
|
+
if isinstance(collection, dict):
|
|
47
|
+
return collection.get(index, default)
|
|
48
|
+
|
|
49
|
+
# Для списков, кортежей и строк проверяем индекс
|
|
50
|
+
if isinstance(collection, (list, tuple, str)):
|
|
51
|
+
if not isinstance(index, int):
|
|
52
|
+
raise SafeUtilityError(f"Для {type(collection).__name__} индекс должен быть числом, получен {type(index).__name__}",
|
|
53
|
+
utility_name="safe_get")
|
|
54
|
+
|
|
55
|
+
if 0 <= index < len(collection):
|
|
56
|
+
return collection[index]
|
|
57
|
+
else:
|
|
58
|
+
return default
|
|
59
|
+
|
|
60
|
+
# Неподдерживаемый тип коллекции
|
|
61
|
+
raise SafeUtilityError(f"Неподдерживаемый тип коллекции: {type(collection).__name__}. "
|
|
62
|
+
f"Поддерживаются: list, tuple, dict, str",
|
|
63
|
+
utility_name="safe_get")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def safe_divide(a: Union[int, float], b: Union[int, float], default: Union[int, float] = 0) -> Union[int, float]:
|
|
67
|
+
"""
|
|
68
|
+
Safely divide two numbers with zero division handling.
|
|
69
|
+
|
|
70
|
+
Предотвращает ошибку ZeroDivisionError, возвращая значение по умолчанию
|
|
71
|
+
при делении на ноль.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
a: Делимое (число)
|
|
75
|
+
b: Делитель (число)
|
|
76
|
+
default: Значение по умолчанию при делении на ноль
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Результат деления или значение по умолчанию
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
SafeUtilityError: If arguments are not numbers
|
|
83
|
+
|
|
84
|
+
Examples:
|
|
85
|
+
>>> safe_divide(10, 2)
|
|
86
|
+
5.0
|
|
87
|
+
>>> safe_divide(10, 0)
|
|
88
|
+
0
|
|
89
|
+
>>> safe_divide(10, 0, -1)
|
|
90
|
+
-1
|
|
91
|
+
"""
|
|
92
|
+
from ..errors.exceptions import SafeUtilityError
|
|
93
|
+
import math
|
|
94
|
+
|
|
95
|
+
# Проверяем типы входных данных
|
|
96
|
+
if not isinstance(a, (int, float)):
|
|
97
|
+
raise SafeUtilityError(f"Делимое должно быть числом, получен {type(a).__name__}",
|
|
98
|
+
utility_name="safe_divide")
|
|
99
|
+
|
|
100
|
+
if not isinstance(b, (int, float)):
|
|
101
|
+
raise SafeUtilityError(f"Делитель должен быть числом, получен {type(b).__name__}",
|
|
102
|
+
utility_name="safe_divide")
|
|
103
|
+
|
|
104
|
+
if not isinstance(default, (int, float)):
|
|
105
|
+
raise SafeUtilityError(f"Значение по умолчанию должно быть числом, получен {type(default).__name__}",
|
|
106
|
+
utility_name="safe_divide")
|
|
107
|
+
|
|
108
|
+
# Проверяем деление на ноль
|
|
109
|
+
if b == 0:
|
|
110
|
+
return default
|
|
111
|
+
|
|
112
|
+
# Выполняем деление
|
|
113
|
+
result = a / b
|
|
114
|
+
|
|
115
|
+
# Проверяем на бесконечность или NaN
|
|
116
|
+
if math.isinf(result) or math.isnan(result):
|
|
117
|
+
return default
|
|
118
|
+
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def safe_max(collection: Union[List, Tuple], default: Any = None) -> Any:
|
|
123
|
+
"""
|
|
124
|
+
Safely find maximum value in a collection.
|
|
125
|
+
|
|
126
|
+
Предотвращает ошибку ValueError при пустой коллекции.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
collection: Коллекция чисел
|
|
130
|
+
default: Значение по умолчанию для пустой коллекции
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Максимальное значение или значение по умолчанию
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
SafeUtilityError: If collection is not a list or tuple, or elements are not comparable
|
|
137
|
+
|
|
138
|
+
Examples:
|
|
139
|
+
>>> safe_max([1, 5, 3])
|
|
140
|
+
5
|
|
141
|
+
>>> safe_max([])
|
|
142
|
+
None
|
|
143
|
+
>>> safe_max([], 0)
|
|
144
|
+
0
|
|
145
|
+
"""
|
|
146
|
+
from ..errors.exceptions import SafeUtilityError
|
|
147
|
+
|
|
148
|
+
if not isinstance(collection, (list, tuple)):
|
|
149
|
+
raise SafeUtilityError(f"Коллекция должна быть списком или кортежем, получен {type(collection).__name__}",
|
|
150
|
+
utility_name="safe_max")
|
|
151
|
+
|
|
152
|
+
if len(collection) == 0:
|
|
153
|
+
return default
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
return max(collection)
|
|
157
|
+
except TypeError as e:
|
|
158
|
+
raise SafeUtilityError(f"Не удалось найти максимум: {str(e)}. "
|
|
159
|
+
f"Убедитесь, что все элементы коллекции сравнимы.",
|
|
160
|
+
utility_name="safe_max", original_error=e)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def safe_min(collection: Union[List, Tuple], default: Any = None) -> Any:
|
|
164
|
+
"""
|
|
165
|
+
Safely find minimum value in a collection.
|
|
166
|
+
|
|
167
|
+
Предотвращает ошибку ValueError при пустой коллекции.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
collection: Коллекция чисел
|
|
171
|
+
default: Значение по умолчанию для пустой коллекции
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Минимальное значение или значение по умолчанию
|
|
175
|
+
|
|
176
|
+
Raises:
|
|
177
|
+
SafeUtilityError: If collection is not a list or tuple, or elements are not comparable
|
|
178
|
+
|
|
179
|
+
Examples:
|
|
180
|
+
>>> safe_min([1, 5, 3])
|
|
181
|
+
1
|
|
182
|
+
>>> safe_min([])
|
|
183
|
+
None
|
|
184
|
+
>>> safe_min([], 0)
|
|
185
|
+
0
|
|
186
|
+
"""
|
|
187
|
+
from ..errors.exceptions import SafeUtilityError
|
|
188
|
+
|
|
189
|
+
if not isinstance(collection, (list, tuple)):
|
|
190
|
+
raise SafeUtilityError(f"Коллекция должна быть списком или кортежем, получен {type(collection).__name__}",
|
|
191
|
+
utility_name="safe_min")
|
|
192
|
+
|
|
193
|
+
if len(collection) == 0:
|
|
194
|
+
return default
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
return min(collection)
|
|
198
|
+
except TypeError as e:
|
|
199
|
+
raise SafeUtilityError(f"Не удалось найти минимум: {str(e)}. "
|
|
200
|
+
f"Убедитесь, что все элементы коллекции сравнимы.",
|
|
201
|
+
utility_name="safe_min", original_error=e)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def safe_sum(collection: Union[List, Tuple], default: Union[int, float] = 0) -> Union[int, float]:
|
|
205
|
+
"""
|
|
206
|
+
Safely calculate sum of a collection.
|
|
207
|
+
|
|
208
|
+
Предотвращает ошибки при пустой коллекции или несовместимых типах.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
collection: Коллекция чисел
|
|
212
|
+
default: Значение по умолчанию для пустой коллекции
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
Сумма элементов или значение по умолчанию
|
|
216
|
+
|
|
217
|
+
Raises:
|
|
218
|
+
SafeUtilityError: If collection is not a list or tuple, or elements are not numbers
|
|
219
|
+
|
|
220
|
+
Examples:
|
|
221
|
+
>>> safe_sum([1, 2, 3])
|
|
222
|
+
6
|
|
223
|
+
>>> safe_sum([])
|
|
224
|
+
0
|
|
225
|
+
>>> safe_sum([], 10)
|
|
226
|
+
10
|
|
227
|
+
"""
|
|
228
|
+
from ..errors.exceptions import SafeUtilityError
|
|
229
|
+
|
|
230
|
+
if not isinstance(collection, (list, tuple)):
|
|
231
|
+
raise SafeUtilityError(f"Коллекция должна быть списком или кортежем, получен {type(collection).__name__}",
|
|
232
|
+
utility_name="safe_sum")
|
|
233
|
+
|
|
234
|
+
if len(collection) == 0:
|
|
235
|
+
return default
|
|
236
|
+
|
|
237
|
+
try:
|
|
238
|
+
return sum(collection)
|
|
239
|
+
except TypeError as e:
|
|
240
|
+
raise SafeUtilityError(f"Не удалось вычислить сумму: {str(e)}. "
|
|
241
|
+
f"Убедитесь, что все элементы коллекции являются числами.",
|
|
242
|
+
utility_name="safe_sum", original_error=e)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Safe file operations for beginners.
|
|
3
|
+
|
|
4
|
+
This module provides safe file handling utilities that prevent common
|
|
5
|
+
file-related errors and provide helpful error messages.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Union, Optional, List
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def safe_read_file(filepath: Union[str, Path], encoding: str = 'utf-8', default: str = '') -> str:
|
|
14
|
+
"""
|
|
15
|
+
Safely read a file with comprehensive error handling.
|
|
16
|
+
|
|
17
|
+
Предотвращает ошибки FileNotFoundError, PermissionError и UnicodeDecodeError,
|
|
18
|
+
возвращая значение по умолчанию вместо исключения.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
filepath: Путь к файлу (строка или Path объект)
|
|
22
|
+
encoding: Кодировка файла (по умолчанию utf-8)
|
|
23
|
+
default: Значение по умолчанию при ошибке чтения
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Содержимое файла или значение по умолчанию
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
SafeUtilityError: If filepath is None or invalid type
|
|
30
|
+
|
|
31
|
+
Examples:
|
|
32
|
+
>>> safe_read_file("example.txt")
|
|
33
|
+
'содержимое файла'
|
|
34
|
+
>>> safe_read_file("несуществующий.txt", default="файл не найден")
|
|
35
|
+
'файл не найден'
|
|
36
|
+
"""
|
|
37
|
+
from ..errors.exceptions import SafeUtilityError
|
|
38
|
+
|
|
39
|
+
if filepath is None:
|
|
40
|
+
raise SafeUtilityError("Путь к файлу не может быть None", utility_name="safe_read_file")
|
|
41
|
+
|
|
42
|
+
if not isinstance(filepath, (str, Path)):
|
|
43
|
+
raise SafeUtilityError(f"Путь к файлу должен быть строкой или Path объектом, получен {type(filepath).__name__}",
|
|
44
|
+
utility_name="safe_read_file")
|
|
45
|
+
|
|
46
|
+
if not isinstance(encoding, str):
|
|
47
|
+
raise SafeUtilityError(f"Кодировка должна быть строкой, получен {type(encoding).__name__}",
|
|
48
|
+
utility_name="safe_read_file")
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
with open(filepath, 'r', encoding=encoding) as file:
|
|
52
|
+
return file.read()
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
return default
|
|
55
|
+
except PermissionError:
|
|
56
|
+
return default
|
|
57
|
+
except UnicodeDecodeError:
|
|
58
|
+
return default
|
|
59
|
+
except OSError:
|
|
60
|
+
# Covers other OS-related errors
|
|
61
|
+
return default
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def safe_write_file(filepath: Union[str, Path], content: str, encoding: str = 'utf-8',
|
|
65
|
+
create_dirs: bool = True) -> bool:
|
|
66
|
+
"""
|
|
67
|
+
Safely write content to a file with error handling.
|
|
68
|
+
|
|
69
|
+
Предотвращает ошибки при записи файла и может создавать директории.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
filepath: Путь к файлу
|
|
73
|
+
content: Содержимое для записи
|
|
74
|
+
encoding: Кодировка файла
|
|
75
|
+
create_dirs: Создавать ли директории если они не существуют
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
True если запись успешна, False при ошибке
|
|
79
|
+
|
|
80
|
+
Raises:
|
|
81
|
+
SafeUtilityError: If arguments have invalid types
|
|
82
|
+
|
|
83
|
+
Examples:
|
|
84
|
+
>>> safe_write_file("output.txt", "Hello World")
|
|
85
|
+
True
|
|
86
|
+
>>> safe_write_file("/invalid/path/file.txt", "content", create_dirs=False)
|
|
87
|
+
False
|
|
88
|
+
"""
|
|
89
|
+
from ..errors.exceptions import SafeUtilityError
|
|
90
|
+
|
|
91
|
+
if filepath is None:
|
|
92
|
+
raise SafeUtilityError("Путь к файлу не может быть None", utility_name="safe_write_file")
|
|
93
|
+
|
|
94
|
+
if not isinstance(filepath, (str, Path)):
|
|
95
|
+
raise SafeUtilityError(f"Путь к файлу должен быть строкой или Path объектом, получен {type(filepath).__name__}",
|
|
96
|
+
utility_name="safe_write_file")
|
|
97
|
+
|
|
98
|
+
if not isinstance(content, str):
|
|
99
|
+
raise SafeUtilityError(f"Содержимое должно быть строкой, получен {type(content).__name__}",
|
|
100
|
+
utility_name="safe_write_file")
|
|
101
|
+
|
|
102
|
+
if not isinstance(encoding, str):
|
|
103
|
+
raise SafeUtilityError(f"Кодировка должна быть строкой, получен {type(encoding).__name__}",
|
|
104
|
+
utility_name="safe_write_file")
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
filepath = Path(filepath)
|
|
108
|
+
|
|
109
|
+
# Create directories if requested
|
|
110
|
+
if create_dirs and filepath.parent != filepath:
|
|
111
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
112
|
+
|
|
113
|
+
with open(filepath, 'w', encoding=encoding) as file:
|
|
114
|
+
file.write(content)
|
|
115
|
+
return True
|
|
116
|
+
except (PermissionError, OSError, UnicodeEncodeError):
|
|
117
|
+
return False
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def safe_file_exists(filepath: Union[str, Path]) -> bool:
|
|
121
|
+
"""
|
|
122
|
+
Safely check if a file exists.
|
|
123
|
+
|
|
124
|
+
Предотвращает ошибки при проверке существования файла.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
filepath: Путь к файлу
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
True если файл существует, False иначе
|
|
131
|
+
|
|
132
|
+
Raises:
|
|
133
|
+
SafeUtilityError: If filepath is None or invalid type
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
>>> safe_file_exists("example.txt")
|
|
137
|
+
True
|
|
138
|
+
>>> safe_file_exists("несуществующий.txt")
|
|
139
|
+
False
|
|
140
|
+
"""
|
|
141
|
+
from ..errors.exceptions import SafeUtilityError
|
|
142
|
+
|
|
143
|
+
if filepath is None:
|
|
144
|
+
raise SafeUtilityError("Путь к файлу не может быть None", utility_name="safe_file_exists")
|
|
145
|
+
|
|
146
|
+
if not isinstance(filepath, (str, Path)):
|
|
147
|
+
raise SafeUtilityError(f"Путь к файлу должен быть строкой или Path объектом, получен {type(filepath).__name__}",
|
|
148
|
+
utility_name="safe_file_exists")
|
|
149
|
+
|
|
150
|
+
try:
|
|
151
|
+
return Path(filepath).exists() and Path(filepath).is_file()
|
|
152
|
+
except (OSError, ValueError):
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def safe_get_file_size(filepath: Union[str, Path], default: int = 0) -> int:
|
|
157
|
+
"""
|
|
158
|
+
Safely get file size in bytes.
|
|
159
|
+
|
|
160
|
+
Предотвращает ошибки при получении размера файла.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
filepath: Путь к файлу
|
|
164
|
+
default: Значение по умолчанию при ошибке
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Размер файла в байтах или значение по умолчанию
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
SafeUtilityError: If filepath is None or invalid type
|
|
171
|
+
|
|
172
|
+
Examples:
|
|
173
|
+
>>> safe_get_file_size("example.txt")
|
|
174
|
+
1024
|
|
175
|
+
>>> safe_get_file_size("несуществующий.txt")
|
|
176
|
+
0
|
|
177
|
+
"""
|
|
178
|
+
from ..errors.exceptions import SafeUtilityError
|
|
179
|
+
|
|
180
|
+
if filepath is None:
|
|
181
|
+
raise SafeUtilityError("Путь к файлу не может быть None", utility_name="safe_get_file_size")
|
|
182
|
+
|
|
183
|
+
if not isinstance(filepath, (str, Path)):
|
|
184
|
+
raise SafeUtilityError(f"Путь к файлу должен быть строкой или Path объектом, получен {type(filepath).__name__}",
|
|
185
|
+
utility_name="safe_get_file_size")
|
|
186
|
+
|
|
187
|
+
try:
|
|
188
|
+
return Path(filepath).stat().st_size
|
|
189
|
+
except (OSError, FileNotFoundError):
|
|
190
|
+
return default
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def safe_list_files(directory: Union[str, Path], pattern: str = "*", default: Optional[List[str]] = None) -> List[str]:
|
|
194
|
+
"""
|
|
195
|
+
Safely list files in a directory.
|
|
196
|
+
|
|
197
|
+
Предотвращает ошибки при чтении содержимого директории.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
directory: Путь к директории
|
|
201
|
+
pattern: Паттерн для фильтрации файлов (например, "*.txt")
|
|
202
|
+
default: Значение по умолчанию при ошибке
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Список имен файлов или значение по умолчанию
|
|
206
|
+
|
|
207
|
+
Raises:
|
|
208
|
+
SafeUtilityError: If directory is None or invalid type
|
|
209
|
+
|
|
210
|
+
Examples:
|
|
211
|
+
>>> safe_list_files(".")
|
|
212
|
+
['file1.txt', 'file2.py']
|
|
213
|
+
>>> safe_list_files("несуществующая_папка")
|
|
214
|
+
[]
|
|
215
|
+
"""
|
|
216
|
+
from ..errors.exceptions import SafeUtilityError
|
|
217
|
+
|
|
218
|
+
if default is None:
|
|
219
|
+
default = []
|
|
220
|
+
|
|
221
|
+
if directory is None:
|
|
222
|
+
raise SafeUtilityError("Путь к директории не может быть None", utility_name="safe_list_files")
|
|
223
|
+
|
|
224
|
+
if not isinstance(directory, (str, Path)):
|
|
225
|
+
raise SafeUtilityError(f"Путь к директории должен быть строкой или Path объектом, получен {type(directory).__name__}",
|
|
226
|
+
utility_name="safe_list_files")
|
|
227
|
+
|
|
228
|
+
if not isinstance(pattern, str):
|
|
229
|
+
raise SafeUtilityError(f"Паттерн должен быть строкой, получен {type(pattern).__name__}",
|
|
230
|
+
utility_name="safe_list_files")
|
|
231
|
+
|
|
232
|
+
try:
|
|
233
|
+
directory_path = Path(directory)
|
|
234
|
+
if not directory_path.exists() or not directory_path.is_dir():
|
|
235
|
+
return default
|
|
236
|
+
|
|
237
|
+
files = [f.name for f in directory_path.glob(pattern) if f.is_file()]
|
|
238
|
+
return sorted(files)
|
|
239
|
+
except (OSError, ValueError):
|
|
240
|
+
return default
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Safe string operations for beginners.
|
|
3
|
+
|
|
4
|
+
This module will contain safe string handling utilities.
|
|
5
|
+
Implementation will be completed in task 7.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def safe_string_operations():
|
|
10
|
+
"""
|
|
11
|
+
Placeholder for safe string operations.
|
|
12
|
+
|
|
13
|
+
Implementation will be completed in task 7.
|
|
14
|
+
"""
|
|
15
|
+
pass
|
fishertools/utils.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Утилиты для работы с файлами, данными и другими частыми задачами
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def read_json(filepath: str) -> Dict[str, Any]:
|
|
13
|
+
"""Читает JSON файл и возвращает словарь"""
|
|
14
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
15
|
+
return json.load(f)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def write_json(data: Dict[str, Any], filepath: str, indent: int = 2) -> None:
|
|
19
|
+
"""Записывает данные в JSON файл"""
|
|
20
|
+
with open(filepath, 'w', encoding='utf-8') as f:
|
|
21
|
+
json.dump(data, f, ensure_ascii=False, indent=indent)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def ensure_dir(path: str) -> None:
|
|
25
|
+
"""Создает директорию если она не существует"""
|
|
26
|
+
Path(path).mkdir(parents=True, exist_ok=True)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_file_size(filepath: str) -> int:
|
|
30
|
+
"""Возвращает размер файла в байтах"""
|
|
31
|
+
return os.path.getsize(filepath)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def list_files(directory: str, extension: Optional[str] = None) -> List[str]:
|
|
35
|
+
"""Возвращает список файлов в директории с опциональной фильтрацией по расширению"""
|
|
36
|
+
path = Path(directory)
|
|
37
|
+
if extension:
|
|
38
|
+
pattern = f"*.{extension.lstrip('.')}"
|
|
39
|
+
return [str(f) for f in path.glob(pattern)]
|
|
40
|
+
return [str(f) for f in path.iterdir() if f.is_file()]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def timestamp() -> str:
|
|
44
|
+
"""Возвращает текущую временную метку в читаемом формате"""
|
|
45
|
+
return time.strftime("%Y-%m-%d %H:%M:%S")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def flatten_dict(d: Dict[str, Any], parent_key: str = '', sep: str = '.') -> Dict[str, Any]:
|
|
49
|
+
"""Превращает вложенный словарь в плоский"""
|
|
50
|
+
items = []
|
|
51
|
+
for k, v in d.items():
|
|
52
|
+
new_key = f"{parent_key}{sep}{k}" if parent_key else k
|
|
53
|
+
if isinstance(v, dict):
|
|
54
|
+
items.extend(flatten_dict(v, new_key, sep=sep).items())
|
|
55
|
+
else:
|
|
56
|
+
items.append((new_key, v))
|
|
57
|
+
return dict(items)
|