style: apply ruff formatting to source and test files
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/type Pipeline was successful

This commit is contained in:
2026-05-02 12:05:14 +03:00
parent 1dbedf0f52
commit 14adcaa3e6
16 changed files with 50 additions and 95 deletions
+1 -3
View File
@@ -27,9 +27,7 @@ class CreatePostUseCase:
# Check if slug already exists
if await self._post_repo.slug_exists(slug.value):
raise AlreadyExistsException(
f"Post with slug '{slug.value}' already exists"
)
raise AlreadyExistsException(f"Post with slug '{slug.value}' already exists")
# Create domain entity
post = Post.create(
+1 -3
View File
@@ -38,9 +38,7 @@ class ListPostsUseCase:
offset: int | None = None,
) -> list[PostResponseDTO]:
"""Get posts by author."""
posts = await self._post_repo.get_by_author(
author_id, limit=limit, offset=offset
)
posts = await self._post_repo.get_by_author(author_id, limit=limit, offset=offset)
return [self._map_to_dto(post) for post in posts]
async def by_tag(
+4 -4
View File
@@ -2,7 +2,7 @@
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from datetime import datetime, timezone
from datetime import UTC, datetime
from typing import Any
from uuid import UUID, uuid4
@@ -12,8 +12,8 @@ class BaseEntity(ABC):
"""Base class for all domain entities."""
id: UUID = field(default_factory=uuid4)
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
updated_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
updated_at: datetime = field(default_factory=lambda: datetime.now(UTC))
def __eq__(self, other: object) -> bool:
if not isinstance(other, BaseEntity):
@@ -25,7 +25,7 @@ class BaseEntity(ABC):
def touch(self) -> None:
"""Update the updated_at timestamp."""
self.updated_at = datetime.now(timezone.utc)
self.updated_at = datetime.now(UTC)
@abstractmethod
def to_dict(self) -> dict[str, Any]:
+2 -1
View File
@@ -1,8 +1,9 @@
"""Role-based access control definitions."""
from collections.abc import Callable
from enum import Enum
from functools import wraps
from typing import Any, Callable
from typing import Any
from app.domain.exceptions import ForbiddenException
+1 -3
View File
@@ -19,9 +19,7 @@ class Slug(ValueObject[str]):
if len(self.value) > self.MAX_LENGTH:
raise ValueError(f"Slug must be at most {self.MAX_LENGTH} characters")
if not re.match(self.SLUG_PATTERN, self.value):
raise ValueError(
"Slug must contain only lowercase letters, numbers, and hyphens"
)
raise ValueError("Slug must contain only lowercase letters, numbers, and hyphens")
@classmethod
def from_title(cls, title: str) -> "Slug":
+3 -3
View File
@@ -1,7 +1,7 @@
"""Database connection and session management."""
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from typing import AsyncGenerator
from sqlalchemy.ext.asyncio import (
AsyncEngine,
@@ -38,7 +38,7 @@ AsyncSessionLocal = async_sessionmaker(
)
async def get_session() -> AsyncGenerator[AsyncSession, None]:
async def get_session() -> AsyncGenerator[AsyncSession]:
"""Get database session."""
async with AsyncSessionLocal() as session:
try:
@@ -48,7 +48,7 @@ async def get_session() -> AsyncGenerator[AsyncSession, None]:
@asynccontextmanager
async def get_session_context() -> AsyncGenerator[AsyncSession, None]:
async def get_session_context() -> AsyncGenerator[AsyncSession]:
"""Get database session as context manager."""
async with AsyncSessionLocal() as session:
try:
+7 -13
View File
@@ -1,6 +1,6 @@
"""SQLAlchemy ORM models."""
from datetime import datetime, timezone
from datetime import UTC, datetime
from uuid import uuid4
from sqlalchemy import JSON, Boolean, DateTime, String, Text
@@ -14,27 +14,21 @@ class PostORM(Base): # type: ignore[valid-type,misc]
__tablename__ = "posts"
id: Mapped[str] = mapped_column(
String(36), primary_key=True, default=lambda: str(uuid4())
)
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=lambda: str(uuid4()))
title: Mapped[str] = mapped_column(String(200), nullable=False)
content: Mapped[str] = mapped_column(Text, nullable=False)
slug: Mapped[str] = mapped_column(
String(200), nullable=False, unique=True, index=True
)
slug: Mapped[str] = mapped_column(String(200), nullable=False, unique=True, index=True)
author_id: Mapped[str] = mapped_column(String(100), nullable=False, index=True)
published: Mapped[bool] = mapped_column(
Boolean, default=False, nullable=False, index=True
)
published: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False, index=True)
tags: Mapped[list[str]] = mapped_column(JSON, default=list)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
default=lambda: datetime.now(UTC),
nullable=False,
)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
default=lambda: datetime.now(timezone.utc),
onupdate=lambda: datetime.now(timezone.utc),
default=lambda: datetime.now(UTC),
onupdate=lambda: datetime.now(UTC),
nullable=False,
)
+2 -2
View File
@@ -1,6 +1,6 @@
"""Dishka providers for dependency injection."""
from typing import AsyncGenerator
from collections.abc import AsyncGenerator
from dishka import Provider, Scope, provide
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession
@@ -30,7 +30,7 @@ class DatabaseProvider(Provider):
return engine
@provide(scope=Scope.REQUEST)
async def get_session(self) -> AsyncGenerator[AsyncSession, None]:
async def get_session(self) -> AsyncGenerator[AsyncSession]:
"""Provide database session per request."""
async with AsyncSessionLocal() as session:
try:
+6 -10
View File
@@ -1,6 +1,6 @@
"""Exception handling middleware."""
from datetime import datetime, timezone
from datetime import UTC, datetime
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
@@ -33,9 +33,7 @@ def get_status_code(exc: DomainException) -> int:
return 500
async def domain_exception_handler(
request: Request, exc: DomainException
) -> JSONResponse:
async def domain_exception_handler(request: Request, exc: DomainException) -> JSONResponse:
"""Handle domain exceptions."""
status_code = get_status_code(exc)
return JSONResponse(
@@ -43,22 +41,20 @@ async def domain_exception_handler(
content={
"error": exc.__class__.__name__,
"message": exc.message,
"timestamp": datetime.now(timezone.utc).isoformat(),
"timestamp": datetime.now(UTC).isoformat(),
"path": str(request.url.path),
},
)
async def http_exception_handler(
request: Request, exc: StarletteHTTPException
) -> JSONResponse:
async def http_exception_handler(request: Request, exc: StarletteHTTPException) -> JSONResponse:
"""Handle HTTP exceptions."""
return JSONResponse(
status_code=exc.status_code,
content={
"error": "HTTPException",
"message": str(exc.detail),
"timestamp": datetime.now(timezone.utc).isoformat(),
"timestamp": datetime.now(UTC).isoformat(),
"path": str(request.url.path),
},
)
@@ -71,7 +67,7 @@ async def generic_exception_handler(request: Request, exc: Exception) -> JSONRes
content={
"error": "InternalServerError",
"message": "An unexpected error occurred",
"timestamp": datetime.now(timezone.utc).isoformat(),
"timestamp": datetime.now(UTC).isoformat(),
"path": str(request.url.path),
},
)
+6 -18
View File
@@ -47,9 +47,7 @@ class SQLAlchemyPostRepository(PostRepository):
async def get_by_id(self, entity_id: UUID) -> Post | None:
"""Get post by ID."""
result = await self._session.execute(
select(PostORM).where(PostORM.id == str(entity_id))
)
result = await self._session.execute(select(PostORM).where(PostORM.id == str(entity_id)))
orm = result.scalar_one_or_none()
return self._to_domain(orm) if orm else None
@@ -67,9 +65,7 @@ class SQLAlchemyPostRepository(PostRepository):
async def update(self, entity: Post) -> None:
"""Update existing post."""
result = await self._session.execute(
select(PostORM).where(PostORM.id == str(entity.id))
)
result = await self._session.execute(select(PostORM).where(PostORM.id == str(entity.id)))
orm = result.scalar_one()
orm.title = entity.title.value
@@ -83,25 +79,19 @@ class SQLAlchemyPostRepository(PostRepository):
async def delete(self, entity_id: UUID) -> None:
"""Delete post by ID."""
result = await self._session.execute(
select(PostORM).where(PostORM.id == str(entity_id))
)
result = await self._session.execute(select(PostORM).where(PostORM.id == str(entity_id)))
orm = result.scalar_one_or_none()
if orm:
await self._session.delete(orm)
async def exists(self, entity_id: UUID) -> bool:
"""Check if post exists."""
result = await self._session.execute(
select(PostORM).where(PostORM.id == str(entity_id))
)
result = await self._session.execute(select(PostORM).where(PostORM.id == str(entity_id)))
return result.scalar_one_or_none() is not None
async def get_by_slug(self, slug: str) -> Post | None:
"""Get post by slug."""
result = await self._session.execute(
select(PostORM).where(PostORM.slug == slug)
)
result = await self._session.execute(select(PostORM).where(PostORM.slug == slug))
orm = result.scalar_one_or_none()
return self._to_domain(orm) if orm else None
@@ -154,9 +144,7 @@ class SQLAlchemyPostRepository(PostRepository):
async def slug_exists(self, slug: str) -> bool:
"""Check if slug exists."""
result = await self._session.execute(
select(PostORM).where(PostORM.slug == slug)
)
result = await self._session.execute(select(PostORM).where(PostORM.slug == slug))
return result.scalar_one_or_none() is not None
async def search(
+2 -2
View File
@@ -1,7 +1,7 @@
"""Application entry point with DDD architecture."""
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from typing import AsyncGenerator
import uvicorn
from dishka import make_async_container
@@ -21,7 +21,7 @@ from app.presentation import router
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
async def lifespan(app: FastAPI) -> AsyncGenerator[None]:
"""Application lifespan manager."""
# Startup
await init_db()