mm-std 0.4.5__py3-none-any.whl → 0.4.6__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.success(cls(**data))
45
+ return Result.ok(cls(**data))
46
46
  except ValidationError as e:
47
- return Result.failure(("validator_error", e), extra={"errors": e.errors()})
47
+ return Result.err(("validator_error", e), extra={"errors": e.errors()})
48
48
  except Exception as e:
49
- return Result.failure(e)
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.success(model)
61
+ return Result.ok(model)
62
62
  except ValidationError as e:
63
- return Result.failure(("validator_error", e), extra={"errors": e.errors()})
63
+ return Result.err(("validator_error", e), extra={"errors": e.errors()})
64
64
  except Exception as e:
65
- return Result.failure(e)
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)
@@ -54,14 +54,14 @@ class HttpResponse:
54
54
  return None
55
55
  raise
56
56
 
57
- def is_error(self) -> bool:
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 to_result_failure[T](self, error: str | Exception | tuple[str, Exception] | None = None) -> Result[T]:
61
- return Result.failure(error or self.error or "error", extra=self.to_dict())
60
+ def to_err_result[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 to_result_success[T](self, result: T) -> Result[T]:
64
- return Result.success(result, extra=self.to_dict())
63
+ def to_ok_result[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.success()` or `Result.failure()` to create instances.
18
+ Use `Result.ok()` or `Result.err()` to create instances.
19
19
  """
20
20
 
21
- ok: T | None # Success value, if any
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 is_error(self) -> bool:
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.ok)
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.ok)
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 unwrap_error() on a success value")
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
- "ok": self.ok,
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.ok))
106
- return Result.success(new_value, extra=self.extra)
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.failure(("map_exception", e), extra=self.extra)
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.ok))
115
- return Result.success(new_value, extra=self.extra)
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.failure(("map_exception", e), extra=self.extra)
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.ok))
137
+ return fn(cast(T, self.value))
124
138
  except Exception as e:
125
- return Result.failure(("and_then_exception", e), extra=self.extra)
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.ok))
145
+ return await fn(cast(T, self.value))
132
146
  except Exception as e:
133
- return Result.failure(("and_then_exception", e), extra=self.extra)
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.ok is not None:
139
- parts.append(f"ok={self.ok!r}")
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.ok,
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.ok == other.ok and self.error == other.error and self.exception == other.exception and self.extra == other.extra
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, ok: T | None, error: str | None, exception: Exception | None, extra: Extra) -> Result[T]:
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.ok = ok
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 success(ok: T, extra: Extra = None) -> Result[T]:
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
- ok: The success value to store in the Result.
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(ok=ok, error=None, exception=None, extra=extra)
203
+ return Result._create(value=value, error=None, exception=None, extra=extra)
187
204
 
188
205
  @staticmethod
189
- def failure(error: str | Exception | tuple[str, Exception], extra: Extra = None) -> Result[T]:
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(ok=None, error=error_, exception=exception, extra=extra)
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
- ok=value.get("ok"),
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.5
3
+ Version: 0.4.6
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.8.1
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=gSMy_KY9kffPFnrDzdUti_JiQP1hB3ZtF7GxhOCc1Vc,2814
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=9KfHTbv-VBbZVs8gzue4CH51ygoiEhZ9aQOmM-O-m74,3080
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=Q4mmX2VP2TNpVFf1J_YWZY-rKwmqJwI1RlMjLsDF7hY,7896
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=gz4kCCL0qr7wvw4NNiK1K2hXlrY0B0Iq4fJuYSrv3bw,3920
31
- mm_std-0.4.5.dist-info/METADATA,sha256=vsoqJPmQ237Lvv4C057WvFRJsNkSHhmawo2Fy9VqOWI,446
32
- mm_std-0.4.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
- mm_std-0.4.5.dist-info/RECORD,,
30
+ mm_std/http/http_response.py,sha256=3m9gg1O0yEo7n8p3-FO_Scicr49gU5e8uRmYHiH_4Ig,3898
31
+ mm_std-0.4.6.dist-info/METADATA,sha256=QwjIOyDK1IT_1z06G3LVq26Hvj902aDE7yi1FgaY0o4,446
32
+ mm_std-0.4.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
33
+ mm_std-0.4.6.dist-info/RECORD,,
File without changes