potato-util 0.0.2__py3-none-any.whl → 0.0.3__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 +3 -5
- potato_util/__version__.py +1 -1
- potato_util/_base.py +0 -60
- potato_util/dt.py +128 -104
- potato_util/generator.py +65 -0
- potato_util/http/_base.py +3 -1
- potato_util/http/fastapi.py +1 -1
- potato_util/io/_async.py +36 -24
- potato_util/io/_sync.py +40 -24
- potato_util/sanitizer.py +12 -3
- potato_util/secure.py +8 -61
- potato_util/validator.py +12 -6
- {potato_util-0.0.2.dist-info → potato_util-0.0.3.dist-info}/METADATA +191 -18
- potato_util-0.0.3.dist-info/RECORD +25 -0
- potato_util-0.0.2.dist-info/RECORD +0 -24
- {potato_util-0.0.2.dist-info → potato_util-0.0.3.dist-info}/WHEEL +0 -0
- {potato_util-0.0.2.dist-info → potato_util-0.0.3.dist-info}/licenses/LICENSE.txt +0 -0
- {potato_util-0.0.2.dist-info → potato_util-0.0.3.dist-info}/top_level.txt +0 -0
potato_util/__init__.py
CHANGED
potato_util/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.0.
|
|
1
|
+
__version__ = "0.0.3"
|
potato_util/_base.py
CHANGED
|
@@ -51,67 +51,7 @@ def camel_to_snake(val: str) -> str:
|
|
|
51
51
|
return val
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
@validate_call
|
|
55
|
-
def clean_obj_dict(obj_dict: dict, cls_name: str) -> dict:
|
|
56
|
-
"""Clean class name from object.__dict__ for str(object).
|
|
57
|
-
|
|
58
|
-
Args:
|
|
59
|
-
obj_dict (dict, required): Object dictionary by object.__dict__.
|
|
60
|
-
cls_name (str , required): Class name by cls.__name__.
|
|
61
|
-
|
|
62
|
-
Returns:
|
|
63
|
-
dict: Clean object dictionary.
|
|
64
|
-
"""
|
|
65
|
-
|
|
66
|
-
try:
|
|
67
|
-
if not obj_dict:
|
|
68
|
-
raise ValueError("'obj_dict' argument value is empty!")
|
|
69
|
-
|
|
70
|
-
if not cls_name:
|
|
71
|
-
raise ValueError("'cls_name' argument value is empty!")
|
|
72
|
-
except ValueError as err:
|
|
73
|
-
logger.error(err)
|
|
74
|
-
raise
|
|
75
|
-
|
|
76
|
-
_self_dict = obj_dict.copy()
|
|
77
|
-
for _key in _self_dict.copy():
|
|
78
|
-
_class_prefix = f"_{cls_name}__"
|
|
79
|
-
if _key.startswith(_class_prefix):
|
|
80
|
-
_new_key = _key.replace(_class_prefix, "")
|
|
81
|
-
_self_dict[_new_key] = _self_dict.pop(_key)
|
|
82
|
-
return _self_dict
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@validate_call(config={"arbitrary_types_allowed": True})
|
|
86
|
-
def obj_to_repr(obj: object) -> str:
|
|
87
|
-
"""Modifying object default repr() to custom info.
|
|
88
|
-
|
|
89
|
-
Args:
|
|
90
|
-
obj (object, required): Any python object.
|
|
91
|
-
|
|
92
|
-
Returns:
|
|
93
|
-
str: String for repr() method.
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
try:
|
|
97
|
-
if not obj:
|
|
98
|
-
raise ValueError("'obj' argument value is empty!")
|
|
99
|
-
except ValueError as err:
|
|
100
|
-
logger.error(err)
|
|
101
|
-
raise
|
|
102
|
-
|
|
103
|
-
_self_repr = (
|
|
104
|
-
f"<{obj.__class__.__module__}.{obj.__class__.__name__} object at {hex(id(obj))}: "
|
|
105
|
-
+ "{"
|
|
106
|
-
+ f"{str(dir(obj)).replace('[', '').replace(']', '')}"
|
|
107
|
-
+ "}>"
|
|
108
|
-
)
|
|
109
|
-
return _self_repr
|
|
110
|
-
|
|
111
|
-
|
|
112
54
|
__all__ = [
|
|
113
55
|
"deep_merge",
|
|
114
56
|
"camel_to_snake",
|
|
115
|
-
"clean_obj_dict",
|
|
116
|
-
"obj_to_repr",
|
|
117
57
|
]
|
potato_util/dt.py
CHANGED
|
@@ -11,13 +11,55 @@ from .constants import WarnEnum, TSUnitEnum
|
|
|
11
11
|
logger = logging.getLogger(__name__)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
def now_local_dt() -> datetime:
|
|
15
|
+
"""Get current datetime in local timezone with tzinfo.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
datetime: Current datetime in local timezone with tzinfo.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
_local_dt = datetime.now().astimezone()
|
|
22
|
+
return _local_dt
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def now_utc_dt() -> datetime:
|
|
26
|
+
"""Get current datetime in UTC timezone with tzinfo.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
datetime: Current datetime in UTC timezone with tzinfo.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
_utc_dt = datetime.now(tz=timezone.utc)
|
|
33
|
+
return _utc_dt
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@validate_call(config={"arbitrary_types_allowed": True})
|
|
37
|
+
def now_dt(tz: ZoneInfo | tzinfo | str | None = None) -> datetime:
|
|
38
|
+
"""Get current datetime in specified timezone with tzinfo.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
tz (ZoneInfo | tzinfo | str | None, optional): Timezone info. Defaults to None (UTC timezone).
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
datetime: Current datetime in specified timezone with tzinfo.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
_dt = now_utc_dt()
|
|
48
|
+
if tz:
|
|
49
|
+
_dt = convert_tz(dt=_dt, tz=tz)
|
|
50
|
+
return _dt
|
|
51
|
+
|
|
52
|
+
|
|
14
53
|
@validate_call(config={"arbitrary_types_allowed": True})
|
|
15
|
-
def
|
|
54
|
+
def replace_tz(dt: datetime, tz: ZoneInfo | tzinfo | str) -> datetime:
|
|
16
55
|
"""Add or replace timezone info to datetime object.
|
|
17
56
|
|
|
18
57
|
Args:
|
|
19
|
-
dt (datetime
|
|
20
|
-
tz (
|
|
58
|
+
dt (datetime , required): Datetime object.
|
|
59
|
+
tz (ZoneInfo | tzinfo | str, required): Timezone info.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ZoneInfoNotFoundError: If `tz` argument value is invalid.
|
|
21
63
|
|
|
22
64
|
Returns:
|
|
23
65
|
datetime: Datetime object with timezone info.
|
|
@@ -30,30 +72,29 @@ def add_tzinfo(dt: datetime, tz: ZoneInfo | tzinfo | str) -> datetime:
|
|
|
30
72
|
return dt
|
|
31
73
|
|
|
32
74
|
|
|
33
|
-
@validate_call
|
|
34
|
-
def
|
|
35
|
-
dt: datetime,
|
|
36
|
-
|
|
37
|
-
|
|
75
|
+
@validate_call(config={"arbitrary_types_allowed": True})
|
|
76
|
+
def convert_tz(
|
|
77
|
+
dt: datetime,
|
|
78
|
+
tz: ZoneInfo | tzinfo | str,
|
|
79
|
+
warn_mode: WarnEnum | str = WarnEnum.ALWAYS,
|
|
80
|
+
) -> datetime:
|
|
81
|
+
"""Convert datetime object to another timezone.
|
|
38
82
|
|
|
39
83
|
Args:
|
|
40
|
-
dt (datetime, required): Datetime object.
|
|
41
|
-
|
|
42
|
-
warn_mode (WarnEnum, optional): Warning mode. Defaults to WarnEnum.
|
|
84
|
+
dt (datetime , required): Datetime object to convert.
|
|
85
|
+
tz (ZoneInfo | tzinfo | str, required): Timezone info to convert.
|
|
86
|
+
warn_mode (WarnEnum | str , optional): Warning mode. Defaults to WarnEnum.ALWAYS.
|
|
43
87
|
|
|
44
88
|
Raises:
|
|
45
|
-
ValueError: If `sep` argument length is greater than 8.
|
|
46
89
|
ValueError: If `dt` argument doesn't have any timezone info and `warn_mode` is set to WarnEnum.ERROR.
|
|
90
|
+
ValueError: If `warn_mode` argument value is invalid.
|
|
47
91
|
|
|
48
92
|
Returns:
|
|
49
|
-
|
|
93
|
+
datetime: Datetime object which has been converted to another timezone.
|
|
50
94
|
"""
|
|
51
95
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
raise ValueError(
|
|
55
|
-
f"`sep` argument length '{len(sep)}' is too long, must be less than or equal to 8!"
|
|
56
|
-
)
|
|
96
|
+
if isinstance(warn_mode, str):
|
|
97
|
+
warn_mode = WarnEnum(warn_mode.strip().upper())
|
|
57
98
|
|
|
58
99
|
if not dt.tzinfo:
|
|
59
100
|
_message = "Not found any timezone info in `dt` argument, assuming it's UTC timezone..."
|
|
@@ -66,30 +107,44 @@ def datetime_to_iso(
|
|
|
66
107
|
logger.error(_message)
|
|
67
108
|
raise ValueError(_message)
|
|
68
109
|
|
|
69
|
-
dt =
|
|
110
|
+
dt = replace_tz(dt=dt, tz="UTC")
|
|
70
111
|
|
|
71
|
-
|
|
72
|
-
|
|
112
|
+
if isinstance(tz, str):
|
|
113
|
+
tz = ZoneInfo(tz)
|
|
73
114
|
|
|
115
|
+
dt = dt.astimezone(tz=tz)
|
|
116
|
+
return dt
|
|
74
117
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
118
|
+
|
|
119
|
+
@validate_call
|
|
120
|
+
def dt_to_iso(
|
|
121
|
+
dt: datetime, sep: str = "T", warn_mode: WarnEnum | str = WarnEnum.IGNORE
|
|
122
|
+
) -> str:
|
|
123
|
+
"""Convert datetime object to ISO 8601 format.
|
|
80
124
|
|
|
81
125
|
Args:
|
|
82
|
-
dt (datetime
|
|
83
|
-
|
|
84
|
-
warn_mode (WarnEnum
|
|
126
|
+
dt (datetime , required): Datetime object.
|
|
127
|
+
sep (str , optional): Separator between date and time. Defaults to "T".
|
|
128
|
+
warn_mode (WarnEnum | str, optional): Warning mode. Defaults to WarnEnum.IGNORE.
|
|
85
129
|
|
|
86
130
|
Raises:
|
|
87
131
|
ValueError: If `dt` argument doesn't have any timezone info and `warn_mode` is set to WarnEnum.ERROR.
|
|
132
|
+
ValueError: If `sep` argument length is greater than 8.
|
|
133
|
+
ValueError: If `warn_mode` argument value is invalid.
|
|
88
134
|
|
|
89
135
|
Returns:
|
|
90
|
-
|
|
136
|
+
str: Datetime string in ISO 8601 format.
|
|
91
137
|
"""
|
|
92
138
|
|
|
139
|
+
sep = sep.strip()
|
|
140
|
+
if 8 < len(sep):
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"`sep` argument length '{len(sep)}' is too long, must be less than or equal to 8!"
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if isinstance(warn_mode, str):
|
|
146
|
+
warn_mode = WarnEnum(warn_mode.strip().upper())
|
|
147
|
+
|
|
93
148
|
if not dt.tzinfo:
|
|
94
149
|
_message = "Not found any timezone info in `dt` argument, assuming it's UTC timezone..."
|
|
95
150
|
if warn_mode == WarnEnum.ALWAYS:
|
|
@@ -101,59 +156,51 @@ def convert_tz(
|
|
|
101
156
|
logger.error(_message)
|
|
102
157
|
raise ValueError(_message)
|
|
103
158
|
|
|
104
|
-
dt =
|
|
159
|
+
dt = replace_tz(dt=dt, tz="UTC")
|
|
105
160
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
dt = dt.astimezone(tz=tz)
|
|
110
|
-
return dt
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def now_utc_dt() -> datetime:
|
|
114
|
-
"""Get current datetime in UTC timezone with tzinfo.
|
|
115
|
-
|
|
116
|
-
Returns:
|
|
117
|
-
datetime: Current datetime in UTC timezone with tzinfo.
|
|
118
|
-
"""
|
|
161
|
+
_dt_str = dt.isoformat(sep=sep, timespec="milliseconds")
|
|
162
|
+
return _dt_str
|
|
119
163
|
|
|
120
|
-
_utc_dt = datetime.now(tz=timezone.utc)
|
|
121
|
-
return _utc_dt
|
|
122
164
|
|
|
165
|
+
@validate_call(config={"arbitrary_types_allowed": True})
|
|
166
|
+
def calc_future_dt(
|
|
167
|
+
delta: timedelta | int,
|
|
168
|
+
dt: datetime | None = None,
|
|
169
|
+
tz: ZoneInfo | tzinfo | str | None = None,
|
|
170
|
+
) -> datetime:
|
|
171
|
+
"""Calculate future datetime by adding delta time to current or specified datetime.
|
|
123
172
|
|
|
124
|
-
|
|
125
|
-
|
|
173
|
+
Args:
|
|
174
|
+
delta (timedelta | int , required): Delta time to add to current or specified datetime.
|
|
175
|
+
dt (datetime | None , optional): Datetime before adding delta time. Defaults to None.
|
|
176
|
+
tz (ZoneInfo | tzinfo, str, None, optional): Timezone info. Defaults to None.
|
|
126
177
|
|
|
127
178
|
Returns:
|
|
128
|
-
datetime:
|
|
179
|
+
datetime: Calculated future datetime.
|
|
129
180
|
"""
|
|
130
181
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
@validate_call(config={"arbitrary_types_allowed": True})
|
|
136
|
-
def now_dt(tz: ZoneInfo | tzinfo | str) -> datetime:
|
|
137
|
-
"""Get current datetime in specified timezone with tzinfo.
|
|
182
|
+
if not dt:
|
|
183
|
+
dt = now_utc_dt()
|
|
138
184
|
|
|
139
|
-
|
|
140
|
-
|
|
185
|
+
if tz:
|
|
186
|
+
dt = convert_tz(dt=dt, tz=tz)
|
|
141
187
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"""
|
|
188
|
+
if isinstance(delta, int):
|
|
189
|
+
delta = timedelta(seconds=delta)
|
|
145
190
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return _dt
|
|
191
|
+
_future_dt = dt + delta
|
|
192
|
+
return _future_dt
|
|
149
193
|
|
|
150
194
|
|
|
151
195
|
@validate_call
|
|
152
|
-
def now_ts(unit: TSUnitEnum = TSUnitEnum.SECONDS) -> int:
|
|
196
|
+
def now_ts(unit: TSUnitEnum | str = TSUnitEnum.SECONDS) -> int:
|
|
153
197
|
"""Get current timestamp in UTC timezone.
|
|
154
198
|
|
|
155
199
|
Args:
|
|
156
|
-
unit (TSUnitEnum, optional): Type of timestamp unit. Defaults to
|
|
200
|
+
unit (TSUnitEnum | str, optional): Type of timestamp unit. Defaults to TSUnitEnum.SECONDS.
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
ValueError: If `unit` argument value is invalid.
|
|
157
204
|
|
|
158
205
|
Returns:
|
|
159
206
|
int: Current timestamp.
|
|
@@ -168,17 +215,22 @@ def now_ts(unit: TSUnitEnum = TSUnitEnum.SECONDS) -> int:
|
|
|
168
215
|
_now_ts = int(time.time_ns() / 1000)
|
|
169
216
|
elif unit == TSUnitEnum.NANOSECONDS:
|
|
170
217
|
_now_ts = int(time.time_ns())
|
|
218
|
+
else:
|
|
219
|
+
raise ValueError(f"`unit` argument value '{unit}' is invalid!")
|
|
171
220
|
|
|
172
221
|
return _now_ts
|
|
173
222
|
|
|
174
223
|
|
|
175
224
|
@validate_call
|
|
176
|
-
def
|
|
225
|
+
def dt_to_ts(dt: datetime, unit: TSUnitEnum | str = TSUnitEnum.SECONDS) -> int:
|
|
177
226
|
"""Convert datetime to timestamp.
|
|
178
227
|
|
|
179
228
|
Args:
|
|
180
|
-
dt (datetime
|
|
181
|
-
unit (TSUnitEnum, optional): Type of timestamp unit. Defaults to `TSUnitEnum.SECONDS`.
|
|
229
|
+
dt (datetime , required): Datetime object to convert.
|
|
230
|
+
unit (TSUnitEnum | str, optional): Type of timestamp unit. Defaults to `TSUnitEnum.SECONDS`.
|
|
231
|
+
|
|
232
|
+
Raises:
|
|
233
|
+
ValueError: If `unit` argument value is invalid.
|
|
182
234
|
|
|
183
235
|
Returns:
|
|
184
236
|
int: Converted timestamp.
|
|
@@ -193,48 +245,20 @@ def convert_ts(dt: datetime, unit: TSUnitEnum = TSUnitEnum.SECONDS) -> int:
|
|
|
193
245
|
_ts = int(dt.timestamp() * 1000000)
|
|
194
246
|
elif unit == TSUnitEnum.NANOSECONDS:
|
|
195
247
|
_ts = int(dt.timestamp() * 1000000000)
|
|
248
|
+
else:
|
|
249
|
+
raise ValueError(f"`unit` argument value '{unit}' is invalid!")
|
|
196
250
|
|
|
197
251
|
return _ts
|
|
198
252
|
|
|
199
253
|
|
|
200
|
-
@validate_call(config={"arbitrary_types_allowed": True})
|
|
201
|
-
def calc_future_dt(
|
|
202
|
-
delta: timedelta | int,
|
|
203
|
-
dt: datetime | None = None,
|
|
204
|
-
tz: ZoneInfo | tzinfo | str | None = None,
|
|
205
|
-
) -> datetime:
|
|
206
|
-
"""Calculate future datetime by adding delta time to current or specified datetime.
|
|
207
|
-
|
|
208
|
-
Args:
|
|
209
|
-
delta (Union[timedelta, int] , required): Delta time to add to current or specified datetime.
|
|
210
|
-
dt (Optional[datetime] , optional): Datetime before adding delta time. Defaults to None.
|
|
211
|
-
tz (Union[ZoneInfo, tzinfo, str, None], optional): Timezone info. Defaults to None.
|
|
212
|
-
|
|
213
|
-
Returns:
|
|
214
|
-
datetime: Calculated future datetime.
|
|
215
|
-
"""
|
|
216
|
-
|
|
217
|
-
if not dt:
|
|
218
|
-
dt = now_utc_dt()
|
|
219
|
-
|
|
220
|
-
if tz:
|
|
221
|
-
dt = convert_tz(dt=dt, tz=tz)
|
|
222
|
-
|
|
223
|
-
if isinstance(delta, int):
|
|
224
|
-
delta = timedelta(seconds=delta)
|
|
225
|
-
|
|
226
|
-
_future_dt = dt + delta
|
|
227
|
-
return _future_dt
|
|
228
|
-
|
|
229
|
-
|
|
230
254
|
__all__ = [
|
|
231
|
-
"add_tzinfo",
|
|
232
|
-
"datetime_to_iso",
|
|
233
|
-
"convert_tz",
|
|
234
255
|
"now_utc_dt",
|
|
235
256
|
"now_local_dt",
|
|
236
257
|
"now_dt",
|
|
237
|
-
"
|
|
238
|
-
"
|
|
258
|
+
"replace_tz",
|
|
259
|
+
"convert_tz",
|
|
260
|
+
"dt_to_iso",
|
|
239
261
|
"calc_future_dt",
|
|
262
|
+
"now_ts",
|
|
263
|
+
"dt_to_ts",
|
|
240
264
|
]
|
potato_util/generator.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
import string
|
|
3
|
+
import secrets
|
|
4
|
+
|
|
5
|
+
from pydantic import validate_call
|
|
6
|
+
|
|
7
|
+
from .dt import now_ts
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@validate_call
|
|
11
|
+
def gen_unique_id(prefix: str = "") -> str:
|
|
12
|
+
"""Generate unique ID. Format: '{prefix}{datetime}_{uuid4}'.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
prefix (str, optional): Prefix of ID. Defaults to ''.
|
|
16
|
+
|
|
17
|
+
Raises:
|
|
18
|
+
ValueError: If `prefix` length is greater than 32.
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
str: Unique ID.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
prefix = prefix.strip()
|
|
25
|
+
if 32 < len(prefix):
|
|
26
|
+
raise ValueError(
|
|
27
|
+
f"`prefix` argument length {len(prefix)} is too long, must be less than or equal to 32!",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
_id = str(f"{prefix}{now_ts()}_{uuid.uuid4().hex}").lower()
|
|
31
|
+
return _id
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@validate_call
|
|
35
|
+
def gen_random_string(length: int = 16, is_alphanum: bool = True) -> str:
|
|
36
|
+
"""Generate secure random string.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
length (int , optional): Length of random string. Defaults to 16.
|
|
40
|
+
is_alphanum (bool, optional): If True, generate only alphanumeric string. Defaults to True.
|
|
41
|
+
|
|
42
|
+
Raises:
|
|
43
|
+
ValueError: If `length` is less than 1.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
str: Generated random string.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
if length < 1:
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"`length` argument value {length} is too small, must be greater than or equal to 1!",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
_base_chars = string.ascii_letters + string.digits
|
|
55
|
+
if not is_alphanum:
|
|
56
|
+
_base_chars += string.punctuation
|
|
57
|
+
|
|
58
|
+
_random_str = "".join(secrets.choice(_base_chars) for _i in range(length))
|
|
59
|
+
return _random_str
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
__all__ = [
|
|
63
|
+
"gen_unique_id",
|
|
64
|
+
"gen_random_string",
|
|
65
|
+
]
|
potato_util/http/_base.py
CHANGED
|
@@ -34,7 +34,9 @@ def get_http_status(status_code: int) -> tuple[HTTPStatus, bool]:
|
|
|
34
34
|
elif (500 <= status_code) and (status_code < 600):
|
|
35
35
|
status_code = 500
|
|
36
36
|
else:
|
|
37
|
-
raise ValueError(
|
|
37
|
+
raise ValueError(
|
|
38
|
+
f"`status_code` argument value '{status_code}' is invalid, must be in range 100-599!",
|
|
39
|
+
)
|
|
38
40
|
|
|
39
41
|
_http_status = HTTPStatus(status_code)
|
|
40
42
|
|
potato_util/http/fastapi.py
CHANGED
|
@@ -8,7 +8,7 @@ def get_relative_url(val: Request | URL) -> str:
|
|
|
8
8
|
"""Get relative url only path with query params from request object or URL object.
|
|
9
9
|
|
|
10
10
|
Args:
|
|
11
|
-
val (
|
|
11
|
+
val (Request | URL, required): Request object or URL object to extract relative url.
|
|
12
12
|
|
|
13
13
|
Returns:
|
|
14
14
|
str: Relative url only path with query params.
|
potato_util/io/_async.py
CHANGED
|
@@ -14,14 +14,14 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
|
|
15
15
|
@validate_call
|
|
16
16
|
async def async_create_dir(
|
|
17
|
-
create_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
17
|
+
create_dir: str, warn_mode: WarnEnum | str = WarnEnum.DEBUG
|
|
18
18
|
) -> None:
|
|
19
19
|
"""Asynchronous create directory if `create_dir` doesn't exist.
|
|
20
20
|
|
|
21
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
|
-
|
|
22
|
+
create_dir (str , required): Create directory path.
|
|
23
|
+
warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
24
|
+
Defaults to 'DEBUG'.
|
|
25
25
|
|
|
26
26
|
Raises:
|
|
27
27
|
ValueError: If `create_dir` argument length is out of range.
|
|
@@ -36,6 +36,9 @@ async def async_create_dir(
|
|
|
36
36
|
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
+
if isinstance(warn_mode, str):
|
|
40
|
+
warn_mode = WarnEnum(warn_mode.strip().upper())
|
|
41
|
+
|
|
39
42
|
if not await aiofiles.os.path.isdir(create_dir):
|
|
40
43
|
try:
|
|
41
44
|
_message = f"Creating '{create_dir}' directory..."
|
|
@@ -66,14 +69,14 @@ async def async_create_dir(
|
|
|
66
69
|
|
|
67
70
|
@validate_call
|
|
68
71
|
async def async_remove_dir(
|
|
69
|
-
remove_dir: str, warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
72
|
+
remove_dir: str, warn_mode: WarnEnum | str = WarnEnum.DEBUG
|
|
70
73
|
) -> None:
|
|
71
74
|
"""Asynchronous remove directory if `remove_dir` exists.
|
|
72
75
|
|
|
73
76
|
Args:
|
|
74
|
-
remove_dir (str, required): Remove directory path.
|
|
75
|
-
warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
76
|
-
|
|
77
|
+
remove_dir (str , required): Remove directory path.
|
|
78
|
+
warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
79
|
+
Defaults to 'DEBUG'.
|
|
77
80
|
|
|
78
81
|
Raises:
|
|
79
82
|
ValueError: If `remove_dir` argument length is out of range.
|
|
@@ -88,6 +91,9 @@ async def async_remove_dir(
|
|
|
88
91
|
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
89
92
|
)
|
|
90
93
|
|
|
94
|
+
if isinstance(warn_mode, str):
|
|
95
|
+
warn_mode = WarnEnum(warn_mode.strip().upper())
|
|
96
|
+
|
|
91
97
|
if await aiofiles.os.path.isdir(remove_dir):
|
|
92
98
|
try:
|
|
93
99
|
_message = f"Removing '{remove_dir}' directory..."
|
|
@@ -118,14 +124,14 @@ async def async_remove_dir(
|
|
|
118
124
|
|
|
119
125
|
@validate_call
|
|
120
126
|
async def async_remove_dirs(
|
|
121
|
-
remove_dirs: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
127
|
+
remove_dirs: list[str], warn_mode: WarnEnum | str = WarnEnum.DEBUG
|
|
122
128
|
) -> None:
|
|
123
129
|
"""Asynchronous remove directories if `remove_dirs` exists.
|
|
124
130
|
|
|
125
131
|
Args:
|
|
126
|
-
remove_dirs (
|
|
127
|
-
warn_mode (str
|
|
128
|
-
|
|
132
|
+
remove_dirs (list[str] , required): Remove directories paths as list.
|
|
133
|
+
warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
134
|
+
Defaults to 'DEBUG'.
|
|
129
135
|
"""
|
|
130
136
|
|
|
131
137
|
for _remove_dir in remove_dirs:
|
|
@@ -136,14 +142,14 @@ async def async_remove_dirs(
|
|
|
136
142
|
|
|
137
143
|
@validate_call
|
|
138
144
|
async def async_remove_file(
|
|
139
|
-
file_path: str, warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
145
|
+
file_path: str, warn_mode: WarnEnum | str = WarnEnum.DEBUG
|
|
140
146
|
) -> None:
|
|
141
147
|
"""Asynchronous remove file if `file_path` exists.
|
|
142
148
|
|
|
143
149
|
Args:
|
|
144
|
-
file_path (str, required): Remove file path.
|
|
145
|
-
warn_mode (str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
146
|
-
|
|
150
|
+
file_path (str , required): Remove file path.
|
|
151
|
+
warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
152
|
+
Defaults to 'DEBUG'.
|
|
147
153
|
|
|
148
154
|
Raises:
|
|
149
155
|
ValueError: If `file_path` argument length is out of range.
|
|
@@ -158,6 +164,9 @@ async def async_remove_file(
|
|
|
158
164
|
f"must be between 1 and {MAX_PATH_LENGTH} characters!"
|
|
159
165
|
)
|
|
160
166
|
|
|
167
|
+
if isinstance(warn_mode, str):
|
|
168
|
+
warn_mode = WarnEnum(warn_mode.strip().upper())
|
|
169
|
+
|
|
161
170
|
if await aiofiles.os.path.isfile(file_path):
|
|
162
171
|
try:
|
|
163
172
|
_message = f"Removing '{file_path}' file..."
|
|
@@ -188,13 +197,13 @@ async def async_remove_file(
|
|
|
188
197
|
|
|
189
198
|
@validate_call
|
|
190
199
|
async def async_remove_files(
|
|
191
|
-
file_paths: list[str], warn_mode: WarnEnum = WarnEnum.DEBUG
|
|
200
|
+
file_paths: list[str], warn_mode: WarnEnum | str = WarnEnum.DEBUG
|
|
192
201
|
) -> None:
|
|
193
202
|
"""Asynchronous remove files if `file_paths` exists.
|
|
194
203
|
|
|
195
204
|
Args:
|
|
196
|
-
file_paths (
|
|
197
|
-
warn_mode (str
|
|
205
|
+
file_paths (list[str] , required): Remove file paths as list.
|
|
206
|
+
warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
198
207
|
Defaults to 'DEBUG'.
|
|
199
208
|
"""
|
|
200
209
|
|
|
@@ -209,15 +218,15 @@ async def async_get_file_checksum(
|
|
|
209
218
|
file_path: str,
|
|
210
219
|
hash_method: HashAlgoEnum = HashAlgoEnum.md5,
|
|
211
220
|
chunk_size: int = 4096,
|
|
212
|
-
warn_mode: WarnEnum = WarnEnum.DEBUG,
|
|
221
|
+
warn_mode: WarnEnum | str = WarnEnum.DEBUG,
|
|
213
222
|
) -> str | None:
|
|
214
223
|
"""Asynchronous get file checksum.
|
|
215
224
|
|
|
216
225
|
Args:
|
|
217
|
-
file_path (str
|
|
218
|
-
hash_method (HashAlgoEnum, optional): Hash method. Defaults to `HashAlgoEnum.md5`.
|
|
219
|
-
chunk_size (int
|
|
220
|
-
warn_mode (str
|
|
226
|
+
file_path (str , required): Target file path.
|
|
227
|
+
hash_method (HashAlgoEnum , optional): Hash method. Defaults to `HashAlgoEnum.md5`.
|
|
228
|
+
chunk_size (int , optional): Chunk size. Defaults to 4096.
|
|
229
|
+
warn_mode (WarnEnum | str, optional): Warning message mode, for example: 'ERROR', 'ALWAYS', 'DEBUG', 'IGNORE'.
|
|
221
230
|
Defaults to 'DEBUG'.
|
|
222
231
|
|
|
223
232
|
Raises:
|
|
@@ -241,6 +250,9 @@ async def async_get_file_checksum(
|
|
|
241
250
|
f"`chunk_size` argument value {chunk_size} is invalid, must be greater than 10!"
|
|
242
251
|
)
|
|
243
252
|
|
|
253
|
+
if isinstance(warn_mode, str):
|
|
254
|
+
warn_mode = WarnEnum(warn_mode.strip().upper())
|
|
255
|
+
|
|
244
256
|
_file_checksum: str | None = None
|
|
245
257
|
if await aiofiles.os.path.isfile(file_path):
|
|
246
258
|
_file_hash = hashlib.new(hash_method.value)
|