add lints

This commit is contained in:
ivankirpichnikov
2025-10-16 23:11:04 +03:00
parent 2ae3323b1e
commit 31d06fc0b4
25 changed files with 56 additions and 52 deletions
@@ -6,7 +6,6 @@ from cryptography.fernet import Fernet
from template_project.application.access_token.cryptographer import AccessTokenCryptographer from template_project.application.access_token.cryptographer import AccessTokenCryptographer
from template_project.application.access_token.entity import AccessTokenId from template_project.application.access_token.entity import AccessTokenId
type RawAccessToken = str type RawAccessToken = str
@@ -1,4 +1,5 @@
from typing import override from typing import override
from template_project.application.access_token.entity import AccessToken from template_project.application.access_token.entity import AccessToken
from template_project.application.access_token.entity_factory import AccessTokenFactory from template_project.application.access_token.entity_factory import AccessTokenFactory
from template_project.application.user.entity import UserId from template_project.application.user.entity import UserId
@@ -2,6 +2,7 @@ from typing import override
from sqlalchemy import select from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from template_project.adapters.data_gateways.tables import access_token_table from template_project.adapters.data_gateways.tables import access_token_table
from template_project.application.access_token.data_gateway import AccessTokenDataGateway from template_project.application.access_token.data_gateway import AccessTokenDataGateway
from template_project.application.access_token.entity import AccessToken, AccessTokenId from template_project.application.access_token.entity import AccessToken, AccessTokenId
@@ -10,7 +10,6 @@ from sqlalchemy.orm import registry
from template_project.application.access_token.entity import AccessToken from template_project.application.access_token.entity import AccessToken
from template_project.application.user.entity import User from template_project.application.user.entity import User
meta_data = MetaData() meta_data = MetaData()
user_table = Table( user_table = Table(
@@ -1,4 +1,5 @@
from typing import override from typing import cast, override
from sqlalchemy import exists, select from sqlalchemy import exists, select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@@ -24,4 +25,4 @@ class DefaultUserDataGateway(UserDataGateway):
result_fetchone = result.fetchone() result_fetchone = result.fetchone()
if result_fetchone is None: if result_fetchone is None:
return False return False
return result_fetchone[0] return cast(bool, result_fetchone[0])
@@ -1,4 +1,3 @@
from abc import abstractmethod
from typing import override from typing import override
import argon2 import argon2
@@ -27,7 +26,7 @@ class ArgonPasswordVerifying(PasswordVerifying):
) -> None: ) -> None:
self._password_hasher = password_hasher self._password_hasher = password_hasher
@abstractmethod @override
def verify( def verify(
self, self,
verifiable_password: SecretString, verifiable_password: SecretString,
@@ -35,6 +34,5 @@ class ArgonPasswordVerifying(PasswordVerifying):
) -> bool: ) -> bool:
try: try:
return self._password_hasher.verify(hashed_password, verifiable_password.get_value()) return self._password_hasher.verify(hashed_password, verifiable_password.get_value())
except Argon2Error as e: except Argon2Error:
return False return False
@@ -3,7 +3,6 @@ from typing import Protocol
from template_project.application.access_token.entity import AccessTokenId from template_project.application.access_token.entity import AccessTokenId
type RawAccessToken = str type RawAccessToken = str
@@ -10,6 +10,7 @@ from template_project.application.user.entity import UserId
AccessTokenId = NewType("AccessTokenId", UUID) AccessTokenId = NewType("AccessTokenId", UUID)
@to_entity @to_entity
class AccessToken(Entity[AccessTokenId]): class AccessToken(Entity[AccessTokenId]):
user_id: UserId user_id: UserId
@@ -32,17 +33,12 @@ class AccessToken(Entity[AccessTokenId]):
revoked=False, revoked=False,
) )
def ensure_expired(self) -> None: def ensure_expired(self) -> None:
if self.expired_predicate(): if self.expired_predicate():
raise AccessTokenExpiredError(id_=self.id) raise AccessTokenExpiredError(id_=self.id)
def expired_predicate(self) -> bool: def expired_predicate(self) -> bool:
return ( return (self.expires_in < datetime.now(tz=UTC)) or self.revoked or self.deleted_at is not None
(self.expires_in < datetime.now(tz=UTC))
or self.revoked
or self.deleted_at is not None
)
def revoke(self) -> None: def revoke(self) -> None:
self.revoked = True self.revoked = True
@@ -7,7 +7,7 @@ from typing import dataclass_transform
eq_default=False, eq_default=False,
kw_only_default=True, kw_only_default=True,
) )
def to_data_structure[_InteractorClsT](interactor_cls: type[_InteractorClsT]) -> type[_InteractorClsT]: def to_data_structure[InteractorClsT](interactor_cls: type[InteractorClsT]) -> type[InteractorClsT]:
return dataclass( return dataclass(
kw_only=True, kw_only=True,
eq=False, eq=False,
@@ -1,20 +1,20 @@
from collections.abc import Hashable from collections.abc import Hashable
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime from datetime import datetime
from typing import dataclass_transform, override from typing import cast, dataclass_transform, override
from uuid import UUID from uuid import UUID
from template_project.application.common.errors import EntityAlreadyDeletedError from template_project.application.common.errors import EntityAlreadyDeletedError
@dataclass_transform(kw_only_default=True) @dataclass_transform(kw_only_default=True)
def to_entity[_EntityCLsT](entity_cls: type[_EntityCLsT]) -> type[_EntityCLsT]: def to_entity[EntityCLsT](entity_cls: type[EntityCLsT]) -> type[EntityCLsT]:
return dataclass(kw_only=True)(entity_cls) return dataclass(kw_only=True)(entity_cls)
@to_entity @to_entity
class Entity[_EntityId: UUID](Hashable): class Entity[EntityId: UUID](Hashable):
id: _EntityId id: EntityId
created_at: datetime created_at: datetime
deleted_at: datetime | None = None deleted_at: datetime | None = None
@@ -25,7 +25,7 @@ class Entity[_EntityId: UUID](Hashable):
@override @override
def __eq__(self, other: object) -> bool: def __eq__(self, other: object) -> bool:
if isinstance(other, Entity): if isinstance(other, Entity):
return self.id == other.id return cast(bool, self.id == other.id)
return NotImplemented return NotImplemented
@override @override
@@ -7,7 +7,7 @@ from typing import dataclass_transform
eq_default=False, eq_default=False,
kw_only_default=True, kw_only_default=True,
) )
def to_interactor[_InteractorClsT](interactor_cls: type[_InteractorClsT]) -> type[_InteractorClsT]: def to_interactor[InteractorClsT](interactor_cls: type[InteractorClsT]) -> type[InteractorClsT]:
return dataclass( return dataclass(
kw_only=True, kw_only=True,
eq=False, eq=False,
@@ -10,8 +10,5 @@ class UserDataGateway(Protocol):
raise NotImplementedError raise NotImplementedError
@abstractmethod @abstractmethod
async def exists_by_email( async def exists_by_email(self, email: str) -> bool:
self,
email: str
) -> bool:
raise NotImplementedError raise NotImplementedError
@@ -8,6 +8,7 @@ from template_project.application.common.entity import Entity, to_entity
UserId = NewType("UserId", UUID) UserId = NewType("UserId", UUID)
@to_entity @to_entity
class User(Entity[UserId]): class User(Entity[UserId]):
email: str email: str
@@ -1,4 +1,5 @@
from typing import override from typing import override
from template_project.application.common.errors import ApplicationError, to_error from template_project.application.common.errors import ApplicationError, to_error
@@ -10,6 +11,7 @@ class UserWithEmailAlreadyExistsError(ApplicationError):
def __str__(self) -> str: def __str__(self) -> str:
return f"User with the email={self.email!r} already exists" return f"User with the email={self.email!r} already exists"
@to_error @to_error
class UserUnauthorizedError(ApplicationError): class UserUnauthorizedError(ApplicationError):
pass pass
@@ -11,6 +11,7 @@ class GetMeResponse:
id: UserId id: UserId
email: str email: str
response_converter = get_converter(User, GetMeResponse) response_converter = get_converter(User, GetMeResponse)
@@ -3,6 +3,7 @@ from datetime import timedelta
from pathlib import Path from pathlib import Path
from tomllib import loads from tomllib import loads
from typing import dataclass_transform from typing import dataclass_transform
from adaptix import P, Retort, loader from adaptix import P, Retort, loader
from template_project.application.common.containers import SecretString from template_project.application.common.containers import SecretString
+8 -3
View File
@@ -1,16 +1,16 @@
import argparse import argparse
import asyncio import asyncio
import sys
from collections.abc import AsyncIterator from collections.abc import AsyncIterator
from contextlib import asynccontextmanager from contextlib import asynccontextmanager
from pathlib import Path from pathlib import Path
import sys
from typing import Final from typing import Final
import uvicorn
from dishka import AsyncContainer from dishka import AsyncContainer
from dishka.integrations.fastapi import setup_dishka from dishka.integrations.fastapi import setup_dishka
from fastapi import FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
import uvicorn
from template_project.web_api.configuration import load_configuration from template_project.web_api.configuration import load_configuration
from template_project.web_api.ioc.make import make_ioc from template_project.web_api.ioc.make import make_ioc
@@ -35,11 +35,13 @@ LOG_CONFIG: Final = {
}, },
} }
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]: async def lifespan(app: FastAPI) -> AsyncIterator[None]:
yield yield
await app.state.dishka_container.close() await app.state.dishka_container.close()
def make_asgi_application( def make_asgi_application(
ioc: AsyncContainer, ioc: AsyncContainer,
) -> FastAPI: ) -> FastAPI:
@@ -65,6 +67,7 @@ def make_asgi_application(
return app return app
def _main( def _main(
configuration_path: Path, configuration_path: Path,
) -> None: ) -> None:
@@ -83,7 +86,8 @@ def _main(
def main() -> None: def main() -> None:
if sys.platform == "win32": if sys.platform == "win32":
from asyncio import WindowsSelectorEventLoopPolicy from asyncio import WindowsSelectorEventLoopPolicy # noqa: PLC0415
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy()) asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
arg_parser = argparse.ArgumentParser() arg_parser = argparse.ArgumentParser()
@@ -95,5 +99,6 @@ def main() -> None:
args = arg_parser.parse_args() args = arg_parser.parse_args()
_main(args.configuration) _main(args.configuration)
if __name__ == "__main__": if __name__ == "__main__":
main() main()
@@ -1,4 +1,4 @@
from abc import abstractmethod from typing import override
from fastapi import Request from fastapi import Request
@@ -11,8 +11,7 @@ from template_project.application.user.data_gateway import UserDataGateway
from template_project.application.user.entity import User from template_project.application.user.entity import User
from template_project.application.user.errors import UserUnauthorizedError from template_project.application.user.errors import UserUnauthorizedError
TOKEN_TYPE = "Bearer" # noqa: S105
TOKEN_TYPE = "Bearer"
BEARER_SECTIONS = 2 BEARER_SECTIONS = 2
AUTH_HEADER = "Authorization" AUTH_HEADER = "Authorization"
@@ -31,7 +30,7 @@ class WebApiIdentityProvider(IdentityProvider):
self._access_token_data_gateway = access_token_data_gateway self._access_token_data_gateway = access_token_data_gateway
self._access_token_cryptographer = access_token_cryptographer self._access_token_cryptographer = access_token_cryptographer
@abstractmethod @override
async def get_current_user(self) -> User: async def get_current_user(self) -> User:
auth_tokn = self._request.headers[AUTH_HEADER] auth_tokn = self._request.headers[AUTH_HEADER]
@@ -1,4 +1,5 @@
from typing import AsyncIterable from collections.abc import AsyncIterable
from dishka import Provider, Scope, provide from dishka import Provider, Scope, provide
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
@@ -1,6 +1,6 @@
import argon2 import argon2
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
from dishka import Provider, Scope, WithParents, provide, provide_all from dishka import BaseScope, Provider, Scope, WithParents, provide, provide_all
from template_project.adapters.access_token.cryptographer import FernetAccessTokenCryptographer from template_project.adapters.access_token.cryptographer import FernetAccessTokenCryptographer
from template_project.adapters.password_utils import ArgonPasswordHasher, ArgonPasswordVerifying from template_project.adapters.password_utils import ArgonPasswordHasher, ArgonPasswordVerifying
@@ -8,7 +8,7 @@ from template_project.web_api.configuration import AccessTokenConfiguration
class CryptographerProvider(Provider): class CryptographerProvider(Provider):
scope = Scope.APP scope: BaseScope | None = Scope.APP
@provide @provide
def argon_password_hasher(self) -> argon2.PasswordHasher: def argon_password_hasher(self) -> argon2.PasswordHasher:
@@ -1,4 +1,4 @@
from dishka import Provider, Scope, WithParents, provide, provide_all from dishka import BaseScope, Provider, Scope, WithParents, provide, provide_all
from template_project.adapters.data_gateways.access_token import DefaultAccessTokenDataGateway from template_project.adapters.data_gateways.access_token import DefaultAccessTokenDataGateway
from template_project.adapters.data_gateways.user import DefaultUserDataGateway from template_project.adapters.data_gateways.user import DefaultUserDataGateway
@@ -6,7 +6,7 @@ from template_project.adapters.unit_of_work import DefaultUnitOfWork
class DataGatewayProvider(Provider): class DataGatewayProvider(Provider):
scope = Scope.REQUEST scope: BaseScope | None = Scope.REQUEST
unit_of_work = provide(WithParents[DefaultUnitOfWork]) unit_of_work = provide(WithParents[DefaultUnitOfWork])
data_gateways = provide_all( data_gateways = provide_all(
+2 -2
View File
@@ -1,10 +1,10 @@
from dishka import Provider, Scope, WithParents, provide_all from dishka import BaseScope, Provider, Scope, WithParents, provide_all
from template_project.adapters.access_token.factory import DefaultAccessTokenFactory from template_project.adapters.access_token.factory import DefaultAccessTokenFactory
class FactoryProvider(Provider): class FactoryProvider(Provider):
scope = Scope.APP scope: BaseScope | None = Scope.APP
provides = provide_all( provides = provide_all(
WithParents[DefaultAccessTokenFactory], WithParents[DefaultAccessTokenFactory],
@@ -1,10 +1,10 @@
from dishka import Provider, Scope, provide_all from dishka import BaseScope, Provider, Scope, provide_all
from template_project.application.user.interactors.sign_up import UserSignUpInteractor from template_project.application.user.interactors.sign_up import UserSignUpInteractor
class InteractorProvider(Provider): class InteractorProvider(Provider):
scope = Scope.REQUEST scope: BaseScope | None = Scope.REQUEST
interactors = provide_all( interactors = provide_all(
UserSignUpInteractor, UserSignUpInteractor,
+7 -2
View File
@@ -1,11 +1,16 @@
from dishka import AsyncContainer, make_async_container from dishka import AsyncContainer, make_async_container
from dishka.integrations.fastapi import FastapiProvider from dishka.integrations.fastapi import FastapiProvider
from template_project.web_api.configuration import AccessTokenConfiguration, Configuration, DatabaseConfiguration, ServerConfiguration from template_project.web_api.configuration import (
AccessTokenConfiguration,
Configuration,
DatabaseConfiguration,
ServerConfiguration,
)
from template_project.web_api.ioc.cryptographer import CryptographerProvider
from template_project.web_api.ioc.data_gateway import DataGatewayProvider from template_project.web_api.ioc.data_gateway import DataGatewayProvider
from template_project.web_api.ioc.factory import FactoryProvider from template_project.web_api.ioc.factory import FactoryProvider
from template_project.web_api.ioc.interactor import InteractorProvider from template_project.web_api.ioc.interactor import InteractorProvider
from template_project.web_api.ioc.cryptographer import CryptographerProvider
def make_ioc(configuration: Configuration) -> AsyncContainer: def make_ioc(configuration: Configuration) -> AsyncContainer:
+1 -3
View File
@@ -6,7 +6,6 @@ from pydantic import BaseModel, SecretStr
from template_project.application.common.containers import SecretString from template_project.application.common.containers import SecretString
from template_project.application.user.interactors.sign_up import UserSignUpInteractor from template_project.application.user.interactors.sign_up import UserSignUpInteractor
router = APIRouter(route_class=DishkaRoute) router = APIRouter(route_class=DishkaRoute)
@@ -25,8 +24,7 @@ async def sign_up(
interactor: FromDishka[UserSignUpInteractor], interactor: FromDishka[UserSignUpInteractor],
) -> UserSignUpResponse: ) -> UserSignUpResponse:
response_interactor = await interactor.execute( response_interactor = await interactor.execute(
email=request.email, email=request.email, password=SecretString(request.password.get_secret_value())
password=SecretString(request.password.get_secret_value())
) )
return UserSignUpResponse( return UserSignUpResponse(
access_token=response_interactor.access_token, access_token=response_interactor.access_token,