mm-std 0.4.5__py3-none-any.whl → 0.4.7__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.
mm_std/__init__.py
CHANGED
@@ -40,6 +40,8 @@ from .random_ import random_choice as random_choice
|
|
40
40
|
from .random_ import random_decimal as random_decimal
|
41
41
|
from .random_ import random_str_choice as random_str_choice
|
42
42
|
from .result import Result as Result
|
43
|
+
from .result import is_err as is_err
|
44
|
+
from .result import is_ok as is_ok
|
43
45
|
from .str import number_with_separator as number_with_separator
|
44
46
|
from .str import str_contains_any as str_contains_any
|
45
47
|
from .str import str_ends_with_any as str_ends_with_any
|
mm_std/config.py
CHANGED
@@ -42,11 +42,11 @@ class BaseConfig(BaseModel):
|
|
42
42
|
else:
|
43
43
|
with config_path.open("rb") as f:
|
44
44
|
data = tomllib.load(f)
|
45
|
-
return Result.
|
45
|
+
return Result.ok(cls(**data))
|
46
46
|
except ValidationError as e:
|
47
|
-
return Result.
|
47
|
+
return Result.err(("validator_error", e), extra={"errors": e.errors()})
|
48
48
|
except Exception as e:
|
49
|
-
return Result.
|
49
|
+
return Result.err(e)
|
50
50
|
|
51
51
|
@classmethod
|
52
52
|
async def read_toml_config_async(cls, config_path: Path, zip_password: str = "") -> Result[Self]: # nosec
|
@@ -58,11 +58,11 @@ class BaseConfig(BaseModel):
|
|
58
58
|
with config_path.open("rb") as f:
|
59
59
|
data = tomllib.load(f)
|
60
60
|
model = await cls.model_validate(data) # type:ignore[misc]
|
61
|
-
return Result.
|
61
|
+
return Result.ok(model)
|
62
62
|
except ValidationError as e:
|
63
|
-
return Result.
|
63
|
+
return Result.err(("validator_error", e), extra={"errors": e.errors()})
|
64
64
|
except Exception as e:
|
65
|
-
return Result.
|
65
|
+
return Result.err(e)
|
66
66
|
|
67
67
|
@classmethod
|
68
68
|
def _print_error_and_exit(cls, res: Result[Any]) -> NoReturn:
|
@@ -73,5 +73,5 @@ class BaseConfig(BaseModel):
|
|
73
73
|
field = ".".join(str(lo) for lo in loc) if len(loc) > 0 else ""
|
74
74
|
print_plain(f"{field} {e['msg']}")
|
75
75
|
else:
|
76
|
-
print_plain(f"can't parse config file: {res.error}")
|
76
|
+
print_plain(f"can't parse config file: {res.error} {res.exception}")
|
77
77
|
sys.exit(1)
|
mm_std/http/http_response.py
CHANGED
@@ -54,14 +54,14 @@ class HttpResponse:
|
|
54
54
|
return None
|
55
55
|
raise
|
56
56
|
|
57
|
-
def
|
57
|
+
def is_err(self) -> bool:
|
58
58
|
return self.error is not None or (self.status_code is not None and self.status_code >= 400)
|
59
59
|
|
60
|
-
def
|
61
|
-
return Result.
|
60
|
+
def to_err[T](self, error: str | Exception | tuple[str, Exception] | None = None) -> Result[T]:
|
61
|
+
return Result.err(error or self.error or "error", extra=self.to_dict())
|
62
62
|
|
63
|
-
def
|
64
|
-
return Result.
|
63
|
+
def to_ok[T](self, value: T) -> Result[T]:
|
64
|
+
return Result.ok(value, extra=self.to_dict())
|
65
65
|
|
66
66
|
def to_dict(self) -> dict[str, Any]:
|
67
67
|
return {
|
mm_std/result.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
from collections.abc import Awaitable, Callable
|
4
|
-
from typing import Any, TypeVar, cast
|
4
|
+
from typing import Any, Protocol, TypeGuard, TypeVar, cast
|
5
5
|
|
6
6
|
from pydantic import GetCoreSchemaHandler
|
7
7
|
from pydantic_core import CoreSchema, core_schema
|
@@ -15,10 +15,10 @@ type Extra = dict[str, Any] | None
|
|
15
15
|
class Result[T]:
|
16
16
|
"""
|
17
17
|
A container representing either a successful result or an error.
|
18
|
-
Use `Result.
|
18
|
+
Use `Result.ok()` or `Result.err()` to create instances.
|
19
19
|
"""
|
20
20
|
|
21
|
-
|
21
|
+
value: T | None # Success value, if any
|
22
22
|
error: str | None # Error message, if any
|
23
23
|
exception: Exception | None # Exception, if any. It's optional.
|
24
24
|
extra: Extra # Optional extra metadata
|
@@ -32,7 +32,7 @@ class Result[T]:
|
|
32
32
|
"""
|
33
33
|
return self.error is None
|
34
34
|
|
35
|
-
def
|
35
|
+
def is_err(self) -> bool:
|
36
36
|
"""
|
37
37
|
Returns True if the result represents an error.
|
38
38
|
"""
|
@@ -51,7 +51,7 @@ class Result[T]:
|
|
51
51
|
"""
|
52
52
|
if not self.is_ok():
|
53
53
|
raise RuntimeError(f"Called unwrap() on a failure value: {self.error}")
|
54
|
-
return cast(T, self.
|
54
|
+
return cast(T, self.value)
|
55
55
|
|
56
56
|
def unwrap_or(self, default: T) -> T:
|
57
57
|
"""
|
@@ -59,7 +59,7 @@ class Result[T]:
|
|
59
59
|
"""
|
60
60
|
if not self.is_ok():
|
61
61
|
return default
|
62
|
-
return cast(T, self.
|
62
|
+
return cast(T, self.value)
|
63
63
|
|
64
64
|
def unwrap_error(self) -> str:
|
65
65
|
"""
|
@@ -67,7 +67,7 @@ class Result[T]:
|
|
67
67
|
Raises RuntimeError if the result is a success.
|
68
68
|
"""
|
69
69
|
if self.is_ok():
|
70
|
-
raise RuntimeError("Called
|
70
|
+
raise RuntimeError("Called unwrap_err() on a success value")
|
71
71
|
return cast(str, self.error)
|
72
72
|
|
73
73
|
def unwrap_exception(self) -> Exception:
|
@@ -93,50 +93,64 @@ class Result[T]:
|
|
93
93
|
Note: the exception is converted to a string if present.
|
94
94
|
"""
|
95
95
|
return {
|
96
|
-
"
|
96
|
+
"value": self.value,
|
97
97
|
"error": self.error,
|
98
98
|
"exception": str(self.exception) if self.exception else None,
|
99
99
|
"extra": self.extra,
|
100
100
|
}
|
101
101
|
|
102
|
+
def with_value(self, value: U) -> Result[U]:
|
103
|
+
"""
|
104
|
+
Returns a copy of this Result with the success value replaced by `value`.
|
105
|
+
The `extra` metadata is preserved.
|
106
|
+
"""
|
107
|
+
return Result.ok(value, self.extra)
|
108
|
+
|
109
|
+
def with_error(self, error: str | Exception | tuple[str, Exception]) -> Result[T]:
|
110
|
+
"""
|
111
|
+
Returns a copy of this Result as an Err with the given `error`.
|
112
|
+
Preserves existing `extra` metadata.
|
113
|
+
"""
|
114
|
+
return Result.err(error, self.extra)
|
115
|
+
|
102
116
|
def map(self, fn: Callable[[T], U]) -> Result[U]:
|
103
117
|
if self.is_ok():
|
104
118
|
try:
|
105
|
-
new_value = fn(cast(T, self.
|
106
|
-
return Result.
|
119
|
+
new_value = fn(cast(T, self.value))
|
120
|
+
return Result.ok(new_value, extra=self.extra)
|
107
121
|
except Exception as e:
|
108
|
-
return Result.
|
122
|
+
return Result.err(("map_exception", e), extra=self.extra)
|
109
123
|
return cast(Result[U], self)
|
110
124
|
|
111
125
|
async def map_async(self, fn: Callable[[T], Awaitable[U]]) -> Result[U]:
|
112
126
|
if self.is_ok():
|
113
127
|
try:
|
114
|
-
new_value = await fn(cast(T, self.
|
115
|
-
return Result.
|
128
|
+
new_value = await fn(cast(T, self.value))
|
129
|
+
return Result.ok(new_value, extra=self.extra)
|
116
130
|
except Exception as e:
|
117
|
-
return Result.
|
131
|
+
return Result.err(("map_exception", e), extra=self.extra)
|
118
132
|
return cast(Result[U], self)
|
119
133
|
|
120
134
|
def and_then(self, fn: Callable[[T], Result[U]]) -> Result[U]:
|
121
135
|
if self.is_ok():
|
122
136
|
try:
|
123
|
-
return fn(cast(T, self.
|
137
|
+
return fn(cast(T, self.value))
|
124
138
|
except Exception as e:
|
125
|
-
return Result.
|
139
|
+
return Result.err(("and_then_exception", e), extra=self.extra)
|
126
140
|
return cast(Result[U], self)
|
127
141
|
|
128
142
|
async def and_then_async(self, fn: Callable[[T], Awaitable[Result[U]]]) -> Result[U]:
|
129
143
|
if self.is_ok():
|
130
144
|
try:
|
131
|
-
return await fn(cast(T, self.
|
145
|
+
return await fn(cast(T, self.value))
|
132
146
|
except Exception as e:
|
133
|
-
return Result.
|
147
|
+
return Result.err(("and_then_exception", e), extra=self.extra)
|
134
148
|
return cast(Result[U], self)
|
135
149
|
|
136
150
|
def __repr__(self) -> str:
|
137
151
|
parts: list[str] = []
|
138
|
-
if self.
|
139
|
-
parts.append(f"
|
152
|
+
if self.value is not None:
|
153
|
+
parts.append(f"value={self.value!r}")
|
140
154
|
if self.error is not None:
|
141
155
|
parts.append(f"error={self.error!r}")
|
142
156
|
if self.exception is not None:
|
@@ -148,7 +162,7 @@ class Result[T]:
|
|
148
162
|
def __hash__(self) -> int:
|
149
163
|
return hash(
|
150
164
|
(
|
151
|
-
self.
|
165
|
+
self.value,
|
152
166
|
self.error,
|
153
167
|
self.exception,
|
154
168
|
frozenset(self.extra.items()) if self.extra else None,
|
@@ -159,34 +173,37 @@ class Result[T]:
|
|
159
173
|
if not isinstance(other, Result):
|
160
174
|
return False
|
161
175
|
return (
|
162
|
-
self.
|
176
|
+
self.value == other.value
|
177
|
+
and self.error == other.error
|
178
|
+
and self.exception == other.exception
|
179
|
+
and self.extra == other.extra
|
163
180
|
)
|
164
181
|
|
165
182
|
@classmethod
|
166
|
-
def _create(cls,
|
183
|
+
def _create(cls, value: T | None, error: str | None, exception: Exception | None, extra: Extra) -> Result[T]:
|
167
184
|
obj = object.__new__(cls)
|
168
|
-
obj.
|
185
|
+
obj.value = value
|
169
186
|
obj.error = error
|
170
187
|
obj.exception = exception
|
171
188
|
obj.extra = extra
|
172
189
|
return obj
|
173
190
|
|
174
191
|
@staticmethod
|
175
|
-
def
|
192
|
+
def ok(value: T, extra: Extra = None) -> Result[T]:
|
176
193
|
"""
|
177
194
|
Creates a successful Result instance.
|
178
195
|
|
179
196
|
Args:
|
180
|
-
|
197
|
+
value: The success value to store in the Result.
|
181
198
|
extra: Optional extra metadata to associate with the Result.
|
182
199
|
|
183
200
|
Returns:
|
184
201
|
A Result instance representing success with the provided value.
|
185
202
|
"""
|
186
|
-
return Result._create(
|
203
|
+
return Result._create(value=value, error=None, exception=None, extra=extra)
|
187
204
|
|
188
205
|
@staticmethod
|
189
|
-
def
|
206
|
+
def err(error: str | Exception | tuple[str, Exception], extra: Extra = None) -> Result[T]:
|
190
207
|
"""
|
191
208
|
Creates a Result instance representing a failure.
|
192
209
|
|
@@ -209,7 +226,7 @@ class Result[T]:
|
|
209
226
|
error_ = error
|
210
227
|
exception = None
|
211
228
|
|
212
|
-
return Result._create(
|
229
|
+
return Result._create(value=None, error=error_, exception=exception, extra=extra)
|
213
230
|
|
214
231
|
@classmethod
|
215
232
|
def __get_pydantic_core_schema__(cls, _source_type: type[Any], _handler: GetCoreSchemaHandler) -> CoreSchema:
|
@@ -225,9 +242,27 @@ class Result[T]:
|
|
225
242
|
return value
|
226
243
|
if isinstance(value, dict):
|
227
244
|
return cls._create(
|
228
|
-
|
245
|
+
value=value.get("value"),
|
229
246
|
error=value.get("error"),
|
230
247
|
exception=value.get("exception"),
|
231
248
|
extra=value.get("extra"),
|
232
249
|
)
|
233
250
|
raise TypeError(f"Invalid value for Result: {value}")
|
251
|
+
|
252
|
+
|
253
|
+
class OkResult(Protocol[T]):
|
254
|
+
value: T
|
255
|
+
error: None
|
256
|
+
|
257
|
+
|
258
|
+
class ErrResult(Protocol[T]): # type:ignore[misc]
|
259
|
+
value: None
|
260
|
+
error: str
|
261
|
+
|
262
|
+
|
263
|
+
def is_ok(res: Result[T]) -> TypeGuard[OkResult[T]]:
|
264
|
+
return res.is_ok()
|
265
|
+
|
266
|
+
|
267
|
+
def is_err(res: Result[T]) -> TypeGuard[ErrResult[T]]:
|
268
|
+
return res.is_err()
|
@@ -1,12 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mm-std
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.7
|
4
4
|
Requires-Python: >=3.12
|
5
5
|
Requires-Dist: aiohttp-socks~=0.10.1
|
6
6
|
Requires-Dist: aiohttp~=3.11.16
|
7
7
|
Requires-Dist: cryptography~=44.0.2
|
8
8
|
Requires-Dist: multidict~=6.4.3
|
9
|
-
Requires-Dist: pydantic-settings>=2.
|
9
|
+
Requires-Dist: pydantic-settings>=2.9.1
|
10
10
|
Requires-Dist: pydantic~=2.11.3
|
11
11
|
Requires-Dist: pydash~=8.0.5
|
12
12
|
Requires-Dist: python-dotenv~=1.1.0
|
@@ -1,6 +1,6 @@
|
|
1
|
-
mm_std/__init__.py,sha256=
|
1
|
+
mm_std/__init__.py,sha256=SlfY7KoSiv9YEkeBrjUksIUtLkkG17Ii-DYpC1TGphY,2886
|
2
2
|
mm_std/command.py,sha256=ze286wjUjg0QSTgIu-2WZks53_Vclg69UaYYgPpQvCU,1283
|
3
|
-
mm_std/config.py,sha256
|
3
|
+
mm_std/config.py,sha256=-XigcK4XLc8JHe1-9_LxufoY5aHX1l9gx-lRizWY18A,3070
|
4
4
|
mm_std/crypto.py,sha256=jdk0_TCmeU0pPXMyz9xH6kQHSjjZ9GcGClBwQps5vBo,340
|
5
5
|
mm_std/date.py,sha256=976eEkSONuNqHQBgSRu8hrtH23tJqztbmHFHLdbP2TY,1879
|
6
6
|
mm_std/dict.py,sha256=6GkhJPXD0LiJDxPcYe6jPdEDw-MN7P7mKu6U5XxwYDk,675
|
@@ -12,7 +12,7 @@ mm_std/net.py,sha256=qdRCBIDneip6FaPNe5mx31UtYVmzqam_AoUF7ydEyjA,590
|
|
12
12
|
mm_std/print_.py,sha256=zB7sVbSSF8RffMxvnOdbKCXjCKtKzKV3R68pBri4NkQ,1638
|
13
13
|
mm_std/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
14
14
|
mm_std/random_.py,sha256=OuUX4VJeSd13NZBya4qrGpR2TfN7_87tfebOY6DBUnI,1113
|
15
|
-
mm_std/result.py,sha256=
|
15
|
+
mm_std/result.py,sha256=ZeGL5juBLdrlYiNgI89K50W_pNn35IOVE71sdfV_1uo,8800
|
16
16
|
mm_std/str.py,sha256=BEjJ1p5O4-uSYK0h-enasSSDdwzkBbiwdQ4_dsrlEE8,3257
|
17
17
|
mm_std/toml.py,sha256=CNznWKR0bpOxS6e3VB5LGS-Oa9lW-wterkcPUFtPcls,610
|
18
18
|
mm_std/types_.py,sha256=9FGd2q47a8M9QQgsWJR1Kq34jLxBAkYSoJuwih4PPqg,257
|
@@ -27,7 +27,7 @@ mm_std/concurrency/sync_task_runner.py,sha256=s5JPlLYLGQGHIxy4oDS-PN7O9gcy-yPZFo
|
|
27
27
|
mm_std/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
28
28
|
mm_std/http/http_request.py,sha256=VnjZKrfaXQfMxrHRUqQ-Sxtr5Qf9FXBiJ-mmJTCzNkY,3709
|
29
29
|
mm_std/http/http_request_sync.py,sha256=bqCBilbe4ZJ9vkhuBQeU5UMTJh6BtvtUwjieEodu6rw,1542
|
30
|
-
mm_std/http/http_response.py,sha256=
|
31
|
-
mm_std-0.4.
|
32
|
-
mm_std-0.4.
|
33
|
-
mm_std-0.4.
|
30
|
+
mm_std/http/http_response.py,sha256=vrRv7Rjr5_kTUGllfW5oJ8ZqkcfiXc9T8PlBGAr-TBM,3884
|
31
|
+
mm_std-0.4.7.dist-info/METADATA,sha256=RM6Tu17kVqjVnLqV08LyQMWh2GuvdKV-wLsBbwon0OE,446
|
32
|
+
mm_std-0.4.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
33
|
+
mm_std-0.4.7.dist-info/RECORD,,
|
File without changes
|