mm-std 0.1.9__py3-none-any.whl → 0.1.11__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
@@ -15,16 +15,17 @@ from .date import utc_now as utc_now
15
15
  from .date import utc_random as utc_random
16
16
  from .dict import replace_empty_values as replace_empty_values
17
17
  from .env import get_dotenv as get_dotenv
18
+ from .http_ import CHROME_USER_AGENT as CHROME_USER_AGENT
19
+ from .http_ import FIREFOX_USER_AGENT as FIREFOX_USER_AGENT
20
+ from .http_ import HResponse as HResponse
21
+ from .http_ import add_query_params_to_url as add_query_params_to_url
22
+ from .http_ import hr as hr
23
+ from .http_ import hrequest as hrequest
18
24
  from .json_ import CustomJSONEncoder as CustomJSONEncoder
19
25
  from .json_ import json_dumps as json_dumps
20
26
  from .log import init_logger as init_logger
21
- from .net import CHROME_USER_AGENT as CHROME_USER_AGENT
22
- from .net import FIREFOX_USER_AGENT as FIREFOX_USER_AGENT
23
- from .net import HResponse as HResponse
24
- from .net import add_query_params_to_url as add_query_params_to_url
25
27
  from .net import check_port as check_port
26
- from .net import hr as hr
27
- from .net import hrequest as hrequest
28
+ from .net import get_free_local_port as get_free_local_port
28
29
  from .print_ import PrintFormat as PrintFormat
29
30
  from .print_ import fatal as fatal
30
31
  from .print_ import print_console as print_console
@@ -42,5 +43,4 @@ from .str import number_with_separator as number_with_separator
42
43
  from .str import str_ends_with_any as str_ends_with_any
43
44
  from .str import str_starts_with_any as str_starts_with_any
44
45
  from .str import str_to_list as str_to_list
45
- from .telegram import send_telegram_message as send_telegram_message
46
46
  from .zip import read_text_from_zip_archive as read_text_from_zip_archive
mm_std/command.py CHANGED
@@ -19,7 +19,7 @@ def run_command(cmd: str, timeout: int | None = 60, capture_output: bool = True,
19
19
  if echo_cmd_console:
20
20
  print(cmd) # noqa: T201
21
21
  try:
22
- process = subprocess.run(cmd, timeout=timeout, capture_output=capture_output, shell=True, check=False) # nosec
22
+ process = subprocess.run(cmd, timeout=timeout, capture_output=capture_output, shell=True, check=False) # noqa: S602 # nosec
23
23
  stdout = process.stdout.decode("utf-8") if capture_output else ""
24
24
  stderr = process.stderr.decode("utf-8") if capture_output else ""
25
25
  return CommandResult(stdout=stdout, stderr=stderr, code=process.returncode)
mm_std/concurrency.py CHANGED
@@ -88,7 +88,7 @@ def synchronized[T, **P](fn: Callable[P, T]) -> Callable[P, T]:
88
88
 
89
89
 
90
90
  class Scheduler:
91
- def __init__(self, log: Logger, loop_delay: float = 0.5, debug: bool = False):
91
+ def __init__(self, log: Logger, loop_delay: float = 0.5, debug: bool = False) -> None:
92
92
  self.log = log
93
93
  self.debug = debug
94
94
  self.loop_delay = loop_delay
@@ -121,8 +121,8 @@ class Scheduler:
121
121
  try:
122
122
  job.func(*job.args)
123
123
  self._debug(f"_run_job: {job} done")
124
- except Exception as e:
125
- self.log.exception("scheduler error: %s", str(e))
124
+ except Exception:
125
+ self.log.exception("scheduler error")
126
126
  self._debug(f"_run_job: {job} error")
127
127
  finally:
128
128
  job.is_running = False
mm_std/config.py CHANGED
@@ -4,7 +4,7 @@ from pathlib import Path
4
4
  import yaml
5
5
  from pydantic import BaseModel, ConfigDict, ValidationError
6
6
 
7
- from .print_ import PrintFormat, print_console, print_json, print_plain, print_table
7
+ from .result import Err, Ok, Result
8
8
  from .str import str_to_list
9
9
  from .zip import read_text_from_zip_archive
10
10
 
@@ -29,47 +29,27 @@ class BaseConfig(BaseModel):
29
29
  return v
30
30
 
31
31
  @classmethod
32
- def read_config[T]( # nosec: B107
33
- cls: type[T],
34
- config_path: io.TextIOWrapper | str | Path,
35
- error_print_type: PrintFormat = PrintFormat.PLAIN,
36
- zip_password: str = "",
37
- ) -> T:
32
+ def read_config[T](cls: type[T], config_path: io.TextIOWrapper | str | Path, zip_password: str = "") -> Result[T]: # nosec
38
33
  try:
39
34
  # is it zip archive?
40
35
  if isinstance(config_path, str) and config_path.endswith(".zip"):
41
36
  config_path = str(Path(config_path).expanduser())
42
- return cls(**yaml.full_load(read_text_from_zip_archive(config_path, password=zip_password)))
37
+ return Ok(cls(**yaml.full_load(read_text_from_zip_archive(config_path, password=zip_password))))
43
38
  if isinstance(config_path, io.TextIOWrapper) and config_path.name.endswith(".zip"):
44
39
  config_path = str(Path(config_path.name).expanduser())
45
- return cls(**yaml.full_load(read_text_from_zip_archive(config_path, password=zip_password)))
40
+ return Ok(cls(**yaml.full_load(read_text_from_zip_archive(config_path, password=zip_password))))
46
41
  if isinstance(config_path, Path) and config_path.name.endswith(".zip"):
47
42
  config_path = str(config_path.expanduser())
48
- return cls(**yaml.full_load(read_text_from_zip_archive(config_path, password=zip_password)))
43
+ return Ok(cls(**yaml.full_load(read_text_from_zip_archive(config_path, password=zip_password))))
49
44
 
50
45
  # plain yml file
51
46
  if isinstance(config_path, str):
52
- return cls(**yaml.full_load(Path(config_path).expanduser().read_text()))
53
- elif isinstance(config_path, Path):
54
- return cls(**yaml.full_load(config_path.expanduser().read_text()))
55
- else:
56
- return cls(**yaml.full_load(config_path))
47
+ return Ok(cls(**yaml.full_load(Path(config_path).expanduser().read_text())))
48
+ if isinstance(config_path, Path):
49
+ return Ok(cls(**yaml.full_load(config_path.expanduser().read_text())))
50
+
51
+ return Ok(cls(**yaml.full_load(config_path)))
57
52
  except ValidationError as err:
58
- print_plain("config validation errors", error_print_type)
59
- json_errors = []
60
- rows = []
61
- for e in err.errors():
62
- loc = e["loc"]
63
- field = ".".join(str(lo) for lo in loc) if len(loc) > 0 else ""
64
- print_plain(f"{field} {e['msg']}", error_print_type)
65
- json_errors.append({field: e["msg"]})
66
- rows.append([field, e["msg"]])
67
- print_table("config validation errors", ["field", "message"], rows)
68
- print_json({"errors": json_errors}, error_print_type)
69
- exit(1)
53
+ return Err("validator error", data={"validaton_errors": err})
70
54
  except Exception as err:
71
- if error_print_type == "json":
72
- print_json({"exception": str(err)})
73
- else:
74
- print_console(f"config error: {err!s}")
75
- exit(1)
55
+ return Err(err)
mm_std/date.py CHANGED
@@ -43,10 +43,10 @@ def parse_date(value: str, ignore_tz: bool = False) -> datetime:
43
43
 
44
44
  for fmt in date_formats:
45
45
  try:
46
- dt = datetime.strptime(value, fmt)
46
+ dt = datetime.strptime(value, fmt) # noqa: DTZ007
47
47
  if ignore_tz and dt.tzinfo is not None:
48
48
  dt = dt.replace(tzinfo=None)
49
- return dt
49
+ return dt # noqa: TRY300
50
50
  except ValueError:
51
51
  continue
52
52
  raise ValueError(f"Time data '{value}' does not match any known format.")
mm_std/http_.py ADDED
@@ -0,0 +1,143 @@
1
+ import json
2
+ from dataclasses import asdict, dataclass, field
3
+ from typing import Any
4
+ from urllib.parse import urlencode
5
+
6
+ import httpx
7
+ import pydash
8
+ from httpx._types import AuthTypes
9
+
10
+ from mm_std.result import Err, Ok, Result
11
+
12
+ FIREFOX_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:134.0) Gecko/20100101 Firefox/134.0"
13
+ SAFARI_USER_AGENT = (
14
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.2 Safari/605.1.15"
15
+ )
16
+ CHROME_USER_AGENT = (
17
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
18
+ )
19
+
20
+
21
+ @dataclass
22
+ class HResponse:
23
+ code: int = 0
24
+ error: str | None = None
25
+ body: str = ""
26
+ headers: dict[str, str] = field(default_factory=dict)
27
+
28
+ _json_data: Any = None
29
+ _json_parsed = False
30
+ _json_parsed_error = False
31
+
32
+ def _parse_json(self) -> None:
33
+ try:
34
+ self._json_data = None
35
+ self._json_data = json.loads(self.body)
36
+ self._json_parsed_error = False
37
+ except json.JSONDecodeError:
38
+ self._json_parsed_error = True
39
+ self._json_parsed = True
40
+
41
+ @property
42
+ def json(self) -> Any: # noqa: ANN401
43
+ if not self._json_parsed:
44
+ self._parse_json()
45
+ return self._json_data
46
+
47
+ @property
48
+ def json_parse_error(self) -> bool:
49
+ if not self._json_parsed:
50
+ self._parse_json()
51
+ return self._json_parsed_error
52
+
53
+ @property
54
+ def content_type(self) -> str | None:
55
+ for key in self.headers:
56
+ if key.lower() == "content-type":
57
+ return self.headers[key]
58
+ return None
59
+
60
+ def to_err_result[T](self, error: str | None = None) -> Err:
61
+ return Err(error or self.error or "error", data=asdict(self))
62
+
63
+ def to_ok_result[T](self, result: T) -> Result[T]:
64
+ return Ok(result, data=asdict(self))
65
+
66
+ def is_error(self) -> bool:
67
+ return self.error is not None
68
+
69
+ def is_timeout_error(self) -> bool:
70
+ return self.error == "timeout"
71
+
72
+ def is_proxy_error(self) -> bool:
73
+ return self.error == "proxy_error"
74
+
75
+ def is_connection_error(self) -> bool:
76
+ return self.error is not None and self.error.startswith("connection_error:")
77
+
78
+ def to_dict(self) -> dict[str, Any]:
79
+ return pydash.omit(asdict(self), "_json_data")
80
+
81
+
82
+ def hrequest(
83
+ url: str,
84
+ *,
85
+ method: str = "GET",
86
+ proxy: str | None = None,
87
+ params: dict[str, Any] | None = None,
88
+ headers: dict[str, Any] | None = None,
89
+ cookies: dict[str, Any] | None = None,
90
+ timeout: float = 10,
91
+ user_agent: str | None = None,
92
+ json_params: bool = True,
93
+ auth: AuthTypes | None = None,
94
+ verify: bool = True,
95
+ ) -> HResponse:
96
+ query_params: dict[str, Any] | None = None
97
+ data: dict[str, Any] | None = None
98
+ json_: dict[str, Any] | None = None
99
+ method = method.upper()
100
+ if not headers:
101
+ headers = {}
102
+ if user_agent:
103
+ headers["user-agent"] = user_agent
104
+ if method == "GET":
105
+ query_params = params
106
+ elif json_params:
107
+ json_ = params
108
+ else:
109
+ data = params
110
+
111
+ try:
112
+ r = httpx.request(
113
+ method,
114
+ url,
115
+ proxy=proxy,
116
+ timeout=timeout,
117
+ cookies=cookies,
118
+ auth=auth,
119
+ verify=verify,
120
+ headers=headers,
121
+ params=query_params,
122
+ json=json_,
123
+ data=data,
124
+ )
125
+ return HResponse(code=r.status_code, body=r.text, headers=dict(r.headers))
126
+ except httpx.TimeoutException:
127
+ return HResponse(error="timeout")
128
+ except httpx.ProxyError:
129
+ return HResponse(error="proxy_error")
130
+ except httpx.HTTPError as err:
131
+ return HResponse(error=f"connection_error: {err}")
132
+ except Exception as err:
133
+ return HResponse(error=f"exception: {err}")
134
+
135
+
136
+ def add_query_params_to_url(url: str, params: dict[str, object]) -> str:
137
+ query_params = urlencode({k: v for k, v in params.items() if v is not None})
138
+ if query_params:
139
+ url += f"?{query_params}"
140
+ return url
141
+
142
+
143
+ hr = hrequest
mm_std/json_.py CHANGED
@@ -4,7 +4,6 @@ from datetime import date, datetime
4
4
  from decimal import Decimal
5
5
  from enum import Enum
6
6
  from json import JSONEncoder
7
- from typing import Any
8
7
 
9
8
  from pydantic import BaseModel
10
9
 
@@ -12,7 +11,7 @@ from mm_std.result import Err, Ok
12
11
 
13
12
 
14
13
  class CustomJSONEncoder(JSONEncoder):
15
- def default(self, o: Any) -> Any:
14
+ def default(self, o: object) -> object:
16
15
  if isinstance(o, Ok):
17
16
  return {"ok": o.ok}
18
17
  if isinstance(o, Err):
mm_std/net.py CHANGED
@@ -1,134 +1,6 @@
1
- import json
2
1
  import socket
3
2
  import time
4
- from dataclasses import asdict, dataclass, field
5
- from json import JSONDecodeError
6
- from typing import Any, cast
7
- from urllib.parse import urlencode
8
-
9
- import httpx
10
- import pydash
11
-
12
- from mm_std.result import Err, Ok, Result
13
-
14
- FIREFOX_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:133.0) Gecko/20100101 Firefox/133.0"
15
- SAFARI_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1.1 Safari/605.1.15" # fmt: skip # noqa
16
- CHROME_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" # fmt: skip # noqa
17
-
18
-
19
- @dataclass
20
- class HResponse:
21
- code: int = 0
22
- error: str | None = None
23
- body: str = ""
24
- headers: dict[str, str] = field(default_factory=dict)
25
-
26
- _json_data: Any = None
27
- _json_parsed = False
28
- _json_parsed_error = False
29
-
30
- def _parse_json(self) -> None:
31
- try:
32
- self._json_data = None
33
- self._json_data = json.loads(self.body)
34
- self._json_parsed_error = False
35
- except JSONDecodeError:
36
- self._json_parsed_error = True
37
- self._json_parsed = True
38
-
39
- @property
40
- def json(self) -> Any:
41
- if not self._json_parsed:
42
- self._parse_json()
43
- return self._json_data
44
-
45
- @property
46
- def json_parse_error(self) -> bool:
47
- if not self._json_parsed:
48
- self._parse_json()
49
- return self._json_parsed_error
50
-
51
- @property
52
- def content_type(self) -> str | None:
53
- for key in self.headers.keys():
54
- if key.lower() == "content-type":
55
- return self.headers[key]
56
- return None
57
-
58
- def to_err_result[T](self, error: str | None = None) -> Err:
59
- return Err(error or self.error or "error", data=asdict(self))
60
-
61
- def to_ok_result[T](self, result: T) -> Result[T]:
62
- return Ok(result, data=asdict(self))
63
-
64
- def is_error(self) -> bool:
65
- return self.error is not None
66
-
67
- def is_timeout_error(self) -> bool:
68
- return self.error == "timeout"
69
-
70
- def is_proxy_error(self) -> bool:
71
- return self.error == "proxy_error"
72
-
73
- def is_connection_error(self) -> bool:
74
- return self.error is not None and self.error.startswith("connection_error:")
75
-
76
- def to_dict(self) -> dict[str, Any]:
77
- return pydash.omit(asdict(self), "_json_data")
78
-
79
-
80
- def hrequest(
81
- url: str,
82
- *,
83
- method: str = "GET",
84
- proxy: str | None = None,
85
- params: dict[str, Any] | None = None,
86
- headers: dict[str, Any] | None = None,
87
- cookies: dict[str, Any] | None = None,
88
- timeout: float = 10,
89
- user_agent: str | None = None,
90
- json_params: bool = True,
91
- auth: Any = None,
92
- verify: bool = True,
93
- ) -> HResponse:
94
- query_params: dict[str, Any] | None = None
95
- data: dict[str, Any] | None = None
96
- json_: dict[str, Any] | None = None
97
- method = method.upper()
98
- if not headers:
99
- headers = {}
100
- if user_agent:
101
- headers["user-agent"] = user_agent
102
- if method == "GET":
103
- query_params = params
104
- elif json_params:
105
- json_ = params
106
- else:
107
- data = params
108
-
109
- try:
110
- r = httpx.request(
111
- method,
112
- url,
113
- proxy=proxy,
114
- timeout=timeout,
115
- cookies=cookies,
116
- auth=auth,
117
- verify=verify,
118
- headers=headers,
119
- params=query_params,
120
- json=json_,
121
- data=data,
122
- )
123
- return HResponse(code=r.status_code, body=r.text, headers=dict(r.headers))
124
- except httpx.TimeoutException:
125
- return HResponse(error="timeout")
126
- except httpx.ProxyError:
127
- return HResponse(error="proxy_error")
128
- except httpx.HTTPError as err:
129
- return HResponse(error=f"connection_error: {err}")
130
- except Exception as err:
131
- return HResponse(error=f"exception: {err}")
3
+ from typing import cast
132
4
 
133
5
 
134
6
  def check_port(ip: str, port: int, attempts: int = 3, sleep_seconds: float = 1, timeout: float = 1) -> bool:
@@ -148,13 +20,3 @@ def get_free_local_port() -> int:
148
20
  port = sock.getsockname()[1]
149
21
  sock.close()
150
22
  return cast(int, port)
151
-
152
-
153
- def add_query_params_to_url(url: str, params: dict[str, object]) -> str:
154
- query_params = urlencode({k: v for k, v in params.items() if v is not None})
155
- if query_params:
156
- url += f"?{query_params}"
157
- return url
158
-
159
-
160
- hr = hrequest
mm_std/print_.py CHANGED
@@ -26,11 +26,10 @@ def print_console(*messages: object, print_json: bool = False) -> None:
26
26
  message = messages[0]
27
27
  if isinstance(message, str):
28
28
  print(message) # noqa: T201
29
+ elif print_json:
30
+ rich.print_json(json_dumps(message))
29
31
  else:
30
- if print_json:
31
- rich.print_json(json_dumps(message))
32
- else:
33
- rich.print(message)
32
+ rich.print(message)
34
33
  else:
35
34
  rich.print(messages)
36
35
 
mm_std/result.py CHANGED
@@ -1,29 +1,30 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
- from collections.abc import Callable
5
- from typing import Any, Literal, NoReturn
4
+ from typing import TYPE_CHECKING, Any, Literal, NoReturn
6
5
 
7
6
  from pydantic_core import core_schema
8
7
 
8
+ if TYPE_CHECKING:
9
+ from collections.abc import Callable
10
+
9
11
 
10
12
  class Ok[T]:
11
13
  __match_args__ = ("ok",)
12
14
 
13
- def __init__(self, ok: T, data: Any = None) -> None:
15
+ def __init__(self, ok: T, data: object = None) -> None:
14
16
  self.ok = ok
15
17
  self.data = data
16
18
 
17
19
  def __repr__(self) -> str:
18
20
  if self.data is None:
19
21
  return f"Ok({self.ok!r})"
20
- else:
21
- return f"Ok({self.ok!r}, data={self.data!r})"
22
+ return f"Ok({self.ok!r}, data={self.data!r})"
22
23
 
23
- def __eq__(self, other: Any) -> bool:
24
+ def __eq__(self, other: object) -> bool:
24
25
  return isinstance(other, Ok) and self.ok == other.ok and self.data == other.data
25
26
 
26
- def __ne__(self, other: Any) -> bool:
27
+ def __ne__(self, other: object) -> bool:
27
28
  return not (self == other)
28
29
 
29
30
  def __hash__(self) -> int:
@@ -54,26 +55,26 @@ class Ok[T]:
54
55
  def unwrap_or[U](self, _default: U) -> T:
55
56
  return self.ok
56
57
 
57
- def unwrap_or_else(self, op: object) -> T:
58
+ def unwrap_or_else(self, _op: object) -> T:
58
59
  return self.ok
59
60
 
60
- def unwrap_or_raise(self, e: object) -> T:
61
+ def unwrap_or_raise(self, _e: object) -> T:
61
62
  return self.ok
62
63
 
63
64
  def map[U](self, op: Callable[[T], U]) -> Ok[U]:
64
65
  return Ok(op(self.ok), data=self.data)
65
66
 
66
- def map_or[U](self, default: object, op: Callable[[T], U]) -> U:
67
+ def map_or[U](self, _default: object, op: Callable[[T], U]) -> U:
67
68
  return op(self.ok)
68
69
 
69
- def map_or_else[U](self, err_op: object, ok_op: Callable[[T], U]) -> U:
70
+ def map_or_else[U](self, _err_op: object, ok_op: Callable[[T], U]) -> U:
70
71
  """
71
72
  The contained result is `Ok`, so return original value mapped to
72
73
  a new value using the passed in `op` function.
73
74
  """
74
75
  return ok_op(self.ok)
75
76
 
76
- def map_err(self, op: object) -> Ok[T]:
77
+ def map_err(self, _op: object) -> Ok[T]:
77
78
  """
78
79
  The contained result is `Ok`, so return `Ok` with the original value
79
80
  """
@@ -93,7 +94,7 @@ class Ok[T]:
93
94
  res.data = self.data
94
95
  return res
95
96
 
96
- def or_else(self, op: object) -> Ok[T]:
97
+ def or_else(self, _op: object) -> Ok[T]:
97
98
  return self
98
99
 
99
100
  def ok_or_err(self) -> T | str:
@@ -103,7 +104,7 @@ class Ok[T]:
103
104
  return self.ok
104
105
 
105
106
  @classmethod
106
- def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema:
107
+ def __get_pydantic_core_schema__(cls, _source_type: object, _handler: object) -> core_schema.CoreSchema:
107
108
  return core_schema.model_schema(
108
109
  cls,
109
110
  core_schema.model_fields_schema(
@@ -118,20 +119,19 @@ class Ok[T]:
118
119
  class Err:
119
120
  __match_args__ = ("err",)
120
121
 
121
- def __init__(self, err: str | Exception, data: Any = None) -> None:
122
+ def __init__(self, err: str | Exception, data: object = None) -> None:
122
123
  self.err = f"exception: {err}" if isinstance(err, Exception) else err
123
124
  self.data = data
124
125
 
125
126
  def __repr__(self) -> str:
126
127
  if self.data is None:
127
128
  return f"Err({self.err!r})"
128
- else:
129
- return f"Err({self.err!r}, data={self.data!r})"
129
+ return f"Err({self.err!r}, data={self.data!r})"
130
130
 
131
- def __eq__(self, other: Any) -> bool:
131
+ def __eq__(self, other: object) -> bool:
132
132
  return isinstance(other, Err) and self.err == other.err and self.data == other.data
133
133
 
134
- def __ne__(self, other: Any) -> bool:
134
+ def __ne__(self, other: object) -> bool:
135
135
  return not (self == other)
136
136
 
137
137
  def __hash__(self) -> int:
@@ -199,25 +199,25 @@ class Err:
199
199
  """
200
200
  raise e(self.err)
201
201
 
202
- def map(self, op: object) -> Err:
202
+ def map(self, _op: object) -> Err:
203
203
  """
204
204
  Return `Err` with the same value
205
205
  """
206
206
  return self
207
207
 
208
- def map_or[U](self, default: U, op: object) -> U:
208
+ def map_or[U](self, default: U, _op: object) -> U:
209
209
  """
210
210
  Return the default value
211
211
  """
212
212
  return default
213
213
 
214
- def map_or_else[U](self, err_op: Callable[[str], U], ok_op: object) -> U:
214
+ def map_or_else[U](self, err_op: Callable[[str], U], _ok_op: object) -> U:
215
215
  """
216
216
  Return the result of the default operation
217
217
  """
218
218
  return err_op(self.err)
219
219
 
220
- def and_then(self, op: object) -> Err:
220
+ def and_then(self, _op: object) -> Err:
221
221
  """
222
222
  The contained result is `Err`, so return `Err` with the original value
223
223
  """
@@ -230,7 +230,7 @@ class Err:
230
230
  return None
231
231
 
232
232
  @classmethod
233
- def __get_pydantic_core_schema__(cls, _source_type: Any, _handler: Any) -> core_schema.CoreSchema:
233
+ def __get_pydantic_core_schema__(cls, _source_type: object, _handler: object) -> core_schema.CoreSchema:
234
234
  return core_schema.model_schema(
235
235
  cls,
236
236
  core_schema.model_fields_schema(
@@ -257,7 +257,7 @@ class UnwrapError(Exception):
257
257
  return self._result
258
258
 
259
259
 
260
- def try_ok[T](fn: Callable[..., Result[T]], *, args: tuple[object], attempts: int, delay: int | float = 0) -> Result[T]:
260
+ def try_ok[T](fn: Callable[..., Result[T]], *, args: tuple[object], attempts: int, delay: float = 0) -> Result[T]:
261
261
  if attempts <= 0:
262
262
  raise ValueError("attempts must be more than zero")
263
263
  res: Result[T] = Err("not started")
mm_std/str.py CHANGED
@@ -62,18 +62,12 @@ def number_with_separator(
62
62
 
63
63
  def str_starts_with_any(value: str, prefixes: list[str]) -> bool:
64
64
  """check if str starts with any of prefixes"""
65
- for prefix in prefixes:
66
- if value.startswith(prefix):
67
- return True
68
- return False
65
+ return any(value.startswith(prefix) for prefix in prefixes)
69
66
 
70
67
 
71
68
  def str_ends_with_any(value: str, prefixes: list[str]) -> bool:
72
69
  """check if str ends with any of prefixes"""
73
- for prefix in prefixes:
74
- if value.endswith(prefix):
75
- return True
76
- return False
70
+ return any(value.endswith(prefix) for prefix in prefixes)
77
71
 
78
72
 
79
73
  def split_on_plus_minus_tokens(value: str) -> list[str]:
mm_std/types_.py ADDED
@@ -0,0 +1,4 @@
1
+ from collections.abc import Callable
2
+ from typing import Any
3
+
4
+ type CallableAny = Callable[..., Any]
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mm-std
3
- Version: 0.1.9
3
+ Version: 0.1.11
4
4
  Requires-Python: >=3.12
5
5
  Requires-Dist: cryptography~=44.0.0
6
6
  Requires-Dist: httpx[http2,socks]~=0.28.1
7
- Requires-Dist: pydantic~=2.10.4
8
- Requires-Dist: pydash~=8.0.4
7
+ Requires-Dist: pydantic~=2.10.5
8
+ Requires-Dist: pydash~=8.0.5
9
9
  Requires-Dist: python-dotenv~=1.0.1
10
10
  Requires-Dist: pyyaml~=6.0.2
11
11
  Requires-Dist: rich~=13.9.4
@@ -0,0 +1,23 @@
1
+ mm_std/__init__.py,sha256=dtYnmQP_HkWxIJvuCJGpex3RHvG2V0Ekh7oYstRQoco,2291
2
+ mm_std/command.py,sha256=ze286wjUjg0QSTgIu-2WZks53_Vclg69UaYYgPpQvCU,1283
3
+ mm_std/concurrency.py,sha256=4kKLhde6YQYsjJJjH6K5eMQj6FtegEz55Mo5TmhQMM0,5242
4
+ mm_std/config.py,sha256=qXvUqA_hvLyKSaCrt_gtEJKRI1jRFw3Nq3oEhalQYj8,2285
5
+ mm_std/crypto.py,sha256=jdk0_TCmeU0pPXMyz9xH6kQHSjjZ9GcGClBwQps5vBo,340
6
+ mm_std/date.py,sha256=976eEkSONuNqHQBgSRu8hrtH23tJqztbmHFHLdbP2TY,1879
7
+ mm_std/dict.py,sha256=kJBPVG9vEqHiSgKKoji8gVGL1yEBbxAmFNn0zz17AUg,180
8
+ mm_std/env.py,sha256=5zaR9VeIfObN-4yfgxoFeU5IM1GDeZZj9SuYf7t9sOA,125
9
+ mm_std/fs.py,sha256=RwarNRJq3tIMG6LVX_g03hasfYpjYFh_O27oVDt5IPQ,291
10
+ mm_std/http_.py,sha256=QaPPXVb-rOS0BpoKdYQ0ABm_-mcR5dNa7Uqn-SeW_kE,4119
11
+ mm_std/json_.py,sha256=VWjJsfLt-dQJiwc7vgpTElMr69NxpzX_mpmBoCkeoT8,999
12
+ mm_std/log.py,sha256=6ux6njNKc_ZCQlvWn1FZR6vcSY2Cem-mQzmNXvsg5IE,913
13
+ mm_std/net.py,sha256=qdRCBIDneip6FaPNe5mx31UtYVmzqam_AoUF7ydEyjA,590
14
+ mm_std/print_.py,sha256=HAhaiEMZZLV3CpG6UfwoUb6-Ftcun16YFC-CRcAcXOY,1525
15
+ mm_std/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ mm_std/random_.py,sha256=OuUX4VJeSd13NZBya4qrGpR2TfN7_87tfebOY6DBUnI,1113
17
+ mm_std/result.py,sha256=w2Y1dji0LozcJpPMnFkqN0UbxJUm1C8Kc5ZB1vLXx20,7443
18
+ mm_std/str.py,sha256=jS7VAI7i_a3iqnfaW4Iw2LZRTv0Tml4kmMbP2S2IUF4,3067
19
+ mm_std/types_.py,sha256=hvZlnvBWyB8CL_MeEWWD0Y0nN677plibYn3yD-5g7xs,99
20
+ mm_std/zip.py,sha256=2EXcae4HO5U4kObj2Lj8jl5F2OUpT-WRlJybTyFzt6I,370
21
+ mm_std-0.1.11.dist-info/METADATA,sha256=sXI3xGl2sE5Z-gh_wHLVqDOhT0jymzXZimdaPYKO3O0,307
22
+ mm_std-0.1.11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
+ mm_std-0.1.11.dist-info/RECORD,,
mm_std/telegram.py DELETED
@@ -1,35 +0,0 @@
1
- import time
2
-
3
- import pydash
4
-
5
- from mm_std import Err, Ok, Result
6
- from mm_std.net import hrequest
7
-
8
-
9
- def send_telegram_message(bot_token: str, chat_id: int, message: str, long_message_delay: int = 3) -> Result[list[int]]:
10
- messages = _split_string(message, 4096)
11
- responses = []
12
- result = []
13
- while True:
14
- text = messages.pop(0)
15
- params = {"chat_id": chat_id, "text": text}
16
- res = hrequest(f"https://api.telegram.org/bot{bot_token}/sendMessage", method="post", params=params)
17
- responses.append(res.json)
18
- if res.error is not None:
19
- return Err(res.error, data={"last_res": res.to_dict(), "responses": responses})
20
-
21
- message_id = pydash.get(res.json, "result.message_id")
22
- if message_id:
23
- result.append(message_id)
24
- else:
25
- return Err("unknown_response", data={"last_res": res.to_dict(), "responses": responses})
26
-
27
- if len(messages):
28
- time.sleep(long_message_delay)
29
- else:
30
- break
31
- return Ok(result, data={"responses": responses})
32
-
33
-
34
- def _split_string(text: str, chars_per_string: int) -> list[str]:
35
- return [text[i : i + chars_per_string] for i in range(0, len(text), chars_per_string)]
mm_std/types.py DELETED
@@ -1,4 +0,0 @@
1
- from collections.abc import Callable
2
- from typing import Any, TypeAlias
3
-
4
- CallableAny: TypeAlias = Callable[..., Any]
@@ -1,23 +0,0 @@
1
- mm_std/__init__.py,sha256=4pa6AjO-0mfrfBi6FS5sqOk1HFAKt2OMovA12ys7UvQ,2288
2
- mm_std/command.py,sha256=r1n9ZHyMFhNkNOH9grRCm5J0hhX4_v0c2wdaal8iCZY,1270
3
- mm_std/concurrency.py,sha256=-Asx1Xl6aFf7xLz_UF6KgwY8oUR904S4-NEXIyVo_lA,5251
4
- mm_std/config.py,sha256=Wo_VS6MO1vTZosOHv97mCo41YYGKZaRAeergaSVzUcs,3111
5
- mm_std/crypto.py,sha256=jdk0_TCmeU0pPXMyz9xH6kQHSjjZ9GcGClBwQps5vBo,340
6
- mm_std/date.py,sha256=Bf63vyACsIIXe2E2Gzhvaw4RZRzP3522Aslk1lHtXQ4,1847
7
- mm_std/dict.py,sha256=kJBPVG9vEqHiSgKKoji8gVGL1yEBbxAmFNn0zz17AUg,180
8
- mm_std/env.py,sha256=5zaR9VeIfObN-4yfgxoFeU5IM1GDeZZj9SuYf7t9sOA,125
9
- mm_std/fs.py,sha256=RwarNRJq3tIMG6LVX_g03hasfYpjYFh_O27oVDt5IPQ,291
10
- mm_std/json_.py,sha256=12uGLwmnrRA63QI0nUx-UU33zXcyShbsKCoOQiIJ8io,1016
11
- mm_std/log.py,sha256=6ux6njNKc_ZCQlvWn1FZR6vcSY2Cem-mQzmNXvsg5IE,913
12
- mm_std/net.py,sha256=0DTeexW2t6LKqDVUE6TePAHsEY3qoHoUuaHBt1IpPNI,4688
13
- mm_std/print_.py,sha256=mMixwfdrLEYW15ez7_QxXdrV-d38q9XJP8tB8F7P2pI,1553
14
- mm_std/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- mm_std/random_.py,sha256=OuUX4VJeSd13NZBya4qrGpR2TfN7_87tfebOY6DBUnI,1113
16
- mm_std/result.py,sha256=1uCb2YySZGHLVNrGRQYhs6BngB8aEW2MKY9Bx6j2wNQ,7407
17
- mm_std/str.py,sha256=nG5XF5870xM2PAvU0LZrJDk-d54LYwRLGnSahIekOVw,3151
18
- mm_std/telegram.py,sha256=QrHPnsy0LTeqpd-g3RaHhk7gWIfHZEgnMs-S5DLW-vU,1220
19
- mm_std/types.py,sha256=KpFtJ-BTmDfmmFeOSlgq6cMbCfGGOQjh1oWvdcrW-kw,116
20
- mm_std/zip.py,sha256=2EXcae4HO5U4kObj2Lj8jl5F2OUpT-WRlJybTyFzt6I,370
21
- mm_std-0.1.9.dist-info/METADATA,sha256=9mAqv05DzOIyS08WQdPkmmnWa7FqsPd_knflEndwItE,306
22
- mm_std-0.1.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
- mm_std-0.1.9.dist-info/RECORD,,