fast-clean 1.1.1__tar.gz → 1.2.0__tar.gz

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 (81) hide show
  1. {fast_clean-1.1.1 → fast_clean-1.2.0}/PKG-INFO +8 -4
  2. fast_clean-1.2.0/fast_clean/contrib/logging/enums.py +10 -0
  3. fast_clean-1.2.0/fast_clean/contrib/logging/sentry.py +23 -0
  4. fast_clean-1.2.0/fast_clean/contrib/monitoring/__init__.py +0 -0
  5. fast_clean-1.2.0/fast_clean/contrib/monitoring/middleware.py +6 -0
  6. fast_clean-1.2.0/fast_clean/contrib/monitoring/router.py +6 -0
  7. fast_clean-1.2.0/fast_clean/contrib/sqlalchemy_utils/__init__.py +0 -0
  8. fast_clean-1.2.0/fast_clean/contrib/sqlalchemy_utils/utils.py +26 -0
  9. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/db.py +9 -6
  10. fast_clean-1.2.0/fast_clean/py.typed +0 -0
  11. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/__init__.py +2 -2
  12. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/crud/__init__.py +9 -9
  13. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/crud/db.py +13 -13
  14. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/crud/in_memory.py +10 -10
  15. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/crud/type_vars.py +13 -13
  16. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/storage/s3.py +1 -1
  17. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/schemas/__init__.py +3 -3
  18. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/schemas/repository.py +3 -3
  19. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/services/lock.py +2 -2
  20. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean.egg-info/PKG-INFO +8 -4
  21. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean.egg-info/SOURCES.txt +8 -4
  22. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean.egg-info/requires.txt +5 -1
  23. {fast_clean-1.1.1 → fast_clean-1.2.0}/pyproject.toml +9 -7
  24. {fast_clean-1.1.1 → fast_clean-1.2.0}/tests/test_db.py +0 -13
  25. fast_clean-1.1.1/fast_clean/tools/__init__.py +0 -6
  26. fast_clean-1.1.1/fast_clean/tools/cryptography.py +0 -52
  27. fast_clean-1.1.1/fast_clean/tools/load_seed.py +0 -31
  28. fast_clean-1.1.1/fast_clean/use_cases.py +0 -38
  29. {fast_clean-1.1.1 → fast_clean-1.2.0}/README.md +0 -0
  30. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/__init__.py +0 -0
  31. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/broker.py +0 -0
  32. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/container.py +0 -0
  33. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/contrib/__init__.py +0 -0
  34. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/contrib/healthcheck/__init__.py +0 -0
  35. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/contrib/healthcheck/router.py +0 -0
  36. /fast_clean-1.1.1/fast_clean/py.typed → /fast_clean-1.2.0/fast_clean/contrib/logging/__init__.py +0 -0
  37. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/depends.py +0 -0
  38. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/enums.py +0 -0
  39. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/exceptions.py +0 -0
  40. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/loggers.py +0 -0
  41. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/middleware.py +0 -0
  42. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/models.py +0 -0
  43. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/redis.py +0 -0
  44. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/cache/__init__.py +0 -0
  45. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/cache/in_memory.py +0 -0
  46. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/cache/redis.py +0 -0
  47. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/settings/__init__.py +0 -0
  48. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/settings/enums.py +0 -0
  49. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/settings/env.py +0 -0
  50. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/settings/exceptions.py +0 -0
  51. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/settings/type_vars.py +0 -0
  52. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/storage/__init__.py +0 -0
  53. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/storage/enums.py +0 -0
  54. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/storage/local.py +0 -0
  55. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/storage/reader.py +0 -0
  56. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/repositories/storage/schemas.py +0 -0
  57. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/schemas/exceptions.py +0 -0
  58. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/schemas/pagination.py +0 -0
  59. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/schemas/request_response.py +0 -0
  60. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/schemas/status_response.py +0 -0
  61. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/services/__init__.py +0 -0
  62. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/services/cryptography/__init__.py +0 -0
  63. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/services/cryptography/aes.py +0 -0
  64. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/services/cryptography/enums.py +0 -0
  65. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/services/seed.py +0 -0
  66. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/services/transaction.py +0 -0
  67. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/settings.py +0 -0
  68. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/__init__.py +0 -0
  69. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/process.py +0 -0
  70. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/pydantic.py +0 -0
  71. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/ssl_context.py +0 -0
  72. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/string.py +0 -0
  73. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/thread.py +0 -0
  74. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/time.py +0 -0
  75. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/type_converters.py +0 -0
  76. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean/utils/typer.py +0 -0
  77. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean.egg-info/dependency_links.txt +0 -0
  78. {fast_clean-1.1.1 → fast_clean-1.2.0}/fast_clean.egg-info/top_level.txt +0 -0
  79. {fast_clean-1.1.1 → fast_clean-1.2.0}/setup.cfg +0 -0
  80. {fast_clean-1.1.1 → fast_clean-1.2.0}/tests/test_broker.py +0 -0
  81. {fast_clean-1.1.1 → fast_clean-1.2.0}/tests/test_exceptions.py +0 -0
@@ -1,12 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-clean
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: FastAPI Clean Architecture implementation
5
- Author-email: Luferov Victor <luferovvs@yandex.ru>, Orlov Artem <squakrazv@yandex.ru>
6
- Requires-Python: >=3.12
5
+ Author-email: Luferov Victor <luferovvs@yandex.ru>, Orlov Artem <squakrazv@yandex.ru>, Kashapov Rustam <hardtechnik91@gmail.com>
6
+ Requires-Python: >=3.13
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: aiofiles>=24.1.0
9
9
  Requires-Dist: aiokafka>=0.12.0
10
+ Requires-Dist: aioprometheus>=23.12.0
11
+ Requires-Dist: alembic>=1.16.2
10
12
  Requires-Dist: cryptography>=44.0.1
11
13
  Requires-Dist: dishka>=1.6.0
12
14
  Requires-Dist: fastapi>=0.115.8
@@ -15,10 +17,12 @@ Requires-Dist: faststream>=0.5.34
15
17
  Requires-Dist: flatten-dict>=0.4.2
16
18
  Requires-Dist: miniopy-async>=1.21.1
17
19
  Requires-Dist: overrides>=7.7.0
18
- Requires-Dist: psycopg>=3.2.4
20
+ Requires-Dist: psycopg[binary]>=3.2.4
19
21
  Requires-Dist: pydantic>=2.10.6
20
22
  Requires-Dist: pydantic-settings>=2.8.0
21
23
  Requires-Dist: pyyaml>=6.0.2
24
+ Requires-Dist: sentry-sdk>=2.32.0
25
+ Requires-Dist: snakecase>=1.0.1
22
26
  Requires-Dist: sqlalchemy-utils>=0.41.2
23
27
  Requires-Dist: sqlalchemy[asyncio]>=2.0.38
24
28
  Requires-Dist: stringcase>=1.2.0
@@ -0,0 +1,10 @@
1
+ from enum import StrEnum, auto
2
+
3
+
4
+ class EnvironmentEnum(StrEnum):
5
+ """
6
+ Окружения
7
+ """
8
+
9
+ DEVELOPMENT = auto()
10
+ PRODUCTION = auto()
@@ -0,0 +1,23 @@
1
+ import logging
2
+
3
+ import sentry_sdk
4
+ from sentry_sdk.integrations.fastapi import FastApiIntegration
5
+ from sentry_sdk.integrations.logging import LoggingIntegration
6
+
7
+ from .enums import EnvironmentEnum
8
+
9
+
10
+ def use_sentry(
11
+ dsn: str | None,
12
+ environment: EnvironmentEnum = EnvironmentEnum.DEVELOPMENT,
13
+ level: int = logging.DEBUG,
14
+ event_level: int = logging.ERROR,
15
+ ) -> None:
16
+ if not dsn:
17
+ return
18
+
19
+ sentry_logging = LoggingIntegration(
20
+ level=level,
21
+ event_level=event_level,
22
+ )
23
+ sentry_sdk.init(dsn=dsn, environment=str(environment), integrations=[sentry_logging, FastApiIntegration()])
@@ -0,0 +1,6 @@
1
+ from aioprometheus.asgi.middleware import MetricsMiddleware
2
+ from fastapi import FastAPI
3
+
4
+
5
+ def use_middleware(app: FastAPI) -> None:
6
+ app.add_middleware(MetricsMiddleware) # type: ignore
@@ -0,0 +1,6 @@
1
+ from aioprometheus.asgi.starlette import metrics
2
+ from fastapi import APIRouter
3
+
4
+ router = APIRouter(tags=['Monitoring'])
5
+
6
+ router.get('/metrics')(metrics)
@@ -0,0 +1,26 @@
1
+ from typing import Any
2
+
3
+ import sqlalchemy_utils
4
+ from alembic.autogenerate.api import AutogenContext
5
+
6
+
7
+ def render_item(type_: str, obj: Any, autogen_context: AutogenContext):
8
+ """
9
+ Apply custom rendering for selected items.
10
+ """
11
+
12
+ if type_ == 'type' and isinstance(obj, sqlalchemy_utils.types.ChoiceType):
13
+ autogen_context.imports.add('import sqlalchemy_utils')
14
+ autogen_context.imports.add(f'from {obj.choices.__module__} import {obj.choices.__name__}')
15
+ return f'sqlalchemy_utils.types.ChoiceType({obj.choices.__name__}, impl=sa.{obj.impl.__class__.__name__}())'
16
+
17
+ if type_ == 'type' and isinstance(obj, sqlalchemy_utils.types.JSONType):
18
+ autogen_context.imports.add('import sqlalchemy_utils')
19
+ return 'sqlalchemy_utils.types.JSONType()'
20
+
21
+ if type_ == 'type' and isinstance(obj, sqlalchemy_utils.types.UUIDType):
22
+ autogen_context.imports.add('import sqlalchemy_utils')
23
+ return f'sqlalchemy_utils.types.UUIDType(binary={obj.binary})'
24
+
25
+ # Default rendering for other objects
26
+ return False
@@ -21,7 +21,7 @@ from sqlalchemy.ext.asyncio import (
21
21
  from sqlalchemy.ext.declarative import declared_attr
22
22
  from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
23
23
  from sqlalchemy.sql import func
24
- from sqlalchemy_utils import UUIDType
24
+ from sqlalchemy_utils.types.uuid import UUIDType
25
25
  from stringcase import snakecase
26
26
 
27
27
  from .settings import CoreDbSettingsSchema, CoreSettingsSchema
@@ -61,7 +61,7 @@ def make_async_session_factory(
61
61
  return async_sessionmaker(asyncio_engine, expire_on_commit=False, autoflush=False)
62
62
 
63
63
 
64
- class BaseParent(AsyncAttrs, DeclarativeBase):
64
+ class Base(AsyncAttrs, DeclarativeBase):
65
65
  """
66
66
  Базовая родительская модель.
67
67
  """
@@ -71,7 +71,7 @@ class BaseParent(AsyncAttrs, DeclarativeBase):
71
71
  metadata = metadata
72
72
 
73
73
 
74
- class Base(BaseParent):
74
+ class BaseUUID(Base):
75
75
  """
76
76
  Базовая родительская модель нового типа.
77
77
  """
@@ -90,7 +90,7 @@ class Base(BaseParent):
90
90
  return snakecase(cls.__name__)
91
91
 
92
92
 
93
- class BaseOld(BaseParent):
93
+ class BaseInt(Base):
94
94
  """
95
95
  Базовая родительская модель старого типа.
96
96
  """
@@ -100,6 +100,10 @@ class BaseOld(BaseParent):
100
100
  id: Mapped[int] = mapped_column(primary_key=True)
101
101
 
102
102
 
103
+ @declared_attr.directive
104
+ def __tablename__(cls) -> str:
105
+ return snakecase(cls.__name__)
106
+
103
107
  class SessionFactory:
104
108
  """
105
109
  Фабрика сессий.
@@ -170,8 +174,7 @@ class SessionManagerImpl:
170
174
  Получаем сессию для выполнения запроса.
171
175
  """
172
176
  if self.session.in_transaction():
173
- async with self.session.begin_nested():
174
- yield self.session
177
+ yield self.session
175
178
  else:
176
179
  async with self.session.begin():
177
180
  if immediate:
File without changes
@@ -6,10 +6,10 @@ from .cache import CacheManager as CacheManager
6
6
  from .cache import CacheRepositoryProtocol as CacheRepositoryProtocol
7
7
  from .cache import InMemoryCacheRepository as InMemoryCacheRepository
8
8
  from .cache import RedisCacheRepository as RedisCacheRepository
9
- from .crud import CrudRepositoryOldProtocol as CrudRepositoryOldProtocol
9
+ from .crud import CrudRepositoryIntProtocol as CrudRepositoryIntProtocol
10
10
  from .crud import CrudRepositoryProtocol as CrudRepositoryProtocol
11
11
  from .crud import DbCrudRepository as DbCrudRepository
12
- from .crud import DbCrudRepositoryOld as DbCrudRepositoryOld
12
+ from .crud import DbCrudRepositoryInt as DbCrudRepositoryInt
13
13
  from .settings import EnvSettingsRepository as EnvSettingsRepository
14
14
  from .settings import SettingsRepositoryError as SettingsRepositoryError
15
15
  from .settings import SettingsRepositoryFactoryImpl as SettingsRepositoryFactoryImpl
@@ -13,17 +13,17 @@ from typing import Any, Protocol, Self
13
13
  from fast_clean.schemas import PaginationResultSchema, PaginationSchema
14
14
 
15
15
  from .db import DbCrudRepository as DbCrudRepository
16
- from .db import DbCrudRepositoryOld as DbCrudRepositoryOld
16
+ from .db import DbCrudRepositoryInt as DbCrudRepositoryInt
17
17
  from .in_memory import InMemoryCrudRepository as InMemoryCrudRepository
18
- from .in_memory import InMemoryCrudRepositoryOld as InMemoryCrudRepositoryOld
18
+ from .in_memory import InMemoryCrudRepositoryInt as InMemoryCrudRepositoryInt
19
19
  from .type_vars import (
20
20
  CreateSchemaBaseType,
21
- CreateSchemaOldType,
21
+ CreateSchemaIntType,
22
22
  CreateSchemaType,
23
23
  IdTypeContravariant,
24
24
  ReadSchemaBaseType,
25
25
  UpdateSchemaBaseType,
26
- UpdateSchemaOldType,
26
+ UpdateSchemaIntType,
27
27
  UpdateSchemaType,
28
28
  )
29
29
 
@@ -118,17 +118,17 @@ class CrudRepositoryBaseProtocol(
118
118
  ...
119
119
 
120
120
 
121
- class CrudRepositoryOldProtocol(
121
+ class CrudRepositoryIntProtocol(
122
122
  CrudRepositoryBaseProtocol[
123
123
  ReadSchemaBaseType,
124
- CreateSchemaOldType,
125
- UpdateSchemaOldType,
124
+ CreateSchemaIntType,
125
+ UpdateSchemaIntType,
126
126
  int,
127
127
  ],
128
128
  Protocol[
129
129
  ReadSchemaBaseType,
130
- CreateSchemaOldType,
131
- UpdateSchemaOldType,
130
+ CreateSchemaIntType,
131
+ UpdateSchemaIntType,
132
132
  ],
133
133
  ):
134
134
  """
@@ -28,17 +28,17 @@ from fast_clean.schemas import PaginationResultSchema, PaginationSchema
28
28
 
29
29
  from .type_vars import (
30
30
  CreateSchemaBaseType,
31
- CreateSchemaOldType,
31
+ CreateSchemaIntType,
32
32
  CreateSchemaType,
33
33
  IdType,
34
34
  ModelBaseType,
35
- ModelOldType,
35
+ ModelIntType,
36
36
  ModelType,
37
37
  ReadSchemaBaseType,
38
- ReadSchemaOldType,
38
+ ReadSchemaIntType,
39
39
  ReadSchemaType,
40
40
  UpdateSchemaBaseType,
41
- UpdateSchemaOldType,
41
+ UpdateSchemaIntType,
42
42
  UpdateSchemaType,
43
43
  )
44
44
 
@@ -526,19 +526,19 @@ class DbCrudRepositoryBase(
526
526
  return order_by_expr
527
527
 
528
528
 
529
- class DbCrudRepositoryOld(
529
+ class DbCrudRepositoryInt(
530
530
  DbCrudRepositoryBase[
531
- ModelOldType,
532
- ReadSchemaOldType,
533
- CreateSchemaOldType,
534
- UpdateSchemaOldType,
531
+ ModelIntType,
532
+ ReadSchemaIntType,
533
+ CreateSchemaIntType,
534
+ UpdateSchemaIntType,
535
535
  int,
536
536
  ],
537
537
  Generic[
538
- ModelOldType,
539
- ReadSchemaOldType,
540
- CreateSchemaOldType,
541
- UpdateSchemaOldType,
538
+ ModelIntType,
539
+ ReadSchemaIntType,
540
+ CreateSchemaIntType,
541
+ UpdateSchemaIntType,
542
542
  ],
543
543
  ):
544
544
  """
@@ -15,14 +15,14 @@ from fast_clean.schemas import PaginationResultSchema, PaginationSchema
15
15
 
16
16
  from .type_vars import (
17
17
  CreateSchemaBaseType,
18
- CreateSchemaOldType,
18
+ CreateSchemaIntType,
19
19
  CreateSchemaType,
20
20
  IdType,
21
21
  ReadSchemaBaseType,
22
- ReadSchemaOldType,
22
+ ReadSchemaIntType,
23
23
  ReadSchemaType,
24
24
  UpdateSchemaBaseType,
25
- UpdateSchemaOldType,
25
+ UpdateSchemaIntType,
26
26
  UpdateSchemaType,
27
27
  )
28
28
 
@@ -311,17 +311,17 @@ class InMemoryCrudRepositoryBase(ABC, Generic[ReadSchemaBaseType, CreateSchemaBa
311
311
  return model
312
312
 
313
313
 
314
- class InMemoryCrudRepositoryOld(
314
+ class InMemoryCrudRepositoryInt(
315
315
  InMemoryCrudRepositoryBase[
316
- ReadSchemaOldType,
317
- CreateSchemaOldType,
318
- UpdateSchemaOldType,
316
+ ReadSchemaIntType,
317
+ CreateSchemaIntType,
318
+ UpdateSchemaIntType,
319
319
  int,
320
320
  ],
321
321
  Generic[
322
- ReadSchemaOldType,
323
- CreateSchemaOldType,
324
- UpdateSchemaOldType,
322
+ ReadSchemaIntType,
323
+ CreateSchemaIntType,
324
+ UpdateSchemaIntType,
325
325
  ],
326
326
  ):
327
327
  """
@@ -5,31 +5,31 @@
5
5
  import uuid
6
6
  from typing import TypeVar
7
7
 
8
- from fast_clean.db import Base, BaseOld
8
+ from fast_clean.db import BaseInt, BaseUUID
9
9
  from fast_clean.schemas import (
10
10
  CreateSchema,
11
- CreateSchemaOld,
11
+ CreateSchemaInt,
12
12
  ReadSchema,
13
- ReadSchemaOld,
13
+ ReadSchemaInt,
14
14
  UpdateSchema,
15
- UpdateSchemaOld,
15
+ UpdateSchemaInt,
16
16
  )
17
17
 
18
- ModelBaseType = TypeVar('ModelBaseType', bound=BaseOld | Base)
19
- CreateSchemaBaseType = TypeVar('CreateSchemaBaseType', bound=CreateSchemaOld | CreateSchema)
20
- ReadSchemaBaseType = TypeVar('ReadSchemaBaseType', bound=ReadSchemaOld | ReadSchema)
21
- UpdateSchemaBaseType = TypeVar('UpdateSchemaBaseType', bound=UpdateSchemaOld | UpdateSchema)
18
+ ModelBaseType = TypeVar('ModelBaseType', bound=BaseInt | BaseUUID)
19
+ CreateSchemaBaseType = TypeVar('CreateSchemaBaseType', bound=CreateSchemaInt | CreateSchema)
20
+ ReadSchemaBaseType = TypeVar('ReadSchemaBaseType', bound=ReadSchemaInt | ReadSchema)
21
+ UpdateSchemaBaseType = TypeVar('UpdateSchemaBaseType', bound=UpdateSchemaInt | UpdateSchema)
22
22
  IdType = TypeVar('IdType', bound=int | uuid.UUID)
23
23
  IdTypeContravariant = TypeVar('IdTypeContravariant', bound=int | uuid.UUID, contravariant=True)
24
24
 
25
25
 
26
- ModelOldType = TypeVar('ModelOldType', bound=BaseOld)
27
- CreateSchemaOldType = TypeVar('CreateSchemaOldType', bound=CreateSchemaOld)
28
- ReadSchemaOldType = TypeVar('ReadSchemaOldType', bound=ReadSchemaOld)
29
- UpdateSchemaOldType = TypeVar('UpdateSchemaOldType', bound=UpdateSchemaOld)
26
+ ModelIntType = TypeVar('ModelIntType', bound=BaseInt)
27
+ CreateSchemaIntType = TypeVar('CreateSchemaIntType', bound=CreateSchemaInt)
28
+ ReadSchemaIntType = TypeVar('ReadSchemaIntType', bound=ReadSchemaInt)
29
+ UpdateSchemaIntType = TypeVar('UpdateSchemaIntType', bound=UpdateSchemaInt)
30
30
 
31
31
 
32
- ModelType = TypeVar('ModelType', bound=Base)
32
+ ModelType = TypeVar('ModelType', bound=BaseUUID)
33
33
  CreateSchemaType = TypeVar('CreateSchemaType', bound=CreateSchema)
34
34
  ReadSchemaType = TypeVar('ReadSchemaType', bound=ReadSchema)
35
35
  UpdateSchemaType = TypeVar('UpdateSchemaType', bound=UpdateSchema)
@@ -24,7 +24,7 @@ class S3StorageRepository:
24
24
  def __init__(self: Self, params: S3StorageParamsSchema):
25
25
  self.params = params
26
26
  self.bucket = self.params.bucket
27
- self.client = miniopy_async.Minio(
27
+ self.client = miniopy_async.Minio( # type: ignore
28
28
  f'{self.params.endpoint}:{self.params.port}',
29
29
  access_key=self.params.access_key,
30
30
  secret_key=self.params.secret_key,
@@ -13,11 +13,11 @@ from .pagination import PaginationResponseSchema as PaginationResponseSchema
13
13
  from .pagination import PaginationResultSchema as PaginationResultSchema
14
14
  from .pagination import PaginationSchema as PaginationSchema
15
15
  from .repository import CreateSchema as CreateSchema
16
- from .repository import CreateSchemaOld as CreateSchemaOld
16
+ from .repository import CreateSchemaInt as CreateSchemaInt
17
17
  from .repository import ReadSchema as ReadSchema
18
- from .repository import ReadSchemaOld as ReadSchemaOld
18
+ from .repository import ReadSchemaInt as ReadSchemaInt
19
19
  from .repository import UpdateSchema as UpdateSchema
20
- from .repository import UpdateSchemaOld as UpdateSchemaOld
20
+ from .repository import UpdateSchemaInt as UpdateSchemaInt
21
21
  from .request_response import RemoteRequestSchema as RemoteRequestSchema
22
22
  from .request_response import RemoteResponseSchema as RemoteResponseSchema
23
23
  from .request_response import RequestSchema as RequestSchema
@@ -34,9 +34,9 @@ class UpdateSchemaGeneric(BaseModel, Generic[IdType]):
34
34
  id: IdType
35
35
 
36
36
 
37
- CreateSchemaOld = CreateSchemaGeneric[int]
38
- ReadSchemaOld = ReadSchemaGeneric[int]
39
- UpdateSchemaOld = UpdateSchemaGeneric[int]
37
+ CreateSchemaInt = CreateSchemaGeneric[int]
38
+ ReadSchemaInt = ReadSchemaGeneric[int]
39
+ UpdateSchemaInt = UpdateSchemaGeneric[int]
40
40
 
41
41
  CreateSchema = CreateSchemaGeneric[uuid.UUID]
42
42
  ReadSchema = ReadSchemaGeneric[uuid.UUID]
@@ -6,9 +6,9 @@ from collections.abc import AsyncIterator
6
6
  from contextlib import asynccontextmanager
7
7
  from typing import AsyncContextManager, Protocol
8
8
 
9
- import redis.asyncio.lock as aioredis_lock
10
9
  from fast_clean.exceptions import LockError
11
10
  from redis import asyncio as aioredis
11
+ from redis.exceptions import LockError as AIORedisLockError
12
12
 
13
13
 
14
14
  class LockServiceProtocol(Protocol):
@@ -53,5 +53,5 @@ class RedisLockService:
53
53
  try:
54
54
  async with self.redis.lock(name, timeout=timeout, sleep=sleep, blocking_timeout=blocking_timeout):
55
55
  yield
56
- except aioredis_lock.LockError as lock_error:
56
+ except AIORedisLockError as lock_error:
57
57
  raise LockError() from lock_error
@@ -1,12 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fast-clean
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: FastAPI Clean Architecture implementation
5
- Author-email: Luferov Victor <luferovvs@yandex.ru>, Orlov Artem <squakrazv@yandex.ru>
6
- Requires-Python: >=3.12
5
+ Author-email: Luferov Victor <luferovvs@yandex.ru>, Orlov Artem <squakrazv@yandex.ru>, Kashapov Rustam <hardtechnik91@gmail.com>
6
+ Requires-Python: >=3.13
7
7
  Description-Content-Type: text/markdown
8
8
  Requires-Dist: aiofiles>=24.1.0
9
9
  Requires-Dist: aiokafka>=0.12.0
10
+ Requires-Dist: aioprometheus>=23.12.0
11
+ Requires-Dist: alembic>=1.16.2
10
12
  Requires-Dist: cryptography>=44.0.1
11
13
  Requires-Dist: dishka>=1.6.0
12
14
  Requires-Dist: fastapi>=0.115.8
@@ -15,10 +17,12 @@ Requires-Dist: faststream>=0.5.34
15
17
  Requires-Dist: flatten-dict>=0.4.2
16
18
  Requires-Dist: miniopy-async>=1.21.1
17
19
  Requires-Dist: overrides>=7.7.0
18
- Requires-Dist: psycopg>=3.2.4
20
+ Requires-Dist: psycopg[binary]>=3.2.4
19
21
  Requires-Dist: pydantic>=2.10.6
20
22
  Requires-Dist: pydantic-settings>=2.8.0
21
23
  Requires-Dist: pyyaml>=6.0.2
24
+ Requires-Dist: sentry-sdk>=2.32.0
25
+ Requires-Dist: snakecase>=1.0.1
22
26
  Requires-Dist: sqlalchemy-utils>=0.41.2
23
27
  Requires-Dist: sqlalchemy[asyncio]>=2.0.38
24
28
  Requires-Dist: stringcase>=1.2.0
@@ -13,7 +13,6 @@ fast_clean/models.py
13
13
  fast_clean/py.typed
14
14
  fast_clean/redis.py
15
15
  fast_clean/settings.py
16
- fast_clean/use_cases.py
17
16
  fast_clean.egg-info/PKG-INFO
18
17
  fast_clean.egg-info/SOURCES.txt
19
18
  fast_clean.egg-info/dependency_links.txt
@@ -22,6 +21,14 @@ fast_clean.egg-info/top_level.txt
22
21
  fast_clean/contrib/__init__.py
23
22
  fast_clean/contrib/healthcheck/__init__.py
24
23
  fast_clean/contrib/healthcheck/router.py
24
+ fast_clean/contrib/logging/__init__.py
25
+ fast_clean/contrib/logging/enums.py
26
+ fast_clean/contrib/logging/sentry.py
27
+ fast_clean/contrib/monitoring/__init__.py
28
+ fast_clean/contrib/monitoring/middleware.py
29
+ fast_clean/contrib/monitoring/router.py
30
+ fast_clean/contrib/sqlalchemy_utils/__init__.py
31
+ fast_clean/contrib/sqlalchemy_utils/utils.py
25
32
  fast_clean/repositories/__init__.py
26
33
  fast_clean/repositories/cache/__init__.py
27
34
  fast_clean/repositories/cache/in_memory.py
@@ -54,9 +61,6 @@ fast_clean/services/transaction.py
54
61
  fast_clean/services/cryptography/__init__.py
55
62
  fast_clean/services/cryptography/aes.py
56
63
  fast_clean/services/cryptography/enums.py
57
- fast_clean/tools/__init__.py
58
- fast_clean/tools/cryptography.py
59
- fast_clean/tools/load_seed.py
60
64
  fast_clean/utils/__init__.py
61
65
  fast_clean/utils/process.py
62
66
  fast_clean/utils/pydantic.py
@@ -1,5 +1,7 @@
1
1
  aiofiles>=24.1.0
2
2
  aiokafka>=0.12.0
3
+ aioprometheus>=23.12.0
4
+ alembic>=1.16.2
3
5
  cryptography>=44.0.1
4
6
  dishka>=1.6.0
5
7
  fastapi>=0.115.8
@@ -8,10 +10,12 @@ faststream>=0.5.34
8
10
  flatten-dict>=0.4.2
9
11
  miniopy-async>=1.21.1
10
12
  overrides>=7.7.0
11
- psycopg>=3.2.4
13
+ psycopg[binary]>=3.2.4
12
14
  pydantic>=2.10.6
13
15
  pydantic-settings>=2.8.0
14
16
  pyyaml>=6.0.2
17
+ sentry-sdk>=2.32.0
18
+ snakecase>=1.0.1
15
19
  sqlalchemy-utils>=0.41.2
16
20
  sqlalchemy[asyncio]>=2.0.38
17
21
  stringcase>=1.2.0
@@ -1,16 +1,19 @@
1
1
  [project]
2
2
  name = "fast-clean"
3
- version = "1.1.1"
3
+ version = "1.2.0"
4
4
  description = "FastAPI Clean Architecture implementation"
5
5
  readme = "README.md"
6
- requires-python = ">=3.12"
6
+ requires-python = ">=3.13"
7
7
  authors = [
8
8
  {name = "Luferov Victor", email = "luferovvs@yandex.ru"},
9
9
  {name = "Orlov Artem", email = "squakrazv@yandex.ru"},
10
+ {name = "Kashapov Rustam", email = "hardtechnik91@gmail.com"},
10
11
  ]
11
12
  dependencies = [
12
13
  "aiofiles>=24.1.0",
13
14
  "aiokafka>=0.12.0",
15
+ "aioprometheus>=23.12.0",
16
+ "alembic>=1.16.2",
14
17
  "cryptography>=44.0.1",
15
18
  "dishka>=1.6.0",
16
19
  "fastapi>=0.115.8",
@@ -19,10 +22,12 @@ dependencies = [
19
22
  "flatten-dict>=0.4.2",
20
23
  "miniopy-async>=1.21.1",
21
24
  "overrides>=7.7.0",
22
- "psycopg>=3.2.4",
25
+ "psycopg[binary]>=3.2.4",
23
26
  "pydantic>=2.10.6",
24
27
  "pydantic-settings>=2.8.0",
25
28
  "pyyaml>=6.0.2",
29
+ "sentry-sdk>=2.32.0",
30
+ "snakecase>=1.0.1",
26
31
  "sqlalchemy-utils>=0.41.2",
27
32
  "sqlalchemy[asyncio]>=2.0.38",
28
33
  "stringcase>=1.2.0",
@@ -43,15 +48,12 @@ dev = [
43
48
  "pytest-sugar>=1.0.0",
44
49
  "pytest-xdist>=3.6.1",
45
50
  "ruff>=0.9.7",
51
+ "types-sqlalchemy-utils>=1.1.0",
46
52
  ]
47
53
 
48
54
  [tool.ruff.format]
49
55
  quote-style = "single"
50
56
 
51
- [tool.black]
52
- skip-string-normalization = true
53
- line-length = 120
54
-
55
57
  [tool.ruff]
56
58
  line-length = 120
57
59
  lint.extend-select = ["Q"]
@@ -41,16 +41,3 @@ class TestSessionManager:
41
41
  assert len(call_args) == 1
42
42
  assert str(call_args[0]) == str(sa.text('SET CONSTRAINTS ALL IMMEDIATE'))
43
43
  assert not session_manager.session.in_transaction()
44
-
45
- @staticmethod
46
- async def test_get_session_begin_nested(
47
- session_manager: SessionManagerImpl,
48
- ) -> None:
49
- """
50
- Тестируем запуск дочерней транзакции в методе `begin`.
51
- """
52
- async with session_manager.session.begin():
53
- assert not session_manager.session.in_nested_transaction()
54
- async with session_manager.get_session():
55
- assert session_manager.session.in_nested_transaction()
56
- assert not session_manager.session.in_nested_transaction()
@@ -1,6 +0,0 @@
1
- """
2
- Пакет, содержащий команды Typer.
3
- """
4
-
5
- from .cryptography import use_cryptography as use_cryptography
6
- from .load_seed import use_load_seed as use_load_seed
@@ -1,52 +0,0 @@
1
- """
2
- Модуль, содержащий команды криптографии для шифрования секретных параметров.
3
- """
4
-
5
- from typing import Annotated
6
-
7
- import typer
8
-
9
- from fast_clean.container import get_container
10
- from fast_clean.services import CryptographicAlgorithmEnum, CryptographyServiceFactory
11
- from fast_clean.utils import typer_async
12
-
13
-
14
- @typer_async
15
- async def encrypt(
16
- data: Annotated[str, typer.Argument(help='Данные для шифровки.')],
17
- algorithm: Annotated[
18
- CryptographicAlgorithmEnum, typer.Option(help='Криптографический алгоритм')
19
- ] = CryptographicAlgorithmEnum.AES_GCM,
20
- ) -> None:
21
- """
22
- Зашифровываем данные.
23
- """
24
- async with get_container() as container:
25
- cryptography_service_factory = await container.get(CryptographyServiceFactory)
26
- cryptography_service = await cryptography_service_factory.make(algorithm)
27
- print(cryptography_service.encrypt(data))
28
-
29
-
30
- @typer_async
31
- async def decrypt(
32
- data: Annotated[str, typer.Argument(help='Данные для расшифровки.')],
33
- algorithm: Annotated[
34
- CryptographicAlgorithmEnum, typer.Option(help='Криптографический алгоритм')
35
- ] = CryptographicAlgorithmEnum.AES_GCM,
36
- ) -> None:
37
- """
38
- Расшифровываем данные.
39
- """
40
- async with get_container() as container:
41
- cryptography_service_factory = await container.get(CryptographyServiceFactory)
42
- cryptography_service = await cryptography_service_factory.make(algorithm)
43
- print(cryptography_service.decrypt(data))
44
-
45
-
46
- def use_cryptography(app: typer.Typer) -> None:
47
- """
48
- Регистрируем команды криптографии для шифрования секретных параметров.
49
- """
50
-
51
- app.command()(encrypt)
52
- app.command()(decrypt)
@@ -1,31 +0,0 @@
1
- """
2
- Модуль, содержащий команды загрузки данных из файлов.
3
- """
4
-
5
- from typing import Annotated
6
-
7
- import typer
8
-
9
- from fast_clean.container import get_container
10
- from fast_clean.services import SeedService
11
- from fast_clean.utils import typer_async
12
-
13
-
14
- @typer_async
15
- async def load_seed(
16
- path: Annotated[str | None, typer.Argument(help='Путь к директории для загрузки данных.')] = None,
17
- ) -> None:
18
- """
19
- Загружаем данные из файлов.
20
- """
21
- async with get_container() as container:
22
- seed_service = await container.get(SeedService)
23
- await seed_service.load_data(path)
24
-
25
-
26
- def use_load_seed(app: typer.Typer) -> None:
27
- """
28
- Регистрируем команды загрузки данных из файлов.
29
- """
30
-
31
- app.command()(load_seed)
@@ -1,38 +0,0 @@
1
- """
2
- Модуль, содержащий варианты использования.
3
- """
4
-
5
- from __future__ import annotations
6
-
7
- from types import new_class
8
- from typing import Any, Protocol, TypeVar, cast
9
-
10
- UseCaseResultSchemaType = TypeVar('UseCaseResultSchemaType', covariant=True)
11
-
12
-
13
- class UseCaseProtocol(Protocol[UseCaseResultSchemaType]):
14
- """
15
- Протокол варианта использования.
16
- """
17
-
18
- async def __call__(self) -> UseCaseResultSchemaType:
19
- """
20
- Вызываем вариант использования.
21
- """
22
- ...
23
-
24
- def __class_getitem__(cls, params: type | tuple[type, ...]) -> type:
25
- """
26
- Создаем уникальный класс.
27
-
28
- По умолчанию одинаковые обобщенные классы указывают на один и тот же _GenericAlias.
29
- Из-за данного поведения поиск по типу становится невозможным.
30
-
31
- UseCaseA = UseCaseProtocol[None]
32
- UseCaseB = UseCaseProtocol[None]
33
- assert UseCaseA is UseCaseB
34
-
35
- Поэтому вместо _GenericAlias возвращаем уникального наследника.
36
- """
37
- generic_alias = cast(Any, super()).__class_getitem__(params)
38
- return new_class(cls.__name__, (generic_alias,))
File without changes
File without changes