arpakitlib 1.7.66__py3-none-any.whl → 1.7.124__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 +4 -0
- arpakitlib/_arpakit_project_template/manage/hello_world.py +2 -2
- arpakitlib/_arpakit_project_template/manage/json_beutify.py +4 -4
- arpakitlib/_arpakit_project_template/manage/poetry_config.sh +2 -0
- arpakitlib/_arpakit_project_template/manage/poetry_self_add_plugin_export.sh +2 -0
- arpakitlib/_arpakit_project_template/manage/sandbox/sandbox_1.py +4 -4
- arpakitlib/_arpakit_project_template/manage/sandbox/sandbox_2.py +4 -4
- arpakitlib/_arpakit_project_template/manage/sandbox/sandbox_3.py +4 -4
- arpakitlib/_arpakit_project_template/manage/sandbox/sandbox_4.py +4 -4
- arpakitlib/_arpakit_project_template/manage/sandbox/sandbox_5.py +4 -4
- arpakitlib/_arpakit_project_template/manage/sandbox/sandbox_6.py +4 -4
- arpakitlib/_arpakit_project_template/manage/sandbox/sandbox_7.py +4 -4
- arpakitlib/_arpakit_project_template/resource/static/healthcheck +1 -0
- arpakitlib/_arpakit_project_template/src/admin1/__init__.py +0 -0
- arpakitlib/_arpakit_project_template/src/admin1/add_admin_in_app.py +25 -0
- arpakitlib/_arpakit_project_template/src/admin1/admin_auth.py +29 -0
- arpakitlib/_arpakit_project_template/src/admin1/model_view.py +19 -0
- arpakitlib/_arpakit_project_template/src/api/auth.py +1 -6
- arpakitlib/_arpakit_project_template/src/api/create_api_app.py +13 -52
- arpakitlib/_arpakit_project_template/src/api/event.py +51 -2
- arpakitlib/_arpakit_project_template/src/api/router/main_router.py +3 -0
- arpakitlib/_arpakit_project_template/src/api/router/v1/get_api_error_info.py +6 -4
- arpakitlib/_arpakit_project_template/src/api/start_api_for_dev_with_reload.py +12 -4
- arpakitlib/_arpakit_project_template/src/api/{start_api_for_dev.py → start_api_for_dev_without_reload.py} +3 -3
- arpakitlib/_arpakit_project_template/src/api/transmitted_api_data.py +4 -0
- arpakitlib/_arpakit_project_template/src/business_service/hello_world.py +12 -0
- arpakitlib/_arpakit_project_template/{manage/logging_check.py → src/core/check_logging.py} +3 -3
- arpakitlib/_arpakit_project_template/src/core/check_settings.py +12 -0
- arpakitlib/_arpakit_project_template/src/core/generate_settings_env_example.py +16 -0
- arpakitlib/_arpakit_project_template/src/core/settings.py +13 -5
- arpakitlib/_arpakit_project_template/src/core/util.py +0 -18
- arpakitlib/_arpakit_project_template/{manage/sqlalchemy_db_check_conn.py → src/db/check_conn_sqlalchemy_db.py} +2 -1
- arpakitlib/_arpakit_project_template/src/db/const.py +0 -0
- arpakitlib/_arpakit_project_template/src/db/init_sqlalchemy_db.py +11 -0
- arpakitlib/_arpakit_project_template/src/db/reinit_sqlalchemy_db.py +13 -0
- arpakitlib/_arpakit_project_template/src/db/util.py +21 -0
- arpakitlib/_arpakit_project_template/src/operation_execution/operation_executor.py +6 -4
- arpakitlib/_arpakit_project_template/src/operation_execution/scheduled_operations.py +3 -3
- arpakitlib/_arpakit_project_template/src/operation_execution/{start_operation_executor_worker_for_dev.py → start_operation_executor_worker.py} +7 -5
- arpakitlib/_arpakit_project_template/src/operation_execution/start_scheduled_operation_creator_worker.py +19 -0
- arpakitlib/_arpakit_project_template/src/operation_execution/util.py +0 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/__init__.py +0 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/router/__init__.py +0 -0
- arpakitlib/_arpakit_project_template/src/tg_bot/start_tg_bot.py +0 -0
- arpakitlib/api_key_util.py +12 -0
- arpakitlib/ar_additional_model_util.py +18 -1
- arpakitlib/ar_arpakit_lib_module_util.py +13 -1
- arpakitlib/ar_arpakit_schedule_uust_api_client_util.py +24 -3
- arpakitlib/ar_arpakitlib_cli_util.py +2 -0
- arpakitlib/ar_base_worker_util.py +67 -18
- arpakitlib/ar_exception_util.py +13 -0
- arpakitlib/ar_fastapi_util.py +129 -98
- arpakitlib/ar_file_util.py +2 -0
- arpakitlib/ar_func_util.py +55 -0
- arpakitlib/ar_json_util.py +11 -9
- arpakitlib/ar_need_type_util.py +8 -1
- arpakitlib/ar_openai_api_client_util.py +16 -2
- arpakitlib/ar_operation_execution_util.py +143 -141
- arpakitlib/ar_schedule_uust_api_client_util.py +13 -6
- arpakitlib/ar_settings_util.py +24 -5
- arpakitlib/ar_sqlalchemy_model_util.py +37 -7
- arpakitlib/ar_ssh_runner_util.py +2 -2
- arpakitlib/ar_str_util.py +30 -1
- arpakitlib/ar_type_util.py +52 -7
- {arpakitlib-1.7.66.dist-info → arpakitlib-1.7.124.dist-info}/METADATA +27 -20
- {arpakitlib-1.7.66.dist-info → arpakitlib-1.7.124.dist-info}/RECORD +70 -62
- {arpakitlib-1.7.66.dist-info → arpakitlib-1.7.124.dist-info}/WHEEL +1 -1
- arpakitlib/_arpakit_project_template/AUTHOR.md +0 -4
- arpakitlib/_arpakit_project_template/manage/example_nginx_proxy.nginx +0 -14
- arpakitlib/_arpakit_project_template/manage/example_poetry_arpakitlib.sh +0 -1
- arpakitlib/_arpakit_project_template/manage/example_pyproject.toml +0 -18
- arpakitlib/_arpakit_project_template/manage/example_systemd.service +0 -12
- arpakitlib/_arpakit_project_template/manage/requirements.txt +0 -209
- arpakitlib/_arpakit_project_template/manage/settings_check.py +0 -10
- arpakitlib/_arpakit_project_template/manage/settings_generate_env_example.py +0 -13
- arpakitlib/_arpakit_project_template/manage/sqlalchemy_db_init.py +0 -10
- arpakitlib/_arpakit_project_template/manage/sqlalchemy_db_reinit.py +0 -10
- arpakitlib/_arpakit_project_template/src/operation_execution/start_scheduled_operation_creator_worker_for_dev.py +0 -16
- {arpakitlib-1.7.66.dist-info → arpakitlib-1.7.124.dist-info}/LICENSE +0 -0
- {arpakitlib-1.7.66.dist-info → arpakitlib-1.7.124.dist-info}/NOTICE +0 -0
- {arpakitlib-1.7.66.dist-info → arpakitlib-1.7.124.dist-info}/entry_points.txt +0 -0
arpakitlib/ar_fastapi_util.py
CHANGED
@@ -21,18 +21,18 @@ from fastapi import FastAPI, APIRouter, Query, Security, Depends
|
|
21
21
|
from fastapi.openapi.docs import get_swagger_ui_html, get_redoc_html
|
22
22
|
from fastapi.security import APIKeyHeader
|
23
23
|
from pydantic import BaseModel, ConfigDict
|
24
|
+
from starlette import status
|
24
25
|
from starlette.middleware.cors import CORSMiddleware
|
25
26
|
from starlette.staticfiles import StaticFiles
|
26
27
|
|
27
|
-
from arpakitlib.ar_base_worker_util import BaseWorker
|
28
28
|
from arpakitlib.ar_dict_util import combine_dicts
|
29
29
|
from arpakitlib.ar_enumeration_util import Enumeration
|
30
|
-
from arpakitlib.
|
31
|
-
from arpakitlib.ar_json_util import
|
30
|
+
from arpakitlib.ar_func_util import raise_if_not_async_func, is_async_function, is_async_object
|
31
|
+
from arpakitlib.ar_json_util import safely_transfer_obj_to_json_str_to_json_obj
|
32
32
|
from arpakitlib.ar_logging_util import setup_normal_logging
|
33
|
-
from arpakitlib.ar_sqlalchemy_model_util import StoryLogDBM
|
33
|
+
from arpakitlib.ar_sqlalchemy_model_util import StoryLogDBM, OperationDBM
|
34
34
|
from arpakitlib.ar_sqlalchemy_util import SQLAlchemyDB
|
35
|
-
from arpakitlib.ar_type_util import raise_for_type,
|
35
|
+
from arpakitlib.ar_type_util import raise_for_type, raise_if_none
|
36
36
|
|
37
37
|
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
38
38
|
|
@@ -96,6 +96,10 @@ class StoryLogSO(SimpleSO):
|
|
96
96
|
title: str | None
|
97
97
|
data: dict[str, Any]
|
98
98
|
|
99
|
+
@classmethod
|
100
|
+
def from_story_log_dbm(cls, *, story_log_dbm: StoryLogDBM) -> StoryLogSO:
|
101
|
+
return cls.model_validate(story_log_dbm.simple_dict(include_sd_properties=True))
|
102
|
+
|
99
103
|
|
100
104
|
class OperationSO(SimpleSO):
|
101
105
|
execution_start_dt: datetime | None
|
@@ -107,13 +111,29 @@ class OperationSO(SimpleSO):
|
|
107
111
|
error_data: dict[str, Any]
|
108
112
|
duration_total_seconds: float | None
|
109
113
|
|
114
|
+
@classmethod
|
115
|
+
def from_operation_dbm(cls, *, operation_dbm: OperationDBM) -> OperationSO:
|
116
|
+
return cls.model_validate(operation_dbm.simple_dict(include_sd_properties=True))
|
117
|
+
|
110
118
|
|
111
119
|
class APIJSONResponse(fastapi.responses.JSONResponse):
|
112
|
-
def __init__(self, *, content: BaseSO, status_code: int = starlette.status.HTTP_200_OK):
|
120
|
+
def __init__(self, *, content: dict | list | BaseSO | None, status_code: int = starlette.status.HTTP_200_OK):
|
121
|
+
if isinstance(content, dict):
|
122
|
+
content = safely_transfer_obj_to_json_str_to_json_obj(content)
|
123
|
+
elif isinstance(content, list):
|
124
|
+
content = safely_transfer_obj_to_json_str_to_json_obj(content)
|
125
|
+
elif isinstance(content, BaseSO):
|
126
|
+
content = safely_transfer_obj_to_json_str_to_json_obj(content.model_dump())
|
127
|
+
elif content is None:
|
128
|
+
content = None
|
129
|
+
else:
|
130
|
+
raise ValueError(f"unknown content type, type(content)={type(content)}")
|
131
|
+
|
113
132
|
self.content_ = content
|
114
133
|
self.status_code_ = status_code
|
134
|
+
|
115
135
|
super().__init__(
|
116
|
-
content=
|
136
|
+
content=content,
|
117
137
|
status_code=status_code
|
118
138
|
)
|
119
139
|
|
@@ -221,6 +241,12 @@ def create_handle_exception(
|
|
221
241
|
error_so.error_specification_code.upper().replace(" ", "_").strip()
|
222
242
|
)
|
223
243
|
|
244
|
+
if error_so.error_code == BaseAPIErrorCodes.not_found:
|
245
|
+
status_code = status.HTTP_404_NOT_FOUND
|
246
|
+
|
247
|
+
if error_so.error_code == BaseAPIErrorCodes.cannot_authorize:
|
248
|
+
status_code = status.HTTP_401_UNAUTHORIZED
|
249
|
+
|
224
250
|
if _need_exc_info:
|
225
251
|
_logger.error(str(exception), exc_info=exception)
|
226
252
|
else:
|
@@ -231,7 +257,7 @@ def create_handle_exception(
|
|
231
257
|
_func_data = func(
|
232
258
|
status_code=status_code, error_so=error_so, request=request, exception=exception, **_kwargs
|
233
259
|
)
|
234
|
-
if
|
260
|
+
if is_async_function(_func_data):
|
235
261
|
_func_data = await _func_data
|
236
262
|
if _func_data is not None:
|
237
263
|
status_code, error_so, _kwargs = _func_data[0], _func_data[1], _func_data[2]
|
@@ -403,37 +429,6 @@ class BaseShutdownAPIEvent:
|
|
403
429
|
self._logger.info("on_shutdown ends")
|
404
430
|
|
405
431
|
|
406
|
-
class InitSqlalchemyDBStartupAPIEvent(BaseStartupAPIEvent):
|
407
|
-
def __init__(self, sqlalchemy_db: SQLAlchemyDB):
|
408
|
-
super().__init__()
|
409
|
-
self.sqlalchemy_db = sqlalchemy_db
|
410
|
-
|
411
|
-
async def async_on_startup(self, *args, **kwargs):
|
412
|
-
self.sqlalchemy_db.init()
|
413
|
-
|
414
|
-
|
415
|
-
class SafeRunWorkerStartupAPIEvent(BaseStartupAPIEvent):
|
416
|
-
def __init__(self, workers: list[BaseWorker], safe_run_in_background_mode: str):
|
417
|
-
super().__init__()
|
418
|
-
self.workers = workers
|
419
|
-
self.safe_run_in_background_mode = safe_run_in_background_mode
|
420
|
-
|
421
|
-
async def async_on_startup(self, *args, **kwargs):
|
422
|
-
for worker in self.workers:
|
423
|
-
_ = worker.safe_run_in_background(safe_run_in_background_mode=self.safe_run_in_background_mode)
|
424
|
-
|
425
|
-
|
426
|
-
class InitFileStoragesInDir(BaseStartupAPIEvent):
|
427
|
-
def __init__(self, file_storages_in_dir: list[FileStorageInDir | None]):
|
428
|
-
super().__init__()
|
429
|
-
file_storages_in_dir = [v for v in file_storages_in_dir if v is not None]
|
430
|
-
self.file_storages_in_dir = file_storages_in_dir
|
431
|
-
|
432
|
-
async def async_on_startup(self, *args, **kwargs):
|
433
|
-
for file_storage_in_dir in self.file_storages_in_dir:
|
434
|
-
file_storage_in_dir.init()
|
435
|
-
|
436
|
-
|
437
432
|
class BaseTransmittedAPIData(BaseModel):
|
438
433
|
model_config = ConfigDict(extra="ignore", arbitrary_types_allowed=True, from_attributes=True)
|
439
434
|
|
@@ -448,27 +443,63 @@ class BaseAPIAuthData(BaseModel):
|
|
448
443
|
require_api_key_string: bool = False
|
449
444
|
require_token_string: bool = False
|
450
445
|
|
446
|
+
require_correct_api_key: bool = False
|
447
|
+
require_correct_token: bool = False
|
448
|
+
|
451
449
|
token_string: str | None = None
|
452
450
|
api_key_string: str | None = None
|
453
451
|
|
452
|
+
is_token_string_correct: bool | None = None
|
453
|
+
is_api_key_string_correct: bool | None = None
|
454
|
+
|
454
455
|
|
455
456
|
def base_api_auth(
|
456
457
|
*,
|
457
458
|
require_api_key_string: bool = False,
|
458
|
-
require_token_string: bool = False
|
459
|
+
require_token_string: bool = False,
|
460
|
+
validate_api_key_func: Callable | None = None,
|
461
|
+
validate_token_func: Callable | None = None,
|
462
|
+
correct_api_keys: str | list[str] | None = None,
|
463
|
+
correct_tokens: str | list[str] | None = None,
|
464
|
+
require_correct_api_key: bool = False,
|
465
|
+
require_correct_token: bool = False,
|
466
|
+
**kwargs
|
459
467
|
) -> Callable:
|
468
|
+
if isinstance(correct_api_keys, str):
|
469
|
+
correct_api_keys = [correct_api_keys]
|
470
|
+
if correct_api_keys is not None:
|
471
|
+
raise_for_type(correct_api_keys, list)
|
472
|
+
validate_api_key_func = lambda *args, **kwargs_: kwargs_["api_key_string"] in correct_api_keys
|
473
|
+
|
474
|
+
if isinstance(correct_tokens, str):
|
475
|
+
correct_tokens = [correct_tokens]
|
476
|
+
if correct_tokens is not None:
|
477
|
+
raise_for_type(correct_tokens, list)
|
478
|
+
validate_token_func = lambda *args, **kwargs_: kwargs_["token_string"] in correct_tokens
|
479
|
+
|
480
|
+
if require_correct_api_key:
|
481
|
+
raise_if_none(validate_api_key_func)
|
482
|
+
require_api_key_string = True
|
483
|
+
|
484
|
+
if require_correct_token:
|
485
|
+
raise_if_none(validate_token_func)
|
486
|
+
require_token_string = True
|
487
|
+
|
460
488
|
async def func(
|
461
489
|
*,
|
462
490
|
ac: fastapi.security.HTTPAuthorizationCredentials | None = fastapi.Security(
|
463
491
|
fastapi.security.HTTPBearer(auto_error=False)
|
464
492
|
),
|
465
493
|
api_key_string: str | None = Security(APIKeyHeader(name="apikey", auto_error=False)),
|
466
|
-
request: starlette.requests.Request
|
494
|
+
request: starlette.requests.Request,
|
495
|
+
transmitted_api_data: BaseTransmittedAPIData = Depends(get_transmitted_api_data)
|
467
496
|
) -> BaseAPIAuthData:
|
468
497
|
|
469
498
|
api_auth_data = BaseAPIAuthData(
|
470
499
|
require_api_key_string=require_api_key_string,
|
471
|
-
require_token_string=require_token_string
|
500
|
+
require_token_string=require_token_string,
|
501
|
+
require_correct_api_key=require_correct_api_key,
|
502
|
+
require_correct_token=require_correct_token
|
472
503
|
)
|
473
504
|
|
474
505
|
# api_key
|
@@ -518,63 +549,75 @@ def base_api_auth(
|
|
518
549
|
if not api_auth_data.token_string:
|
519
550
|
api_auth_data.token_string = None
|
520
551
|
|
552
|
+
# api_key
|
553
|
+
|
521
554
|
if require_api_key_string and not api_auth_data.api_key_string:
|
522
555
|
raise APIException(
|
523
556
|
status_code=starlette.status.HTTP_401_UNAUTHORIZED,
|
524
557
|
error_code=BaseAPIErrorCodes.cannot_authorize,
|
525
|
-
error_data=
|
558
|
+
error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
|
526
559
|
)
|
527
560
|
|
561
|
+
# token
|
562
|
+
|
528
563
|
if require_token_string and not api_auth_data.token_string:
|
529
564
|
raise APIException(
|
530
565
|
status_code=starlette.status.HTTP_401_UNAUTHORIZED,
|
531
566
|
error_code=BaseAPIErrorCodes.cannot_authorize,
|
532
|
-
error_data=
|
567
|
+
error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
|
533
568
|
)
|
534
569
|
|
535
|
-
|
570
|
+
# api_key
|
536
571
|
|
537
|
-
|
572
|
+
if validate_api_key_func is not None:
|
573
|
+
validate_api_key_func_data = validate_api_key_func(
|
574
|
+
api_key_string=api_auth_data.api_key_string,
|
575
|
+
token_string=api_auth_data.token_string,
|
576
|
+
base_api_auth_data=api_auth_data,
|
577
|
+
transmitted_api_data=transmitted_api_data,
|
578
|
+
request=request,
|
579
|
+
**kwargs
|
580
|
+
)
|
581
|
+
if is_async_object(validate_api_key_func_data):
|
582
|
+
validate_api_key_func_data = await validate_api_key_func_data
|
583
|
+
api_auth_data.is_api_key_string_correct = validate_api_key_func_data
|
538
584
|
|
585
|
+
# token
|
539
586
|
|
540
|
-
|
541
|
-
|
587
|
+
if validate_token_func is not None:
|
588
|
+
validate_token_func_data = validate_token_func(
|
589
|
+
api_key_string=api_auth_data.api_key_string,
|
590
|
+
token_string=api_auth_data.token_string,
|
591
|
+
base_api_auth_data=api_auth_data,
|
592
|
+
transmitted_api_data=transmitted_api_data,
|
593
|
+
request=request,
|
594
|
+
**kwargs
|
595
|
+
)
|
596
|
+
if is_async_object(validate_token_func_data):
|
597
|
+
validate_token_func_data_data = await validate_token_func_data
|
598
|
+
api_auth_data.is_token_string_correct = validate_token_func_data_data
|
542
599
|
|
600
|
+
# api_key
|
543
601
|
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
551
|
-
raise_if_none(validate_api_key_func)
|
602
|
+
if require_correct_api_key:
|
603
|
+
if not api_auth_data.is_api_key_string_correct:
|
604
|
+
raise APIException(
|
605
|
+
status_code=starlette.status.HTTP_401_UNAUTHORIZED,
|
606
|
+
error_code=BaseAPIErrorCodes.cannot_authorize,
|
607
|
+
error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
|
608
|
+
)
|
552
609
|
|
553
|
-
|
554
|
-
*,
|
555
|
-
base_api_auth_data: BaseAPIAuthData = Depends(base_api_auth(
|
556
|
-
require_api_key_string=True,
|
557
|
-
require_token_string=False
|
558
|
-
)),
|
559
|
-
transmitted_api_data: BaseTransmittedAPIData = Depends(get_transmitted_api_data),
|
560
|
-
request: starlette.requests.Request
|
561
|
-
) -> CheckAPIKeyAPIAuthData:
|
562
|
-
check_api_key_api_auth_data = CheckAPIKeyAPIAuthData.model_validate(base_api_auth_data)
|
563
|
-
check_api_key_api_auth_data.is_api_key_correct = validate_api_key_func(
|
564
|
-
api_key_string=base_api_auth_data.api_key_string,
|
565
|
-
base_api_auth_data=base_api_auth_data,
|
566
|
-
transmitted_api_data=transmitted_api_data,
|
567
|
-
request=request
|
568
|
-
)
|
610
|
+
# token
|
569
611
|
|
570
|
-
if
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
612
|
+
if require_correct_token:
|
613
|
+
if not api_auth_data.is_token_string_correct:
|
614
|
+
raise APIException(
|
615
|
+
status_code=starlette.status.HTTP_401_UNAUTHORIZED,
|
616
|
+
error_code=BaseAPIErrorCodes.cannot_authorize,
|
617
|
+
error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
|
618
|
+
)
|
576
619
|
|
577
|
-
return
|
620
|
+
return api_auth_data
|
578
621
|
|
579
622
|
return func
|
580
623
|
|
@@ -617,18 +660,12 @@ def simple_api_router_for_testing():
|
|
617
660
|
return router
|
618
661
|
|
619
662
|
|
620
|
-
DEFAULT_CONTACT = {
|
621
|
-
"name": "ARPAKIT Company",
|
622
|
-
"email": "support@arpakit.com"
|
623
|
-
}
|
624
|
-
|
625
|
-
|
626
663
|
def create_fastapi_app(
|
627
664
|
*,
|
628
665
|
title: str = "arpakitlib FastAPI",
|
629
666
|
description: str | None = "arpakitlib FastAPI",
|
630
667
|
log_filepath: str | None = "./story.log",
|
631
|
-
handle_exception_: Callable | None =
|
668
|
+
handle_exception_: Callable | None = None,
|
632
669
|
startup_api_events: list[BaseStartupAPIEvent | None] | None = None,
|
633
670
|
shutdown_api_events: list[BaseShutdownAPIEvent | None] | None = None,
|
634
671
|
transmitted_api_data: BaseTransmittedAPIData = BaseTransmittedAPIData(),
|
@@ -641,8 +678,8 @@ def create_fastapi_app(
|
|
641
678
|
|
642
679
|
setup_normal_logging(log_filepath=log_filepath)
|
643
680
|
|
644
|
-
if
|
645
|
-
|
681
|
+
if handle_exception_ is None:
|
682
|
+
handle_exception_ = create_handle_exception()
|
646
683
|
|
647
684
|
if not startup_api_events:
|
648
685
|
startup_api_events = [BaseStartupAPIEvent()]
|
@@ -679,16 +716,10 @@ def create_fastapi_app(
|
|
679
716
|
|
680
717
|
add_swagger_to_app(app=app)
|
681
718
|
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
)
|
687
|
-
else:
|
688
|
-
add_exception_handler_to_app(
|
689
|
-
app=app,
|
690
|
-
handle_exception=create_handle_exception()
|
691
|
-
)
|
719
|
+
add_exception_handler_to_app(
|
720
|
+
app=app,
|
721
|
+
handle_exception=handle_exception_
|
722
|
+
)
|
692
723
|
|
693
724
|
add_needed_api_router_to_app(app=app)
|
694
725
|
|
arpakitlib/ar_file_util.py
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
import asyncio
|
2
|
+
import inspect
|
3
|
+
from typing import Callable
|
4
|
+
|
5
|
+
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
6
|
+
|
7
|
+
|
8
|
+
def is_async_function(func: Callable) -> bool:
|
9
|
+
return asyncio.iscoroutinefunction(func)
|
10
|
+
|
11
|
+
|
12
|
+
def is_async_object(obj: object) -> bool:
|
13
|
+
return asyncio.iscoroutine(obj)
|
14
|
+
|
15
|
+
|
16
|
+
def is_sync_function(func: Callable) -> bool:
|
17
|
+
return inspect.isfunction(func) and not is_async_function(func)
|
18
|
+
|
19
|
+
|
20
|
+
def raise_if_not_async_func(func: Callable):
|
21
|
+
if not is_async_function(func):
|
22
|
+
raise TypeError(f"The provided function '{func.__name__}' is not an async function")
|
23
|
+
|
24
|
+
|
25
|
+
def raise_if_not_sync_func(func: Callable):
|
26
|
+
if not is_sync_function(func):
|
27
|
+
raise TypeError(f"The provided function '{func.__name__}' is not a sync function")
|
28
|
+
|
29
|
+
|
30
|
+
def raise_if_async_func(func: Callable):
|
31
|
+
if is_async_function(func):
|
32
|
+
raise TypeError(f"The provided function '{func.__name__}' should not be async")
|
33
|
+
|
34
|
+
|
35
|
+
def raise_if_sync_func(func: Callable):
|
36
|
+
if is_sync_function(func):
|
37
|
+
raise TypeError(f"The provided function '{func.__name__}' should not be sync")
|
38
|
+
|
39
|
+
|
40
|
+
def __example():
|
41
|
+
def one():
|
42
|
+
pass
|
43
|
+
|
44
|
+
async def two():
|
45
|
+
pass
|
46
|
+
|
47
|
+
print(is_sync_function(one))
|
48
|
+
print(is_sync_function(two))
|
49
|
+
|
50
|
+
print(is_async_function(one))
|
51
|
+
print(is_async_function(two))
|
52
|
+
|
53
|
+
|
54
|
+
if __name__ == '__main__':
|
55
|
+
__example()
|
arpakitlib/ar_json_util.py
CHANGED
@@ -1,29 +1,31 @@
|
|
1
1
|
# arpakit
|
2
2
|
|
3
3
|
import json
|
4
|
-
from typing import
|
4
|
+
from typing import Any
|
5
5
|
|
6
6
|
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
7
7
|
|
8
8
|
|
9
|
-
def
|
9
|
+
def safely_transfer_str_to_json_obj(data: str) -> dict[Any, Any] | list[Any] | None:
|
10
10
|
if not isinstance(data, str):
|
11
11
|
raise ValueError("not isinstance(data, str)")
|
12
12
|
return json.loads(data)
|
13
13
|
|
14
14
|
|
15
|
-
def
|
16
|
-
if not isinstance(data, dict) and not isinstance(data, list):
|
17
|
-
raise ValueError("not isinstance(data, dict) and not isinstance(data, list)")
|
15
|
+
def safely_transfer_obj_to_json_str(data: dict[Any, Any] | list[Any] | None) -> str:
|
16
|
+
if not isinstance(data, dict) and not isinstance(data, list) and data is not None:
|
17
|
+
raise ValueError("not isinstance(data, dict) and not isinstance(data, list) and data is not None")
|
18
18
|
return json.dumps(data, ensure_ascii=False, indent=2, default=str)
|
19
19
|
|
20
20
|
|
21
|
-
def
|
22
|
-
|
21
|
+
def safely_transfer_obj_to_json_str_to_json_obj(
|
22
|
+
data: dict[Any, Any] | list[Any] | None
|
23
|
+
) -> dict[Any, Any] | list[Any] | None:
|
24
|
+
return safely_transfer_str_to_json_obj(safely_transfer_obj_to_json_str(data))
|
23
25
|
|
24
26
|
|
25
|
-
def
|
26
|
-
return
|
27
|
+
def safely_transfer_str_to_json_obj_to_json_str(data: str) -> str:
|
28
|
+
return safely_transfer_obj_to_json_str(safely_transfer_str_to_json_obj(data))
|
27
29
|
|
28
30
|
|
29
31
|
def __example():
|
arpakitlib/ar_need_type_util.py
CHANGED
@@ -56,7 +56,14 @@ def parse_need_type(value: Any, need_type: str, allow_none: bool = False) -> Any
|
|
56
56
|
|
57
57
|
|
58
58
|
def __example():
|
59
|
-
|
59
|
+
print(parse_need_type(value=123, need_type="int"))
|
60
|
+
print(parse_need_type(value="True", need_type="bool"))
|
61
|
+
print(parse_need_type(value=123.456, need_type="float"))
|
62
|
+
print(parse_need_type(value='[1, 2, 3]', need_type="list_of_int"))
|
63
|
+
print(parse_need_type(value='["a", "b", "c"]', need_type="list_of_str"))
|
64
|
+
print(parse_need_type(value='[1.1, 2.2, 3.3]', need_type="list_of_float"))
|
65
|
+
print(parse_need_type(value='{"key": "value"}', need_type="json"))
|
66
|
+
print(parse_need_type(value="hello world", need_type="str"))
|
60
67
|
|
61
68
|
|
62
69
|
if __name__ == '__main__':
|
@@ -49,11 +49,25 @@ class OpenAIAPIClient:
|
|
49
49
|
|
50
50
|
|
51
51
|
def __example():
|
52
|
-
|
52
|
+
open_ai = OpenAI(api_key="your-api-key")
|
53
|
+
client = OpenAIAPIClient(open_ai=open_ai)
|
54
|
+
|
55
|
+
print("Checking OpenAI API connection...")
|
56
|
+
if client.is_conn_good():
|
57
|
+
print("Connection to OpenAI API is good")
|
58
|
+
else:
|
59
|
+
print("Failed to connect to OpenAI API")
|
53
60
|
|
54
61
|
|
55
62
|
async def __async_example():
|
56
|
-
|
63
|
+
async_open_ai = AsyncOpenAI(api_key="your-api-key")
|
64
|
+
client = OpenAIAPIClient(async_open_ai=async_open_ai)
|
65
|
+
|
66
|
+
print("Checking OpenAI API async connection...")
|
67
|
+
if await client.async_is_conn_good():
|
68
|
+
print("Async connection to OpenAI API is good")
|
69
|
+
else:
|
70
|
+
print("Failed to async connect to OpenAI API")
|
57
71
|
|
58
72
|
|
59
73
|
if __name__ == '__main__':
|