mm-std 0.3.23__py3-none-any.whl → 0.3.25__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/http_.py CHANGED
@@ -1,13 +1,13 @@
1
- import asyncio
2
1
  import json
3
2
  from dataclasses import asdict, dataclass, field
4
- from typing import Any
5
- from urllib.parse import urlencode
3
+ from typing import Any, cast
4
+ from urllib.parse import urlencode, urlparse
6
5
 
7
6
  import aiohttp
8
7
  import pydash
9
8
  import requests
10
9
  from aiohttp_socks import ProxyConnector
10
+ from multidict import CIMultiDictProxy
11
11
  from requests.auth import AuthBase
12
12
 
13
13
  from mm_std.result import Err, Ok, Result
@@ -73,10 +73,13 @@ class HResponse:
73
73
  return self.error == "timeout"
74
74
 
75
75
  def is_proxy_error(self) -> bool:
76
- return self.error == "proxy_error"
76
+ return self.error == "proxy"
77
77
 
78
78
  def is_connection_error(self) -> bool:
79
- return self.error is not None and self.error.startswith("connection_error:")
79
+ return self.error is not None and self.error.startswith("connection:")
80
+
81
+ def is_dns_error(self) -> bool:
82
+ return self.error is not None and self.error.startswith("dns:")
80
83
 
81
84
  def to_dict(self) -> dict[str, Any]:
82
85
  return pydash.omit(asdict(self), "_json_data")
@@ -136,9 +139,9 @@ def hrequest(
136
139
  except requests.exceptions.Timeout:
137
140
  return HResponse(error="timeout")
138
141
  except requests.exceptions.ProxyError:
139
- return HResponse(error="proxy_error")
142
+ return HResponse(error="proxy")
140
143
  except requests.exceptions.RequestException as err:
141
- return HResponse(error=f"connection_error: {err}")
144
+ return HResponse(error=f"connection: {err}")
142
145
  except Exception as err:
143
146
  return HResponse(error=f"exception: {err}")
144
147
 
@@ -155,7 +158,6 @@ async def hrequest_async(
155
158
  user_agent: str | None = None,
156
159
  json_params: bool = True,
157
160
  auth: tuple[str, str] | None = None,
158
- verify: bool = True,
159
161
  ) -> HResponse:
160
162
  query_params: dict[str, Any] | None = None
161
163
  data: dict[str, Any] | None = None
@@ -174,54 +176,65 @@ async def hrequest_async(
174
176
  data = params
175
177
 
176
178
  try:
177
- # Configure connector based on proxy type
178
- if proxy:
179
- # HTTP proxy will be handled in request kwargs
180
- connector = ProxyConnector.from_url(proxy) if proxy.startswith("socks5://") else aiohttp.TCPConnector(ssl=verify)
181
- else:
182
- connector = aiohttp.TCPConnector(ssl=verify)
183
-
184
- timeout_obj = aiohttp.ClientTimeout(total=timeout)
185
-
186
- async with aiohttp.ClientSession(connector=connector, timeout=timeout_obj, cookies=cookies) as session:
187
- request_kwargs: dict[str, Any] = {"headers": headers}
188
-
189
- if query_params:
190
- request_kwargs["params"] = query_params
191
- if json_:
192
- request_kwargs["json"] = json_
193
- if data:
194
- request_kwargs["data"] = data
195
-
196
- if auth and isinstance(auth, tuple) and len(auth) == 2:
197
- request_kwargs["auth"] = aiohttp.BasicAuth(auth[0], auth[1])
198
-
199
- # Set HTTP proxy (not needed for SOCKS5)
200
- if proxy and not proxy.startswith("socks5://"):
201
- request_kwargs["proxy"] = proxy
202
-
203
- try:
204
- async with await asyncio.wait_for(session.request(method, url, **request_kwargs), timeout=timeout) as response:
205
- body = await response.text()
206
- return HResponse(code=response.status, body=body, headers=dict(response.headers))
207
- except TimeoutError:
208
- return HResponse(error="timeout")
209
- except (aiohttp.ClientProxyConnectionError, aiohttp.ClientHttpProxyError):
210
- return HResponse(error="proxy_error")
211
- except aiohttp.ClientConnectorError as err:
212
- return HResponse(error=f"connection_error: {err}")
213
- except aiohttp.ClientError as err:
214
- return HResponse(error=f"connection_error: {err}")
215
- except Exception as err:
216
- if "couldn't connect to proxy" in str(err).lower():
217
- return HResponse(error="proxy_error")
218
- return HResponse(error=f"exception: {err}")
179
+ request_kwargs: dict[str, Any] = {"headers": headers}
180
+ if query_params:
181
+ request_kwargs["params"] = query_params
182
+ if json_:
183
+ request_kwargs["json"] = json_
184
+ if data:
185
+ request_kwargs["data"] = data
186
+ if cookies:
187
+ request_kwargs["cookies"] = cookies
188
+ if auth and isinstance(auth, tuple) and len(auth) == 2:
189
+ request_kwargs["auth"] = aiohttp.BasicAuth(auth[0], auth[1])
190
+
191
+ if proxy and proxy.startswith("socks"):
192
+ return await _aiohttp_socks5(url, method, proxy, request_kwargs, timeout)
193
+ return await _aiohttp(url, method, request_kwargs, timeout=timeout, proxy=proxy)
194
+
219
195
  except TimeoutError:
220
196
  return HResponse(error="timeout")
197
+ except (aiohttp.ClientProxyConnectionError, aiohttp.ClientHttpProxyError, aiohttp.ClientConnectorError) as err:
198
+ if is_proxy_error(str(err), proxy):
199
+ return HResponse(error="proxy")
200
+ return HResponse(error=f"connection: {err}")
201
+ except aiohttp.ClientError as err:
202
+ return HResponse(error=f"error: {err}")
221
203
  except Exception as err:
222
204
  return HResponse(error=f"exception: {err}")
223
205
 
224
206
 
207
+ def is_proxy_error(error_message: str, proxy: str | None) -> bool:
208
+ if not proxy:
209
+ return False
210
+ error_message = error_message.lower()
211
+ if "proxy" in error_message:
212
+ return True
213
+ return bool("cannot connect to" in error_message and cast(str, urlparse(proxy).hostname) in error_message)
214
+
215
+
216
+ async def _aiohttp(
217
+ url: str, method: str, request_kwargs: dict[str, object], timeout: float | None = None, proxy: str | None = None
218
+ ) -> HResponse:
219
+ if proxy:
220
+ request_kwargs["proxy"] = proxy
221
+ client_timeout = aiohttp.ClientTimeout(total=timeout) if timeout else None
222
+ async with aiohttp.ClientSession(timeout=client_timeout) as session, session.request(method, url, **request_kwargs) as res: # type: ignore[arg-type]
223
+ return HResponse(code=res.status, body=await res.text(), headers=headers_dict(res.headers))
224
+
225
+
226
+ async def _aiohttp_socks5(
227
+ url: str, method: str, proxy: str, request_kwargs: dict[str, object], timeout: float | None = None
228
+ ) -> HResponse:
229
+ connector = ProxyConnector.from_url(proxy)
230
+ client_timeout = aiohttp.ClientTimeout(total=timeout) if timeout else None
231
+ async with (
232
+ aiohttp.ClientSession(connector=connector, timeout=client_timeout) as session,
233
+ session.request(method, url, **request_kwargs) as res, # type: ignore[arg-type]
234
+ ):
235
+ return HResponse(code=res.status, body=await res.text(), headers=headers_dict(res.headers))
236
+
237
+
225
238
  def add_query_params_to_url(url: str, params: dict[str, object]) -> str:
226
239
  query_params = urlencode({k: v for k, v in params.items() if v is not None})
227
240
  if query_params:
@@ -231,3 +244,14 @@ def add_query_params_to_url(url: str, params: dict[str, object]) -> str:
231
244
 
232
245
  hr = hrequest
233
246
  hra = hrequest_async
247
+
248
+
249
+ def headers_dict(headers: CIMultiDictProxy[str]) -> dict[str, str]:
250
+ result: dict[str, str] = {}
251
+ for key in headers:
252
+ values = headers.getall(key)
253
+ if len(values) == 1:
254
+ result[key] = values[0]
255
+ else:
256
+ result[key] = ", ".join(values)
257
+ return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mm-std
3
- Version: 0.3.23
3
+ Version: 0.3.25
4
4
  Requires-Python: >=3.12
5
5
  Requires-Dist: aiohttp-socks~=0.10.1
6
6
  Requires-Dist: aiohttp~=3.11.16
@@ -6,7 +6,7 @@ mm_std/date.py,sha256=976eEkSONuNqHQBgSRu8hrtH23tJqztbmHFHLdbP2TY,1879
6
6
  mm_std/dict.py,sha256=6GkhJPXD0LiJDxPcYe6jPdEDw-MN7P7mKu6U5XxwYDk,675
7
7
  mm_std/env.py,sha256=5zaR9VeIfObN-4yfgxoFeU5IM1GDeZZj9SuYf7t9sOA,125
8
8
  mm_std/fs.py,sha256=RwarNRJq3tIMG6LVX_g03hasfYpjYFh_O27oVDt5IPQ,291
9
- mm_std/http_.py,sha256=75GX21e3OSVjJsHzUjFoMIfT8Iq4Fe6YHaEr9EWOF4o,7422
9
+ mm_std/http_.py,sha256=5_V5Hx1M07xwzf0VVqAsxSFJkvtaAhj9_sFQP_HliQw,8215
10
10
  mm_std/json_.py,sha256=Naa6mBE4D0yiQGkPNRrFvndnUH3R7ovw3FeaejWV60o,1196
11
11
  mm_std/log.py,sha256=0TkTsAlUTt00gjgukvsvnZRIAGELq0MI6Lv8mKP-Wz4,2887
12
12
  mm_std/net.py,sha256=qdRCBIDneip6FaPNe5mx31UtYVmzqam_AoUF7ydEyjA,590
@@ -25,6 +25,6 @@ mm_std/concurrency/async_task_runner.py,sha256=EN7tN2enkVYVgDbhSiAr-_W4o9m9wBXCv
25
25
  mm_std/concurrency/sync_decorators.py,sha256=syCQBOmN7qPO55yzgJB2rbkh10CVww376hmyvs6e5tA,1080
26
26
  mm_std/concurrency/sync_scheduler.py,sha256=j4tBL_cBI1spr0cZplTA7N2CoYsznuORMeRN8rpR6gY,2407
27
27
  mm_std/concurrency/sync_task_runner.py,sha256=s5JPlLYLGQGHIxy4oDS-PN7O9gcy-yPZFoNm8RQwzcw,1780
28
- mm_std-0.3.23.dist-info/METADATA,sha256=8i2Wl_kTQ4IApdDBUzpbp8Q6P0RG3V8bffoMp2_9UeE,415
29
- mm_std-0.3.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
- mm_std-0.3.23.dist-info/RECORD,,
28
+ mm_std-0.3.25.dist-info/METADATA,sha256=dLHZWg8G8cs5y3S8bu3zwxW-qgJaGaBGVQxS8vl4lcg,415
29
+ mm_std-0.3.25.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
30
+ mm_std-0.3.25.dist-info/RECORD,,