mm-std 0.4.17__py3-none-any.whl → 0.5.0__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/result.py DELETED
@@ -1,315 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import sys
4
- from collections.abc import Awaitable, Callable
5
- from typing import Any, Protocol, TypeGuard, TypeVar, cast
6
-
7
- from pydantic import GetCoreSchemaHandler
8
- from pydantic_core import CoreSchema, core_schema
9
-
10
- T = TypeVar("T")
11
- U = TypeVar("U")
12
-
13
- type Extra = dict[str, Any] | None
14
-
15
-
16
- class Result[T]:
17
- """
18
- A container representing either a successful result or an error.
19
- Use `Result.ok()` or `Result.err()` to create instances.
20
- """
21
-
22
- value: T | None # Success value, if any
23
- error: str | None # Error message, if any
24
- exception: Exception | None # Exception, if any. It's optional.
25
- extra: Extra # Optional extra metadata
26
-
27
- def __init__(self) -> None:
28
- raise RuntimeError("Result is not intended to be instantiated directly. Use the static methods instead.")
29
-
30
- def is_ok(self) -> bool:
31
- """
32
- Returns True if the result represents success.
33
- """
34
- return self.error is None
35
-
36
- def is_err(self) -> bool:
37
- """
38
- Returns True if the result represents an error.
39
- """
40
- return self.error is not None
41
-
42
- def is_exception(self) -> bool:
43
- """
44
- Returns True if an exception is attached to the result.
45
- """
46
- return self.exception is not None
47
-
48
- def unwrap(self, message_prefix: str | None = None, include_error: bool = True) -> T:
49
- """
50
- Returns the success value if the Result is Ok, otherwise raises a RuntimeError.
51
-
52
- Args:
53
- message_prefix: Optional custom prefix for the error message if the Result is an error.
54
- If not provided, a default message will be used.
55
- include_error: If True, appends the internal error message from the Result to the final exception message.
56
-
57
- Raises:
58
- RuntimeError: If the Result is an error.
59
-
60
- Returns:
61
- The success value of type T.
62
- """
63
- if not self.is_ok():
64
- # Use the provided message or a default fallback
65
- error_message = message_prefix or "Called unwrap() on a failure value"
66
- # Optionally append the error detail
67
- if include_error:
68
- error_message = f"{error_message}: {self.error}"
69
- # Raise with the final constructed message
70
- raise RuntimeError(error_message)
71
- # Return the success value if present
72
- return cast(T, self.value)
73
-
74
- def unwrap_or_exit(
75
- self,
76
- message_prefix: str | None = None,
77
- include_error: bool = True,
78
- exit_code: int = 1,
79
- ) -> T:
80
- """
81
- Returns the success value if the Result is Ok, otherwise prints an error to stderr and exits.
82
-
83
- Args:
84
- message_prefix: Optional custom prefix for the error message.
85
- include_error: If True, includes the internal error message in the printed message.
86
- exit_code: The exit code to use when terminating the program on error.
87
-
88
- Returns:
89
- The success value of type T.
90
-
91
- Exits:
92
- Exits the program with the specified exit code if the result is an error.
93
- """
94
- if self.is_ok():
95
- return cast(T, self.value)
96
-
97
- error_message = message_prefix or "Called unwrap_or_exit() on a failure value"
98
- if include_error:
99
- error_message = f"{error_message}: {self.error}"
100
- print(error_message, file=sys.stderr) # noqa: T201
101
- sys.exit(exit_code)
102
-
103
- def unwrap_or(self, default: T) -> T:
104
- """
105
- Returns the success value if available, otherwise returns the given default.
106
- """
107
- if not self.is_ok():
108
- return default
109
- return cast(T, self.value)
110
-
111
- def unwrap_error(self) -> str:
112
- """
113
- Returns the error message.
114
- Raises RuntimeError if the result is a success.
115
- """
116
- if self.is_ok():
117
- raise RuntimeError("Called unwrap_err() on a success value")
118
- return cast(str, self.error)
119
-
120
- def unwrap_exception(self) -> Exception:
121
- """
122
- Returns the attached exception if present.
123
- Raises RuntimeError if the result has no exception attached.
124
- """
125
- if self.exception is not None:
126
- return self.exception
127
- raise RuntimeError("No exception provided")
128
-
129
- def value_or_error(self) -> T | str:
130
- """
131
- Returns the success value if available, otherwise returns the error message.
132
- """
133
- if self.is_ok():
134
- return self.unwrap()
135
- return self.unwrap_error()
136
-
137
- def to_dict(self) -> dict[str, object]:
138
- """
139
- Returns a dictionary representation of the result.
140
- Note: the exception is converted to a string if present.
141
- """
142
- return {
143
- "value": self.value,
144
- "error": self.error,
145
- "exception": str(self.exception) if self.exception else None,
146
- "extra": self.extra,
147
- }
148
-
149
- def with_value(self, value: U) -> Result[U]:
150
- """
151
- Returns a copy of this Result with the success value replaced by `value`.
152
- The `extra` metadata is preserved.
153
- """
154
- return Result.ok(value, self.extra)
155
-
156
- def with_error(self, error: str | Exception | tuple[str, Exception]) -> Result[T]:
157
- """
158
- Returns a copy of this Result as an Err with the given `error`.
159
- Preserves existing `extra` metadata.
160
- """
161
- return Result.err(error, self.extra)
162
-
163
- def map(self, fn: Callable[[T], U]) -> Result[U]:
164
- if self.is_ok():
165
- try:
166
- new_value = fn(cast(T, self.value))
167
- return Result.ok(new_value, extra=self.extra)
168
- except Exception as e:
169
- return Result.err(("map_exception", e), extra=self.extra)
170
- return cast(Result[U], self)
171
-
172
- async def map_async(self, fn: Callable[[T], Awaitable[U]]) -> Result[U]:
173
- if self.is_ok():
174
- try:
175
- new_value = await fn(cast(T, self.value))
176
- return Result.ok(new_value, extra=self.extra)
177
- except Exception as e:
178
- return Result.err(("map_exception", e), extra=self.extra)
179
- return cast(Result[U], self)
180
-
181
- def and_then(self, fn: Callable[[T], Result[U]]) -> Result[U]:
182
- if self.is_ok():
183
- try:
184
- return fn(cast(T, self.value))
185
- except Exception as e:
186
- return Result.err(("and_then_exception", e), extra=self.extra)
187
- return cast(Result[U], self)
188
-
189
- async def and_then_async(self, fn: Callable[[T], Awaitable[Result[U]]]) -> Result[U]:
190
- if self.is_ok():
191
- try:
192
- return await fn(cast(T, self.value))
193
- except Exception as e:
194
- return Result.err(("and_then_exception", e), extra=self.extra)
195
- return cast(Result[U], self)
196
-
197
- def __repr__(self) -> str:
198
- parts: list[str] = []
199
- if self.value is not None:
200
- parts.append(f"value={self.value!r}")
201
- if self.error is not None:
202
- parts.append(f"error={self.error!r}")
203
- if self.exception is not None:
204
- parts.append(f"exception={self.exception!r}")
205
- if self.extra is not None:
206
- parts.append(f"extra={self.extra!r}")
207
- return f"Result({', '.join(parts)})"
208
-
209
- def __hash__(self) -> int:
210
- return hash(
211
- (
212
- self.value,
213
- self.error,
214
- self.exception,
215
- frozenset(self.extra.items()) if self.extra else None,
216
- )
217
- )
218
-
219
- def __eq__(self, other: object) -> bool:
220
- if not isinstance(other, Result):
221
- return False
222
- return (
223
- self.value == other.value
224
- and self.error == other.error
225
- and self.exception == other.exception
226
- and self.extra == other.extra
227
- )
228
-
229
- @classmethod
230
- def _create(cls, value: T | None, error: str | None, exception: Exception | None, extra: Extra) -> Result[T]:
231
- obj = object.__new__(cls)
232
- obj.value = value
233
- obj.error = error
234
- obj.exception = exception
235
- obj.extra = extra
236
- return obj
237
-
238
- @staticmethod
239
- def ok(value: T, extra: Extra = None) -> Result[T]:
240
- """
241
- Creates a successful Result instance.
242
-
243
- Args:
244
- value: The success value to store in the Result.
245
- extra: Optional extra metadata to associate with the Result.
246
-
247
- Returns:
248
- A Result instance representing success with the provided value.
249
- """
250
- return Result._create(value=value, error=None, exception=None, extra=extra)
251
-
252
- @staticmethod
253
- def err(error: str | Exception | tuple[str, Exception], extra: Extra = None) -> Result[T]:
254
- """
255
- Creates a Result instance representing a failure.
256
-
257
- Args:
258
- error: The error information, which can be:
259
- - A string error message
260
- - An Exception object
261
- - A tuple containing (error_message, exception)
262
- extra: Optional extra metadata to associate with the Result.
263
-
264
- Returns:
265
- A Result instance representing failure with the provided error information.
266
- """
267
- if isinstance(error, tuple):
268
- error_, exception = error
269
- elif isinstance(error, Exception):
270
- error_ = "exception"
271
- exception = error
272
- else:
273
- error_ = error
274
- exception = None
275
-
276
- return Result._create(value=None, error=error_, exception=exception, extra=extra)
277
-
278
- @classmethod
279
- def __get_pydantic_core_schema__(cls, _source_type: type[Any], _handler: GetCoreSchemaHandler) -> CoreSchema:
280
- return core_schema.no_info_after_validator_function(
281
- cls._validate,
282
- core_schema.any_schema(),
283
- serialization=core_schema.plain_serializer_function_ser_schema(lambda x: x.to_dict()),
284
- )
285
-
286
- @classmethod
287
- def _validate(cls, value: object) -> Result[Any]:
288
- if isinstance(value, cls):
289
- return value
290
- if isinstance(value, dict):
291
- return cls._create(
292
- value=value.get("value"),
293
- error=value.get("error"),
294
- exception=value.get("exception"),
295
- extra=value.get("extra"),
296
- )
297
- raise TypeError(f"Invalid value for Result: {value}")
298
-
299
-
300
- class OkResult(Protocol[T]):
301
- value: T
302
- error: None
303
-
304
-
305
- class ErrResult(Protocol[T]): # type:ignore[misc]
306
- value: None
307
- error: str
308
-
309
-
310
- def is_ok(res: Result[T]) -> TypeGuard[OkResult[T]]:
311
- return res.is_ok()
312
-
313
-
314
- def is_err(res: Result[T]) -> TypeGuard[ErrResult[T]]:
315
- return res.is_err()
mm_std/str.py DELETED
@@ -1,105 +0,0 @@
1
- import re
2
- from collections.abc import Iterable
3
- from decimal import Decimal
4
-
5
- import pydash
6
-
7
-
8
- def str_to_list(
9
- data: str | Iterable[object] | None,
10
- lower: bool = False,
11
- remove_comments: bool = False,
12
- unique: bool = False,
13
- split_line: bool = False,
14
- ) -> list[str]:
15
- match data:
16
- case None | "" | []:
17
- return []
18
- case str():
19
- if lower:
20
- data = data.lower()
21
- result = [line.strip() for line in data.split("\n") if line.strip()]
22
- if remove_comments:
23
- result = [line.split("#")[0].strip() for line in result]
24
- result = [line for line in result if line]
25
- if unique:
26
- result = pydash.uniq(result)
27
-
28
- if split_line:
29
- new_result = []
30
- for line in result:
31
- new_result.extend(line.split())
32
- return new_result
33
-
34
- return result
35
- case Iterable():
36
- return [str(x) for x in data]
37
- case _:
38
- raise ValueError("data has a wrong type")
39
-
40
-
41
- def number_with_separator(
42
- value: float | str | Decimal | None,
43
- prefix: str = "",
44
- suffix: str = "",
45
- separator: str = "_",
46
- hide_zero: bool = False,
47
- round_digits: int = 2,
48
- ) -> str:
49
- if value is None or value == "":
50
- return ""
51
- if float(value) == 0:
52
- return "" if hide_zero else f"{prefix}0{suffix}"
53
- if float(value) > 1000:
54
- value = "".join(
55
- reversed([x + (separator if i and not i % 3 else "") for i, x in enumerate(reversed(str(int(value))))]),
56
- )
57
- else:
58
- value = round(value, round_digits) # type:ignore[arg-type,assignment]
59
-
60
- return f"{prefix}{value}{suffix}"
61
-
62
-
63
- def str_starts_with_any(value: str, prefixes: list[str]) -> bool:
64
- """check if str starts with any of prefixes"""
65
- return any(value.startswith(prefix) for prefix in prefixes)
66
-
67
-
68
- def str_ends_with_any(value: str, prefixes: list[str]) -> bool:
69
- """check if str ends with any of prefixes"""
70
- return any(value.endswith(prefix) for prefix in prefixes)
71
-
72
-
73
- def str_contains_any(value: str, substrings: list[str]) -> bool:
74
- """Check if str contains any of the given substrings"""
75
- return any(substring in value for substring in substrings)
76
-
77
-
78
- def split_on_plus_minus_tokens(value: str) -> list[str]:
79
- value = "".join(value.split())
80
- if not value:
81
- raise ValueError("value is empty")
82
- if "++" in value:
83
- raise ValueError("++ in value")
84
- if "--" in value:
85
- raise ValueError("-- in value")
86
- if value.endswith("-"):
87
- raise ValueError("ends with -")
88
- if value.endswith("+"):
89
- raise ValueError("ends with +")
90
-
91
- if not value.startswith("+") and not value.startswith("-"):
92
- value = "+" + value
93
-
94
- result: list[str] = []
95
- rest_value = value
96
- while True:
97
- if not rest_value:
98
- return result
99
- items = re.split(r"[+\-]", rest_value)
100
- if rest_value.startswith("+"):
101
- result.append("+" + items[1])
102
- rest_value = rest_value.removeprefix("+" + items[1])
103
- elif rest_value.startswith("-"):
104
- result.append("-" + items[1])
105
- rest_value = rest_value.removeprefix("-" + items[1])
mm_std/toml.py DELETED
@@ -1,22 +0,0 @@
1
- from collections.abc import Mapping
2
- from decimal import Decimal
3
-
4
- import tomlkit
5
- from tomlkit.items import Float, Trivia
6
-
7
-
8
- def encode_decimal(value: object) -> Float:
9
- if isinstance(value, Decimal):
10
- return Float(value=float(value), trivia=Trivia(), raw=str(value))
11
- raise TypeError(f"Cannot convert {type(value)} to TOML item")
12
-
13
-
14
- tomlkit.register_encoder(encode_decimal)
15
-
16
-
17
- def toml_dumps(data: Mapping[str, object], sort_keys: bool = False) -> str:
18
- return tomlkit.dumps(data, sort_keys=sort_keys)
19
-
20
-
21
- def toml_loads(string: str | bytes) -> tomlkit.TOMLDocument:
22
- return tomlkit.loads(string)
mm_std/types_.py DELETED
@@ -1,8 +0,0 @@
1
- from collections.abc import Awaitable, Callable
2
- from typing import Any
3
-
4
- type CallableAny = Callable[..., Any]
5
- type Func = Callable[..., object]
6
- type AsyncFunc = Callable[..., Awaitable[object]]
7
- type Args = tuple[object, ...]
8
- type Kwargs = dict[str, object]
mm_std/zip.py DELETED
@@ -1,9 +0,0 @@
1
- from pathlib import Path
2
- from zipfile import ZipFile
3
-
4
-
5
- def read_text_from_zip_archive(zip_archive_path: Path, filename: str | None = None, password: str | None = None) -> str:
6
- with ZipFile(zip_archive_path) as zipfile:
7
- if filename is None:
8
- filename = zipfile.filelist[0].filename
9
- return zipfile.read(filename, pwd=password.encode() if password else None).decode()
@@ -1,14 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: mm-std
3
- Version: 0.4.17
4
- Requires-Python: >=3.12
5
- Requires-Dist: aiohttp-socks~=0.10.1
6
- Requires-Dist: aiohttp~=3.12.2
7
- Requires-Dist: cryptography~=45.0.3
8
- Requires-Dist: pydantic-settings>=2.9.1
9
- Requires-Dist: pydantic~=2.11.5
10
- Requires-Dist: pydash~=8.0.5
11
- Requires-Dist: python-dotenv~=1.1.0
12
- Requires-Dist: requests[socks]~=2.32.3
13
- Requires-Dist: rich>=13.9.0
14
- Requires-Dist: tomlkit~=0.13.2
@@ -1,35 +0,0 @@
1
- mm_std/__init__.py,sha256=OpZAZw3BG3wyDnlRR3p2BZO2cZcE-imScPCf2qRWuUk,3189
2
- mm_std/command.py,sha256=ze286wjUjg0QSTgIu-2WZks53_Vclg69UaYYgPpQvCU,1283
3
- mm_std/config.py,sha256=3-FxFCtOffY2t9vuu9adojwbzTPwdAH2P6qDaZnHLbY,3206
4
- mm_std/date.py,sha256=976eEkSONuNqHQBgSRu8hrtH23tJqztbmHFHLdbP2TY,1879
5
- mm_std/dict.py,sha256=DxFbZnl5KK4vJ4wLH_pCT0PWzfaIL3YyLNpRsVexfjw,1465
6
- mm_std/env.py,sha256=5zaR9VeIfObN-4yfgxoFeU5IM1GDeZZj9SuYf7t9sOA,125
7
- mm_std/fs.py,sha256=RwarNRJq3tIMG6LVX_g03hasfYpjYFh_O27oVDt5IPQ,291
8
- mm_std/json_.py,sha256=YVvROb5egcF1aQ2fXzyWG8Yw0JvYwpNBwtcBzsOADPo,1133
9
- mm_std/log.py,sha256=0TkTsAlUTt00gjgukvsvnZRIAGELq0MI6Lv8mKP-Wz4,2887
10
- mm_std/net.py,sha256=qdRCBIDneip6FaPNe5mx31UtYVmzqam_AoUF7ydEyjA,590
11
- mm_std/print_.py,sha256=zB7sVbSSF8RffMxvnOdbKCXjCKtKzKV3R68pBri4NkQ,1638
12
- mm_std/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- mm_std/random_.py,sha256=OuUX4VJeSd13NZBya4qrGpR2TfN7_87tfebOY6DBUnI,1113
14
- mm_std/result.py,sha256=ltq9lJ-mZYRWuwGfhVtsfb4CX9Gw-tOUKyj5cEoyHiE,10706
15
- mm_std/str.py,sha256=BEjJ1p5O4-uSYK0h-enasSSDdwzkBbiwdQ4_dsrlEE8,3257
16
- mm_std/toml.py,sha256=CNznWKR0bpOxS6e3VB5LGS-Oa9lW-wterkcPUFtPcls,610
17
- mm_std/types_.py,sha256=9FGd2q47a8M9QQgsWJR1Kq34jLxBAkYSoJuwih4PPqg,257
18
- mm_std/zip.py,sha256=axzF1BwcIygtfNNTefZH7hXKaQqwe-ZH3ChuRWr9dnk,396
19
- mm_std/concurrency/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- mm_std/concurrency/async_decorators.py,sha256=xEpyipzp3ZhaPHtdeTE-Ikrt67SUTFKBE6LQPeoeh6Q,1735
21
- mm_std/concurrency/async_scheduler.py,sha256=TSu2WDJ8vfKZ4-saM3iZdcpGz8yPpSmud-7jJ4XKdJs,5579
22
- mm_std/concurrency/async_task_runner.py,sha256=EN7tN2enkVYVgDbhSiAr-_W4o9m9wBXCveXGT8aVXII,4994
23
- mm_std/concurrency/sync_decorators.py,sha256=syCQBOmN7qPO55yzgJB2rbkh10CVww376hmyvs6e5tA,1080
24
- mm_std/concurrency/sync_scheduler.py,sha256=j4tBL_cBI1spr0cZplTA7N2CoYsznuORMeRN8rpR6gY,2407
25
- mm_std/concurrency/sync_task_runner.py,sha256=s5JPlLYLGQGHIxy4oDS-PN7O9gcy-yPZFoNm8RQwzcw,1780
26
- mm_std/crypto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- mm_std/crypto/fernet.py,sha256=jdk0_TCmeU0pPXMyz9xH6kQHSjjZ9GcGClBwQps5vBo,340
28
- mm_std/crypto/openssl.py,sha256=x8LTRG6-jXLucMcsWS746V7B5fGrJ1c27d08rNK5-Ng,8173
29
- mm_std/http/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
- mm_std/http/http_request.py,sha256=6bg3t49c3dG0jKRFxhcceeYb5yKrMoZwuyb25zBG3tY,4088
31
- mm_std/http/http_request_sync.py,sha256=zXLeDplYWTFIwaD1Ydyg9yTi37WcI-fReLM0mVnuvhM,1835
32
- mm_std/http/http_response.py,sha256=7ZllZFPKJ9s6m-18Dfhrm7hwc2XFnyX7ppt0O8gNmlE,3916
33
- mm_std-0.4.17.dist-info/METADATA,sha256=0rG7NElzy9ZOLdx3hnag32xev6avidgXTZe46h85waU,414
34
- mm_std-0.4.17.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
35
- mm_std-0.4.17.dist-info/RECORD,,