arpakitlib 1.7.242__py3-none-any.whl → 1.7.244__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.
- arpakitlib/_arpakit_project_template/example.env +2 -1
- arpakitlib/_arpakit_project_template/src/api/create_api_app.py +5 -24
- arpakitlib/_arpakit_project_template/src/api/create_handle_exception_.py +59 -0
- arpakitlib/_arpakit_project_template/src/core/settings.py +38 -9
- arpakitlib/_arpakit_project_template/src/db/util.py +1 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/blank/__init__.py +0 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/blank/blank.py +10 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/blank/util.py +12 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/const.py +19 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/middleware/__init__.py +0 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/router/error.py +3 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/router/router.py +7 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/start_tg_bot.py +7 -1
- arpakitlib/ar_aiogram_util.py +1 -8
- arpakitlib/ar_blank_util.py +8 -0
- arpakitlib/ar_fastapi_util.py +58 -27
- arpakitlib/ar_sqlalchemy_util.py +111 -18
- {arpakitlib-1.7.242.dist-info → arpakitlib-1.7.244.dist-info}/METADATA +3 -1
- {arpakitlib-1.7.242.dist-info → arpakitlib-1.7.244.dist-info}/RECORD +22 -13
- {arpakitlib-1.7.242.dist-info → arpakitlib-1.7.244.dist-info}/LICENSE +0 -0
- {arpakitlib-1.7.242.dist-info → arpakitlib-1.7.244.dist-info}/WHEEL +0 -0
- {arpakitlib-1.7.242.dist-info → arpakitlib-1.7.244.dist-info}/entry_points.txt +0 -0
@@ -9,7 +9,8 @@
|
|
9
9
|
# api_init_sql_db_at_start=
|
10
10
|
# api_title=
|
11
11
|
# api_description=
|
12
|
-
#
|
12
|
+
# api_logging_func_before_response=
|
13
|
+
# api_story_log_func_before_response=
|
13
14
|
# api_start_operation_executor_worker=
|
14
15
|
# api_start_scheduled_operation_creator_worker=
|
15
16
|
# api_port=
|
@@ -1,11 +1,7 @@
|
|
1
1
|
from fastapi import FastAPI
|
2
|
-
from starlette import status
|
3
2
|
|
4
|
-
from arpakitlib.ar_fastapi_util import create_fastapi_app
|
5
|
-
|
6
|
-
from arpakitlib.ar_sqlalchemy_util import SQLAlchemyDB
|
7
|
-
from arpakitlib.ar_type_util import raise_for_type
|
8
|
-
from src.api.const import APIErrorCodes
|
3
|
+
from arpakitlib.ar_fastapi_util import create_fastapi_app
|
4
|
+
from src.api.create_handle_exception_ import create_handle_exception_
|
9
5
|
from src.api.event import StartupAPIEvent, ShutdownAPIEvent
|
10
6
|
from src.api.router.main_router import main_api_router
|
11
7
|
from src.api.transmitted_api_data import TransmittedAPIData
|
@@ -31,23 +27,6 @@ def create_api_app() -> FastAPI:
|
|
31
27
|
dump_file_storage_in_dir=get_cached_dump_file_storage_in_dir()
|
32
28
|
)
|
33
29
|
|
34
|
-
funcs_before_response = []
|
35
|
-
|
36
|
-
if settings.api_create_story_log_before_response_in_handle_exception:
|
37
|
-
raise_for_type(sqlalchemy_db, SQLAlchemyDB)
|
38
|
-
funcs_before_response.append(
|
39
|
-
create_story_log_before_response_in_handle_exception(
|
40
|
-
sqlalchemy_db=sqlalchemy_db,
|
41
|
-
ignore_api_error_codes=[APIErrorCodes.not_found],
|
42
|
-
ignore_status_codes=[status.HTTP_404_NOT_FOUND]
|
43
|
-
)
|
44
|
-
)
|
45
|
-
|
46
|
-
handle_exception = create_handle_exception(
|
47
|
-
funcs_before_response=funcs_before_response,
|
48
|
-
async_funcs_after_response=[]
|
49
|
-
)
|
50
|
-
|
51
30
|
startup_api_events = []
|
52
31
|
|
53
32
|
startup_api_events.append(StartupAPIEvent(transmitted_api_data=transmitted_api_data))
|
@@ -56,11 +35,13 @@ def create_api_app() -> FastAPI:
|
|
56
35
|
|
57
36
|
shutdown_api_events.append(ShutdownAPIEvent(transmitted_api_data=transmitted_api_data))
|
58
37
|
|
38
|
+
handle_exception_ = create_handle_exception_(transmitted_api_data=transmitted_api_data)
|
39
|
+
|
59
40
|
api_app = create_fastapi_app(
|
60
41
|
title=settings.api_title.strip(),
|
61
42
|
description=settings.api_description.strip(),
|
62
43
|
log_filepath=settings.log_filepath,
|
63
|
-
handle_exception_=
|
44
|
+
handle_exception_=handle_exception_,
|
64
45
|
startup_api_events=startup_api_events,
|
65
46
|
shutdown_api_events=shutdown_api_events,
|
66
47
|
transmitted_api_data=transmitted_api_data,
|
@@ -0,0 +1,59 @@
|
|
1
|
+
import fastapi.exceptions
|
2
|
+
import starlette.exceptions
|
3
|
+
import starlette.status
|
4
|
+
|
5
|
+
from arpakitlib.ar_fastapi_util import create_handle_exception, story_log_func_before_response, \
|
6
|
+
logging_func_before_response
|
7
|
+
from src.api.const import APIErrorCodes
|
8
|
+
from src.api.transmitted_api_data import TransmittedAPIData
|
9
|
+
|
10
|
+
|
11
|
+
def create_handle_exception_(*, transmitted_api_data: TransmittedAPIData):
|
12
|
+
funcs_before_response = []
|
13
|
+
|
14
|
+
if transmitted_api_data.settings.api_logging_func_before_response:
|
15
|
+
funcs_before_response.append(
|
16
|
+
logging_func_before_response(
|
17
|
+
ignore_api_error_codes=[
|
18
|
+
APIErrorCodes.cannot_authorize,
|
19
|
+
APIErrorCodes.error_in_request,
|
20
|
+
APIErrorCodes.not_found
|
21
|
+
],
|
22
|
+
ignore_status_codes=[
|
23
|
+
starlette.status.HTTP_401_UNAUTHORIZED,
|
24
|
+
starlette.status.HTTP_422_UNPROCESSABLE_ENTITY,
|
25
|
+
starlette.status.HTTP_404_NOT_FOUND
|
26
|
+
],
|
27
|
+
ignore_exception_types=[
|
28
|
+
fastapi.exceptions.RequestValidationError
|
29
|
+
],
|
30
|
+
need_exc_info=False
|
31
|
+
)
|
32
|
+
)
|
33
|
+
|
34
|
+
if transmitted_api_data.settings.api_story_log_func_before_response:
|
35
|
+
funcs_before_response.append(
|
36
|
+
story_log_func_before_response(
|
37
|
+
sqlalchemy_db=transmitted_api_data.sqlalchemy_db,
|
38
|
+
ignore_api_error_codes=[
|
39
|
+
APIErrorCodes.cannot_authorize,
|
40
|
+
APIErrorCodes.error_in_request,
|
41
|
+
APIErrorCodes.not_found
|
42
|
+
],
|
43
|
+
ignore_status_codes=[
|
44
|
+
starlette.status.HTTP_401_UNAUTHORIZED,
|
45
|
+
starlette.status.HTTP_422_UNPROCESSABLE_ENTITY,
|
46
|
+
starlette.status.HTTP_404_NOT_FOUND
|
47
|
+
],
|
48
|
+
ignore_exception_types=[
|
49
|
+
fastapi.exceptions.RequestValidationError
|
50
|
+
],
|
51
|
+
)
|
52
|
+
)
|
53
|
+
|
54
|
+
async_funcs_after_response = []
|
55
|
+
|
56
|
+
return create_handle_exception(
|
57
|
+
funcs_before_response=funcs_before_response,
|
58
|
+
async_funcs_after_response=async_funcs_after_response
|
59
|
+
)
|
@@ -8,6 +8,7 @@ from pydantic_core.core_schema import ValidationInfo
|
|
8
8
|
|
9
9
|
from arpakitlib.ar_json_util import safely_transfer_obj_to_json_str
|
10
10
|
from arpakitlib.ar_settings_util import SimpleSettings
|
11
|
+
from arpakitlib.ar_sqlalchemy_util import generate_sqlalchemy_url
|
11
12
|
from src.core.const import ProjectPaths
|
12
13
|
|
13
14
|
|
@@ -27,14 +28,40 @@ class Settings(SimpleSettings):
|
|
27
28
|
@field_validator("sql_db_url", mode="after")
|
28
29
|
def validate_sql_db_url(cls, v: Any, validation_info: ValidationInfo) -> str | None:
|
29
30
|
if v is not None:
|
30
|
-
return
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
return
|
31
|
+
return v
|
32
|
+
|
33
|
+
user = validation_info.data.get("sql_db_user")
|
34
|
+
password = validation_info.data.get("sql_db_password")
|
35
|
+
port = validation_info.data.get("sql_db_port")
|
36
|
+
database = validation_info.data.get("sql_db_database")
|
37
|
+
|
38
|
+
return generate_sqlalchemy_url(
|
39
|
+
base="postgresql",
|
40
|
+
user=user,
|
41
|
+
password=password,
|
42
|
+
port=port,
|
43
|
+
database=database
|
44
|
+
)
|
45
|
+
|
46
|
+
async_sql_db_url: str | None = None
|
47
|
+
|
48
|
+
@field_validator("async_sql_db_url", mode="after")
|
49
|
+
def validate_async_sql_db_url(cls, v: Any, validation_info: ValidationInfo) -> str | None:
|
50
|
+
if v is not None:
|
51
|
+
return v
|
52
|
+
|
53
|
+
user = validation_info.data.get("sql_db_user")
|
54
|
+
password = validation_info.data.get("sql_db_password")
|
55
|
+
port = validation_info.data.get("sql_db_port")
|
56
|
+
database = validation_info.data.get("sql_db_database")
|
57
|
+
|
58
|
+
return generate_sqlalchemy_url(
|
59
|
+
base="postgresql+asyncpg",
|
60
|
+
user=user,
|
61
|
+
password=password,
|
62
|
+
port=port,
|
63
|
+
database=database
|
64
|
+
)
|
38
65
|
|
39
66
|
sql_db_echo: bool = False
|
40
67
|
|
@@ -44,7 +71,9 @@ class Settings(SimpleSettings):
|
|
44
71
|
|
45
72
|
api_description: str = f"{project_name} (arpakitlib)"
|
46
73
|
|
47
|
-
|
74
|
+
api_logging_func_before_response: bool = True
|
75
|
+
|
76
|
+
api_story_log_func_before_response: bool = True
|
48
77
|
|
49
78
|
api_start_operation_executor_worker: bool = False
|
50
79
|
|
@@ -16,6 +16,7 @@ def get_base_dbm() -> type[BaseDBM]:
|
|
16
16
|
def create_sqlalchemy_db() -> SQLAlchemyDB:
|
17
17
|
return SQLAlchemyDB(
|
18
18
|
db_url=get_cached_settings().sql_db_url,
|
19
|
+
async_db_url=get_cached_settings().async_sql_db_url,
|
19
20
|
db_echo=get_cached_settings().sql_db_echo,
|
20
21
|
base_dbm=get_base_dbm()
|
21
22
|
)
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
from functools import lru_cache
|
2
|
+
|
3
|
+
from src.tg_bot.blank.blank import TgBotBlank
|
4
|
+
|
5
|
+
|
6
|
+
def get_create_tg_bot_blank() -> TgBotBlank:
|
7
|
+
return TgBotBlank()
|
8
|
+
|
9
|
+
|
10
|
+
@lru_cache()
|
11
|
+
def get_cached_tg_bot_blank() -> TgBotBlank:
|
12
|
+
return get_create_tg_bot_blank()
|
@@ -0,0 +1,19 @@
|
|
1
|
+
from arpakitlib.ar_enumeration_util import Enumeration
|
2
|
+
|
3
|
+
|
4
|
+
class TgBotPublicCommands(Enumeration):
|
5
|
+
start = "start"
|
6
|
+
|
7
|
+
|
8
|
+
class TgBotPrivateCommands(Enumeration):
|
9
|
+
init_db = "init_db"
|
10
|
+
reinit_db = "reinit_db"
|
11
|
+
drop_db = "drop_db"
|
12
|
+
set_tg_bot_commands = "set_tg_bot_commands"
|
13
|
+
raise_fake_err = "raise_fake_err"
|
14
|
+
log_file = "log_file"
|
15
|
+
clear_log_file = "clear_log_file"
|
16
|
+
kb_with_old_data = "kb_with_old_data"
|
17
|
+
kb_with_not_modified = "kb_with_not_modified"
|
18
|
+
kb_with_fake_error = "kb_with_fake_error"
|
19
|
+
kb_with_remove_message = "kb_with_remove_message"
|
File without changes
|
arpakitlib/ar_aiogram_util.py
CHANGED
@@ -2,10 +2,9 @@
|
|
2
2
|
|
3
3
|
import asyncio
|
4
4
|
import logging
|
5
|
-
from abc import ABC
|
6
5
|
from typing import Optional, Any, Union, Callable, Iterable
|
7
6
|
|
8
|
-
from aiogram import types,
|
7
|
+
from aiogram import types, Bot
|
9
8
|
from aiogram.client.default import DefaultBotProperties
|
10
9
|
from aiogram.client.session.aiohttp import AiohttpSession
|
11
10
|
from aiogram.enums import ChatType, ParseMode
|
@@ -312,12 +311,6 @@ def as_tg_command(
|
|
312
311
|
return decorator
|
313
312
|
|
314
313
|
|
315
|
-
class SimpleMiddleware(BaseMiddleware, ABC):
|
316
|
-
def __init__(self):
|
317
|
-
self.middleware_name = self.__class__.__name__
|
318
|
-
self._logger = logging.getLogger(self.__class__.__name__)
|
319
|
-
|
320
|
-
|
321
314
|
class BaseTransmittedTgBotData(BaseModel):
|
322
315
|
model_config = ConfigDict(extra="ignore", arbitrary_types_allowed=True, from_attributes=True)
|
323
316
|
|
arpakitlib/ar_fastapi_util.py
CHANGED
@@ -7,7 +7,6 @@ import datetime as dt
|
|
7
7
|
import logging
|
8
8
|
import os.path
|
9
9
|
import pathlib
|
10
|
-
import traceback
|
11
10
|
from contextlib import suppress
|
12
11
|
from typing import Any, Callable
|
13
12
|
|
@@ -27,8 +26,9 @@ from starlette.staticfiles import StaticFiles
|
|
27
26
|
|
28
27
|
from arpakitlib.ar_dict_util import combine_dicts
|
29
28
|
from arpakitlib.ar_enumeration_util import Enumeration
|
30
|
-
from arpakitlib.
|
31
|
-
from arpakitlib.
|
29
|
+
from arpakitlib.ar_exception_util import exception_to_traceback_str
|
30
|
+
from arpakitlib.ar_func_util import raise_if_not_async_func, is_async_object
|
31
|
+
from arpakitlib.ar_json_util import safely_transfer_obj_to_json_str_to_json_obj, safely_transfer_obj_to_json_str
|
32
32
|
from arpakitlib.ar_logging_util import setup_normal_logging
|
33
33
|
from arpakitlib.ar_sqlalchemy_model_util import StoryLogDBM, OperationDBM
|
34
34
|
from arpakitlib.ar_sqlalchemy_util import SQLAlchemyDB
|
@@ -239,19 +239,15 @@ def create_handle_exception(
|
|
239
239
|
old_error_data = error_so.error_data
|
240
240
|
error_so = exception.error_so
|
241
241
|
error_so.error_data = combine_dicts(old_error_data, error_so.error_data)
|
242
|
-
_need_exc_info = False
|
243
242
|
|
244
243
|
elif isinstance(exception, starlette.exceptions.HTTPException):
|
245
244
|
status_code = exception.status_code
|
246
245
|
if status_code in (starlette.status.HTTP_403_FORBIDDEN, starlette.status.HTTP_401_UNAUTHORIZED):
|
247
246
|
error_so.error_code = BaseAPIErrorCodes.cannot_authorize
|
248
|
-
_need_exc_info = False
|
249
247
|
elif status_code == starlette.status.HTTP_404_NOT_FOUND:
|
250
248
|
error_so.error_code = BaseAPIErrorCodes.not_found
|
251
|
-
_need_exc_info = False
|
252
249
|
else:
|
253
250
|
status_code = starlette.status.HTTP_500_INTERNAL_SERVER_ERROR
|
254
|
-
_need_exc_info = True
|
255
251
|
with suppress(Exception):
|
256
252
|
error_so.error_data["exception.detail"] = exception.detail
|
257
253
|
|
@@ -260,13 +256,10 @@ def create_handle_exception(
|
|
260
256
|
error_so.error_code = BaseAPIErrorCodes.error_in_request
|
261
257
|
with suppress(Exception):
|
262
258
|
error_so.error_data["exception.errors"] = str(exception.errors()) if exception.errors() else {}
|
263
|
-
_need_exc_info = False
|
264
259
|
|
265
260
|
else:
|
266
261
|
status_code = starlette.status.HTTP_500_INTERNAL_SERVER_ERROR
|
267
262
|
error_so.error_code = BaseAPIErrorCodes.unknown_error
|
268
|
-
_logger.exception(exception)
|
269
|
-
_need_exc_info = True
|
270
263
|
|
271
264
|
if error_so.error_code:
|
272
265
|
error_so.error_code = error_so.error_code.upper().replace(" ", "_").strip()
|
@@ -282,17 +275,12 @@ def create_handle_exception(
|
|
282
275
|
if error_so.error_code == BaseAPIErrorCodes.cannot_authorize:
|
283
276
|
status_code = status.HTTP_401_UNAUTHORIZED
|
284
277
|
|
285
|
-
if _need_exc_info:
|
286
|
-
_logger.error(str(exception), exc_info=exception)
|
287
|
-
else:
|
288
|
-
_logger.error(str(exception))
|
289
|
-
|
290
278
|
_kwargs = {}
|
291
279
|
for func in funcs_before_response:
|
292
280
|
_func_data = func(
|
293
281
|
status_code=status_code, error_so=error_so, request=request, exception=exception, **_kwargs
|
294
282
|
)
|
295
|
-
if
|
283
|
+
if is_async_object(_func_data):
|
296
284
|
_func_data = await _func_data
|
297
285
|
if _func_data is not None:
|
298
286
|
status_code, error_so, _kwargs = _func_data[0], _func_data[1], _func_data[2]
|
@@ -314,13 +302,49 @@ def create_handle_exception(
|
|
314
302
|
return handle_exception
|
315
303
|
|
316
304
|
|
317
|
-
def
|
305
|
+
def logging_func_before_response(
|
306
|
+
*,
|
307
|
+
ignore_api_error_codes: list[str] | None = None,
|
308
|
+
ignore_status_codes: list[int] | None = None,
|
309
|
+
ignore_exception_types: list[type[Exception]] | None = None,
|
310
|
+
need_exc_info: bool = False
|
311
|
+
):
|
312
|
+
def func(
|
313
|
+
*,
|
314
|
+
status_code: int,
|
315
|
+
error_so: ErrorSO,
|
316
|
+
request: starlette.requests.Request,
|
317
|
+
exception: Exception,
|
318
|
+
**kwargs
|
319
|
+
) -> (int, ErrorSO, dict[str, Any]):
|
320
|
+
kwargs["logging_before_response_in_handle_exception"] = True
|
321
|
+
|
322
|
+
if ignore_api_error_codes and error_so.error_code in ignore_api_error_codes:
|
323
|
+
return status_code, error_so, kwargs
|
324
|
+
|
325
|
+
if ignore_status_codes and status_code in ignore_status_codes:
|
326
|
+
return status_code, error_so, kwargs
|
327
|
+
|
328
|
+
if ignore_exception_types and (
|
329
|
+
exception in ignore_exception_types or type(exception) in ignore_exception_types
|
330
|
+
):
|
331
|
+
return status_code, error_so, kwargs
|
332
|
+
|
333
|
+
_logger.error(safely_transfer_obj_to_json_str(error_so.model_dump()), exc_info=need_exc_info)
|
334
|
+
|
335
|
+
return func
|
336
|
+
|
337
|
+
|
338
|
+
def story_log_func_before_response(
|
318
339
|
*,
|
319
340
|
sqlalchemy_db: SQLAlchemyDB,
|
320
341
|
ignore_api_error_codes: list[str] | None = None,
|
321
|
-
ignore_status_codes: list[int] | None = None
|
342
|
+
ignore_status_codes: list[int] | None = None,
|
343
|
+
ignore_exception_types: list[type[Exception]] | None = None
|
322
344
|
) -> Callable:
|
323
|
-
|
345
|
+
raise_for_type(sqlalchemy_db, SQLAlchemyDB)
|
346
|
+
|
347
|
+
async def async_func(
|
324
348
|
*,
|
325
349
|
status_code: int,
|
326
350
|
error_so: ErrorSO,
|
@@ -328,31 +352,38 @@ def create_story_log_before_response_in_handle_exception(
|
|
328
352
|
exception: Exception,
|
329
353
|
**kwargs
|
330
354
|
) -> (int, ErrorSO, dict[str, Any]):
|
355
|
+
kwargs["create_story_log_before_response_in_handle_exception"] = True
|
356
|
+
|
331
357
|
if ignore_api_error_codes and error_so.error_code in ignore_api_error_codes:
|
332
358
|
return status_code, error_so, kwargs
|
333
359
|
|
334
360
|
if ignore_status_codes and status_code in ignore_status_codes:
|
335
361
|
return status_code, error_so, kwargs
|
336
362
|
|
337
|
-
|
338
|
-
|
339
|
-
|
363
|
+
if ignore_exception_types and (
|
364
|
+
exception in ignore_exception_types or type(exception) in ignore_exception_types
|
365
|
+
):
|
366
|
+
return status_code, error_so, kwargs
|
367
|
+
|
368
|
+
async with sqlalchemy_db.new_async_session() as session:
|
340
369
|
story_log_dbm = StoryLogDBM(
|
341
370
|
level=StoryLogDBM.Levels.error,
|
342
|
-
title=
|
371
|
+
title=f"{status_code}, {type(exception)}",
|
343
372
|
data={
|
344
373
|
"error_so": error_so.model_dump(),
|
345
|
-
"traceback_str":
|
374
|
+
"traceback_str": exception_to_traceback_str(exception=exception)
|
346
375
|
}
|
347
376
|
)
|
348
377
|
session.add(story_log_dbm)
|
349
|
-
session.commit()
|
350
|
-
session.refresh(story_log_dbm)
|
378
|
+
await session.commit()
|
379
|
+
await session.refresh(story_log_dbm)
|
380
|
+
|
351
381
|
error_so.error_data.update({"story_log_long_id": story_log_dbm.long_id})
|
352
382
|
kwargs["story_log_id"] = story_log_dbm.id
|
383
|
+
|
353
384
|
return status_code, error_so, kwargs
|
354
385
|
|
355
|
-
return
|
386
|
+
return async_func
|
356
387
|
|
357
388
|
|
358
389
|
def add_exception_handler_to_app(*, app: FastAPI, handle_exception: Callable) -> FastAPI:
|
arpakitlib/ar_sqlalchemy_util.py
CHANGED
@@ -3,9 +3,11 @@ import asyncio
|
|
3
3
|
import logging
|
4
4
|
from datetime import timedelta, datetime
|
5
5
|
from typing import Any
|
6
|
+
from urllib.parse import quote_plus
|
6
7
|
from uuid import uuid4
|
7
8
|
|
8
|
-
from sqlalchemy import create_engine, QueuePool, text, func
|
9
|
+
from sqlalchemy import create_engine, QueuePool, text, func, inspect, AsyncAdaptedQueuePool
|
10
|
+
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
|
9
11
|
from sqlalchemy.orm import sessionmaker, DeclarativeBase
|
10
12
|
from sqlalchemy.orm.session import Session
|
11
13
|
|
@@ -22,28 +24,105 @@ def get_string_info_from_declarative_base(class_: type[DeclarativeBase]):
|
|
22
24
|
return res
|
23
25
|
|
24
26
|
|
27
|
+
def generate_sqlalchemy_url(
|
28
|
+
*,
|
29
|
+
base: str = "postgresql",
|
30
|
+
user: str | None = None,
|
31
|
+
password: str | None = None,
|
32
|
+
host: str = "127.0.0.1",
|
33
|
+
port: int | None = None,
|
34
|
+
database: str | None = None,
|
35
|
+
**query_params
|
36
|
+
) -> str:
|
37
|
+
"""
|
38
|
+
Генерация URL для SQLAlchemy.
|
39
|
+
|
40
|
+
:param base: Базовая строка для подключения, например "postgresql+asyncpg" или "sqlite".
|
41
|
+
:param user: Имя пользователя (необязательно).
|
42
|
+
:param password: Пароль (необязательно).
|
43
|
+
:param host: Хост (по умолчанию "127.0.0.1").
|
44
|
+
:param port: Порт (необязательно).
|
45
|
+
:param database: Имя базы данных или путь к файлу для SQLite (необязательно).
|
46
|
+
:param query_params: Дополнительные параметры строки подключения.
|
47
|
+
:return: Строка подключения для SQLAlchemy.
|
48
|
+
"""
|
49
|
+
# Формируем часть с авторизацией
|
50
|
+
auth_part = ""
|
51
|
+
if user and password:
|
52
|
+
auth_part = f"{quote_plus(user)}:{quote_plus(password)}@"
|
53
|
+
elif user:
|
54
|
+
auth_part = f"{quote_plus(user)}@"
|
55
|
+
|
56
|
+
# Формируем часть с хостом и портом
|
57
|
+
host_part = ""
|
58
|
+
if base.startswith("sqlite"):
|
59
|
+
# Для SQLite хост и порт не нужны
|
60
|
+
host_part = ""
|
61
|
+
else:
|
62
|
+
host_part = f"{host}"
|
63
|
+
if port:
|
64
|
+
host_part += f":{port}"
|
65
|
+
|
66
|
+
# Формируем часть с базой данных
|
67
|
+
database_part = f"/{database}" if database else ""
|
68
|
+
if base.startswith("sqlite") and database:
|
69
|
+
# Для SQLite путь указывается как абсолютный
|
70
|
+
database_part = f"/{database}"
|
71
|
+
|
72
|
+
# Дополнительные параметры
|
73
|
+
query_part = ""
|
74
|
+
if query_params:
|
75
|
+
query_items = [f"{key}={quote_plus(str(value))}" for key, value in query_params.items()]
|
76
|
+
query_part = f"?{'&'.join(query_items)}"
|
77
|
+
|
78
|
+
return f"{base}://{auth_part}{host_part}{database_part}{query_part}"
|
79
|
+
|
80
|
+
|
25
81
|
class SQLAlchemyDB:
|
26
82
|
def __init__(
|
27
83
|
self,
|
28
84
|
*,
|
29
|
-
db_url: str = "postgresql://arpakitlib:arpakitlib@localhost:
|
85
|
+
db_url: str | None = "postgresql://arpakitlib:arpakitlib@localhost:50517/arpakitlib",
|
86
|
+
async_db_url: str | None = "postgresql+asyncpg://arpakitlib:arpakitlib@localhost:50517/arpakitlib",
|
30
87
|
db_echo: bool = False,
|
31
88
|
base_dbm: type[BaseDBM] | None = None,
|
32
89
|
db_models: list[Any] | None = None,
|
33
90
|
):
|
34
|
-
self._logger = logging.getLogger(
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
91
|
+
self._logger = logging.getLogger()
|
92
|
+
|
93
|
+
self.db_url = db_url
|
94
|
+
if self.db_url is not None:
|
95
|
+
self.engine = create_engine(
|
96
|
+
url=db_url,
|
97
|
+
echo=db_echo,
|
98
|
+
pool_size=10,
|
99
|
+
max_overflow=10,
|
100
|
+
poolclass=QueuePool,
|
101
|
+
pool_timeout=timedelta(seconds=30).total_seconds(),
|
102
|
+
)
|
43
103
|
self.sessionmaker = sessionmaker(bind=self.engine)
|
44
104
|
self.func_new_session_counter = 0
|
105
|
+
|
106
|
+
self.async_db_url = async_db_url
|
107
|
+
if self.async_db_url is not None:
|
108
|
+
self.async_engine = create_async_engine(
|
109
|
+
url=async_db_url,
|
110
|
+
echo=db_echo,
|
111
|
+
pool_size=10,
|
112
|
+
max_overflow=10,
|
113
|
+
poolclass=AsyncAdaptedQueuePool,
|
114
|
+
pool_timeout=timedelta(seconds=30).total_seconds()
|
115
|
+
)
|
116
|
+
self.async_sessionmaker = async_sessionmaker(bind=self.async_engine)
|
117
|
+
self.func_new_async_session_counter = 0
|
118
|
+
|
45
119
|
self.base_dbm = base_dbm
|
46
120
|
|
121
|
+
def is_table_exists(self, table_name: str) -> bool:
|
122
|
+
with self.engine.connect() as connection:
|
123
|
+
inspector = inspect(connection)
|
124
|
+
return table_name in inspector.get_table_names()
|
125
|
+
|
47
126
|
def drop_celery_tables(self):
|
48
127
|
with self.engine.connect() as connection:
|
49
128
|
connection.execute(text("DROP TABLE IF EXISTS celery_tasksetmeta CASCADE;"))
|
@@ -52,32 +131,42 @@ class SQLAlchemyDB:
|
|
52
131
|
self._logger.info("celery tables were dropped")
|
53
132
|
|
54
133
|
def remove_celery_tables_data(self):
|
134
|
+
if not self.is_table_exists("celery_tasksetmeta"):
|
135
|
+
self._logger.info("table celery_tasksetmeta not exists")
|
136
|
+
return
|
55
137
|
with self.engine.connect() as connection:
|
56
138
|
connection.execute(text("DELETE FROM celery_tasksetmeta;"))
|
57
139
|
connection.execute(text("DELETE FROM celery_taskmeta;"))
|
58
140
|
connection.commit()
|
59
141
|
self._logger.info("celery tables data were removed")
|
60
142
|
|
143
|
+
def drop_alembic_tables(self):
|
144
|
+
with self.engine.connect() as connection:
|
145
|
+
connection.execute(text("DROP TABLE IF EXISTS alembic_version CASCADE;"))
|
146
|
+
connection.execute(text("DROP TABLE IF EXISTS alembic_version CASCADE;"))
|
147
|
+
connection.commit()
|
148
|
+
self._logger.info("alembic_version tables were dropped")
|
149
|
+
|
61
150
|
def remove_alembic_tables_data(self):
|
151
|
+
if not self.is_table_exists("alembic_version"):
|
152
|
+
self._logger.info("table alembic_version not exists")
|
153
|
+
return
|
62
154
|
with self.engine.connect() as connection:
|
63
155
|
connection.execute(text("DELETE FROM alembic_version;"))
|
64
156
|
connection.commit()
|
65
157
|
self._logger.info("alembic tables data were removed")
|
66
158
|
|
67
159
|
def init(self):
|
68
|
-
|
69
|
-
BaseDBM.metadata.create_all(bind=self.engine, checkfirst=True)
|
160
|
+
self.base_dbm.metadata.create_all(bind=self.engine, checkfirst=True)
|
70
161
|
self._logger.info("db was inited")
|
71
162
|
|
72
163
|
def drop(self):
|
73
|
-
|
74
|
-
BaseDBM.metadata.drop_all(bind=self.engine, checkfirst=True)
|
164
|
+
self.base_dbm.metadata.drop_all(bind=self.engine, checkfirst=True)
|
75
165
|
self._logger.info("db was dropped")
|
76
166
|
|
77
167
|
def reinit(self):
|
78
|
-
|
79
|
-
|
80
|
-
BaseDBM.metadata.create_all(bind=self.engine, checkfirst=True)
|
168
|
+
self.base_dbm.metadata.drop_all(bind=self.engine, checkfirst=True)
|
169
|
+
self.base_dbm.metadata.create_all(bind=self.engine, checkfirst=True)
|
81
170
|
self._logger.info("db was reinited")
|
82
171
|
|
83
172
|
def check_conn(self):
|
@@ -88,6 +177,10 @@ class SQLAlchemyDB:
|
|
88
177
|
self.func_new_session_counter += 1
|
89
178
|
return self.sessionmaker(**kwargs)
|
90
179
|
|
180
|
+
def new_async_session(self, **kwargs) -> AsyncSession:
|
181
|
+
self.func_new_async_session_counter += 1
|
182
|
+
return self.async_sessionmaker(**kwargs)
|
183
|
+
|
91
184
|
def is_conn_good(self) -> bool:
|
92
185
|
try:
|
93
186
|
self.check_conn()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: arpakitlib
|
3
|
-
Version: 1.7.
|
3
|
+
Version: 1.7.244
|
4
4
|
Summary: arpakitlib
|
5
5
|
License: Apache-2.0
|
6
6
|
Keywords: arpakitlib,arpakit,arpakit-company,arpakitcompany,arpakit_company
|
@@ -39,6 +39,7 @@ Requires-Dist: paramiko (>=3.5.0,<4.0.0)
|
|
39
39
|
Requires-Dist: pika (>=1.3.2,<2.0.0)
|
40
40
|
Requires-Dist: poetry (>=2.0.1,<3.0.0)
|
41
41
|
Requires-Dist: psycopg2-binary (>=2.9.10,<3.0.0)
|
42
|
+
Requires-Dist: pulp (>=2.9.0,<3.0.0)
|
42
43
|
Requires-Dist: pydantic (>=2.10.5,<3.0.0)
|
43
44
|
Requires-Dist: pydantic-settings (>=2.7.1,<3.0.0)
|
44
45
|
Requires-Dist: pyjwt (>=2.10.1,<3.0.0)
|
@@ -46,6 +47,7 @@ Requires-Dist: pymongo (>=4.10.1,<5.0.0)
|
|
46
47
|
Requires-Dist: pytz (>=2024.2,<2025.0)
|
47
48
|
Requires-Dist: pyzabbix (>=1.3.1,<2.0.0)
|
48
49
|
Requires-Dist: requests[socks] (>=2.32.3,<3.0.0)
|
50
|
+
Requires-Dist: scipy (>=1.15.1,<2.0.0)
|
49
51
|
Requires-Dist: sqladmin (>=0.20.1,<0.21.0)
|
50
52
|
Requires-Dist: sqlalchemy (>=2.0.37,<3.0.0)
|
51
53
|
Requires-Dist: twine (>=6.1.0,<7.0.0)
|
@@ -4,7 +4,7 @@ arpakitlib/_arpakit_project_template/.python-version,sha256=XMd40XBnlTFfBSmMldd-
|
|
4
4
|
arpakitlib/_arpakit_project_template/ARPAKITLIB,sha256=3-iAkMXtesLzJXHw_IIv2k2M0oH8cTjHzW22Vvbi0IE,4
|
5
5
|
arpakitlib/_arpakit_project_template/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
|
6
6
|
arpakitlib/_arpakit_project_template/README.md,sha256=AwqCtmMeywF2dJhZbKwCBA_wPnLF_VmoLGfPbFjH3bM,62
|
7
|
-
arpakitlib/_arpakit_project_template/example.env,sha256=
|
7
|
+
arpakitlib/_arpakit_project_template/example.env,sha256=ZlAMMUEHEYncVpGu2urz-P34CDG5Ljs8_lPp-oCCpYI,625
|
8
8
|
arpakitlib/_arpakit_project_template/manage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
9
|
arpakitlib/_arpakit_project_template/manage/docker_ps.sh,sha256=uwm8vHgeuNLCOn0o9hgP_uc-PUkS9FwLyzZh6ItZ3do,15
|
10
10
|
arpakitlib/_arpakit_project_template/manage/docker_ps_a.sh,sha256=nOQejihYlzstg9oROvYwHIsSLt2Sw0DWQEeT3GBaBNs,18
|
@@ -69,7 +69,8 @@ arpakitlib/_arpakit_project_template/src/api/_start_api_without_reload.py,sha256
|
|
69
69
|
arpakitlib/_arpakit_project_template/src/api/asgi.py,sha256=a5UBxOyNC8NG3E0ayhiDo3t5tPoB3WtOf2gbZJFWBAA,74
|
70
70
|
arpakitlib/_arpakit_project_template/src/api/auth.py,sha256=dcvj5C9E2F2KCsGZPBBncQf_EvVJAC1qQgnyD8P4ZEw,6
|
71
71
|
arpakitlib/_arpakit_project_template/src/api/const.py,sha256=7d4qD5hedqr7QxVzbfsA7E1bNZn2Pm2U8joXGtpANu0,287
|
72
|
-
arpakitlib/_arpakit_project_template/src/api/create_api_app.py,sha256=
|
72
|
+
arpakitlib/_arpakit_project_template/src/api/create_api_app.py,sha256=lpn_AYx39hyuwOev9Yzrji_2gJfRSgP3G1-CXTbz9rU,2211
|
73
|
+
arpakitlib/_arpakit_project_template/src/api/create_handle_exception_.py,sha256=Q0YvleHXdKQKVXslXUgSdTqkEWHVkfE0m8vrpiJSb-c,2213
|
73
74
|
arpakitlib/_arpakit_project_template/src/api/event.py,sha256=Ld4Ww3QyglvqEDm8LD-VmBB4KwkaXj9vc7JFlbJcEkM,3035
|
74
75
|
arpakitlib/_arpakit_project_template/src/api/router/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
76
|
arpakitlib/_arpakit_project_template/src/api/router/main_router.py,sha256=Yv699WCJDcdiJMXFg1kPTvolqj-NAGoXfqe-vzbMzIU,228
|
@@ -89,7 +90,7 @@ arpakitlib/_arpakit_project_template/src/core/_check_logging.py,sha256=APQp8jQa3
|
|
89
90
|
arpakitlib/_arpakit_project_template/src/core/_check_settings.py,sha256=BQUcr-yj3cxz5GQo0jXe99wMoqHKrrrKD3-UovsJAt0,284
|
90
91
|
arpakitlib/_arpakit_project_template/src/core/_generate_settings_env_example.py,sha256=SiEJe8AYQPOWicsaCwf9RdXp6UAmfkBdBT18AAInGb4,483
|
91
92
|
arpakitlib/_arpakit_project_template/src/core/const.py,sha256=e2Y0NIQHfzm3bmnbQnGy3Z5YKt6OYnIsRoqVY8oIV2E,1008
|
92
|
-
arpakitlib/_arpakit_project_template/src/core/settings.py,sha256=
|
93
|
+
arpakitlib/_arpakit_project_template/src/core/settings.py,sha256=R4dsQHOB9wVBn-A76sbJFQbsfCHyyR3AMKV8HZHIHmM,3819
|
93
94
|
arpakitlib/_arpakit_project_template/src/core/util.py,sha256=mcikqcjljZa2qhYeoR1tR9JUSprrVSod8XcIK_PqS6o,1547
|
94
95
|
arpakitlib/_arpakit_project_template/src/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
95
96
|
arpakitlib/_arpakit_project_template/src/db/_check_conn_sqlalchemy_db.py,sha256=GPK7-w7x8ESqbZ0v1nI8m_ysWfnmjgRwyyh6kM8vYBQ,249
|
@@ -98,7 +99,7 @@ arpakitlib/_arpakit_project_template/src/db/_init_sqlalchemy_db.py,sha256=tHJ1NB
|
|
98
99
|
arpakitlib/_arpakit_project_template/src/db/_reinit_sqlalchemy_db.py,sha256=_4O_xr6FXt3qcWOmO82CKG3XX6_RqbqaOYSEygxpBfA,339
|
99
100
|
arpakitlib/_arpakit_project_template/src/db/const.py,sha256=dcvj5C9E2F2KCsGZPBBncQf_EvVJAC1qQgnyD8P4ZEw,6
|
100
101
|
arpakitlib/_arpakit_project_template/src/db/sqlalchemy_model.py,sha256=tIdSerx8m6FA0cx4PDNJF8cWY1utpS_g0sZ0jBzUAbI,286
|
101
|
-
arpakitlib/_arpakit_project_template/src/db/util.py,sha256=
|
102
|
+
arpakitlib/_arpakit_project_template/src/db/util.py,sha256=wYMVXtw_XURv4DFSx4n6CXnKYE-xskzpYSRk9lBIELY,848
|
102
103
|
arpakitlib/_arpakit_project_template/src/operation_execution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
103
104
|
arpakitlib/_arpakit_project_template/src/operation_execution/_remove_operations.py,sha256=KsvAk2zQCp0PX6-QiRW0LKHoJfmH0wVJcBZviFqAoBI,471
|
104
105
|
arpakitlib/_arpakit_project_template/src/operation_execution/_start_operation_executor_worker.py,sha256=K861UI9LFNxfCoPpQ5AJX-oUwjB6bfFNKScKsyU9Xd8,575
|
@@ -114,6 +115,10 @@ arpakitlib/_arpakit_project_template/src/test_data/make_test_data_3.py,sha256=t5
|
|
114
115
|
arpakitlib/_arpakit_project_template/src/test_data/make_test_data_4.py,sha256=t1iYFim7v9NNR7Y10rUVRMVyq76Pdc82d5tQKpNlUFI,100
|
115
116
|
arpakitlib/_arpakit_project_template/src/test_data/make_test_data_5.py,sha256=ptptUxpEa7sX7coToAYZHvy8oxXXQExxS1zqng5ET2I,100
|
116
117
|
arpakitlib/_arpakit_project_template/src/tg_bot/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
118
|
+
arpakitlib/_arpakit_project_template/src/tg_bot/blank/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
119
|
+
arpakitlib/_arpakit_project_template/src/tg_bot/blank/blank.py,sha256=ylQGIhLV3CMEW9qmQ4jr9rcXi0B72qIhbRmx9VLBzHc,204
|
120
|
+
arpakitlib/_arpakit_project_template/src/tg_bot/blank/util.py,sha256=zhMRY4BUA8XMhfo8R4Jri655w4fwl0uniGq48SjchrY,247
|
121
|
+
arpakitlib/_arpakit_project_template/src/tg_bot/const.py,sha256=oz6kWH00rtu4WvAMnrR4CNgT8rVl-gyMqEqWuZwwyZM,578
|
117
122
|
arpakitlib/_arpakit_project_template/src/tg_bot/event.py,sha256=YMwoBH8TQhdjoTQBrxoxOvJqHqlw1yYVE5ymDp2D_Ak,1260
|
118
123
|
arpakitlib/_arpakit_project_template/src/tg_bot/filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
119
124
|
arpakitlib/_arpakit_project_template/src/tg_bot/filter/not_prod_mode.py,sha256=uzkYsquQ5UKD68lEqe0478rbKFQYXZhbLIbxW0QhI44,235
|
@@ -127,12 +132,15 @@ arpakitlib/_arpakit_project_template/src/tg_bot/kb/inline_/callback.py,sha256=47
|
|
127
132
|
arpakitlib/_arpakit_project_template/src/tg_bot/kb/inline_/common.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
128
133
|
arpakitlib/_arpakit_project_template/src/tg_bot/kb/static_/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
129
134
|
arpakitlib/_arpakit_project_template/src/tg_bot/kb/static_/common.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
135
|
+
arpakitlib/_arpakit_project_template/src/tg_bot/middleware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
130
136
|
arpakitlib/_arpakit_project_template/src/tg_bot/router/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
131
|
-
arpakitlib/_arpakit_project_template/src/tg_bot/
|
137
|
+
arpakitlib/_arpakit_project_template/src/tg_bot/router/error.py,sha256=5ktxguLc2pI7BUlJ9jgMp_VnH5gtOt1jzJZskKkmhzI,53
|
138
|
+
arpakitlib/_arpakit_project_template/src/tg_bot/router/router.py,sha256=ZxfPNouuewz3uEW7jLxXdCuT7v-HVjl9YagrKbUMzwc,151
|
139
|
+
arpakitlib/_arpakit_project_template/src/tg_bot/start_tg_bot.py,sha256=7JWCdhyRyYkwV198PjKl6YPqPl7qr-_1Z2tVItfPZlQ,218
|
132
140
|
arpakitlib/_arpakit_project_template/src/tg_bot/transmitted_tg_data.py,sha256=XPNROag9Aq-eSIY7_KxaIxNvAp4orTkpkghRCh3nuz4,585
|
133
141
|
arpakitlib/_arpakit_project_template/src/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
134
142
|
arpakitlib/ar_additional_model_util.py,sha256=GFg-glLCxH9X95R2bhTJsscVwv37FgE1qbaAAyXrnIE,917
|
135
|
-
arpakitlib/ar_aiogram_util.py,sha256=
|
143
|
+
arpakitlib/ar_aiogram_util.py,sha256=GFoIVjuKStzpR_PYPZdyfP3txpjJq1oaCsXTCNMp4Qw,11966
|
136
144
|
arpakitlib/ar_api_key_util.py,sha256=E84JlJXiDHtxLQmV8BNHvqNKu_G8-Dox0XxknYJQ37Q,422
|
137
145
|
arpakitlib/ar_arpakit_lib_module_util.py,sha256=g9uWwTK2eEzmErqwYeVgXDYVMREN8m5CdmgEumAEQfw,5919
|
138
146
|
arpakitlib/ar_arpakit_project_template_util.py,sha256=c7yc8w2IvZGH5hH8eOpL7JuD005hUxZ0GVDcSkJF5iI,3705
|
@@ -141,6 +149,7 @@ arpakitlib/ar_arpakit_schedule_uust_site_util.py,sha256=8wLct9Gd4MWkXzB6nSmETAwT
|
|
141
149
|
arpakitlib/ar_arpakitlib_cli_util.py,sha256=208k_kWc-XHTYqL39k-rYrLcTKFF-3og21PVIsq5b2k,3171
|
142
150
|
arpakitlib/ar_base64_util.py,sha256=aZkg2cZTuAaP2IWeG_LXJ6RO7qhyskVwec-Lks0iM-k,676
|
143
151
|
arpakitlib/ar_base_worker_util.py,sha256=8xKNo_ob-VZsZ2THwM_cJDcA3Y4R-ruM7vKUfzKI94U,5600
|
152
|
+
arpakitlib/ar_blank_util.py,sha256=6O54Z8NnCg8BXxUVWt-oo1kqzt0836k9KJeJofwmj0g,113
|
144
153
|
arpakitlib/ar_cache_file_util.py,sha256=Fo2pH-Zqm966KWFBHG_pbiySGZvhIFCYqy7k1weRfJ0,3476
|
145
154
|
arpakitlib/ar_class_util.py,sha256=Eb4orGm2EFSaHfmrY2A_Nis5iwFMDKFaz1_nxTnfmnQ,487
|
146
155
|
arpakitlib/ar_datetime_util.py,sha256=Xe1NiT9oPQzNSG7RVRkhukhbg4i-hhS5ImmV7sPUc8o,971
|
@@ -169,7 +178,7 @@ arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css,sha256=jzPZlgJTFwSdSphk9C
|
|
169
178
|
arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.css.map,sha256=5wq8eXMLU6Zxb45orZPL1zAsBFJReFw6GjYqGpUX3hg,262650
|
170
179
|
arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js,sha256=ffrLZHHEQ_g84A-ul3yWa10Kk09waOAxHcQXPuZuavg,339292
|
171
180
|
arpakitlib/ar_fastapi_static/swagger-ui/swagger-ui.js.map,sha256=9UhIW7MqCOZPAz1Sl1IKfZUuhWU0p-LJqrnjjJD9Xhc,1159454
|
172
|
-
arpakitlib/ar_fastapi_util.py,sha256=
|
181
|
+
arpakitlib/ar_fastapi_util.py,sha256=KQTiZ9it3ZYmatwBB0k_J9QOvNGGpAtQYkGNMRWazh4,28550
|
173
182
|
arpakitlib/ar_file_storage_in_dir_util.py,sha256=D3e3rGuHoI6xqAA5mVvEpVVpOWY1jyjNsjj2UhyHRbE,3674
|
174
183
|
arpakitlib/ar_file_util.py,sha256=GUdJYm1tUZnYpY-SIPRHAZBHGra8NKy1eYEI0D5AfhY,489
|
175
184
|
arpakitlib/ar_func_util.py,sha256=bCuWbSMoFXBaMNhb89sevj2oaXRk4Jk6Qjot8OXMDT4,1319
|
@@ -196,14 +205,14 @@ arpakitlib/ar_settings_util.py,sha256=rnoTqbRuhiq7294D4crD5kbnU8-gNWJbwGU_Ls2gJo
|
|
196
205
|
arpakitlib/ar_sleep_util.py,sha256=OaLtRaJQWMkGjfj_mW1RB2P4RaSWsAIH8LUoXqsH0zM,1061
|
197
206
|
arpakitlib/ar_sqladmin_util.py,sha256=6Nv9VQssk9PB0piyuss__soYKdjVhdbIeXIv4AgUxmQ,2660
|
198
207
|
arpakitlib/ar_sqlalchemy_model_util.py,sha256=ezui1VEVuABYgrywpP_A2wYvDBlIxwQTZ0HQgBJfZE0,6293
|
199
|
-
arpakitlib/ar_sqlalchemy_util.py,sha256=
|
208
|
+
arpakitlib/ar_sqlalchemy_util.py,sha256=9dXCdKWmUPyMt19sF2YVBB-4x_tl5g2Bsfhn0c_SQ6M,8184
|
200
209
|
arpakitlib/ar_ssh_runner_util.py,sha256=e9deuUdBW7Eh0Exx2nTBhk57SaOZYaJaSjNk8q6dbJk,6804
|
201
210
|
arpakitlib/ar_str_util.py,sha256=yU5gOwNXUQaH5b_tM5t6fXUn9oUcv5EQbVnq2wXXIpQ,3378
|
202
211
|
arpakitlib/ar_type_util.py,sha256=9C3ErtUVs0tAUqtK-foFzjJOykfBOntfCz2IogDOgfA,4134
|
203
212
|
arpakitlib/ar_yookassa_api_client_util.py,sha256=VozuZeCJjmLd1zj2BdC9WfiAQ3XYOrIMsdpNK-AUlm0,5347
|
204
213
|
arpakitlib/ar_zabbix_api_client_util.py,sha256=Q-VR4MvoZ9aHwZeYZr9G3LwN-ANx1T5KFmF6pvPM-9M,6402
|
205
|
-
arpakitlib-1.7.
|
206
|
-
arpakitlib-1.7.
|
207
|
-
arpakitlib-1.7.
|
208
|
-
arpakitlib-1.7.
|
209
|
-
arpakitlib-1.7.
|
214
|
+
arpakitlib-1.7.244.dist-info/LICENSE,sha256=GPEDQMam2r7FSTYqM1mm7aKnxLaWcBotH7UvQtea-ec,11355
|
215
|
+
arpakitlib-1.7.244.dist-info/METADATA,sha256=7Q2Y0Wb_OIUHWc5aXYt1L2EHIfYCEeelb-JrayEGrA0,3386
|
216
|
+
arpakitlib-1.7.244.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
217
|
+
arpakitlib-1.7.244.dist-info/entry_points.txt,sha256=36xqR3PJFT2kuwjkM_EqoIy0qFUDPKSm_mJaI7emewE,87
|
218
|
+
arpakitlib-1.7.244.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|