arpakitlib 1.4.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.

Potentially problematic release.


This version of arpakitlib might be problematic. Click here for more details.

Files changed (68) hide show
  1. arpakitlib/AUTHOR.md +6 -0
  2. arpakitlib/LICENSE +201 -0
  3. arpakitlib/NOTICE +2 -0
  4. arpakitlib/README.md +6 -0
  5. arpakitlib/__init__.py +0 -0
  6. arpakitlib/ar_additional_model_util.py +8 -0
  7. arpakitlib/ar_aiogram_util.py +363 -0
  8. arpakitlib/ar_arpakit_lib_module_util.py +150 -0
  9. arpakitlib/ar_arpakit_schedule_uust_api_client.py +527 -0
  10. arpakitlib/ar_arpakitlib_info.py +11 -0
  11. arpakitlib/ar_base64_util.py +30 -0
  12. arpakitlib/ar_base_worker.py +77 -0
  13. arpakitlib/ar_cache_file.py +124 -0
  14. arpakitlib/ar_datetime_util.py +38 -0
  15. arpakitlib/ar_dict_util.py +24 -0
  16. arpakitlib/ar_dream_ai_api_client.py +120 -0
  17. arpakitlib/ar_encrypt_and_decrypt_util.py +23 -0
  18. arpakitlib/ar_enumeration.py +76 -0
  19. arpakitlib/ar_fastapi_static/redoc/redoc.standalone.js +1826 -0
  20. arpakitlib/ar_fastapi_static/swagger-ui/favicon-16x16.png +0 -0
  21. arpakitlib/ar_fastapi_static/swagger-ui/favicon-32x32.png +0 -0
  22. arpakitlib/ar_fastapi_static/swagger-ui/index.css +16 -0
  23. arpakitlib/ar_fastapi_static/swagger-ui/index.html +19 -0
  24. arpakitlib/ar_fastapi_static/swagger-ui/oauth2-redirect.html +79 -0
  25. arpakitlib/ar_fastapi_static/swagger-ui/swagger-initializer.js +20 -0
  26. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-bundle.js +2 -0
  27. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-bundle.js.map +1 -0
  28. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle-core.js +3 -0
  29. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle-core.js.map +1 -0
  30. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle.js +2 -0
  31. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-es-bundle.js.map +1 -0
  32. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-standalone-preset.js +2 -0
  33. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui-standalone-preset.js.map +1 -0
  34. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css +3 -0
  35. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css.map +1 -0
  36. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js +2 -0
  37. arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js.map +1 -0
  38. arpakitlib/ar_fastapi_util.py +294 -0
  39. arpakitlib/ar_file_storage_in_dir.py +127 -0
  40. arpakitlib/ar_generate_env_example.py +16 -0
  41. arpakitlib/ar_hash_util.py +19 -0
  42. arpakitlib/ar_http_request_util.py +75 -0
  43. arpakitlib/ar_ip_util.py +50 -0
  44. arpakitlib/ar_json_db.py +231 -0
  45. arpakitlib/ar_json_util.py +28 -0
  46. arpakitlib/ar_jwt_util.py +38 -0
  47. arpakitlib/ar_list_of_dicts_to_xlsx.py +32 -0
  48. arpakitlib/ar_list_util.py +26 -0
  49. arpakitlib/ar_logging_util.py +45 -0
  50. arpakitlib/ar_mongodb_util.py +143 -0
  51. arpakitlib/ar_need_type_util.py +58 -0
  52. arpakitlib/ar_openai_util.py +59 -0
  53. arpakitlib/ar_parse_command.py +102 -0
  54. arpakitlib/ar_postgresql_util.py +45 -0
  55. arpakitlib/ar_run_cmd.py +48 -0
  56. arpakitlib/ar_safe_sleep.py +23 -0
  57. arpakitlib/ar_schedule_uust_api_client.py +216 -0
  58. arpakitlib/ar_sqlalchemy_util.py +124 -0
  59. arpakitlib/ar_ssh_runner.py +260 -0
  60. arpakitlib/ar_str_util.py +79 -0
  61. arpakitlib/ar_type_util.py +82 -0
  62. arpakitlib/ar_yookassa_api_client.py +224 -0
  63. arpakitlib/ar_zabbix_util.py +190 -0
  64. arpakitlib-1.4.0.dist-info/LICENSE +201 -0
  65. arpakitlib-1.4.0.dist-info/METADATA +327 -0
  66. arpakitlib-1.4.0.dist-info/NOTICE +2 -0
  67. arpakitlib-1.4.0.dist-info/RECORD +68 -0
  68. arpakitlib-1.4.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,294 @@
1
+ import asyncio
2
+ import logging
3
+ import os.path
4
+ import pathlib
5
+ from typing import Any, Callable
6
+
7
+ import fastapi.exceptions
8
+ import fastapi.responses
9
+ import starlette.exceptions
10
+ import starlette.requests
11
+ import starlette.status
12
+ from fastapi import FastAPI
13
+ from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
14
+ from pydantic import BaseModel, ConfigDict
15
+ from starlette.middleware.cors import CORSMiddleware
16
+ from starlette.staticfiles import StaticFiles
17
+
18
+ from arpakitlib.ar_enumeration import EasyEnumeration
19
+
20
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
21
+
22
+ _logger = logging.getLogger(__name__)
23
+
24
+
25
+ class BaseAPISchema(BaseModel):
26
+ model_config = ConfigDict(extra="ignore", arbitrary_types_allowed=True, from_attributes=True)
27
+ _bus_data: dict[str, Any] = {}
28
+
29
+ @classmethod
30
+ def __pydantic_init_subclass__(cls, **kwargs: Any) -> None:
31
+ if not (
32
+ cls.__name__.endswith("SO")
33
+ or cls.__name__.endswith("SI")
34
+ or cls.__name__.endswith("SchemaIn")
35
+ or cls.__name__.endswith("SchemaOut")
36
+ ):
37
+ raise ValueError("APISchema class should ends with SO | SI | SchemaIn | SchemaOut")
38
+ super().__init_subclass__(**kwargs)
39
+
40
+
41
+ class BaseAPISI(BaseAPISchema):
42
+ pass
43
+
44
+
45
+ class BaseAPISO(BaseAPISchema):
46
+ pass
47
+
48
+
49
+ class APIJSONResponse(fastapi.responses.JSONResponse):
50
+ def __init__(self, *, content: BaseAPISO, status_code: int = starlette.status.HTTP_200_OK):
51
+ super().__init__(
52
+ content=content.model_dump(mode="json"),
53
+ status_code=status_code
54
+ )
55
+
56
+
57
+ class APIErrorSO(BaseAPISO):
58
+ class ErrorCodes(EasyEnumeration):
59
+ cannot_authorize = "CANNOT_AUTHORIZE"
60
+ unknown_error = "UNKNOWN_ERROR"
61
+ error_in_request = "ERROR_IN_REQUEST"
62
+ not_found = "NOT_FOUND"
63
+
64
+ has_error: bool = True
65
+ error_code: str | None = None
66
+ error_code_specification: str | None = None
67
+ error_description: str | None = None
68
+ error_data: dict[str, Any] = {}
69
+
70
+
71
+ class APIException(fastapi.exceptions.HTTPException):
72
+ def __init__(
73
+ self,
74
+ *,
75
+ status_code: int = starlette.status.HTTP_400_BAD_REQUEST,
76
+ error_code: str | None = APIErrorSO.ErrorCodes.unknown_error,
77
+ error_code_specification: str | None = None,
78
+ error_description: str | None = None,
79
+ error_data: dict[str, Any] | None = None
80
+ ):
81
+ self.status_code = status_code
82
+ self.error_code = error_code
83
+ self.error_code_specification = error_code_specification
84
+ self.error_description = error_description
85
+ if error_data is None:
86
+ error_data = {}
87
+ self.error_data = error_data
88
+
89
+ self.easy_api_error_so = APIErrorSO(
90
+ has_error=True,
91
+ error_code=self.error_code,
92
+ error_specification=self.error_code_specification,
93
+ error_description=self.error_description,
94
+ error_data=self.error_data
95
+ )
96
+
97
+ super().__init__(
98
+ status_code=self.status_code,
99
+ detail=self.easy_api_error_so.model_dump(mode="json")
100
+ )
101
+
102
+
103
+ def simple_api_handle_exception(request: starlette.requests.Request, exception: Exception) -> APIJSONResponse:
104
+ return from_exception_to_api_json_response(request=request, exception=exception)
105
+
106
+
107
+ def from_exception_to_api_json_response(
108
+ request: starlette.requests.Request, exception: Exception
109
+ ) -> APIJSONResponse:
110
+ easy_api_error_so = APIErrorSO(
111
+ has_error=True,
112
+ error_code=APIErrorSO.ErrorCodes.unknown_error
113
+ )
114
+
115
+ status_code = starlette.status.HTTP_500_INTERNAL_SERVER_ERROR
116
+
117
+ if isinstance(exception, APIException):
118
+ easy_api_error_so = exception.easy_api_error_so
119
+
120
+ elif isinstance(exception, starlette.exceptions.HTTPException):
121
+ status_code = exception.status_code
122
+ if status_code in (starlette.status.HTTP_403_FORBIDDEN, starlette.status.HTTP_401_UNAUTHORIZED):
123
+ easy_api_error_so.error_code = APIErrorSO.ErrorCodes.cannot_authorize
124
+ elif status_code == starlette.status.HTTP_404_NOT_FOUND:
125
+ easy_api_error_so.error_code = APIErrorSO.ErrorCodes.not_found
126
+ else:
127
+ easy_api_error_so.error_code = APIErrorSO.ErrorCodes.unknown_error
128
+ if (
129
+ isinstance(exception.detail, dict)
130
+ or isinstance(exception.detail, list)
131
+ or isinstance(exception.detail, str)
132
+ or isinstance(exception.detail, int)
133
+ or isinstance(exception.detail, float)
134
+ or isinstance(exception.detail, bool)
135
+ ):
136
+ easy_api_error_so.error_data["raw"] = exception.detail
137
+
138
+ elif isinstance(exception, fastapi.exceptions.RequestValidationError):
139
+ status_code = starlette.status.HTTP_422_UNPROCESSABLE_ENTITY
140
+ easy_api_error_so.error_code = APIErrorSO.ErrorCodes.error_in_request
141
+ easy_api_error_so.error_data["raw"] = str(exception.errors()) if exception.errors() else {}
142
+
143
+ else:
144
+ status_code = starlette.status.HTTP_500_INTERNAL_SERVER_ERROR
145
+ easy_api_error_so.error_code = APIErrorSO.ErrorCodes.unknown_error
146
+ easy_api_error_so.error_data["raw"] = str(exception)
147
+ _logger.exception(exception)
148
+
149
+ if easy_api_error_so.error_code:
150
+ easy_api_error_so.error_code = easy_api_error_so.error_code.upper().replace(" ", "_").strip()
151
+
152
+ if easy_api_error_so.error_code_specification:
153
+ easy_api_error_so.error_code_specification = (
154
+ easy_api_error_so.error_code_specification.upper().replace(" ", "_").strip()
155
+ )
156
+
157
+ return APIJSONResponse(
158
+ content=easy_api_error_so,
159
+ status_code=status_code
160
+ )
161
+
162
+
163
+ def add_exception_handler_to_fastapi_app(*, fastapi_app: FastAPI, api_handle_exception_: Callable) -> FastAPI:
164
+ fastapi_app.add_exception_handler(
165
+ exc_class_or_status_code=Exception,
166
+ handler=api_handle_exception_
167
+ )
168
+ fastapi_app.add_exception_handler(
169
+ exc_class_or_status_code=ValueError,
170
+ handler=api_handle_exception_
171
+ )
172
+ fastapi_app.add_exception_handler(
173
+ exc_class_or_status_code=fastapi.exceptions.RequestValidationError,
174
+ handler=api_handle_exception_
175
+ )
176
+ fastapi_app.add_exception_handler(
177
+ exc_class_or_status_code=starlette.exceptions.HTTPException,
178
+ handler=api_handle_exception_
179
+ )
180
+ return fastapi_app
181
+
182
+
183
+ def add_middleware_cors_to_fastapi_app(*, fastapi_app: FastAPI) -> FastAPI:
184
+ fastapi_app.add_middleware(
185
+ CORSMiddleware,
186
+ allow_origins=["*"],
187
+ allow_credentials=True,
188
+ allow_methods=["*"],
189
+ allow_headers=["*"],
190
+ )
191
+ return fastapi_app
192
+
193
+
194
+ def add_ar_fastapi_static_to_fastapi_app(*, fastapi_app: FastAPI):
195
+ ar_fastapi_static_dirpath = os.path.join(str(pathlib.Path(__file__).parent), "ar_fastapi_static")
196
+ fastapi_app.mount(
197
+ "/ar_fastapi_static",
198
+ StaticFiles(directory=ar_fastapi_static_dirpath),
199
+ name="ar_fastapi_static"
200
+ )
201
+
202
+
203
+ def add_ar_fastapi_static_docs_and_redoc_handlers_to_fastapi_app(
204
+ *,
205
+ fastapi_app: FastAPI,
206
+ favicon_url: str | None = None
207
+ ):
208
+ add_ar_fastapi_static_to_fastapi_app(fastapi_app=fastapi_app)
209
+
210
+ @fastapi_app.get("/docs", include_in_schema=False)
211
+ async def custom_swagger_ui_html():
212
+ return get_swagger_ui_html(
213
+ openapi_url=fastapi_app.openapi_url,
214
+ title=fastapi_app.title,
215
+ swagger_js_url="/ar_fastapi_static/swagger-ui/swagger-ui-bundle.js",
216
+ swagger_css_url="/ar_fastapi_static/swagger-ui/swagger-ui.css",
217
+ swagger_favicon_url=favicon_url
218
+ )
219
+
220
+ @fastapi_app.get("/redoc", include_in_schema=False)
221
+ async def custom_redoc_html():
222
+ return get_redoc_html(
223
+ openapi_url=fastapi_app.openapi_url,
224
+ title=fastapi_app.title,
225
+ redoc_js_url="/ar_fastapi_static/redoc/redoc.standalone.js",
226
+ redoc_favicon_url=favicon_url
227
+ )
228
+
229
+ return fastapi_app
230
+
231
+
232
+ class BaseAPIStartupEvent:
233
+ def __init__(self, *args, **kwargs):
234
+ self._logger = logging.getLogger(self.__class__.__name__)
235
+
236
+ async def on_startup(self, *args, **kwargs):
237
+ self._logger.info("on_startup")
238
+
239
+
240
+ class BaseAPIShutdownEvent:
241
+ def __init__(self, *args, **kwargs):
242
+ self._logger = logging.getLogger(self.__class__.__name__)
243
+
244
+ async def on_startup(self, *args, **kwargs):
245
+ self._logger.info("on_startup")
246
+
247
+
248
+ def create_fastapi_app(
249
+ *,
250
+ title: str,
251
+ description: str | None = None,
252
+ api_startup_event: BaseAPIStartupEvent | None = None,
253
+ api_shutdown_event: BaseAPIShutdownEvent | None = None,
254
+ api_handle_exception_: Callable | None = simple_api_handle_exception
255
+ ):
256
+ app = FastAPI(
257
+ title=title,
258
+ description=description,
259
+ docs_url=None,
260
+ redoc_url=None,
261
+ openapi_url="/openapi",
262
+ on_startup=[api_startup_event.on_startup] if api_startup_event else [],
263
+ on_shutdown=[api_shutdown_event.on_startup] if api_shutdown_event else []
264
+ )
265
+
266
+ add_middleware_cors_to_fastapi_app(fastapi_app=app)
267
+
268
+ add_ar_fastapi_static_docs_and_redoc_handlers_to_fastapi_app(fastapi_app=app)
269
+
270
+ if api_handle_exception_:
271
+ add_exception_handler_to_fastapi_app(
272
+ fastapi_app=app,
273
+ api_handle_exception_=api_handle_exception_
274
+ )
275
+ else:
276
+ add_exception_handler_to_fastapi_app(
277
+ fastapi_app=app,
278
+ api_handle_exception_=simple_api_handle_exception
279
+ )
280
+
281
+ return app
282
+
283
+
284
+ def __example():
285
+ pass
286
+
287
+
288
+ async def __async_example():
289
+ pass
290
+
291
+
292
+ if __name__ == '__main__':
293
+ __example()
294
+ asyncio.run(__async_example())
@@ -0,0 +1,127 @@
1
+ import logging
2
+ import os
3
+ import shutil
4
+ import uuid
5
+ from typing import Optional, Union, Iterator
6
+
7
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
8
+
9
+
10
+ class FileStorageInDir:
11
+
12
+ def __init__(self, dirpath: str):
13
+ if not isinstance(dirpath, str):
14
+ raise TypeError("not isinstance(dirpath, str)")
15
+ self.dirpath = os.path.abspath(dirpath)
16
+ self._logger = logging.getLogger(self.__class__.__name__)
17
+
18
+ def __str__(self) -> str:
19
+ return self.dirpath
20
+
21
+ def __repr__(self) -> str:
22
+ return self.dirpath
23
+
24
+ def __len__(self) -> int:
25
+ return len(os.listdir(self.dirpath))
26
+
27
+ def __iter__(self) -> Iterator[str]:
28
+ self.init()
29
+ for path in self.get_paths():
30
+ yield path
31
+
32
+ def init(self):
33
+ if not os.path.exists(self.dirpath):
34
+ os.makedirs(self.dirpath, exist_ok=True)
35
+
36
+ def reinit(self):
37
+ if os.path.exists(self.dirpath):
38
+ shutil.rmtree(self.dirpath)
39
+ self.init()
40
+
41
+ def get_paths(self) -> list[str]:
42
+ return [os.path.join(self.dirpath, path) for path in os.listdir(self.dirpath)]
43
+
44
+ def generate_filepath(
45
+ self,
46
+ *,
47
+ filename: Optional[str] = None,
48
+ file_extension: Optional[str] = None,
49
+ content_to_write: Optional[Union[str, bytes]] = None,
50
+ create: bool = False,
51
+ raise_if_exists: bool = True
52
+ ) -> str:
53
+ self.init()
54
+
55
+ if filename is not None:
56
+ if file_extension is not None:
57
+ filename += f".{file_extension}"
58
+ else:
59
+ filename = str(uuid.uuid4())
60
+ if file_extension is not None:
61
+ filename += f".{file_extension}"
62
+ while filename in os.listdir(self.dirpath):
63
+ filename = str(uuid.uuid4())
64
+ if file_extension is not None:
65
+ filename += f".{file_extension}"
66
+
67
+ if raise_if_exists and filename in os.listdir(self.dirpath):
68
+ raise ValueError(f"file ({filename}) already exists")
69
+
70
+ filepath = os.path.join(self.dirpath, filename)
71
+
72
+ if create is True:
73
+ content_to_write = ""
74
+
75
+ if isinstance(content_to_write, str):
76
+ content_to_write = content_to_write.encode()
77
+ if isinstance(content_to_write, bytes):
78
+ with open(filepath, mode="wb") as f:
79
+ f.write(content_to_write)
80
+
81
+ return filepath
82
+
83
+ def generate_dirpath(
84
+ self, *, dirname: Optional[str] = None, create: bool = True, raise_if_exists: bool = True
85
+ ) -> str:
86
+ self.init()
87
+
88
+ if dirname is None:
89
+ dirname = str(uuid.uuid4())
90
+ while dirname in os.listdir(self.dirpath):
91
+ dirname = str(uuid.uuid4())
92
+
93
+ if raise_if_exists and dirname in os.listdir(self.dirpath):
94
+ raise ValueError(f"dir ({dirname}) already exists")
95
+
96
+ dirpath = os.path.join(self.dirpath, dirname)
97
+
98
+ if create:
99
+ os.mkdir(dirpath)
100
+
101
+ return dirpath
102
+
103
+ def rm_by_filepath(self, filepath: str):
104
+ if os.path.exists(filepath):
105
+ os.remove(filepath)
106
+
107
+ def rm_by_filename(self, filename: str):
108
+ filepath = os.path.join(self.dirpath, filename)
109
+ if os.path.exists(filepath):
110
+ os.remove(filepath)
111
+
112
+ def rm_by_dirpath(self, dirpath: str):
113
+ if os.path.exists(dirpath):
114
+ shutil.rmtree(dirpath)
115
+
116
+ def rm_by_dirname(self, dirname: str):
117
+ dirpath = os.path.join(self.dirpath, dirname)
118
+ if os.path.exists(dirpath):
119
+ shutil.rmtree(dirpath)
120
+
121
+
122
+ def __example():
123
+ pass
124
+
125
+
126
+ if __name__ == '__main__':
127
+ __example()
@@ -0,0 +1,16 @@
1
+ from typing import Union
2
+
3
+ from pydantic_settings import BaseSettings
4
+
5
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
6
+
7
+
8
+ def generate_env_example(settings_class: Union[BaseSettings, type[BaseSettings]]):
9
+ res = ""
10
+ for k in settings_class.model_fields:
11
+ res += f"{k}=\n"
12
+ return res
13
+
14
+
15
+ def __example():
16
+ pass
@@ -0,0 +1,19 @@
1
+ import hashlib
2
+
3
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
4
+
5
+
6
+ def hash_string(string: str) -> str:
7
+ return hashlib.sha256(string.encode()).hexdigest()
8
+
9
+
10
+ def check_string_hash(string: str, string_hash: str) -> bool:
11
+ return hash_string(string) == string_hash
12
+
13
+
14
+ def __example():
15
+ pass
16
+
17
+
18
+ if __name__ == '__main__':
19
+ __example()
@@ -0,0 +1,75 @@
1
+ import asyncio
2
+ import inspect
3
+ import logging
4
+ from datetime import timedelta
5
+
6
+ import aiohttp
7
+ import requests
8
+
9
+ from arpakitlib.ar_safe_sleep import safe_sleep
10
+
11
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
12
+
13
+
14
+ def sync_make_request(*, method: str, url: str, **kwargs) -> requests.Response:
15
+ _logger = logging.getLogger(inspect.currentframe().f_code.co_name)
16
+
17
+ max_tries = 7
18
+ tries = 0
19
+
20
+ kwargs["method"] = method
21
+ kwargs["url"] = url
22
+ if "timeout" not in kwargs:
23
+ kwargs["timeout"] = (timedelta(seconds=15).total_seconds(), timedelta(seconds=15).total_seconds())
24
+
25
+ while True:
26
+ tries += 1
27
+ _logger.info(f"{method} {url}")
28
+ try:
29
+ return requests.request(**kwargs)
30
+ except Exception as err:
31
+ _logger.warning(f"{tries}/{max_tries} {method} {url} {err}")
32
+ if tries >= max_tries:
33
+ raise Exception(err)
34
+ safe_sleep(timedelta(seconds=0.1).total_seconds())
35
+ continue
36
+
37
+
38
+ async def async_make_request(*, method: str, url: str, **kwargs) -> aiohttp.ClientResponse:
39
+ _logger = logging.getLogger(inspect.currentframe().f_code.co_name)
40
+
41
+ max_tries = 7
42
+ tries = 0
43
+
44
+ kwargs["method"] = method
45
+ kwargs["url"] = url
46
+ if "timeout" not in kwargs:
47
+ kwargs["timeout"] = aiohttp.ClientTimeout(total=timedelta(seconds=15).total_seconds())
48
+
49
+ while True:
50
+ tries += 1
51
+ _logger.info(f"{method} {url}")
52
+ try:
53
+ async with aiohttp.ClientSession() as session:
54
+ async with session.request(**kwargs) as response:
55
+ await response.read()
56
+ return response
57
+ except Exception as err:
58
+ _logger.warning(f"{tries}/{max_tries} {method} {url} {err}")
59
+ if tries >= max_tries:
60
+ raise Exception(err)
61
+ await asyncio.sleep(timedelta(seconds=0.1).total_seconds())
62
+ continue
63
+
64
+
65
+ def __example():
66
+ pass
67
+
68
+
69
+ async def __async_example():
70
+ pass
71
+
72
+
73
+ if __name__ == '__main__':
74
+ __example()
75
+ asyncio.run(__async_example())
@@ -0,0 +1,50 @@
1
+ import ipaddress
2
+
3
+ _ARPAKIT_LIB_MODULE_VERSION = "3.0"
4
+
5
+
6
+ def is_ipv4_address(value: str) -> bool:
7
+ try:
8
+ ipaddress.IPv4Address(value)
9
+ except ValueError:
10
+ return False
11
+ return True
12
+
13
+
14
+ def raise_if_not_ipv4_address(value: str):
15
+ if not is_ipv4_address(value):
16
+ raise ValueError(f"not is_ipv4_address({value})")
17
+
18
+
19
+ def is_ipv6_address(value: str) -> bool:
20
+ try:
21
+ ipaddress.IPv6Address(value)
22
+ except ValueError:
23
+ return False
24
+ return True
25
+
26
+
27
+ def raise_if_not_ipv6_address(value: str):
28
+ if not is_ipv6_address(value):
29
+ raise ValueError(f"not is_ipv6_address({value})")
30
+
31
+
32
+ def is_ipv4_interface(value: str) -> bool:
33
+ try:
34
+ ipaddress.IPv4Interface(value)
35
+ except ValueError:
36
+ return False
37
+ return True
38
+
39
+
40
+ def raise_if_not_ipv4_interface(value: str):
41
+ if not is_ipv4_interface(value):
42
+ raise ValueError(f"not is_ipv4_interface({value})")
43
+
44
+
45
+ def __example():
46
+ pass
47
+
48
+
49
+ if __name__ == '__main__':
50
+ __example()