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.
- fast_clean/__init__.py +3 -0
- fast_clean/broker.py +123 -0
- fast_clean/container.py +235 -0
- fast_clean/contrib/__init__.py +3 -0
- fast_clean/contrib/healthcheck/__init__.py +3 -0
- fast_clean/contrib/healthcheck/router.py +17 -0
- fast_clean/db.py +179 -0
- fast_clean/depends.py +255 -0
- fast_clean/enums.py +39 -0
- fast_clean/exceptions.py +281 -0
- fast_clean/loggers.py +26 -0
- fast_clean/middleware.py +20 -0
- fast_clean/models.py +33 -0
- fast_clean/py.typed +0 -0
- fast_clean/redis.py +23 -0
- fast_clean/repositories/__init__.py +30 -0
- fast_clean/repositories/cache/__init__.py +83 -0
- fast_clean/repositories/cache/in_memory.py +62 -0
- fast_clean/repositories/cache/redis.py +58 -0
- fast_clean/repositories/crud/__init__.py +149 -0
- fast_clean/repositories/crud/db.py +559 -0
- fast_clean/repositories/crud/in_memory.py +369 -0
- fast_clean/repositories/crud/type_vars.py +35 -0
- fast_clean/repositories/settings/__init__.py +52 -0
- fast_clean/repositories/settings/enums.py +16 -0
- fast_clean/repositories/settings/env.py +55 -0
- fast_clean/repositories/settings/exceptions.py +13 -0
- fast_clean/repositories/settings/type_vars.py +9 -0
- fast_clean/repositories/storage/__init__.py +114 -0
- fast_clean/repositories/storage/enums.py +20 -0
- fast_clean/repositories/storage/local.py +118 -0
- fast_clean/repositories/storage/reader.py +122 -0
- fast_clean/repositories/storage/s3.py +118 -0
- fast_clean/repositories/storage/schemas.py +31 -0
- fast_clean/schemas/__init__.py +25 -0
- fast_clean/schemas/exceptions.py +32 -0
- fast_clean/schemas/pagination.py +65 -0
- fast_clean/schemas/repository.py +43 -0
- fast_clean/schemas/request_response.py +36 -0
- fast_clean/schemas/status_response.py +13 -0
- fast_clean/services/__init__.py +16 -0
- fast_clean/services/cryptography/__init__.py +57 -0
- fast_clean/services/cryptography/aes.py +120 -0
- fast_clean/services/cryptography/enums.py +20 -0
- fast_clean/services/lock.py +57 -0
- fast_clean/services/seed.py +91 -0
- fast_clean/services/transaction.py +40 -0
- fast_clean/settings.py +189 -0
- fast_clean/tools/__init__.py +6 -0
- fast_clean/tools/cryptography.py +56 -0
- fast_clean/tools/load_seed.py +31 -0
- fast_clean/use_cases.py +38 -0
- fast_clean/utils/__init__.py +15 -0
- fast_clean/utils/process.py +31 -0
- fast_clean/utils/pydantic.py +23 -0
- fast_clean/utils/ssl_context.py +31 -0
- fast_clean/utils/string.py +28 -0
- fast_clean/utils/thread.py +21 -0
- fast_clean/utils/time.py +14 -0
- fast_clean/utils/type_converters.py +18 -0
- fast_clean/utils/typer.py +23 -0
- fast_clean-0.4.0.dist-info/METADATA +38 -0
- fast_clean-0.4.0.dist-info/RECORD +65 -0
- fast_clean-0.4.0.dist-info/WHEEL +5 -0
- fast_clean-0.4.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
"""
|
2
|
+
Пакет, содержащий репозитории.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from .cache import CacheManager as CacheManager
|
6
|
+
from .cache import CacheRepositoryProtocol as CacheRepositoryProtocol
|
7
|
+
from .cache import InMemoryCacheRepository as InMemoryCacheRepository
|
8
|
+
from .cache import RedisCacheRepository as RedisCacheRepository
|
9
|
+
from .crud import CrudRepositoryOldProtocol as CrudRepositoryOldProtocol
|
10
|
+
from .crud import CrudRepositoryProtocol as CrudRepositoryProtocol
|
11
|
+
from .crud import DbCrudRepository as DbCrudRepository
|
12
|
+
from .crud import DbCrudRepositoryOld as DbCrudRepositoryOld
|
13
|
+
from .settings import EnvSettingsRepository as EnvSettingsRepository
|
14
|
+
from .settings import SettingsRepositoryError as SettingsRepositoryError
|
15
|
+
from .settings import SettingsRepositoryFactoryImpl as SettingsRepositoryFactoryImpl
|
16
|
+
from .settings import SettingsRepositoryFactoryProtocol as SettingsRepositoryFactoryProtocol
|
17
|
+
from .settings import SettingsRepositoryProtocol as SettingsRepositoryProtocol
|
18
|
+
from .settings import SettingsSourceEnum as SettingsSourceEnum
|
19
|
+
from .storage import LocalStorageParamsSchema as LocalStorageParamsSchema
|
20
|
+
from .storage import LocalStorageRepository as LocalStorageRepository
|
21
|
+
from .storage import S3StorageParamsSchema as S3StorageParamsSchema
|
22
|
+
from .storage import S3StorageRepository as S3StorageRepository
|
23
|
+
from .storage import StorageRepositoryFactoryImpl as StorageRepositoryFactoryImpl
|
24
|
+
from .storage import (
|
25
|
+
StorageRepositoryFactoryProtocol as StorageRepositoryFactoryProtocol,
|
26
|
+
)
|
27
|
+
from .storage import StorageRepositoryProtocol as StorageRepositoryProtocol
|
28
|
+
from .storage import StorageTypeEnum as StorageTypeEnum
|
29
|
+
from .storage import StreamReaderProtocol as StreamReaderProtocol
|
30
|
+
from .storage import StreamReadProtocol as StreamReadProtocol
|
@@ -0,0 +1,83 @@
|
|
1
|
+
"""
|
2
|
+
Пакет, содержащий репозиторий кеша.
|
3
|
+
|
4
|
+
Представлено две реализации:
|
5
|
+
- InMemory
|
6
|
+
- Redis
|
7
|
+
"""
|
8
|
+
|
9
|
+
from typing import ClassVar, Protocol, Self, Tuple, cast
|
10
|
+
|
11
|
+
from fastapi_cache import FastAPICache
|
12
|
+
|
13
|
+
from fast_clean.settings import CoreCacheSettingsSchema
|
14
|
+
from redis import asyncio as aioredis
|
15
|
+
|
16
|
+
from .in_memory import InMemoryCacheRepository as InMemoryCacheRepository
|
17
|
+
from .redis import RedisCacheRepository as RedisCacheRepository
|
18
|
+
|
19
|
+
|
20
|
+
class CacheRepositoryProtocol(Protocol):
|
21
|
+
"""
|
22
|
+
Протокол репозитория кеша.
|
23
|
+
"""
|
24
|
+
|
25
|
+
async def get(self: Self, key: str) -> str | None:
|
26
|
+
"""
|
27
|
+
Получаем значение.
|
28
|
+
"""
|
29
|
+
...
|
30
|
+
|
31
|
+
async def set(self: Self, key: str, value: str, expire: int | None = None, nx: bool = False) -> None:
|
32
|
+
"""
|
33
|
+
Устанавливаем значение.
|
34
|
+
"""
|
35
|
+
...
|
36
|
+
|
37
|
+
async def get_with_ttl(self: Self, key: str) -> Tuple[int, str | None]:
|
38
|
+
"""
|
39
|
+
Получаем значение со сроком жизни.
|
40
|
+
"""
|
41
|
+
...
|
42
|
+
|
43
|
+
async def incr(self: Self, key: str, amount: int = 1) -> int:
|
44
|
+
"""
|
45
|
+
Инкрементируем значения.
|
46
|
+
"""
|
47
|
+
...
|
48
|
+
|
49
|
+
async def decr(self: Self, key: str, amount: int = 1) -> int:
|
50
|
+
"""
|
51
|
+
Декрементируем значения.
|
52
|
+
"""
|
53
|
+
...
|
54
|
+
|
55
|
+
async def clear(self: Self, namespace: str | None = None, key: str | None = None) -> int:
|
56
|
+
"""
|
57
|
+
Удаляем значение.
|
58
|
+
"""
|
59
|
+
...
|
60
|
+
|
61
|
+
|
62
|
+
class CacheManager:
|
63
|
+
"""
|
64
|
+
Менеджер для реализации кеширования.
|
65
|
+
"""
|
66
|
+
|
67
|
+
cache: ClassVar[CacheRepositoryProtocol | None] = None
|
68
|
+
|
69
|
+
@classmethod
|
70
|
+
def init(cls, cache_settings: CoreCacheSettingsSchema, redis: aioredis.Redis | None) -> None:
|
71
|
+
"""
|
72
|
+
Инициализируем кеш.
|
73
|
+
"""
|
74
|
+
if cls.cache is None:
|
75
|
+
cache_backend: InMemoryCacheRepository | RedisCacheRepository
|
76
|
+
match cache_settings.provider:
|
77
|
+
case 'in_memory':
|
78
|
+
cache_backend = InMemoryCacheRepository()
|
79
|
+
case 'redis':
|
80
|
+
assert redis is not None
|
81
|
+
cache_backend = RedisCacheRepository(redis)
|
82
|
+
FastAPICache.init(cache_backend, prefix=cache_settings.prefix)
|
83
|
+
cls.cache = cast(CacheRepositoryProtocol, cache_backend)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
"""
|
2
|
+
Модуль, содержащий репозиторий кеша в памяти.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Self
|
6
|
+
|
7
|
+
from fastapi_cache.backends.inmemory import InMemoryBackend, Value
|
8
|
+
from overrides import override
|
9
|
+
|
10
|
+
|
11
|
+
class InMemoryCacheRepository(InMemoryBackend):
|
12
|
+
"""
|
13
|
+
Репозиторий кеша в памяти.
|
14
|
+
"""
|
15
|
+
|
16
|
+
@override(check_signature=False)
|
17
|
+
def _get(self, key: str) -> Value | None:
|
18
|
+
"""
|
19
|
+
Получаем внутреннее значение.
|
20
|
+
|
21
|
+
Родительский метод работает неправильно, т.к. не рассчитан на правильную логику метода `set`.
|
22
|
+
"""
|
23
|
+
v = self._store.get(key)
|
24
|
+
if v:
|
25
|
+
if v.ttl_ts == -1 or v.ttl_ts >= self._now:
|
26
|
+
return v
|
27
|
+
else:
|
28
|
+
del self._store[key]
|
29
|
+
return None
|
30
|
+
|
31
|
+
@override(check_signature=False)
|
32
|
+
async def set(self: Self, key: str, value: str, expire: int | None = None, nx: bool = False) -> None:
|
33
|
+
"""
|
34
|
+
Устанавливаем значение.
|
35
|
+
|
36
|
+
Родительский метод работает неправильно, т.к. при отсутствии `expire` должно устанавливаться `-1`.
|
37
|
+
Старая логика приводит к тому, что метод работает не так, как `RedisBackend`.
|
38
|
+
"""
|
39
|
+
async with self._lock:
|
40
|
+
ttl_ts = self._now + expire if expire is not None else -1
|
41
|
+
existing_value = self._get(key)
|
42
|
+
if not nx or existing_value is None:
|
43
|
+
self._store[key] = Value(value, ttl_ts) # type: ignore
|
44
|
+
|
45
|
+
async def incr(self: Self, key: str, amount: int = 1) -> int:
|
46
|
+
"""
|
47
|
+
Инкремент значения.
|
48
|
+
"""
|
49
|
+
v = self._get(key)
|
50
|
+
if v is None:
|
51
|
+
n_value = amount
|
52
|
+
await self.set(key, str(amount))
|
53
|
+
return n_value
|
54
|
+
n_value = int(v.data) + amount
|
55
|
+
await self.set(key, str(n_value))
|
56
|
+
return n_value
|
57
|
+
|
58
|
+
async def decr(self: Self, key: str, amount: int = 1) -> int:
|
59
|
+
"""
|
60
|
+
Декремент значения.
|
61
|
+
"""
|
62
|
+
return await self.incr(key, -amount)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
"""
|
2
|
+
Модуль, содержащий репозиторий кеша с помощью Redis.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Self
|
6
|
+
|
7
|
+
from fastapi_cache.backends.redis import RedisBackend
|
8
|
+
from overrides import override
|
9
|
+
|
10
|
+
from redis.asyncio.client import Redis
|
11
|
+
|
12
|
+
|
13
|
+
class RedisCacheRepository(RedisBackend):
|
14
|
+
"""
|
15
|
+
Репозиторий кеша с помощью Redis.
|
16
|
+
"""
|
17
|
+
|
18
|
+
def __init__(self, redis: Redis):
|
19
|
+
super().__init__(redis)
|
20
|
+
self.redis: Redis
|
21
|
+
|
22
|
+
@override(check_signature=False)
|
23
|
+
async def set(self: Self, key: str, value: str, expire: int | None = None, nx: bool = False) -> None:
|
24
|
+
"""
|
25
|
+
Устанавливаем значение.
|
26
|
+
"""
|
27
|
+
await self.redis.set(key, value, ex=expire, nx=nx)
|
28
|
+
|
29
|
+
async def incr(self: Self, key: str, amount: int = 1) -> int:
|
30
|
+
"""
|
31
|
+
Инкремент значения.
|
32
|
+
"""
|
33
|
+
return await self.redis.incr(key, amount)
|
34
|
+
|
35
|
+
async def decr(self: Self, key: str, amount: int = 1) -> int:
|
36
|
+
"""
|
37
|
+
Декремент значения.
|
38
|
+
"""
|
39
|
+
return await self.redis.decr(key, amount)
|
40
|
+
|
41
|
+
async def clear(self, namespace: str | None = None, key: str | None = None) -> int:
|
42
|
+
"""
|
43
|
+
Удаляем значение.
|
44
|
+
|
45
|
+
Родительский метод работает не правильно и не подсчитывает количество удаленных записей.
|
46
|
+
https://github.com/long2ice/fastapi-cache/issues/241
|
47
|
+
"""
|
48
|
+
if namespace:
|
49
|
+
cursor = 0
|
50
|
+
removed = 0
|
51
|
+
while True:
|
52
|
+
cursor, keys = await self.redis.scan(cursor, match=f'{namespace}:*', count=500)
|
53
|
+
removed += await self.redis.delete(*keys)
|
54
|
+
if cursor == 0:
|
55
|
+
return removed
|
56
|
+
elif key:
|
57
|
+
return await self.redis.delete(key)
|
58
|
+
return 0
|
@@ -0,0 +1,149 @@
|
|
1
|
+
"""
|
2
|
+
Пакет, содержащий репозиторий для выполнения CRUD операций над моделями.
|
3
|
+
|
4
|
+
Представлено две реализации:
|
5
|
+
- InMemory
|
6
|
+
- Db
|
7
|
+
"""
|
8
|
+
|
9
|
+
import uuid
|
10
|
+
from collections.abc import Iterable, Sequence
|
11
|
+
from typing import Any, Protocol, Self
|
12
|
+
|
13
|
+
from fast_clean.schemas import PaginationResultSchema, PaginationSchema
|
14
|
+
|
15
|
+
from .db import DbCrudRepository as DbCrudRepository
|
16
|
+
from .db import DbCrudRepositoryOld as DbCrudRepositoryOld
|
17
|
+
from .in_memory import InMemoryCrudRepository as InMemoryCrudRepository
|
18
|
+
from .in_memory import InMemoryCrudRepositoryOld as InMemoryCrudRepositoryOld
|
19
|
+
from .type_vars import (
|
20
|
+
CreateSchemaBaseType,
|
21
|
+
CreateSchemaOldType,
|
22
|
+
CreateSchemaType,
|
23
|
+
IdTypeContravariant,
|
24
|
+
ReadSchemaBaseType,
|
25
|
+
UpdateSchemaBaseType,
|
26
|
+
UpdateSchemaOldType,
|
27
|
+
UpdateSchemaType,
|
28
|
+
)
|
29
|
+
|
30
|
+
|
31
|
+
class CrudRepositoryBaseProtocol(
|
32
|
+
Protocol[
|
33
|
+
ReadSchemaBaseType,
|
34
|
+
CreateSchemaBaseType,
|
35
|
+
UpdateSchemaBaseType,
|
36
|
+
IdTypeContravariant,
|
37
|
+
]
|
38
|
+
):
|
39
|
+
"""
|
40
|
+
Протокол базового репозитория для выполнения CRUD операций над моделями.
|
41
|
+
"""
|
42
|
+
|
43
|
+
async def get(self: Self, id: IdTypeContravariant) -> ReadSchemaBaseType:
|
44
|
+
"""
|
45
|
+
Получаем модель по идентификатору.
|
46
|
+
"""
|
47
|
+
...
|
48
|
+
|
49
|
+
async def get_or_none(self: Self, id: IdTypeContravariant) -> ReadSchemaBaseType | None:
|
50
|
+
"""
|
51
|
+
Получаем модель или None по идентификатору.
|
52
|
+
"""
|
53
|
+
...
|
54
|
+
|
55
|
+
async def get_by_ids(
|
56
|
+
self: Self, ids: Sequence[IdTypeContravariant], *, exact: bool = False
|
57
|
+
) -> list[ReadSchemaBaseType]:
|
58
|
+
"""
|
59
|
+
Получаем список моделей по идентификаторам.
|
60
|
+
"""
|
61
|
+
...
|
62
|
+
|
63
|
+
async def get_all(self: Self) -> list[ReadSchemaBaseType]:
|
64
|
+
"""
|
65
|
+
Получаем все модели.
|
66
|
+
"""
|
67
|
+
...
|
68
|
+
|
69
|
+
async def paginate(
|
70
|
+
self: Self,
|
71
|
+
pagination: PaginationSchema,
|
72
|
+
user: Any,
|
73
|
+
policies: list[str],
|
74
|
+
*,
|
75
|
+
search: str | None = None,
|
76
|
+
search_by: Iterable[str] | None = None,
|
77
|
+
sorting: Iterable[str] | None = None,
|
78
|
+
) -> PaginationResultSchema[ReadSchemaBaseType]:
|
79
|
+
"""
|
80
|
+
Получаем список моделей с пагинацией, поиском и сортировкой.
|
81
|
+
"""
|
82
|
+
...
|
83
|
+
|
84
|
+
async def create(self: Self, create_object: CreateSchemaBaseType) -> ReadSchemaBaseType:
|
85
|
+
"""
|
86
|
+
Создаем модель.
|
87
|
+
"""
|
88
|
+
...
|
89
|
+
|
90
|
+
async def bulk_create(self: Self, create_objects: list[CreateSchemaBaseType]) -> list[ReadSchemaBaseType]:
|
91
|
+
"""
|
92
|
+
Создаем несколько моделей.
|
93
|
+
"""
|
94
|
+
...
|
95
|
+
|
96
|
+
async def update(self: Self, update_object: UpdateSchemaBaseType) -> ReadSchemaBaseType:
|
97
|
+
"""
|
98
|
+
Обновляем модель.
|
99
|
+
"""
|
100
|
+
...
|
101
|
+
|
102
|
+
async def bulk_update(self: Self, update_objects: list[UpdateSchemaBaseType]) -> None:
|
103
|
+
"""
|
104
|
+
Обновляем несколько моделей.
|
105
|
+
"""
|
106
|
+
...
|
107
|
+
|
108
|
+
async def upsert(self: Self, create_object: CreateSchemaBaseType) -> ReadSchemaBaseType:
|
109
|
+
"""
|
110
|
+
Создаем или обновляем модель.
|
111
|
+
"""
|
112
|
+
...
|
113
|
+
|
114
|
+
async def delete(self: Self, ids: Sequence[IdTypeContravariant]) -> None:
|
115
|
+
"""
|
116
|
+
Удаляем модели.
|
117
|
+
"""
|
118
|
+
...
|
119
|
+
|
120
|
+
|
121
|
+
class CrudRepositoryOldProtocol(
|
122
|
+
CrudRepositoryBaseProtocol[
|
123
|
+
ReadSchemaBaseType,
|
124
|
+
CreateSchemaOldType,
|
125
|
+
UpdateSchemaOldType,
|
126
|
+
int,
|
127
|
+
],
|
128
|
+
Protocol[
|
129
|
+
ReadSchemaBaseType,
|
130
|
+
CreateSchemaOldType,
|
131
|
+
UpdateSchemaOldType,
|
132
|
+
],
|
133
|
+
):
|
134
|
+
"""
|
135
|
+
Протокол репозитория для выполнения CRUD операций над моделями старого типа.
|
136
|
+
"""
|
137
|
+
|
138
|
+
...
|
139
|
+
|
140
|
+
|
141
|
+
class CrudRepositoryProtocol(
|
142
|
+
CrudRepositoryBaseProtocol[ReadSchemaBaseType, CreateSchemaType, UpdateSchemaType, uuid.UUID],
|
143
|
+
Protocol[ReadSchemaBaseType, CreateSchemaType, UpdateSchemaType],
|
144
|
+
):
|
145
|
+
"""
|
146
|
+
Протокол репозитория для выполнения CRUD операций над моделями нового типа.
|
147
|
+
"""
|
148
|
+
|
149
|
+
...
|