arpakitlib 1.7.250__py3-none-any.whl → 1.7.252__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/alembic/README +1 -0
- arpakitlib/_arpakit_project_template/alembic/env.py +83 -0
- arpakitlib/_arpakit_project_template/alembic/script.py.mako +26 -0
- arpakitlib/_arpakit_project_template/alembic.ini +119 -0
- arpakitlib/_arpakit_project_template/example.env +5 -14
- arpakitlib/_arpakit_project_template/manage/docker_run_postgres.sh +4 -3
- arpakitlib/_arpakit_project_template/manage/docker_start_postgres.sh +2 -1
- arpakitlib/_arpakit_project_template/manage/docker_stop_postgres.sh +2 -1
- arpakitlib/_arpakit_project_template/manage/git_set_arpakit_company_origin.sh +4 -2
- arpakitlib/_arpakit_project_template/manage/git_set_arpakit_origin.sh +4 -2
- arpakitlib/_arpakit_project_template/src/additional_model/additional_model.py +0 -7
- arpakitlib/_arpakit_project_template/src/api/auth.py +52 -0
- arpakitlib/_arpakit_project_template/src/api/create_api_app.py +21 -14
- arpakitlib/_arpakit_project_template/src/api/create_handle_exception_.py +13 -13
- arpakitlib/_arpakit_project_template/src/api/event.py +24 -2
- arpakitlib/_arpakit_project_template/src/api/transmitted_api_data.py +3 -11
- arpakitlib/_arpakit_project_template/src/core/settings.py +3 -111
- arpakitlib/_arpakit_project_template/src/db/util.py +1 -3
- arpakitlib/_arpakit_project_template/src/just_script/__init__.py +0 -0
- arpakitlib/_arpakit_project_template/src/just_script/example.py +16 -0
- arpakitlib/ar_arpakit_project_template_util.py +8 -18
- arpakitlib/ar_arpakit_schedule_uust_api_client_util.py +5 -4
- arpakitlib/ar_arpakitlib_cli_util.py +10 -22
- arpakitlib/ar_class_util.py +0 -1
- arpakitlib/ar_cryptomus_api_client_util.py +21 -0
- arpakitlib/ar_fastapi_util.py +101 -70
- arpakitlib/ar_schedule_uust_api_client_util.py +24 -24
- arpakitlib/ar_settings_util.py +166 -14
- arpakitlib/ar_sqlalchemy_model_util.py +1 -1
- arpakitlib/ar_steam_payment_api_client_util.py +21 -0
- arpakitlib/ar_wata_api_client.py +21 -0
- {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/METADATA +1 -1
- {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/RECORD +37 -28
- /arpakitlib/_arpakit_project_template/src/core/{_check_settings.py → _show_settings.py} +0 -0
- {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/LICENSE +0 -0
- {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/WHEEL +0 -0
- {arpakitlib-1.7.250.dist-info → arpakitlib-1.7.252.dist-info}/entry_points.txt +0 -0
arpakitlib/ar_fastapi_util.py
CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
|
|
4
4
|
|
5
5
|
import asyncio
|
6
6
|
import datetime as dt
|
7
|
+
import inspect
|
7
8
|
import logging
|
8
9
|
import os.path
|
9
10
|
import pathlib
|
@@ -24,12 +25,14 @@ from starlette import status
|
|
24
25
|
from starlette.middleware.cors import CORSMiddleware
|
25
26
|
from starlette.staticfiles import StaticFiles
|
26
27
|
|
28
|
+
from arpakitlib.ar_datetime_util import now_utc_dt
|
27
29
|
from arpakitlib.ar_dict_util import combine_dicts
|
28
30
|
from arpakitlib.ar_enumeration_util import Enumeration
|
29
31
|
from arpakitlib.ar_exception_util import exception_to_traceback_str
|
32
|
+
from arpakitlib.ar_file_storage_in_dir_util import FileStorageInDir
|
30
33
|
from arpakitlib.ar_func_util import raise_if_not_async_func, is_async_object
|
31
34
|
from arpakitlib.ar_json_util import safely_transfer_obj_to_json_str_to_json_obj, safely_transfer_obj_to_json_str
|
32
|
-
from arpakitlib.
|
35
|
+
from arpakitlib.ar_settings_util import SimpleSettings
|
33
36
|
from arpakitlib.ar_sqlalchemy_model_util import StoryLogDBM, OperationDBM
|
34
37
|
from arpakitlib.ar_sqlalchemy_util import SQLAlchemyDB
|
35
38
|
from arpakitlib.ar_type_util import raise_for_type, raise_if_none
|
@@ -212,19 +215,20 @@ class APIException(fastapi.exceptions.HTTPException):
|
|
212
215
|
|
213
216
|
def create_handle_exception(
|
214
217
|
*,
|
215
|
-
|
216
|
-
|
218
|
+
funcs_before: list[Callable | None] | None = None,
|
219
|
+
async_funcs_after: list[Callable | None] | None = None,
|
217
220
|
) -> Callable:
|
218
|
-
if
|
219
|
-
|
220
|
-
|
221
|
+
if funcs_before is None:
|
222
|
+
funcs_before = []
|
223
|
+
funcs_before = [v for v in funcs_before if v is not None]
|
221
224
|
|
222
|
-
if
|
223
|
-
|
224
|
-
|
225
|
+
if async_funcs_after is None:
|
226
|
+
async_funcs_after = []
|
227
|
+
async_funcs_after = [v for v in async_funcs_after if v is not None]
|
225
228
|
|
226
|
-
async def
|
227
|
-
request: starlette.requests.Request,
|
229
|
+
async def func(
|
230
|
+
request: starlette.requests.Request,
|
231
|
+
exception: Exception
|
228
232
|
) -> APIJSONResponse:
|
229
233
|
status_code = starlette.status.HTTP_500_INTERNAL_SERVER_ERROR
|
230
234
|
|
@@ -266,10 +270,10 @@ def create_handle_exception(
|
|
266
270
|
status_code = starlette.status.HTTP_500_INTERNAL_SERVER_ERROR
|
267
271
|
error_so.error_code = BaseAPIErrorCodes.unknown_error
|
268
272
|
|
269
|
-
if error_so.error_code:
|
273
|
+
if error_so.error_code is not None:
|
270
274
|
error_so.error_code = error_so.error_code.upper().replace(" ", "_").strip()
|
271
275
|
|
272
|
-
if error_so.error_specification_code:
|
276
|
+
if error_so.error_specification_code is not None:
|
273
277
|
error_so.error_specification_code = (
|
274
278
|
error_so.error_specification_code.upper().replace(" ", "_").strip()
|
275
279
|
)
|
@@ -280,23 +284,29 @@ def create_handle_exception(
|
|
280
284
|
if error_so.error_code == BaseAPIErrorCodes.cannot_authorize:
|
281
285
|
status_code = status.HTTP_401_UNAUTHORIZED
|
282
286
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
+
error_so.error_data["status_code"] = status_code
|
288
|
+
|
289
|
+
# funcs_before
|
290
|
+
|
291
|
+
_transmitted_kwargs = {}
|
292
|
+
for func_before in funcs_before:
|
293
|
+
_func_data = func_before(
|
294
|
+
request=request, status_code=status_code, error_so=error_so, exception=exception,
|
295
|
+
transmitted_kwargs=_transmitted_kwargs
|
287
296
|
)
|
288
297
|
if is_async_object(_func_data):
|
289
298
|
_func_data = await _func_data
|
290
299
|
if _func_data is not None:
|
291
|
-
|
292
|
-
raise_for_type(status_code, int)
|
300
|
+
error_so, _transmitted_kwargs = _func_data[0], _func_data[1]
|
293
301
|
raise_for_type(error_so, ErrorSO)
|
294
|
-
raise_for_type(
|
302
|
+
raise_for_type(_transmitted_kwargs, dict)
|
303
|
+
|
304
|
+
# async_funcs_after
|
295
305
|
|
296
|
-
for
|
297
|
-
raise_if_not_async_func(
|
298
|
-
_ = asyncio.create_task(
|
299
|
-
|
306
|
+
for async_func_after in async_funcs_after:
|
307
|
+
raise_if_not_async_func(async_func_after)
|
308
|
+
_ = asyncio.create_task(async_func_after(
|
309
|
+
request=request, status_code=status_code, error_so=error_so, exception=exception
|
300
310
|
))
|
301
311
|
|
302
312
|
return APIJSONResponse(
|
@@ -304,43 +314,46 @@ def create_handle_exception(
|
|
304
314
|
status_code=status_code
|
305
315
|
)
|
306
316
|
|
307
|
-
return
|
317
|
+
return func
|
308
318
|
|
309
319
|
|
310
|
-
def
|
320
|
+
def logging__api_func_before_in_handle_exception(
|
311
321
|
*,
|
312
322
|
ignore_api_error_codes: list[str] | None = None,
|
313
323
|
ignore_status_codes: list[int] | None = None,
|
314
324
|
ignore_exception_types: list[type[Exception]] | None = None,
|
315
325
|
need_exc_info: bool = False
|
316
|
-
):
|
326
|
+
) -> Callable:
|
327
|
+
current_func_name = inspect.currentframe().f_code.co_name
|
328
|
+
|
317
329
|
def func(
|
318
330
|
*,
|
331
|
+
request: starlette.requests.Request,
|
319
332
|
status_code: int,
|
320
333
|
error_so: ErrorSO,
|
321
|
-
request: starlette.requests.Request,
|
322
334
|
exception: Exception,
|
335
|
+
transmitted_kwargs: dict[str, Any],
|
323
336
|
**kwargs
|
324
|
-
) -> (
|
325
|
-
|
337
|
+
) -> (ErrorSO, dict[str, Any]):
|
338
|
+
transmitted_kwargs[current_func_name] = now_utc_dt()
|
326
339
|
|
327
340
|
if ignore_api_error_codes and error_so.error_code in ignore_api_error_codes:
|
328
|
-
return
|
341
|
+
return error_so, transmitted_kwargs
|
329
342
|
|
330
343
|
if ignore_status_codes and status_code in ignore_status_codes:
|
331
|
-
return
|
344
|
+
return error_so, transmitted_kwargs
|
332
345
|
|
333
346
|
if ignore_exception_types and (
|
334
347
|
exception in ignore_exception_types or type(exception) in ignore_exception_types
|
335
348
|
):
|
336
|
-
return
|
349
|
+
return error_so, transmitted_kwargs
|
337
350
|
|
338
351
|
_logger.error(safely_transfer_obj_to_json_str(error_so.model_dump()), exc_info=need_exc_info)
|
339
352
|
|
340
353
|
return func
|
341
354
|
|
342
355
|
|
343
|
-
def
|
356
|
+
def story_log__api_func_before_in_handle_exception(
|
344
357
|
*,
|
345
358
|
sqlalchemy_db: SQLAlchemyDB,
|
346
359
|
ignore_api_error_codes: list[str] | None = None,
|
@@ -349,26 +362,29 @@ def story_log_func_before_response(
|
|
349
362
|
) -> Callable:
|
350
363
|
raise_for_type(sqlalchemy_db, SQLAlchemyDB)
|
351
364
|
|
365
|
+
current_func_name = inspect.currentframe().f_code.co_name
|
366
|
+
|
352
367
|
async def async_func(
|
353
368
|
*,
|
369
|
+
request: starlette.requests.Request,
|
354
370
|
status_code: int,
|
355
371
|
error_so: ErrorSO,
|
356
|
-
request: starlette.requests.Request,
|
357
372
|
exception: Exception,
|
373
|
+
transmitted_kwargs: dict[str, Any],
|
358
374
|
**kwargs
|
359
|
-
) -> (
|
360
|
-
|
375
|
+
) -> (ErrorSO, dict[str, Any]):
|
376
|
+
transmitted_kwargs[current_func_name] = now_utc_dt()
|
361
377
|
|
362
378
|
if ignore_api_error_codes and error_so.error_code in ignore_api_error_codes:
|
363
|
-
return
|
379
|
+
return error_so, transmitted_kwargs
|
364
380
|
|
365
381
|
if ignore_status_codes and status_code in ignore_status_codes:
|
366
|
-
return
|
382
|
+
return error_so, transmitted_kwargs
|
367
383
|
|
368
384
|
if ignore_exception_types and (
|
369
385
|
exception in ignore_exception_types or type(exception) in ignore_exception_types
|
370
386
|
):
|
371
|
-
return
|
387
|
+
return error_so, transmitted_kwargs
|
372
388
|
|
373
389
|
async with sqlalchemy_db.new_async_session() as session:
|
374
390
|
story_log_dbm = StoryLogDBM(
|
@@ -384,9 +400,9 @@ def story_log_func_before_response(
|
|
384
400
|
await session.refresh(story_log_dbm)
|
385
401
|
|
386
402
|
error_so.error_data.update({"story_log_long_id": story_log_dbm.long_id})
|
387
|
-
|
403
|
+
transmitted_kwargs["story_log_id"] = story_log_dbm.id
|
388
404
|
|
389
|
-
return
|
405
|
+
return error_so, transmitted_kwargs
|
390
406
|
|
391
407
|
return async_func
|
392
408
|
|
@@ -463,7 +479,7 @@ class ARPAKITLibSO(BaseSO):
|
|
463
479
|
arpakitlib: bool = True
|
464
480
|
|
465
481
|
|
466
|
-
def
|
482
|
+
def create_needed_api_router():
|
467
483
|
api_router = APIRouter()
|
468
484
|
|
469
485
|
@api_router.get(
|
@@ -490,9 +506,7 @@ def add_needed_api_router_to_app(*, app: FastAPI):
|
|
490
506
|
content=ARPAKITLibSO(arpakitlib=True)
|
491
507
|
)
|
492
508
|
|
493
|
-
|
494
|
-
|
495
|
-
return app
|
509
|
+
return api_router
|
496
510
|
|
497
511
|
|
498
512
|
class BaseStartupAPIEvent:
|
@@ -517,6 +531,17 @@ class BaseTransmittedAPIData(BaseModel):
|
|
517
531
|
model_config = ConfigDict(extra="ignore", arbitrary_types_allowed=True, from_attributes=True)
|
518
532
|
|
519
533
|
|
534
|
+
class SimpleTransmittedAPIData(BaseTransmittedAPIData):
|
535
|
+
settings: SimpleSettings | None = None
|
536
|
+
|
537
|
+
|
538
|
+
class AdvancedTransmittedAPIData(SimpleTransmittedAPIData):
|
539
|
+
sqlalchemy_db: SQLAlchemyDB | None = None
|
540
|
+
media_file_storage_in_dir: FileStorageInDir | None = None
|
541
|
+
cache_file_storage_in_dir: FileStorageInDir | None = None
|
542
|
+
dump_file_storage_in_dir: FileStorageInDir | None = None
|
543
|
+
|
544
|
+
|
520
545
|
def get_transmitted_api_data(request: starlette.requests.Request) -> BaseTransmittedAPIData:
|
521
546
|
return request.app.state.transmitted_api_data
|
522
547
|
|
@@ -574,7 +599,9 @@ def base_api_auth(
|
|
574
599
|
ac: fastapi.security.HTTPAuthorizationCredentials | None = fastapi.Security(
|
575
600
|
fastapi.security.HTTPBearer(auto_error=False)
|
576
601
|
),
|
577
|
-
api_key_string: str | None = Security(
|
602
|
+
api_key_string: str | None = Security(
|
603
|
+
APIKeyHeader(name="apikey", auto_error=False)
|
604
|
+
),
|
578
605
|
request: starlette.requests.Request,
|
579
606
|
transmitted_api_data: BaseTransmittedAPIData = Depends(get_transmitted_api_data)
|
580
607
|
) -> BaseAPIAuthData:
|
@@ -586,7 +613,7 @@ def base_api_auth(
|
|
586
613
|
require_correct_token=require_correct_token
|
587
614
|
)
|
588
615
|
|
589
|
-
# api_key
|
616
|
+
# parse api_key
|
590
617
|
|
591
618
|
api_auth_data.api_key_string = api_key_string
|
592
619
|
|
@@ -604,7 +631,7 @@ def base_api_auth(
|
|
604
631
|
if not api_auth_data.api_key_string and "apikey" in request.query_params.keys():
|
605
632
|
api_auth_data.api_key_string = request.query_params["apikey"]
|
606
633
|
|
607
|
-
# token
|
634
|
+
# parse token
|
608
635
|
|
609
636
|
api_auth_data.token_string = ac.credentials if ac and ac.credentials and ac.credentials.strip() else None
|
610
637
|
|
@@ -633,7 +660,7 @@ def base_api_auth(
|
|
633
660
|
if not api_auth_data.token_string:
|
634
661
|
api_auth_data.token_string = None
|
635
662
|
|
636
|
-
#
|
663
|
+
# require_api_key_string
|
637
664
|
|
638
665
|
if require_api_key_string and not api_auth_data.api_key_string:
|
639
666
|
raise APIException(
|
@@ -642,7 +669,7 @@ def base_api_auth(
|
|
642
669
|
error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
|
643
670
|
)
|
644
671
|
|
645
|
-
#
|
672
|
+
# require_token_string
|
646
673
|
|
647
674
|
if require_token_string and not api_auth_data.token_string:
|
648
675
|
raise APIException(
|
@@ -651,10 +678,10 @@ def base_api_auth(
|
|
651
678
|
error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump())
|
652
679
|
)
|
653
680
|
|
654
|
-
#
|
681
|
+
# validate_api_key_func
|
655
682
|
|
656
683
|
if validate_api_key_func is not None:
|
657
|
-
|
684
|
+
validate_api_key_func_res = validate_api_key_func(
|
658
685
|
api_key_string=api_auth_data.api_key_string,
|
659
686
|
token_string=api_auth_data.token_string,
|
660
687
|
base_api_auth_data=api_auth_data,
|
@@ -662,14 +689,14 @@ def base_api_auth(
|
|
662
689
|
request=request,
|
663
690
|
**kwargs
|
664
691
|
)
|
665
|
-
if is_async_object(
|
666
|
-
|
667
|
-
api_auth_data.is_api_key_correct =
|
692
|
+
if is_async_object(validate_api_key_func_res):
|
693
|
+
validate_api_key_func_res = await validate_api_key_func_res
|
694
|
+
api_auth_data.is_api_key_correct = validate_api_key_func_res
|
668
695
|
|
669
|
-
#
|
696
|
+
# validate_token_func
|
670
697
|
|
671
698
|
if validate_token_func is not None:
|
672
|
-
|
699
|
+
validate_token_func_res = validate_token_func(
|
673
700
|
api_key_string=api_auth_data.api_key_string,
|
674
701
|
token_string=api_auth_data.token_string,
|
675
702
|
base_api_auth_data=api_auth_data,
|
@@ -677,11 +704,11 @@ def base_api_auth(
|
|
677
704
|
request=request,
|
678
705
|
**kwargs
|
679
706
|
)
|
680
|
-
if is_async_object(
|
681
|
-
|
682
|
-
api_auth_data.is_token_correct =
|
707
|
+
if is_async_object(validate_token_func_res):
|
708
|
+
validate_token_func_res = await validate_token_func_res
|
709
|
+
api_auth_data.is_token_correct = validate_token_func_res
|
683
710
|
|
684
|
-
#
|
711
|
+
# require_correct_api_key
|
685
712
|
|
686
713
|
if require_correct_api_key:
|
687
714
|
if not api_auth_data.is_api_key_correct:
|
@@ -692,7 +719,7 @@ def base_api_auth(
|
|
692
719
|
error_data=safely_transfer_obj_to_json_str_to_json_obj(api_auth_data.model_dump()),
|
693
720
|
)
|
694
721
|
|
695
|
-
#
|
722
|
+
# require_correct_token
|
696
723
|
|
697
724
|
if require_correct_token:
|
698
725
|
if not api_auth_data.is_token_correct:
|
@@ -750,7 +777,6 @@ def create_fastapi_app(
|
|
750
777
|
*,
|
751
778
|
title: str = "arpakitlib FastAPI",
|
752
779
|
description: str | None = "arpakitlib FastAPI",
|
753
|
-
log_filepath: str | None = "./story.log",
|
754
780
|
handle_exception_: Callable | None = None,
|
755
781
|
startup_api_events: list[BaseStartupAPIEvent | None] | None = None,
|
756
782
|
shutdown_api_events: list[BaseShutdownAPIEvent | None] | None = None,
|
@@ -760,12 +786,14 @@ def create_fastapi_app(
|
|
760
786
|
media_dirpath: str | None = None,
|
761
787
|
static_dirpath: str | None = None
|
762
788
|
):
|
763
|
-
_logger.info("start
|
764
|
-
|
765
|
-
setup_normal_logging(log_filepath=log_filepath)
|
789
|
+
_logger.info("start")
|
766
790
|
|
767
791
|
if handle_exception_ is None:
|
768
|
-
handle_exception_ = create_handle_exception(
|
792
|
+
handle_exception_ = create_handle_exception(
|
793
|
+
funcs_before=[
|
794
|
+
logging__api_func_before_in_handle_exception()
|
795
|
+
]
|
796
|
+
)
|
769
797
|
|
770
798
|
if not startup_api_events:
|
771
799
|
startup_api_events = [BaseStartupAPIEvent()]
|
@@ -807,11 +835,14 @@ def create_fastapi_app(
|
|
807
835
|
handle_exception=handle_exception_
|
808
836
|
)
|
809
837
|
|
810
|
-
|
838
|
+
app.include_router(
|
839
|
+
router=create_needed_api_router(),
|
840
|
+
prefix=""
|
841
|
+
)
|
811
842
|
|
812
843
|
app.include_router(router=main_api_router)
|
813
844
|
|
814
|
-
_logger.info("finish
|
845
|
+
_logger.info("finish")
|
815
846
|
|
816
847
|
return app
|
817
848
|
|
@@ -43,6 +43,30 @@ class ScheduleUUSTAPIClient:
|
|
43
43
|
)
|
44
44
|
}
|
45
45
|
|
46
|
+
async def _async_make_http_request(
|
47
|
+
self,
|
48
|
+
*,
|
49
|
+
method: str = "GET",
|
50
|
+
url: str,
|
51
|
+
params: dict[str, Any] | None = None,
|
52
|
+
**kwargs
|
53
|
+
) -> ClientResponse:
|
54
|
+
response = await async_make_http_request(
|
55
|
+
method=method,
|
56
|
+
url=url,
|
57
|
+
headers=self.headers,
|
58
|
+
params=combine_dicts(params, self.auth_params()),
|
59
|
+
max_tries_=9,
|
60
|
+
proxy_url_=self.api_proxy_url,
|
61
|
+
raise_for_status_=True,
|
62
|
+
timeout_=timedelta(seconds=15),
|
63
|
+
**kwargs
|
64
|
+
)
|
65
|
+
json_data = await response.json()
|
66
|
+
if "error" in json_data.keys():
|
67
|
+
raise Exception(f"error in json_data, {json_data}")
|
68
|
+
return response
|
69
|
+
|
46
70
|
def auth_params(self) -> dict[str, Any]:
|
47
71
|
if self.api_password:
|
48
72
|
return {
|
@@ -72,30 +96,6 @@ class ScheduleUUSTAPIClient:
|
|
72
96
|
def generate_v2_token(self) -> str:
|
73
97
|
return self.generate_new_v2_token(password_first_part=self.api_password_first_part)
|
74
98
|
|
75
|
-
async def _async_make_http_request(
|
76
|
-
self,
|
77
|
-
*,
|
78
|
-
method: str = "GET",
|
79
|
-
url: str,
|
80
|
-
params: dict[str, Any] | None = None,
|
81
|
-
**kwargs
|
82
|
-
) -> ClientResponse:
|
83
|
-
response = await async_make_http_request(
|
84
|
-
method=method,
|
85
|
-
url=url,
|
86
|
-
headers=self.headers,
|
87
|
-
params=combine_dicts(params, self.auth_params()),
|
88
|
-
max_tries_=9,
|
89
|
-
proxy_url_=self.api_proxy_url,
|
90
|
-
raise_for_status_=True,
|
91
|
-
timeout_=timedelta(seconds=15),
|
92
|
-
**kwargs
|
93
|
-
)
|
94
|
-
json_data = await response.json()
|
95
|
-
if "error" in json_data.keys():
|
96
|
-
raise Exception(f"error in json_data, {json_data}")
|
97
|
-
return response
|
98
|
-
|
99
99
|
async def get_current_week(self) -> int:
|
100
100
|
"""
|
101
101
|
response.json example
|
arpakitlib/ar_settings_util.py
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
# arpakit
|
2
|
-
|
2
|
+
import os
|
3
3
|
from typing import Union, Any
|
4
4
|
|
5
|
+
import pytz
|
5
6
|
from pydantic import ConfigDict, field_validator, model_validator
|
6
7
|
from pydantic_core import PydanticUndefined
|
8
|
+
from pydantic_core.core_schema import ValidationInfo
|
7
9
|
from pydantic_settings import BaseSettings
|
8
10
|
|
9
11
|
from arpakitlib.ar_enumeration_util import Enumeration
|
12
|
+
from arpakitlib.ar_json_util import safely_transfer_obj_to_json_str
|
13
|
+
from arpakitlib.ar_sqlalchemy_util import generate_sqlalchemy_url
|
10
14
|
|
11
15
|
_ARPAKIT_LIB_MODULE_VERSION = "3.0"
|
12
16
|
|
@@ -26,11 +30,9 @@ class ModeTypes(Enumeration):
|
|
26
30
|
prod: str = "prod"
|
27
31
|
|
28
32
|
|
29
|
-
class
|
33
|
+
class BaseSettings2(BaseSettings):
|
30
34
|
model_config = ConfigDict(extra="ignore")
|
31
35
|
|
32
|
-
mode_type: str = ModeTypes.not_prod
|
33
|
-
|
34
36
|
@model_validator(mode="before")
|
35
37
|
@classmethod
|
36
38
|
def validate_all_fields(cls, values: dict[str, Any]) -> dict[str, Any]:
|
@@ -39,6 +41,21 @@ class SimpleSettings(BaseSettings):
|
|
39
41
|
values[key] = None
|
40
42
|
return values
|
41
43
|
|
44
|
+
@classmethod
|
45
|
+
def generate_env_example(cls) -> str:
|
46
|
+
return generate_env_example(settings_class=cls)
|
47
|
+
|
48
|
+
@classmethod
|
49
|
+
def save_env_example_to_file(cls, filepath: str) -> str:
|
50
|
+
env_example = cls.generate_env_example()
|
51
|
+
with open(filepath, mode="w") as f:
|
52
|
+
f.write(env_example)
|
53
|
+
return env_example
|
54
|
+
|
55
|
+
|
56
|
+
class SimpleSettings(BaseSettings2):
|
57
|
+
mode_type: str = ModeTypes.not_prod
|
58
|
+
|
42
59
|
@field_validator("mode_type")
|
43
60
|
@classmethod
|
44
61
|
def validate_mode_type(cls, v: str):
|
@@ -61,20 +78,155 @@ class SimpleSettings(BaseSettings):
|
|
61
78
|
if self.is_mode_type_prod:
|
62
79
|
raise ValueError(f"mode type = {self.mode_type}")
|
63
80
|
|
64
|
-
@classmethod
|
65
|
-
def generate_env_example(cls) -> str:
|
66
|
-
return generate_env_example(settings_class=cls)
|
67
81
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
82
|
+
class AdvancedSettings(SimpleSettings):
|
83
|
+
project_name: str | None = None
|
84
|
+
|
85
|
+
sql_db_user: str | None = None
|
86
|
+
|
87
|
+
@field_validator("sql_db_user", mode="after")
|
88
|
+
def validate_sql_db_user(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
89
|
+
if v is not None:
|
90
|
+
return v
|
91
|
+
res = validation_info.data.get("project_name")
|
92
|
+
if res is not None:
|
93
|
+
return res
|
94
|
+
return res
|
95
|
+
|
96
|
+
sql_db_password: str | None = None
|
97
|
+
|
98
|
+
@field_validator("sql_db_password", mode="after")
|
99
|
+
def validate_sql_db_password(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
100
|
+
if v is not None:
|
101
|
+
return v
|
102
|
+
res = validation_info.data.get("project_name")
|
103
|
+
if res is not None:
|
104
|
+
return res
|
105
|
+
return res
|
106
|
+
|
107
|
+
sql_db_port: int | None = int("50506") if "50506".strip().isdigit() else None
|
108
|
+
|
109
|
+
sql_db_database: str | None = None
|
110
|
+
|
111
|
+
@field_validator("sql_db_database", mode="after")
|
112
|
+
def validate_sql_db_database(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
113
|
+
if v is not None:
|
114
|
+
return v
|
115
|
+
res = validation_info.data.get("project_name")
|
116
|
+
if res is not None:
|
117
|
+
return res
|
118
|
+
return res
|
119
|
+
|
120
|
+
sync_sql_db_url: str | None = None
|
121
|
+
|
122
|
+
@field_validator("sync_sql_db_url", mode="after")
|
123
|
+
def validate_sync_sql_db_url(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
124
|
+
if v is not None:
|
125
|
+
return v
|
126
|
+
|
127
|
+
return generate_sqlalchemy_url(
|
128
|
+
base="postgresql+asyncpg",
|
129
|
+
user=validation_info.data.get("sql_db_user"),
|
130
|
+
password=validation_info.data.get("sql_db_password"),
|
131
|
+
port=validation_info.data.get("sql_db_port"),
|
132
|
+
database=validation_info.data.get("sql_db_database")
|
133
|
+
)
|
134
|
+
|
135
|
+
async_sql_db_url: str | None = None
|
136
|
+
|
137
|
+
@field_validator("async_sql_db_url", mode="after")
|
138
|
+
def validate_async_sql_db_url(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
139
|
+
if v is not None:
|
140
|
+
return v
|
141
|
+
|
142
|
+
return generate_sqlalchemy_url(
|
143
|
+
base="postgresql+asyncpg",
|
144
|
+
user=validation_info.data.get("sql_db_user"),
|
145
|
+
password=validation_info.data.get("sql_db_password"),
|
146
|
+
port=validation_info.data.get("sql_db_port"),
|
147
|
+
database=validation_info.data.get("sql_db_database")
|
148
|
+
)
|
149
|
+
|
150
|
+
sql_db_echo: bool = False
|
151
|
+
|
152
|
+
api_port: int | None = None
|
153
|
+
|
154
|
+
@field_validator("api_port", mode="before")
|
155
|
+
def validate_api_port(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> int | None:
|
156
|
+
if isinstance(v, str):
|
157
|
+
if v.isdigit():
|
158
|
+
return int(v)
|
159
|
+
return None
|
160
|
+
|
161
|
+
api_init_sql_db_at_start: bool = True
|
162
|
+
|
163
|
+
api_logging__api_func_before_in_handle_exception: bool = True
|
164
|
+
|
165
|
+
api_story_log__api_func_before_in_handle_exception: bool = True
|
166
|
+
|
167
|
+
api_correct_api_key: str | None = "1"
|
168
|
+
|
169
|
+
api_correct_token: str | None = "1"
|
170
|
+
|
171
|
+
api_enable_admin1: bool = True
|
172
|
+
|
173
|
+
admin1_secret_key: str | None = "85a9583cb91c4de7a78d7eb1e5306a04418c9c43014c447ea8ec8dd5deb4cf71"
|
174
|
+
|
175
|
+
var_dirpath: str | None = "./var"
|
176
|
+
|
177
|
+
log_filepath: str | None = None
|
178
|
+
|
179
|
+
@field_validator("log_filepath", mode="after")
|
180
|
+
def validate_log_filepath(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
181
|
+
if v is not None:
|
182
|
+
return v
|
183
|
+
var_dirpath = validation_info.data.get("var_dirpath")
|
184
|
+
if var_dirpath is None:
|
185
|
+
return None
|
186
|
+
return os.path.join(var_dirpath, "story.log")
|
187
|
+
|
188
|
+
cache_dirpath: str | None = None
|
189
|
+
|
190
|
+
@field_validator("cache_dirpath", mode="after")
|
191
|
+
def validate_cache_dirpath(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
192
|
+
if v is not None:
|
193
|
+
return v
|
194
|
+
var_dirpath = validation_info.data.get("var_dirpath")
|
195
|
+
if var_dirpath is None:
|
196
|
+
return None
|
197
|
+
return os.path.join(var_dirpath, "cache")
|
198
|
+
|
199
|
+
media_dirpath: str | None = None
|
200
|
+
|
201
|
+
@field_validator("media_dirpath", mode="after")
|
202
|
+
def validate_media_dirpath(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
203
|
+
if v is not None:
|
204
|
+
return v
|
205
|
+
var_dirpath = validation_info.data.get("var_dirpath")
|
206
|
+
if var_dirpath is None:
|
207
|
+
return None
|
208
|
+
return os.path.join(var_dirpath, "media")
|
209
|
+
|
210
|
+
dump_dirpath: str | None = None
|
211
|
+
|
212
|
+
@field_validator("dump_dirpath", mode="after")
|
213
|
+
def validate_dump_dirpath(cls, v: Any, validation_info: ValidationInfo, **kwargs) -> str | None:
|
214
|
+
if v is not None:
|
215
|
+
return v
|
216
|
+
var_dirpath = validation_info.data.get("var_dirpath")
|
217
|
+
if var_dirpath is None:
|
218
|
+
return None
|
219
|
+
return os.path.join(var_dirpath, "dump")
|
220
|
+
|
221
|
+
local_timezone: str | None = None
|
222
|
+
|
223
|
+
@property
|
224
|
+
def local_timezone_as_pytz(self) -> Any:
|
225
|
+
return pytz.timezone(self.local_timezone)
|
74
226
|
|
75
227
|
|
76
228
|
def __example():
|
77
|
-
|
229
|
+
print(safely_transfer_obj_to_json_str(AdvancedSettings(var_dirpath="./var").model_dump(mode="json")))
|
78
230
|
|
79
231
|
|
80
232
|
if __name__ == '__main__':
|
@@ -67,7 +67,7 @@ class BaseDBM(DeclarativeBase):
|
|
67
67
|
|
68
68
|
return res
|
69
69
|
|
70
|
-
def simple_dict_with_sd_properties(self) -> dict[str
|
70
|
+
def simple_dict_with_sd_properties(self) -> dict[str, Any]:
|
71
71
|
return self.simple_dict(include_sd_properties=True)
|
72
72
|
|
73
73
|
def simple_json(self, *, include_sd_properties: bool = True) -> str:
|