mm-std 0.3.28__py3-none-any.whl → 0.3.30__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/data_result.py
CHANGED
@@ -10,41 +10,33 @@ T = TypeVar("T")
|
|
10
10
|
U = TypeVar("U")
|
11
11
|
|
12
12
|
|
13
|
+
type Data = dict[str, object] | None
|
14
|
+
|
15
|
+
|
13
16
|
class DataResult(Generic[T]):
|
14
17
|
"""
|
15
18
|
A result wrapper that encapsulates either a successful result (`ok`) or an error message (`err`).
|
16
|
-
Optionally carries
|
19
|
+
Optionally carries `data` field regardless of success or failure.
|
17
20
|
"""
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
) -> None:
|
26
|
-
# Sanity check: at least one of ok or err must be provided, unless explicitly allowed via `ok_is_none`
|
27
|
-
if ok is None and err is None and not ok_is_none:
|
28
|
-
raise ValueError("Either ok or err must be set")
|
29
|
-
# You can't set both ok and err unless ok_is_none is True (used to explicitly accept None as success)
|
30
|
-
if (ok_is_none or ok is not None) and err is not None:
|
31
|
-
raise ValueError("Cannot set both ok and err")
|
32
|
-
|
33
|
-
self.ok = ok
|
34
|
-
self.err = err
|
35
|
-
self.data = data
|
22
|
+
_ok: T | None
|
23
|
+
_err: str | None
|
24
|
+
data: Data | None
|
25
|
+
|
26
|
+
def __init__(self) -> None:
|
27
|
+
raise RuntimeError("DataResult is not intended to be instantiated directly. Use the static methods instead.")
|
36
28
|
|
37
29
|
def is_ok(self) -> bool:
|
38
30
|
"""
|
39
31
|
Returns True if the result represents a success.
|
40
32
|
"""
|
41
|
-
return self.
|
33
|
+
return self._err is None
|
42
34
|
|
43
35
|
def is_err(self) -> bool:
|
44
36
|
"""
|
45
37
|
Returns True if the result represents an error.
|
46
38
|
"""
|
47
|
-
return self.
|
39
|
+
return self._err is not None
|
48
40
|
|
49
41
|
def unwrap(self) -> T:
|
50
42
|
"""
|
@@ -52,7 +44,7 @@ class DataResult(Generic[T]):
|
|
52
44
|
"""
|
53
45
|
if self.is_err():
|
54
46
|
raise RuntimeError(f"Called `unwrap()` on an `Err` value: {self.err!r}")
|
55
|
-
return cast(T, self.
|
47
|
+
return cast(T, self._ok)
|
56
48
|
|
57
49
|
def unwrap_ok_or(self, default: T) -> T:
|
58
50
|
"""
|
@@ -66,7 +58,7 @@ class DataResult(Generic[T]):
|
|
66
58
|
The success value or the default value.
|
67
59
|
"""
|
68
60
|
if self.is_ok():
|
69
|
-
return cast(T, self.
|
61
|
+
return cast(T, self._ok)
|
70
62
|
return default
|
71
63
|
|
72
64
|
def unwrap_err(self) -> str:
|
@@ -75,13 +67,13 @@ class DataResult(Generic[T]):
|
|
75
67
|
"""
|
76
68
|
if self.is_ok():
|
77
69
|
raise RuntimeError(f"Called `unwrap_err()` on an `Ok` value: {self.ok!r}")
|
78
|
-
return cast(str, self.
|
70
|
+
return cast(str, self._err)
|
79
71
|
|
80
72
|
def dict(self) -> dict[str, object]:
|
81
73
|
"""
|
82
74
|
Returns a dictionary representation of the result.
|
83
75
|
"""
|
84
|
-
return {"ok": self.
|
76
|
+
return {"ok": self._ok, "err": self._err, "data": self.data}
|
85
77
|
|
86
78
|
def map(self, fn: Callable[[T], U]) -> DataResult[U]:
|
87
79
|
"""
|
@@ -96,13 +88,13 @@ class DataResult(Generic[T]):
|
|
96
88
|
A new DataResult with the transformed success value or an error.
|
97
89
|
"""
|
98
90
|
if self.is_err():
|
99
|
-
return DataResult[U](
|
91
|
+
return DataResult[U].err(self.unwrap_err(), self.data)
|
100
92
|
|
101
93
|
try:
|
102
94
|
mapped_ok = fn(self.unwrap())
|
103
|
-
return DataResult[U](
|
95
|
+
return DataResult[U].ok(mapped_ok, self.data)
|
104
96
|
except Exception as e:
|
105
|
-
return DataResult[U](
|
97
|
+
return DataResult[U].exception(e, data={"original_data": self.data, "original_ok": self.ok})
|
106
98
|
|
107
99
|
async def map_async(self, fn: Callable[[T], Awaitable[U]]) -> DataResult[U]:
|
108
100
|
"""
|
@@ -117,13 +109,13 @@ class DataResult(Generic[T]):
|
|
117
109
|
A new DataResult with the transformed success value or an error.
|
118
110
|
"""
|
119
111
|
if self.is_err():
|
120
|
-
return DataResult[U](
|
112
|
+
return DataResult[U].err(self.unwrap_err(), self.data)
|
121
113
|
|
122
114
|
try:
|
123
115
|
mapped_ok = await fn(self.unwrap())
|
124
|
-
return DataResult[U](
|
116
|
+
return DataResult[U].ok(mapped_ok, self.data)
|
125
117
|
except Exception as e:
|
126
|
-
return DataResult[U](
|
118
|
+
return DataResult[U].exception(e, data={"original_data": self.data, "original_ok": self.ok})
|
127
119
|
|
128
120
|
def and_then(self, fn: Callable[[T], DataResult[U]]) -> DataResult[U]:
|
129
121
|
"""
|
@@ -139,12 +131,12 @@ class DataResult(Generic[T]):
|
|
139
131
|
The result of the function application or the original error.
|
140
132
|
"""
|
141
133
|
if self.is_err():
|
142
|
-
return DataResult[U](
|
134
|
+
return DataResult[U].err(self.unwrap_err(), self.data)
|
143
135
|
|
144
136
|
try:
|
145
137
|
return fn(self.unwrap())
|
146
138
|
except Exception as e:
|
147
|
-
return DataResult[U](
|
139
|
+
return DataResult[U].exception(e, data={"original_data": self.data, "original_ok": self.ok})
|
148
140
|
|
149
141
|
async def and_then_async(self, fn: Callable[[T], Awaitable[DataResult[U]]]) -> DataResult[U]:
|
150
142
|
"""
|
@@ -160,18 +152,18 @@ class DataResult(Generic[T]):
|
|
160
152
|
The result of the function application or the original error.
|
161
153
|
"""
|
162
154
|
if self.is_err():
|
163
|
-
return DataResult[U](
|
155
|
+
return DataResult[U].err(self.unwrap_err(), self.data)
|
164
156
|
|
165
157
|
try:
|
166
158
|
return await fn(self.unwrap())
|
167
159
|
except Exception as e:
|
168
|
-
return DataResult[U](
|
160
|
+
return DataResult[U].exception(e, {"original_data": self.data, "original_ok": self.ok})
|
169
161
|
|
170
162
|
def __repr__(self) -> str:
|
171
163
|
"""
|
172
164
|
Returns the debug representation of the result.
|
173
165
|
"""
|
174
|
-
result = f"DataResult(ok={self.
|
166
|
+
result = f"DataResult(ok={self._ok!r}" if self.is_ok() else f"DataResult(err={self._err!r}"
|
175
167
|
if self.data is not None:
|
176
168
|
result += f", data={self.data!r}"
|
177
169
|
return result + ")"
|
@@ -188,7 +180,7 @@ class DataResult(Generic[T]):
|
|
188
180
|
"""
|
189
181
|
if not isinstance(other, DataResult):
|
190
182
|
return NotImplemented
|
191
|
-
return self.
|
183
|
+
return self._ok == other._ok and self._err == other._err and self.data == other.data
|
192
184
|
|
193
185
|
@classmethod
|
194
186
|
def __get_pydantic_core_schema__(cls, _source_type: type[Any], _handler: GetCoreSchemaHandler) -> CoreSchema:
|
@@ -210,9 +202,48 @@ class DataResult(Generic[T]):
|
|
210
202
|
if isinstance(v, cls):
|
211
203
|
return v
|
212
204
|
if isinstance(v, dict):
|
213
|
-
return cls(
|
205
|
+
return cls._create(
|
214
206
|
ok=v.get("ok"),
|
215
207
|
err=v.get("err"),
|
216
208
|
data=v.get("data"),
|
217
209
|
)
|
218
210
|
raise TypeError(f"Cannot parse value as {cls.__name__}: {v}")
|
211
|
+
|
212
|
+
@classmethod
|
213
|
+
def _create(cls, ok: T | None, err: str | None, data: Data) -> DataResult[T]:
|
214
|
+
"""
|
215
|
+
Internal method to create a DataResult instance.
|
216
|
+
"""
|
217
|
+
obj = object.__new__(cls)
|
218
|
+
obj._ok = ok # noqa: SLF001
|
219
|
+
obj._err = err # noqa: SLF001
|
220
|
+
obj.data = data
|
221
|
+
return obj
|
222
|
+
|
223
|
+
@staticmethod
|
224
|
+
def ok(value: T, data: Data = None) -> DataResult[T]:
|
225
|
+
"""
|
226
|
+
Static method to create a successful DataResult.
|
227
|
+
"""
|
228
|
+
return DataResult._create(ok=value, err=None, data=data)
|
229
|
+
|
230
|
+
@staticmethod
|
231
|
+
def err(error: str, data: Data = None) -> DataResult[T]:
|
232
|
+
"""
|
233
|
+
Static method to create an error DataResult.
|
234
|
+
"""
|
235
|
+
return DataResult._create(ok=None, err=error, data=data)
|
236
|
+
|
237
|
+
@staticmethod
|
238
|
+
def exception(err: Exception, data: Data = None) -> DataResult[T]:
|
239
|
+
"""
|
240
|
+
Static method to create an error DataResult from an exception.
|
241
|
+
"""
|
242
|
+
if data is None:
|
243
|
+
data = {}
|
244
|
+
key = "exception_message"
|
245
|
+
while key in data:
|
246
|
+
key += "_"
|
247
|
+
data[key] = str(err)
|
248
|
+
|
249
|
+
return DataResult._create(ok=None, err="exception", data=data)
|
mm_std/http/http_request.py
CHANGED
@@ -74,7 +74,7 @@ async def _request_with_http_or_none_proxy(
|
|
74
74
|
method, url, params=params, data=data, json=json, headers=headers, cookies=cookies, proxy=proxy, timeout=timeout
|
75
75
|
) as res:
|
76
76
|
return HttpResponse(
|
77
|
-
|
77
|
+
status_code=res.status,
|
78
78
|
error=None,
|
79
79
|
error_message=None,
|
80
80
|
body=(await res.read()).decode(),
|
@@ -102,7 +102,7 @@ async def _request_with_socks_proxy(
|
|
102
102
|
) as res,
|
103
103
|
):
|
104
104
|
return HttpResponse(
|
105
|
-
|
105
|
+
status_code=res.status,
|
106
106
|
error=None,
|
107
107
|
error_message=None,
|
108
108
|
body=(await res.read()).decode(),
|
mm_std/http/response.py
CHANGED
@@ -3,8 +3,9 @@ import json
|
|
3
3
|
from typing import Any
|
4
4
|
|
5
5
|
import pydash
|
6
|
-
from pydantic import
|
7
|
-
|
6
|
+
from pydantic import BaseModel
|
7
|
+
|
8
|
+
from mm_std.data_result import DataResult
|
8
9
|
|
9
10
|
|
10
11
|
@enum.unique
|
@@ -15,79 +16,32 @@ class HttpError(str, enum.Enum):
|
|
15
16
|
ERROR = "error"
|
16
17
|
|
17
18
|
|
18
|
-
class HttpResponse:
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
body: str | None = None,
|
25
|
-
headers: dict[str, str] | None = None,
|
26
|
-
) -> None:
|
27
|
-
self.status = status
|
28
|
-
self.error = error
|
29
|
-
self.error_message = error_message
|
30
|
-
self.body = body
|
31
|
-
self.headers = headers
|
32
|
-
|
33
|
-
self._json_data: Any = None
|
34
|
-
self._json_parsed = False
|
35
|
-
self._json_parsed_error = False
|
19
|
+
class HttpResponse(BaseModel):
|
20
|
+
status_code: int | None = None
|
21
|
+
error: HttpError | None = None
|
22
|
+
error_message: str | None = None
|
23
|
+
body: str | None = None
|
24
|
+
headers: dict[str, str] | None = None
|
36
25
|
|
37
|
-
def
|
26
|
+
def parse_json_body(self, path: str | None = None, none_on_error: bool = False) -> Any: # noqa: ANN401
|
38
27
|
if self.body is None:
|
39
|
-
|
40
|
-
|
28
|
+
if none_on_error:
|
29
|
+
return None
|
30
|
+
raise ValueError("Body is None")
|
31
|
+
|
41
32
|
try:
|
42
|
-
|
43
|
-
|
44
|
-
self._json_parsed_error = False
|
33
|
+
res = json.loads(self.body)
|
34
|
+
return pydash.get(res, path, None) if path else res
|
45
35
|
except json.JSONDecodeError:
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def json(self, path: str | None = None) -> Any: # noqa: ANN401
|
50
|
-
if not self._json_parsed:
|
51
|
-
self._parse_json()
|
52
|
-
if path:
|
53
|
-
return pydash.get(self._json_data, path, None)
|
54
|
-
return self._json_data
|
55
|
-
|
56
|
-
def dict(self) -> dict[str, object]:
|
57
|
-
return {
|
58
|
-
"status": self.status,
|
59
|
-
"error": self.error,
|
60
|
-
"error_message": self.error_message,
|
61
|
-
"body": self.body,
|
62
|
-
"headers": self.headers,
|
63
|
-
}
|
64
|
-
|
65
|
-
def is_json_parse_error(self) -> bool:
|
66
|
-
if not self._json_parsed:
|
67
|
-
self._parse_json()
|
68
|
-
return self._json_parsed_error
|
36
|
+
if none_on_error:
|
37
|
+
return None
|
38
|
+
raise
|
69
39
|
|
70
|
-
def
|
71
|
-
return
|
40
|
+
def is_error(self) -> bool:
|
41
|
+
return self.error is not None or (self.status_code is not None and self.status_code >= 400)
|
72
42
|
|
73
|
-
|
74
|
-
|
75
|
-
return core_schema.no_info_after_validator_function(
|
76
|
-
cls._validate,
|
77
|
-
core_schema.any_schema(),
|
78
|
-
serialization=core_schema.plain_serializer_function_ser_schema(lambda x: x.dict()),
|
79
|
-
)
|
43
|
+
def to_data_result_err[T](self, error: str | None = None) -> DataResult[T]:
|
44
|
+
return DataResult.err(error or self.error or "error", self.model_dump())
|
80
45
|
|
81
|
-
|
82
|
-
|
83
|
-
if isinstance(v, cls):
|
84
|
-
return v
|
85
|
-
if isinstance(v, dict):
|
86
|
-
return cls(
|
87
|
-
status=v.get("status"),
|
88
|
-
error=HttpError(v["error"]) if v.get("error") else None,
|
89
|
-
error_message=v.get("error_message"),
|
90
|
-
body=v.get("body"),
|
91
|
-
headers=v.get("headers"),
|
92
|
-
)
|
93
|
-
raise TypeError(f"Cannot parse value as {cls.__name__}: {v}")
|
46
|
+
def to_data_result_ok[T](self, result: T) -> DataResult[T]:
|
47
|
+
return DataResult.ok(result, self.model_dump())
|
@@ -1,10 +1,11 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: mm-std
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.30
|
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
|
+
Requires-Dist: multidict~=6.4.3
|
8
9
|
Requires-Dist: pydantic-settings>=2.8.1
|
9
10
|
Requires-Dist: pydantic~=2.11.3
|
10
11
|
Requires-Dist: pydash~=8.0.5
|
@@ -2,7 +2,7 @@ mm_std/__init__.py,sha256=ePkjykjImp2Wu10lFzK3Fl1HpGqqXFb_HZK_BSiU3MM,3316
|
|
2
2
|
mm_std/command.py,sha256=ze286wjUjg0QSTgIu-2WZks53_Vclg69UaYYgPpQvCU,1283
|
3
3
|
mm_std/config.py,sha256=4ox4D2CgGR76bvZ2n2vGQOYUDagFnlKEDb87to5zpxE,1871
|
4
4
|
mm_std/crypto.py,sha256=jdk0_TCmeU0pPXMyz9xH6kQHSjjZ9GcGClBwQps5vBo,340
|
5
|
-
mm_std/data_result.py,sha256=
|
5
|
+
mm_std/data_result.py,sha256=aUDhnxp5EagYFkCiOGKYReQshDrK_3zXfGYla5mJ8Yk,8739
|
6
6
|
mm_std/date.py,sha256=976eEkSONuNqHQBgSRu8hrtH23tJqztbmHFHLdbP2TY,1879
|
7
7
|
mm_std/dict.py,sha256=6GkhJPXD0LiJDxPcYe6jPdEDw-MN7P7mKu6U5XxwYDk,675
|
8
8
|
mm_std/env.py,sha256=5zaR9VeIfObN-4yfgxoFeU5IM1GDeZZj9SuYf7t9sOA,125
|
@@ -27,8 +27,8 @@ mm_std/concurrency/sync_decorators.py,sha256=syCQBOmN7qPO55yzgJB2rbkh10CVww376hm
|
|
27
27
|
mm_std/concurrency/sync_scheduler.py,sha256=j4tBL_cBI1spr0cZplTA7N2CoYsznuORMeRN8rpR6gY,2407
|
28
28
|
mm_std/concurrency/sync_task_runner.py,sha256=s5JPlLYLGQGHIxy4oDS-PN7O9gcy-yPZFoNm8RQwzcw,1780
|
29
29
|
mm_std/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
|
-
mm_std/http/http_request.py,sha256=
|
31
|
-
mm_std/http/response.py,sha256=
|
32
|
-
mm_std-0.3.
|
33
|
-
mm_std-0.3.
|
34
|
-
mm_std-0.3.
|
30
|
+
mm_std/http/http_request.py,sha256=h74_ZjACwdbeINjzhuEwNUpvnaHZvyGl7TExjlBwMGg,3704
|
31
|
+
mm_std/http/response.py,sha256=WNQWd1HC134WvjEOSvikHCmujbsvs2332VzUPp9DnyM,1377
|
32
|
+
mm_std-0.3.30.dist-info/METADATA,sha256=wv2Gs6_yVnt-f58T01_Dt6-cP8btIij1MFm91G04xYM,447
|
33
|
+
mm_std-0.3.30.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
34
|
+
mm_std-0.3.30.dist-info/RECORD,,
|
File without changes
|