fast-clean 0.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.
Files changed (65) hide show
  1. fast_clean/__init__.py +3 -0
  2. fast_clean/broker.py +123 -0
  3. fast_clean/container.py +235 -0
  4. fast_clean/contrib/__init__.py +3 -0
  5. fast_clean/contrib/healthcheck/__init__.py +3 -0
  6. fast_clean/contrib/healthcheck/router.py +17 -0
  7. fast_clean/db.py +179 -0
  8. fast_clean/depends.py +255 -0
  9. fast_clean/enums.py +39 -0
  10. fast_clean/exceptions.py +281 -0
  11. fast_clean/loggers.py +26 -0
  12. fast_clean/middleware.py +20 -0
  13. fast_clean/models.py +33 -0
  14. fast_clean/py.typed +0 -0
  15. fast_clean/redis.py +23 -0
  16. fast_clean/repositories/__init__.py +30 -0
  17. fast_clean/repositories/cache/__init__.py +83 -0
  18. fast_clean/repositories/cache/in_memory.py +62 -0
  19. fast_clean/repositories/cache/redis.py +58 -0
  20. fast_clean/repositories/crud/__init__.py +149 -0
  21. fast_clean/repositories/crud/db.py +559 -0
  22. fast_clean/repositories/crud/in_memory.py +369 -0
  23. fast_clean/repositories/crud/type_vars.py +35 -0
  24. fast_clean/repositories/settings/__init__.py +52 -0
  25. fast_clean/repositories/settings/enums.py +16 -0
  26. fast_clean/repositories/settings/env.py +55 -0
  27. fast_clean/repositories/settings/exceptions.py +13 -0
  28. fast_clean/repositories/settings/type_vars.py +9 -0
  29. fast_clean/repositories/storage/__init__.py +114 -0
  30. fast_clean/repositories/storage/enums.py +20 -0
  31. fast_clean/repositories/storage/local.py +118 -0
  32. fast_clean/repositories/storage/reader.py +122 -0
  33. fast_clean/repositories/storage/s3.py +118 -0
  34. fast_clean/repositories/storage/schemas.py +31 -0
  35. fast_clean/schemas/__init__.py +25 -0
  36. fast_clean/schemas/exceptions.py +32 -0
  37. fast_clean/schemas/pagination.py +65 -0
  38. fast_clean/schemas/repository.py +43 -0
  39. fast_clean/schemas/request_response.py +36 -0
  40. fast_clean/schemas/status_response.py +13 -0
  41. fast_clean/services/__init__.py +16 -0
  42. fast_clean/services/cryptography/__init__.py +57 -0
  43. fast_clean/services/cryptography/aes.py +120 -0
  44. fast_clean/services/cryptography/enums.py +20 -0
  45. fast_clean/services/lock.py +57 -0
  46. fast_clean/services/seed.py +91 -0
  47. fast_clean/services/transaction.py +40 -0
  48. fast_clean/settings.py +189 -0
  49. fast_clean/tools/__init__.py +6 -0
  50. fast_clean/tools/cryptography.py +56 -0
  51. fast_clean/tools/load_seed.py +31 -0
  52. fast_clean/use_cases.py +38 -0
  53. fast_clean/utils/__init__.py +15 -0
  54. fast_clean/utils/process.py +31 -0
  55. fast_clean/utils/pydantic.py +23 -0
  56. fast_clean/utils/ssl_context.py +31 -0
  57. fast_clean/utils/string.py +28 -0
  58. fast_clean/utils/thread.py +21 -0
  59. fast_clean/utils/time.py +14 -0
  60. fast_clean/utils/type_converters.py +18 -0
  61. fast_clean/utils/typer.py +23 -0
  62. fast_clean-0.4.0.dist-info/METADATA +38 -0
  63. fast_clean-0.4.0.dist-info/RECORD +65 -0
  64. fast_clean-0.4.0.dist-info/WHEEL +5 -0
  65. fast_clean-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,31 @@
1
+ """
2
+ Модуль запуска тяжелых операций в ProcessPoolExecutor.
3
+ """
4
+
5
+ import asyncio
6
+ import multiprocessing as mp
7
+ from concurrent.futures import ProcessPoolExecutor
8
+ from functools import partial
9
+ from typing import Callable, TypeVar
10
+
11
+ from typing_extensions import ParamSpec
12
+
13
+ P = ParamSpec('P')
14
+ R = TypeVar('R')
15
+
16
+
17
+ process_pool: ProcessPoolExecutor | None = None
18
+
19
+
20
+ async def run_in_processpool(fn: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
21
+ """
22
+ Запуск функции в отдельном процессе.
23
+
24
+ Используем fork в связи с https://github.com/python/cpython/issues/94765.
25
+ """
26
+ global process_pool
27
+ if process_pool is None:
28
+ process_pool = ProcessPoolExecutor(mp_context=mp.get_context('fork'))
29
+ kwargs_fn = partial(fn, *args, **kwargs)
30
+ loop = asyncio.get_running_loop()
31
+ return await loop.run_in_executor(process_pool, kwargs_fn)
@@ -0,0 +1,23 @@
1
+ """
2
+ Модуль, содержащий вспомогательные функции для работы с Pydantic.
3
+ """
4
+
5
+ import importlib
6
+ from collections.abc import Iterable
7
+
8
+ from pydantic import BaseModel
9
+
10
+
11
+ def rebuild_schemas(modules: Iterable[str]) -> None:
12
+ """
13
+ Перестраиваем не полностью объявленные схемы из-за циклических зависимостей.
14
+
15
+ https://docs.pydantic.dev/2.10/concepts/models/#rebuilding-model-schema
16
+ """
17
+ schemas: dict[str, type[BaseModel]] = {}
18
+ for module in modules:
19
+ for key, obj in importlib.import_module(module).__dict__.items():
20
+ if isinstance(obj, type) and issubclass(obj, BaseModel) and obj is not BaseModel:
21
+ schemas[key] = obj
22
+ for schema in schemas.values():
23
+ schema.model_rebuild(_types_namespace=schemas)
@@ -0,0 +1,31 @@
1
+ """
2
+ Модуль для создания SSL контекста.
3
+ """
4
+
5
+ import ssl
6
+ from typing import TypeAlias
7
+
8
+ from pydantic import BaseModel
9
+
10
+ StrOrBytesPath: TypeAlias = str | bytes # stable
11
+
12
+
13
+ class CertificateSchema(BaseModel):
14
+ """
15
+ Схема необходимых файлов для создания SSL контекста.
16
+ """
17
+
18
+ ca_file: StrOrBytesPath
19
+ cert_file: StrOrBytesPath
20
+ key_file: StrOrBytesPath
21
+ password: str | None = None
22
+
23
+
24
+ def make_ssl_context(params: CertificateSchema, check_hostname: bool = False) -> ssl.SSLContext:
25
+ """
26
+ Создаем SSL контекст.
27
+ """
28
+ ssl_context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=params.ca_file)
29
+ ssl_context.load_cert_chain(certfile=params.cert_file, keyfile=params.key_file, password=params.password)
30
+ ssl_context.check_hostname = check_hostname
31
+ return ssl_context
@@ -0,0 +1,28 @@
1
+ """
2
+ Модуль для работы со строками.
3
+ """
4
+
5
+ import base64
6
+ import string
7
+ from random import choice
8
+
9
+
10
+ def make_random_string(size: int) -> str:
11
+ """
12
+ Создаем случайную строку.
13
+ """
14
+ return ''.join(choice(string.ascii_letters + string.digits) for _ in range(size))
15
+
16
+
17
+ def encode_base64(raw_value: str) -> str:
18
+ """
19
+ Кодируем строку в base64.
20
+ """
21
+ return base64.b64encode(raw_value.encode()).decode()
22
+
23
+
24
+ def decode_base64(value: str) -> str:
25
+ """
26
+ Декодируем строку из base64.
27
+ """
28
+ return base64.b64decode(value).decode()
@@ -0,0 +1,21 @@
1
+ """
2
+ Модель запуска тяжелых операций в ThreadPoolExecutor.
3
+ """
4
+
5
+ import asyncio
6
+ from functools import partial
7
+ from typing import Callable, TypeVar
8
+
9
+ from typing_extensions import ParamSpec
10
+
11
+ R = TypeVar('R')
12
+ P = ParamSpec('P')
13
+
14
+
15
+ async def run_in_threadpool(fn: Callable[P, R], *args: P.args, **kwargs: P.kwargs) -> R:
16
+ """
17
+ Запуск функции в отдельном потоке.
18
+ """
19
+ kwargs_fn = partial(fn, *args, **kwargs)
20
+ loop = asyncio.get_running_loop()
21
+ return await loop.run_in_executor(None, kwargs_fn)
@@ -0,0 +1,14 @@
1
+ """
2
+ Модуль для работы с датой и временем.
3
+ """
4
+
5
+ import datetime as dt
6
+
7
+
8
+ def ts_now() -> float:
9
+ """
10
+ Возвращает текущий timestamp по GMT.
11
+
12
+ :return: Значение в секундах
13
+ """
14
+ return dt.datetime.now(dt.timezone.utc).timestamp()
@@ -0,0 +1,18 @@
1
+ """
2
+ Модуль, содержащий функции для преобразования типов.
3
+ """
4
+
5
+ true_set = {'yes', 'true', 't', 'y', '1'}
6
+ false_set = {'no', 'false', 'f', 'n', '0'}
7
+
8
+
9
+ def str_to_bool(value: str) -> bool:
10
+ """
11
+ Преобразуем строку к логическом значению.
12
+ """
13
+ value = value.lower()
14
+ if value in true_set:
15
+ return True
16
+ if value in false_set:
17
+ return False
18
+ raise ValueError('Expected "%s"' % '", "'.join(true_set | false_set))
@@ -0,0 +1,23 @@
1
+ """
2
+ Модуль, содержащий вспомогательные функции для работы с Typer.
3
+ """
4
+
5
+ import asyncio
6
+ from collections.abc import Callable, Coroutine
7
+ from functools import wraps
8
+ from typing import Any, ParamSpec, TypeVar
9
+
10
+ Param = ParamSpec('Param')
11
+ RetType = TypeVar('RetType')
12
+
13
+
14
+ def typer_async(func: Callable[Param, Coroutine[Any, Any, RetType]]) -> Callable[Param, RetType]:
15
+ """
16
+ Декоратор для асинхронного запуска Typer.
17
+ """
18
+
19
+ @wraps(func)
20
+ def wrapper(*args: Param.args, **kwargs: Param.kwargs) -> RetType:
21
+ return asyncio.run(func(*args, **kwargs))
22
+
23
+ return wrapper
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: fast-clean
3
+ Version: 0.4.0
4
+ Summary: FastAPI Clean Architecture implementation
5
+ Author-email: Luferov Victor <luferovvs@yandex.ru>, Orlov Artem <squakrazv@yandex.ru>
6
+ Requires-Python: >=3.12
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: aiofiles>=24.1.0
9
+ Requires-Dist: aiokafka>=0.12.0
10
+ Requires-Dist: cryptography>=44.0.1
11
+ Requires-Dist: fastapi>=0.115.8
12
+ Requires-Dist: fastapi-cache2[redis]>=0.2.2
13
+ Requires-Dist: faststream>=0.5.34
14
+ Requires-Dist: flatten-dict>=0.4.2
15
+ Requires-Dist: miniopy-async>=1.21.1
16
+ Requires-Dist: overrides>=7.7.0
17
+ Requires-Dist: psycopg>=3.2.4
18
+ Requires-Dist: pydantic>=2.10.6
19
+ Requires-Dist: pydantic-settings>=2.8.0
20
+ Requires-Dist: pyyaml>=6.0.2
21
+ Requires-Dist: sqlalchemy-utils>=0.41.2
22
+ Requires-Dist: sqlalchemy[asyncio]>=2.0.38
23
+ Requires-Dist: stringcase>=1.2.0
24
+ Requires-Dist: typer>=0.15.1
25
+
26
+ # FastClean
27
+
28
+ FastAPI clean architecture implementation
29
+
30
+
31
+
32
+ ## Contribution
33
+
34
+ ```
35
+ git clone git@github.com:Luferov/fast-clean.git
36
+ uv sync --all-extras --dev
37
+ uv run pre-commit install
38
+ ```
@@ -0,0 +1,65 @@
1
+ fast_clean/__init__.py,sha256=sT4tb75t5PXws8W_7wpA0jNtNxkWPFLAMrPlDGS7RHw,51
2
+ fast_clean/broker.py,sha256=CHnL4Jd6jF5gKgtUXi33j9QFG2EUM4uqhVqdLuxIrZs,4474
3
+ fast_clean/container.py,sha256=i7ZLZY_UN6ohGn-PT3RgAkzRPza3-DWRfgRdoaeMBWU,9562
4
+ fast_clean/db.py,sha256=d03D9cYHpq8iQ7ErAwZYggLhITmxD5feSr071kv0_x0,5507
5
+ fast_clean/depends.py,sha256=94mIIfXoY7X_lm0xrcd15QjrGkyr_QLF0Om5byJNuJ0,8824
6
+ fast_clean/enums.py,sha256=lPhC_2_r6YFby7Mq-9u_JSiuyZ0e57F2VxBfUwnBZ18,826
7
+ fast_clean/exceptions.py,sha256=Sp-k-a5z1Gedu0slzj1-rORnr4GP1FXDHKCKRaJq-7o,9485
8
+ fast_clean/loggers.py,sha256=hVvZSDMMxYnK-p_yyjd4R7SyHpmxQF3eKQEeMu9Q-jo,705
9
+ fast_clean/middleware.py,sha256=7iaA39ug85oOtSdQP8T3u8cX4KAAvDlfAlUg43_riR0,461
10
+ fast_clean/models.py,sha256=qnNUSwLf0gOW8C98PMIs6vTw7UP3-Nk-k6YoFvHstVM,880
11
+ fast_clean/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ fast_clean/redis.py,sha256=H_SLnDhY_ute8pYHdhIypUGlCwMcVfFA4S2j8vLUph0,578
13
+ fast_clean/settings.py,sha256=o460LZjXW0_WYe0QB9g0fTgch8dhkUq-6flAnf4LKM4,4715
14
+ fast_clean/use_cases.py,sha256=XzRqnS3LOkaadbL50-E9f7q3Bi5Yz4AOCHKh82AH0Mg,1384
15
+ fast_clean/contrib/__init__.py,sha256=AcFNyhc0QGsOnYvzQGanDN3QIAsKpn4d8RIj73F-sGc,63
16
+ fast_clean/contrib/healthcheck/__init__.py,sha256=p8hUCLdv2qGngTwAeTGIV4h_ZGDm9ZNWMrA5_k3Yi0E,106
17
+ fast_clean/contrib/healthcheck/router.py,sha256=6kyFuNqR5m3pB_fzerrZ7m7yvoqL_BiwkUMeLrxJVnE,408
18
+ fast_clean/repositories/__init__.py,sha256=IpETRNot2t6rI5qUtsyqinkloS2fhcqSVpB5-s1iUmY,1753
19
+ fast_clean/repositories/cache/__init__.py,sha256=9i3_AbUDt4HbPBRUfvNb2HqYGTDhZ6ZRRqz9jKE5o9c,2479
20
+ fast_clean/repositories/cache/in_memory.py,sha256=Hb68UrTmQozALcyLrmYPBIfJfi67NvsCTDe1RfqwBHQ,2259
21
+ fast_clean/repositories/cache/redis.py,sha256=UjrA2CXQtMfHTpowz6Ot952y73YjTEr6zJlBbWblaws,1908
22
+ fast_clean/repositories/crud/__init__.py,sha256=CrfjDlf0QLQXzX4GO7VkDf_qaZMPl1Pz9pXv9fCyWSU,4391
23
+ fast_clean/repositories/crud/db.py,sha256=OXQKBZ8pQGUgKiK_v1B0-HJIgXkS0EVwanXjDzjEwpc,23432
24
+ fast_clean/repositories/crud/in_memory.py,sha256=0JZJC4iYAnJLNYBDpwlekKj580Qao9X0HL1hpfP-x14,13407
25
+ fast_clean/repositories/crud/type_vars.py,sha256=YfSLmHAcjXLiR_uUevrAcT4_5dxc03ZLKPqRBN2aJG4,1306
26
+ fast_clean/repositories/settings/__init__.py,sha256=ZxrncvTDs8pNkhWSy2cxV3a8uElTnrM-b1-vq4ouJok,1485
27
+ fast_clean/repositories/settings/enums.py,sha256=coqZg6xe_mRFWeihBfnSkCByLuD0pT8Vv4g02tpBk-w,358
28
+ fast_clean/repositories/settings/env.py,sha256=maQttYENMJyTf4vnSXa4L3R6tKiLmb-d0Q5VS-r9ZuE,2153
29
+ fast_clean/repositories/settings/exceptions.py,sha256=SKU45z-ahPzI_G6k4A9twupx1v3GaXDj2pbFkg3YgFE,348
30
+ fast_clean/repositories/settings/type_vars.py,sha256=_Oe8x4JwwrN9WOVjLA05BN6gv7cBcBmq2YR2ZI4Hz5w,197
31
+ fast_clean/repositories/storage/__init__.py,sha256=mP_2NTx_Ec19WCmxecJsbjvNjhy8Oj8001lJC-BTGB8,3582
32
+ fast_clean/repositories/storage/enums.py,sha256=bS4L63aEXNaGnJql8A1jmsK4KY916cWnzTW5p_PyLmg,375
33
+ fast_clean/repositories/storage/local.py,sha256=s5REPU7xczvzin13sKyZtFdiocrgAMk8bnDIbJ90KT4,4270
34
+ fast_clean/repositories/storage/reader.py,sha256=yAxj51ITWJf0u-KGC3DJ0iTB3pDI1p9ixi_h0ZcWoZ4,3299
35
+ fast_clean/repositories/storage/s3.py,sha256=GcJ0qo6RgMSCIOKdafSwH4kz1YHc_2cOYuvgxT81yvI,4302
36
+ fast_clean/repositories/storage/schemas.py,sha256=etlogfK_1uUZPQjHWQN6LWy6-8YY2Sago3Zbf6p0KcQ,623
37
+ fast_clean/schemas/__init__.py,sha256=u7U2j-QURCEIL_Gg3cQyp3oCbsT497lD3X54nqWDfzQ,1361
38
+ fast_clean/schemas/exceptions.py,sha256=E7G9jv4G82Ede7OQ3619vPGwEywc7tKmXW6EolOGRFQ,723
39
+ fast_clean/schemas/pagination.py,sha256=GEQ-Tbhx6xkMMXhDNWrTEhPv8IdnAOJxH2P1tscmn60,1384
40
+ fast_clean/schemas/repository.py,sha256=up4-c7irCRm73Xsq0jMu5pot1xMDOuNRNNopId0-Zn8,889
41
+ fast_clean/schemas/request_response.py,sha256=i4HTpjelWl4DxJ1sQaeanTWB_PThlhVJRhtMMGqRAiQ,693
42
+ fast_clean/schemas/status_response.py,sha256=mASZRCNtKJnDbmhr8_pBytkE_3NguyTIFqO4aw-nNEQ,269
43
+ fast_clean/services/__init__.py,sha256=hhT-tt4pQxD7EanesryzxPa9MqIavaU8bj4lrVGXLi0,964
44
+ fast_clean/services/lock.py,sha256=sWrlEP4rWkKTJgcnR1UrBOnZ8kOvuji4aNWr9VPzjwo,1665
45
+ fast_clean/services/seed.py,sha256=dytqnztOdvAoja3-bs_ldqf6LnoAUcaGEti2TxTTYOU,3201
46
+ fast_clean/services/transaction.py,sha256=9XW084PnBgN-g9hBSSDs5NN6QbrsVArBjOLMuhn-W_8,1093
47
+ fast_clean/services/cryptography/__init__.py,sha256=aXGBi49z9qeaRyTM7ZU9qncN-PfwlSXAJ_WSBuyYMf4,2071
48
+ fast_clean/services/cryptography/aes.py,sha256=_k0WtnKDaEKdUBegfwmqerE75ER44307CEQ-I2W0abo,4616
49
+ fast_clean/services/cryptography/enums.py,sha256=cLibSGv6LNVTUI2rm3_DtDwU68GYIAf4kY3GGbtnw1A,494
50
+ fast_clean/tools/__init__.py,sha256=m8n09uN47JGtAfgWVbXCJOxpzlrUazogqtLo6xPWe3s,181
51
+ fast_clean/tools/cryptography.py,sha256=NznbY5wtXeX8doyn6Hf76ztakckto5QnsUDK1GK7RWE,2001
52
+ fast_clean/tools/load_seed.py,sha256=Bql-c-hjFdrf-qsGo5V7DSX9sa9_RCk_P9BLF7G5xts,910
53
+ fast_clean/utils/__init__.py,sha256=Q3OiJNdWl51Vd_wSP7iuZQIq4_SjM1mYkqIWPaw94WU,709
54
+ fast_clean/utils/process.py,sha256=6k2E1q7d31Wq6G5BqJqrX5czimvJExeltk7uO7CxiSg,936
55
+ fast_clean/utils/pydantic.py,sha256=FtBkNsxdlrhrlEiIHu2wZwF-UR4THETV8mw-h_jevYg,871
56
+ fast_clean/utils/ssl_context.py,sha256=I3tM9bDB6LVMaKCDcrpREzBE4AoTWr3NQDU3_A0Kt3k,904
57
+ fast_clean/utils/string.py,sha256=8Dy3MeDHn-V9SUknuYZp8M6iakuU_UAmkMC9UreoN8k,630
58
+ fast_clean/utils/thread.py,sha256=ChEWBLupnSEMq4Wro_aiW0QvCLUKedKc0TQFMu7Zg4g,565
59
+ fast_clean/utils/time.py,sha256=nvavbtG4zR_gkrGSbsqKAsBdePxO3LuTeoISbFZIgn0,307
60
+ fast_clean/utils/type_converters.py,sha256=bMEJeoQB9Q6Qok1-ppn4Ii8ZpIkZwJbD2IzCydSStHw,523
61
+ fast_clean/utils/typer.py,sha256=1O7BsNGn68bBzNbj0-Ycfhv35WpLzwvYTKn510YNXQQ,663
62
+ fast_clean-0.4.0.dist-info/METADATA,sha256=h8qZAmNHcHtt9YXK4eJyw5uxEOG0d-q3aG3TVNOUncY,1030
63
+ fast_clean-0.4.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
64
+ fast_clean-0.4.0.dist-info/top_level.txt,sha256=QfsGs-QLmPCZWWPFOukD0zhMnokH68FoO2KeObl6ZIA,11
65
+ fast_clean-0.4.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.7.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ fast_clean