potato-util 0.0.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.
- potato_util/__init__.py +6 -0
- potato_util/__version__.py +1 -0
- potato_util/_base.py +117 -0
- potato_util/constants/__init__.py +5 -0
- potato_util/constants/_base.py +5 -0
- potato_util/constants/_enum.py +31 -0
- potato_util/constants/_regex.py +23 -0
- potato_util/dt.py +240 -0
- potato_util/http/__init__.py +12 -0
- potato_util/http/_async.py +42 -0
- potato_util/http/_base.py +46 -0
- potato_util/http/_sync.py +45 -0
- potato_util/http/fastapi.py +26 -0
- potato_util/io/__init__.py +11 -0
- potato_util/io/_async.py +274 -0
- potato_util/io/_sync.py +264 -0
- potato_util/sanitizer.py +85 -0
- potato_util/secure.py +90 -0
- potato_util/validator.py +150 -0
- potato_util-0.0.1.dist-info/METADATA +287 -0
- potato_util-0.0.1.dist-info/RECORD +24 -0
- potato_util-0.0.1.dist-info/WHEEL +5 -0
- potato_util-0.0.1.dist-info/licenses/LICENSE.txt +21 -0
- potato_util-0.0.1.dist-info/top_level.txt +1 -0
potato_util/io/_async.py
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import errno
|
|
2
|
+
import hashlib
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import aioshutil
|
|
6
|
+
import aiofiles.os
|
|
7
|
+
from pydantic import validate_call
|
|
8
|
+
|
|
9
|
+
from ..constants import WarnEnum, HashAlgoEnum, MAX_PATH_LENGTH
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@validate_call
|
|
16
|
+
async def async_create_dir(
|
|
17
|
+
create_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
18
|
+
) -> None:
|
|
19
|
+
"""Asynchronous create directory if `create_dir` doesn't exist.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
create_dir (str, required): Create directory path.
|
|
23
|
+
warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
24
|
+
Defaults to 'DEBUG'.
|
|
25
|
+
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: If `create_dir` argument length is out of range.
|
|
28
|
+
OSError : When warning mode is set to ERROR and directory already exists.
|
|
29
|
+
OSError : If failed to create directory.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
create_dir = create_dir.strip()
|
|
33
|
+
if (len(create_dir) < 1) or (MAX_PATH_LENGTH < len(create_dir)):
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"`create_dir` argument length {len(create_dir)} is out of range, "
|
|
36
|
+
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if not await aiofiles.os.path.isdir(create_dir):
|
|
40
|
+
try:
|
|
41
|
+
_message = f"Creating '{create_dir}' directory..."
|
|
42
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
43
|
+
logger.info(_message)
|
|
44
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
45
|
+
logger.debug(_message)
|
|
46
|
+
|
|
47
|
+
await aiofiles.os.makedirs(create_dir)
|
|
48
|
+
except OSError as err:
|
|
49
|
+
if (err.errno == errno.EEXIST) and (warn_mode == WarnEnum.DEBUG):
|
|
50
|
+
logger.debug(f"'{create_dir}' directory already exists!")
|
|
51
|
+
else:
|
|
52
|
+
logger.error(f"Failed to create '{create_dir}' directory!")
|
|
53
|
+
raise
|
|
54
|
+
|
|
55
|
+
_message = f"Successfully created '{create_dir}' directory."
|
|
56
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
57
|
+
logger.info(_message)
|
|
58
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
59
|
+
logger.debug(_message)
|
|
60
|
+
|
|
61
|
+
elif warn_mode == WarnEnum.ERROR:
|
|
62
|
+
raise OSError(errno.EEXIST, f"'{create_dir}' directory already exists!")
|
|
63
|
+
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@validate_call
|
|
68
|
+
async def async_remove_dir(
|
|
69
|
+
remove_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
70
|
+
) -> None:
|
|
71
|
+
"""Asynchronous remove directory if `remove_dir` exists.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
remove_dir (str, required): Remove directory path.
|
|
75
|
+
warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
76
|
+
Defaults to 'DEBUG'.
|
|
77
|
+
|
|
78
|
+
Raises:
|
|
79
|
+
ValueError: If `remove_dir` argument length is out of range.
|
|
80
|
+
OSError : When warning mode is set to ERROR and directory doesn't exist.
|
|
81
|
+
OSError : If failed to remove directory.
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
remove_dir = remove_dir.strip()
|
|
85
|
+
if (len(remove_dir) < 1) or (MAX_PATH_LENGTH < len(remove_dir)):
|
|
86
|
+
raise ValueError(
|
|
87
|
+
f"`remove_dir` argument length {len(remove_dir)} is out of range, "
|
|
88
|
+
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if await aiofiles.os.path.isdir(remove_dir):
|
|
92
|
+
try:
|
|
93
|
+
_message = f"Removing '{remove_dir}' directory..."
|
|
94
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
95
|
+
logger.info(_message)
|
|
96
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
97
|
+
logger.debug(_message)
|
|
98
|
+
|
|
99
|
+
await aioshutil.rmtree(remove_dir)
|
|
100
|
+
except OSError as err:
|
|
101
|
+
if (err.errno == errno.ENOENT) and (warn_mode == WarnEnum.DEBUG):
|
|
102
|
+
logger.debug(f"'{remove_dir}' directory doesn't exist!")
|
|
103
|
+
else:
|
|
104
|
+
logger.error(f"Failed to remove '{remove_dir}' directory!")
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
_message = f"Successfully removed '{remove_dir}' directory."
|
|
108
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
109
|
+
logger.info(_message)
|
|
110
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
111
|
+
logger.debug(_message)
|
|
112
|
+
|
|
113
|
+
elif warn_mode == WarnEnum.ERROR:
|
|
114
|
+
raise OSError(errno.ENOENT, f"'{remove_dir}' directory doesn't exist!")
|
|
115
|
+
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@validate_call
|
|
120
|
+
async def async_remove_dirs(
|
|
121
|
+
remove_dirs: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
122
|
+
) -> None:
|
|
123
|
+
"""Asynchronous remove directories if `remove_dirs` exists.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
remove_dirs (List[str], required): Remove directories paths as list.
|
|
127
|
+
warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
128
|
+
Defaults to 'DEBUG'.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
for _remove_dir in remove_dirs:
|
|
132
|
+
await async_remove_dir(remove_dir=_remove_dir, warn_mode=warn_mode)
|
|
133
|
+
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@validate_call
|
|
138
|
+
async def async_remove_file(
|
|
139
|
+
file_path: str, warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
140
|
+
) -> None:
|
|
141
|
+
"""Asynchronous remove file if `file_path` exists.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
file_path (str, required): Remove file path.
|
|
145
|
+
warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
146
|
+
Defaults to 'DEBUG'.
|
|
147
|
+
|
|
148
|
+
Raises:
|
|
149
|
+
ValueError: If `file_path` argument length is out of range.
|
|
150
|
+
OSError : When warning mode is set to ERROR and file doesn't exist.
|
|
151
|
+
OSError : If failed to remove file.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
file_path = file_path.strip()
|
|
155
|
+
if (len(file_path) < 1) or (MAX_PATH_LENGTH < len(file_path)):
|
|
156
|
+
raise ValueError(
|
|
157
|
+
f"`file_path` argument length {len(file_path)} is out of range, "
|
|
158
|
+
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if await aiofiles.os.path.isfile(file_path):
|
|
162
|
+
try:
|
|
163
|
+
_message = f"Removing '{file_path}' file..."
|
|
164
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
165
|
+
logger.info(_message)
|
|
166
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
167
|
+
logger.debug(_message)
|
|
168
|
+
|
|
169
|
+
await aiofiles.os.remove(file_path)
|
|
170
|
+
except OSError as err:
|
|
171
|
+
if (err.errno == errno.ENOENT) and (warn_mode == WarnEnum.DEBUG):
|
|
172
|
+
logger.debug(f"'{file_path}' file doesn't exist!")
|
|
173
|
+
else:
|
|
174
|
+
logger.error(f"Failed to remove '{file_path}' file!")
|
|
175
|
+
raise
|
|
176
|
+
|
|
177
|
+
_message = f"Successfully removed '{file_path}' file."
|
|
178
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
179
|
+
logger.info(_message)
|
|
180
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
181
|
+
logger.debug(_message)
|
|
182
|
+
|
|
183
|
+
elif warn_mode == WarnEnum.ERROR:
|
|
184
|
+
raise OSError(errno.ENOENT, f"'{file_path}' file doesn't exist!")
|
|
185
|
+
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@validate_call
|
|
190
|
+
async def async_remove_files(
|
|
191
|
+
file_paths: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
192
|
+
) -> None:
|
|
193
|
+
"""Asynchronous remove files if `file_paths` exists.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
file_paths (List[str], required): Remove file paths as list.
|
|
197
|
+
warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
198
|
+
Defaults to 'DEBUG'.
|
|
199
|
+
"""
|
|
200
|
+
|
|
201
|
+
for _file_path in file_paths:
|
|
202
|
+
await async_remove_file(file_path=_file_path, warn_mode=warn_mode)
|
|
203
|
+
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@validate_call
|
|
208
|
+
async def async_get_file_checksum(
|
|
209
|
+
file_path: str,
|
|
210
|
+
hash_method: HashAlgoEnum = HashAlgoEnum.md5,
|
|
211
|
+
chunk_size: int = 4096,
|
|
212
|
+
warn_mode: WarnEnum = WarnEnum.DEBUG,
|
|
213
|
+
) -> str | None:
|
|
214
|
+
"""Asynchronous get file checksum.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
file_path (str , required): Target file path.
|
|
218
|
+
hash_method (HashAlgoEnum, optional): Hash method. Defaults to `HashAlgoEnum.md5`.
|
|
219
|
+
chunk_size (int , optional): Chunk size. Defaults to 4096.
|
|
220
|
+
warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
221
|
+
Defaults to 'DEBUG'.
|
|
222
|
+
|
|
223
|
+
Raises:
|
|
224
|
+
ValueError: If `file_path` argument length is out of range.
|
|
225
|
+
ValueError: If `chunk_size` argument value is invalid.
|
|
226
|
+
OSError : When warning mode is set to ERROR and file doesn't exist.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
str | None: File checksum or None if file doesn't exist.
|
|
230
|
+
"""
|
|
231
|
+
|
|
232
|
+
file_path = file_path.strip()
|
|
233
|
+
if (len(file_path) < 1) or (MAX_PATH_LENGTH < len(file_path)):
|
|
234
|
+
raise ValueError(
|
|
235
|
+
f"`file_path` argument length {len(file_path)} is out of range, "
|
|
236
|
+
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
if chunk_size < 10:
|
|
240
|
+
raise ValueError(
|
|
241
|
+
f"`chunk_size` argument value {chunk_size} is invalid, must be greater than 10!"
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
_file_checksum: str | None = None
|
|
245
|
+
if await aiofiles.os.path.isfile(file_path):
|
|
246
|
+
_file_hash = hashlib.new(hash_method.value)
|
|
247
|
+
async with aiofiles.open(file_path, "rb") as _file:
|
|
248
|
+
while True:
|
|
249
|
+
_file_chunk = await _file.read(chunk_size)
|
|
250
|
+
if not _file_chunk:
|
|
251
|
+
break
|
|
252
|
+
_file_hash.update(_file_chunk)
|
|
253
|
+
|
|
254
|
+
_file_checksum = _file_hash.hexdigest()
|
|
255
|
+
else:
|
|
256
|
+
_message = f"'{file_path}' file doesn't exist!"
|
|
257
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
258
|
+
logger.warning(_message)
|
|
259
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
260
|
+
logger.debug(_message)
|
|
261
|
+
elif warn_mode == WarnEnum.ERROR:
|
|
262
|
+
raise OSError(errno.ENOENT, _message)
|
|
263
|
+
|
|
264
|
+
return _file_checksum
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
__all__ = [
|
|
268
|
+
"async_create_dir",
|
|
269
|
+
"async_remove_dir",
|
|
270
|
+
"async_remove_dirs",
|
|
271
|
+
"async_remove_file",
|
|
272
|
+
"async_remove_files",
|
|
273
|
+
"async_get_file_checksum",
|
|
274
|
+
]
|
potato_util/io/_sync.py
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import errno
|
|
3
|
+
import shutil
|
|
4
|
+
import hashlib
|
|
5
|
+
import logging
|
|
6
|
+
|
|
7
|
+
from pydantic import validate_call
|
|
8
|
+
|
|
9
|
+
from ..constants import WarnEnum, HashAlgoEnum, MAX_PATH_LENGTH
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@validate_call
|
|
16
|
+
def create_dir(create_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
|
|
17
|
+
"""Create directory if `create_dir` doesn't exist.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
create_dir (str, required): Create directory path.
|
|
21
|
+
warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
22
|
+
Defaults to 'DEBUG'.
|
|
23
|
+
|
|
24
|
+
Raises:
|
|
25
|
+
ValueError: If `create_dir` argument length is out of range.
|
|
26
|
+
OSError : When warning mode is set to ERROR and directory already exists.
|
|
27
|
+
OSError : If failed to create directory.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
create_dir = create_dir.strip()
|
|
31
|
+
if (len(create_dir) < 1) or (MAX_PATH_LENGTH < len(create_dir)):
|
|
32
|
+
raise ValueError(
|
|
33
|
+
f"`create_dir` argument length {len(create_dir)} is out of range, "
|
|
34
|
+
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
if not os.path.isdir(create_dir):
|
|
38
|
+
try:
|
|
39
|
+
_message = f"Creating '{create_dir}' directory..."
|
|
40
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
41
|
+
logger.info(_message)
|
|
42
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
43
|
+
logger.debug(_message)
|
|
44
|
+
|
|
45
|
+
os.makedirs(create_dir)
|
|
46
|
+
except OSError as err:
|
|
47
|
+
if (err.errno == errno.EEXIST) and (warn_mode == WarnEnum.DEBUG):
|
|
48
|
+
logger.debug(f"'{create_dir}' directory already exists!")
|
|
49
|
+
else:
|
|
50
|
+
logger.error(f"Failed to create '{create_dir}' directory!")
|
|
51
|
+
raise
|
|
52
|
+
|
|
53
|
+
_message = f"Successfully created '{create_dir}' directory."
|
|
54
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
55
|
+
logger.info(_message)
|
|
56
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
57
|
+
logger.debug(_message)
|
|
58
|
+
|
|
59
|
+
elif warn_mode == WarnEnum.ERROR:
|
|
60
|
+
raise OSError(errno.EEXIST, f"'{create_dir}' directory already exists!")
|
|
61
|
+
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@validate_call
|
|
66
|
+
def remove_dir(remove_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
|
|
67
|
+
"""Remove directory if `remove_dir` exists.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
remove_dir (str, required): Remove directory path.
|
|
71
|
+
warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
72
|
+
Defaults to 'DEBUG'.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
ValueError: If `remove_dir` argument length is out of range.
|
|
76
|
+
OSError : When warning mode is set to ERROR and directory doesn't exist.
|
|
77
|
+
OSError : If failed to remove directory.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
remove_dir = remove_dir.strip()
|
|
81
|
+
if (len(remove_dir) < 1) or (MAX_PATH_LENGTH < len(remove_dir)):
|
|
82
|
+
raise ValueError(
|
|
83
|
+
f"`remove_dir` argument length {len(remove_dir)} is out of range, "
|
|
84
|
+
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if os.path.isdir(remove_dir):
|
|
88
|
+
try:
|
|
89
|
+
_message = f"Removing '{remove_dir}' directory..."
|
|
90
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
91
|
+
logger.info(_message)
|
|
92
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
93
|
+
logger.debug(_message)
|
|
94
|
+
|
|
95
|
+
shutil.rmtree(remove_dir)
|
|
96
|
+
except OSError as err:
|
|
97
|
+
if (err.errno == errno.ENOENT) and (warn_mode == WarnEnum.DEBUG):
|
|
98
|
+
logger.debug(f"'{remove_dir}' directory doesn't exist!")
|
|
99
|
+
else:
|
|
100
|
+
logger.error(f"Failed to remove '{remove_dir}' directory!")
|
|
101
|
+
raise
|
|
102
|
+
|
|
103
|
+
_message = f"Successfully removed '{remove_dir}' directory."
|
|
104
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
105
|
+
logger.info(_message)
|
|
106
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
107
|
+
logger.debug(_message)
|
|
108
|
+
|
|
109
|
+
elif warn_mode == WarnEnum.ERROR:
|
|
110
|
+
raise OSError(errno.ENOENT, f"'{remove_dir}' directory doesn't exist!")
|
|
111
|
+
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@validate_call
|
|
116
|
+
def remove_dirs(remove_dirs: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
|
|
117
|
+
"""Remove directories if `remove_dirs` exist.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
remove_dirs (List[str], required): Remove directory paths as list.
|
|
121
|
+
warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
122
|
+
Defaults to 'DEBUG'.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
for _remove_dir in remove_dirs:
|
|
126
|
+
remove_dir(remove_dir=_remove_dir, warn_mode=warn_mode)
|
|
127
|
+
|
|
128
|
+
return
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@validate_call
|
|
132
|
+
def remove_file(file_path: str, warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
|
|
133
|
+
"""Remove file if `file_path` exists.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
file_path (str, required): Remove file path.
|
|
137
|
+
warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
138
|
+
Defaults to 'DEBUG'.
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
ValueError: If `file_path` argument length is out of range.
|
|
142
|
+
OSError : When warning mode is set to ERROR and file doesn't exist.
|
|
143
|
+
OSError : If failed to remove file.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
file_path = file_path.strip()
|
|
147
|
+
if (len(file_path) < 1) or (MAX_PATH_LENGTH < len(file_path)):
|
|
148
|
+
raise ValueError(
|
|
149
|
+
f"`file_path` argument length {len(file_path)} is out of range, "
|
|
150
|
+
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if os.path.isfile(file_path):
|
|
154
|
+
try:
|
|
155
|
+
_message = f"Removing '{file_path}' file..."
|
|
156
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
157
|
+
logger.info(_message)
|
|
158
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
159
|
+
logger.debug(_message)
|
|
160
|
+
|
|
161
|
+
os.remove(file_path)
|
|
162
|
+
except OSError as err:
|
|
163
|
+
if (err.errno == errno.ENOENT) and (warn_mode == WarnEnum.DEBUG):
|
|
164
|
+
logger.debug(f"'{file_path}' file doesn't exist!")
|
|
165
|
+
else:
|
|
166
|
+
logger.error(f"Failed to remove '{file_path}' file!")
|
|
167
|
+
raise
|
|
168
|
+
|
|
169
|
+
_message = f"Successfully removed '{file_path}' file."
|
|
170
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
171
|
+
logger.info(_message)
|
|
172
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
173
|
+
logger.debug(_message)
|
|
174
|
+
|
|
175
|
+
elif warn_mode == WarnEnum.ERROR:
|
|
176
|
+
raise OSError(errno.ENOENT, f"'{file_path}' file doesn't exist!")
|
|
177
|
+
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@validate_call
|
|
182
|
+
def remove_files(file_paths: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG) -> None:
|
|
183
|
+
"""Remove files if `file_paths` exist.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
file_paths (List[str], required): Remove file paths as list.
|
|
187
|
+
warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
188
|
+
Defaults to 'DEBUG'.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
for _file_path in file_paths:
|
|
192
|
+
remove_file(file_path=_file_path, warn_mode=warn_mode)
|
|
193
|
+
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@validate_call
|
|
198
|
+
def get_file_checksum(
|
|
199
|
+
file_path: str,
|
|
200
|
+
hash_method: HashAlgoEnum = HashAlgoEnum.md5,
|
|
201
|
+
chunk_size: int = 4096,
|
|
202
|
+
warn_mode: WarnEnum = WarnEnum.DEBUG,
|
|
203
|
+
) -> str | None:
|
|
204
|
+
"""Get file checksum.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
file_path (str , required): Target file path.
|
|
208
|
+
hash_method (HashAlgoEnum, optional): Hash method. Defaults to `HashAlgoEnum.md5`.
|
|
209
|
+
chunk_size (int , optional): Chunk size. Defaults to 4096.
|
|
210
|
+
warn_mode (str , optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
211
|
+
Defaults to 'DEBUG'.
|
|
212
|
+
|
|
213
|
+
Raises:
|
|
214
|
+
ValueError: If `file_path` argument length is out of range.
|
|
215
|
+
ValueError: If `chunk_size` argument value is invalid.
|
|
216
|
+
OSError : When warning mode is set to ERROR and file doesn't exist.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
str | None: File checksum or None if file doesn't exist.
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
file_path = file_path.strip()
|
|
223
|
+
if (len(file_path) < 1) or (MAX_PATH_LENGTH < len(file_path)):
|
|
224
|
+
raise ValueError(
|
|
225
|
+
f"`file_path` argument length {len(file_path)} is out of range, "
|
|
226
|
+
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if chunk_size < 10:
|
|
230
|
+
raise ValueError(
|
|
231
|
+
f"`chunk_size` argument value {chunk_size} is invalid, must be greater than 10!"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
_file_checksum: str | None = None
|
|
235
|
+
if os.path.isfile(file_path):
|
|
236
|
+
_file_hash = hashlib.new(hash_method.value)
|
|
237
|
+
with open(file_path, "rb") as _file:
|
|
238
|
+
while True:
|
|
239
|
+
_file_chunk = _file.read(chunk_size)
|
|
240
|
+
if not _file_chunk:
|
|
241
|
+
break
|
|
242
|
+
_file_hash.update(_file_chunk)
|
|
243
|
+
|
|
244
|
+
_file_checksum = _file_hash.hexdigest()
|
|
245
|
+
else:
|
|
246
|
+
_message = f"'{file_path}' file doesn't exist!"
|
|
247
|
+
if warn_mode == WarnEnum.ALWAYS:
|
|
248
|
+
logger.warning(_message)
|
|
249
|
+
elif warn_mode == WarnEnum.DEBUG:
|
|
250
|
+
logger.debug(_message)
|
|
251
|
+
elif warn_mode == WarnEnum.ERROR:
|
|
252
|
+
raise OSError(errno.ENOENT, _message)
|
|
253
|
+
|
|
254
|
+
return _file_checksum
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
__all__ = [
|
|
258
|
+
"create_dir",
|
|
259
|
+
"remove_dir",
|
|
260
|
+
"remove_dirs",
|
|
261
|
+
"remove_file",
|
|
262
|
+
"remove_files",
|
|
263
|
+
"get_file_checksum",
|
|
264
|
+
]
|
potato_util/sanitizer.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import re
|
|
2
|
+
import html
|
|
3
|
+
from urllib.parse import quote
|
|
4
|
+
|
|
5
|
+
from pydantic import validate_call, AnyHttpUrl
|
|
6
|
+
|
|
7
|
+
from .constants import (
|
|
8
|
+
SPECIAL_CHARS_BASE_REGEX,
|
|
9
|
+
SPECIAL_CHARS_LOW_REGEX,
|
|
10
|
+
SPECIAL_CHARS_MEDIUM_REGEX,
|
|
11
|
+
SPECIAL_CHARS_HIGH_REGEX,
|
|
12
|
+
SPECIAL_CHARS_STRICT_REGEX,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@validate_call
|
|
17
|
+
def escape_html(val: str) -> str:
|
|
18
|
+
"""Escape HTML characters.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
val (str, required): String to escape.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
str: Escaped string.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
val = val.strip()
|
|
28
|
+
_escaped = html.escape(val)
|
|
29
|
+
return _escaped
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@validate_call
|
|
33
|
+
def escape_url(val: AnyHttpUrl) -> str:
|
|
34
|
+
"""Escape URL characters.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
val (AnyHttpUrl, required): String to escape.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
str: Escaped string.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
_escaped = quote(str(val))
|
|
44
|
+
return _escaped
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@validate_call
|
|
48
|
+
def sanitize_special_chars(val: str, mode: str = "LOW") -> str:
|
|
49
|
+
"""Sanitize special characters.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
val (str, required): String to sanitize.
|
|
53
|
+
mode (str, optional): Sanitization mode. Defaults to "LOW".
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
ValueError: If `mode` is unsupported.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
str: Sanitized string.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
_pattern = r""
|
|
63
|
+
mode = mode.upper().strip()
|
|
64
|
+
if (mode == "BASE") or (mode == "HTML"):
|
|
65
|
+
_pattern = SPECIAL_CHARS_BASE_REGEX
|
|
66
|
+
elif mode == "LOW":
|
|
67
|
+
_pattern = SPECIAL_CHARS_LOW_REGEX
|
|
68
|
+
elif mode == "MEDIUM":
|
|
69
|
+
_pattern = SPECIAL_CHARS_MEDIUM_REGEX
|
|
70
|
+
elif (mode == "HIGH") or (mode == "SCRIPT") or (mode == "SQL"):
|
|
71
|
+
_pattern = SPECIAL_CHARS_HIGH_REGEX
|
|
72
|
+
elif mode == "STRICT":
|
|
73
|
+
_pattern = SPECIAL_CHARS_STRICT_REGEX
|
|
74
|
+
else:
|
|
75
|
+
raise ValueError(f"Unsupported mode: {mode}")
|
|
76
|
+
|
|
77
|
+
_sanitized = re.sub(pattern=_pattern, repl="", string=val)
|
|
78
|
+
return _sanitized
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
__all__ = [
|
|
82
|
+
"escape_html",
|
|
83
|
+
"escape_url",
|
|
84
|
+
"sanitize_special_chars",
|
|
85
|
+
]
|