Merge pull request #197 from grillazz/195-switch-project-to-uv

This commit is contained in:
Ordinary Hobbit 2025-03-08 20:10:04 +01:00 committed by GitHub
commit 3f4f1efb35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 750 additions and 288 deletions

View File

@ -54,6 +54,10 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- name: Lint with ruff
run: uv run --frozen ruff check .
- name: Test with python ${{ matrix.python-version }}
run: uv run --frozen pytest

View File

@ -39,7 +39,7 @@ safety: ## Check project and dependencies with safety https://github.com/pyupio/
.PHONY: py-upgrade
py-upgrade: ## Upgrade project py files with pyupgrade library for python version 3.10
pyupgrade --py312-plus `find app -name "*.py"`
pyupgrade --py313-plus `find app -name "*.py"`
.PHONY: lint
lint: ## Lint project code.

View File

@ -1,12 +1,11 @@
import logging
from typing import Annotated
from fastapi import APIRouter, status, Request, Depends, Query
from fastapi import APIRouter, Depends, Query, Request, status
from pydantic import EmailStr
from starlette.concurrency import run_in_threadpool
from app.services.smtp import SMTPEmailService
from app.utils.logging import AppLogger
logger = AppLogger().get_logger()

View File

@ -1,7 +1,8 @@
import io
from fastapi import APIRouter, Depends, status, UploadFile, HTTPException
from sqlalchemy.exc import SQLAlchemyError
import polars as pl
from fastapi import APIRouter, Depends, HTTPException, UploadFile, status
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db

View File

@ -3,8 +3,6 @@ from typing import Annotated
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi_cache.decorator import cache
from app.database import get_db
from app.models.shakespeare import Paragraph
@ -14,7 +12,6 @@ router = APIRouter(prefix="/v1/shakespeare")
@router.get(
"/",
)
@cache(namespace="test-2", expire=60)
async def find_paragraph(
character: Annotated[str, Query(description="Character name")],
db_session: AsyncSession = Depends(get_db),

View File

@ -1,4 +1,4 @@
from fastapi import APIRouter, Depends, HTTPException, status, Request
from fastapi import APIRouter, Depends, HTTPException, Request, status
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession

View File

@ -1,11 +1,11 @@
from typing import Annotated
from fastapi import APIRouter, Depends, status, Request, HTTPException, Form
from fastapi import APIRouter, Depends, Form, HTTPException, Request, status
from sqlalchemy.ext.asyncio import AsyncSession
from app.database import get_db
from app.models.user import User
from app.schemas.user import UserSchema, UserResponse, UserLogin, TokenResponse
from app.schemas.user import TokenResponse, UserLogin, UserResponse, UserSchema
from app.services.auth import create_access_token
from app.utils.logging import AppLogger

View File

@ -1,6 +1,6 @@
import os
from pydantic import PostgresDsn, RedisDsn, computed_field, BaseModel
from pydantic import BaseModel, PostgresDsn, RedisDsn, computed_field
from pydantic_core import MultiHostUrl
from pydantic_settings import BaseSettings, SettingsConfigDict

View File

@ -1,7 +1,6 @@
from collections.abc import AsyncGenerator
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.ext.asyncio import async_sessionmaker
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from app.config import settings as global_settings
from app.utils.logging import AppLogger

View File

@ -1,25 +1,22 @@
import asyncpg
from apscheduler.eventbrokers.redis import RedisEventBroker
from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
from fastapi import FastAPI, Depends
from fastapi_cache import FastAPICache
from fastapi_cache.backends.redis import RedisBackend
from contextlib import asynccontextmanager
import asyncpg
from apscheduler import AsyncScheduler
from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
from apscheduler.eventbrokers.redis import RedisEventBroker
from fastapi import Depends, FastAPI
from app.api.health import router as health_router
from app.api.nonsense import router as nonsense_router
from app.api.shakespeare import router as shakespeare_router
from app.api.stuff import router as stuff_router
from app.api.user import router as user_router
from app.config import settings as global_settings
from app.database import engine
from app.utils.logging import AppLogger
from app.api.user import router as user_router
from app.api.health import router as health_router
from app.redis import get_redis, get_cache
from app.redis import get_redis
from app.services.auth import AuthBearer
from app.services.scheduler import SchedulerMiddleware
from contextlib import asynccontextmanager
from apscheduler import AsyncScheduler
from app.utils.logging import AppLogger
logger = AppLogger().get_logger()
@ -32,10 +29,7 @@ async def lifespan(_app: FastAPI):
_postgres_dsn = global_settings.postgres_url.unicode_string()
try:
# Initialize the cache with the redis connection
redis_cache = await get_cache()
FastAPICache.init(RedisBackend(redis_cache), prefix="fastapi-cache")
# logger.info(FastAPICache.get_cache_status_header())
# TODO: cache with the redis connection
# Initialize the postgres connection pool
_app.postgres_pool = await asyncpg.create_pool(
dsn=_postgres_dsn,

View File

@ -2,9 +2,10 @@ from typing import Any
from asyncpg import UniqueViolationError
from fastapi import HTTPException, status
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import declared_attr, DeclarativeBase
from sqlalchemy.orm import DeclarativeBase, declared_attr
from app.utils.logging import AppLogger
logger = AppLogger().get_logger()

View File

@ -4,7 +4,7 @@ from fastapi import HTTPException, status
from sqlalchemy import String, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import mapped_column, Mapped
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base

View File

@ -11,6 +11,7 @@ from sqlalchemy import (
)
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.models.base import Base

View File

@ -1,9 +1,9 @@
import uuid
from sqlalchemy import String, select, ForeignKey
from sqlalchemy import ForeignKey, String, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import mapped_column, Mapped, relationship, joinedload
from sqlalchemy.orm import Mapped, joinedload, mapped_column, relationship
from app.models.base import Base
from app.models.nonsense import Nonsense

View File

@ -3,10 +3,10 @@ from typing import Any
import bcrypt
from pydantic import SecretStr
from sqlalchemy import String, LargeBinary, select, Column
from sqlalchemy import Column, LargeBinary, String, select
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import mapped_column, Mapped
from sqlalchemy.orm import Mapped, mapped_column
from app.models.base import Base

View File

@ -1,6 +1,6 @@
from uuid import UUID
from pydantic import BaseModel, Field, ConfigDict
from pydantic import BaseModel, ConfigDict, Field
config = ConfigDict(from_attributes=True)

View File

@ -1,6 +1,6 @@
from uuid import UUID
from pydantic import BaseModel, Field, ConfigDict
from pydantic import BaseModel, ConfigDict, Field
config = ConfigDict(from_attributes=True)

View File

@ -1,6 +1,6 @@
from uuid import UUID
from pydantic import BaseModel, Field, EmailStr, ConfigDict, SecretStr
from pydantic import BaseModel, ConfigDict, EmailStr, Field, SecretStr
config = ConfigDict(from_attributes=True)

View File

@ -1,11 +1,11 @@
import time
import jwt
from fastapi import HTTPException, Request
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from app.config import settings as global_settings
from app.models.user import User
from fastapi import Request, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from app.utils.logging import AppLogger
logger = AppLogger().get_logger()

View File

@ -1,11 +1,10 @@
from datetime import datetime
from attrs import define
from sqlalchemy import text
from starlette.types import ASGIApp, Receive, Scope, Send
from apscheduler import AsyncScheduler
from apscheduler.triggers.interval import IntervalTrigger
from attrs import define
from sqlalchemy import text
from starlette.types import ASGIApp, Receive, Scope, Send
from app.database import AsyncSessionFactory
from app.utils.logging import AppLogger

View File

@ -1,18 +1,15 @@
from attrs import define, field
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from app.config import settings as global_settings
from attrs import define, field
from fastapi.templating import Jinja2Templates
from pydantic import EmailStr
from app.config import settings as global_settings
from app.utils.logging import AppLogger
from app.utils.singleton import SingletonMetaNoArgs
logger = AppLogger().get_logger()

View File

@ -1,7 +1,7 @@
from sqlalchemy.dialects import postgresql
from functools import wraps
from sqlalchemy.dialects import postgresql
def compile_sql_or_scalar(func):
"""

View File

@ -3,7 +3,6 @@ import logging
from rich.console import Console
from rich.logging import RichHandler
from app.utils.singleton import SingletonMeta
@ -21,5 +20,5 @@ class RichConsoleHandler(RichHandler):
def __init__(self, width=200, style=None, **kwargs):
super().__init__(
console=Console(color_system="256", width=width, style=style, stderr=True),
**kwargs
**kwargs,
)

View File

@ -1,4 +1,4 @@
from locust import HttpUser, task, between
from locust import HttpUser, between, task
class Stuff(HttpUser):

View File

@ -5,30 +5,71 @@ description = "A modern FastAPI application with SQLAlchemy 2.0 and AsyncPG for
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"fastapi[all]>=0.115.6",
"pydantic[email]>=2.10.3",
"pydantic-settings>=2.7.0",
"sqlalchemy>=2.0.36",
"fastapi[all]>=0.115.11",
"pydantic[email]>=2.10.6",
"pydantic-settings>=2.8.1",
"sqlalchemy>=2.0.38",
"uvicorn[standard]>=0.34.0",
"asyncpg>=0.30.0",
"alembic>=1.14.0",
"alembic>=1.15.1",
"httpx>=0.28.1",
"pytest>=8.3.4",
"pytest>=8.3.5",
"pytest-cov>=6.0.0",
"uvloop>=0.21.0",
"httptools>=0.6.4",
"rich>=13.9.4",
"pyjwt[cryptography]>=2.10.1",
"pyjwt>=2.10.1",
"redis>=5.2.1",
"bcrypt>=4.2.1",
"polars>=1.17.1",
"bcrypt>=4.3.0",
"polars>=1.24.0",
"python-multipart>=0.0.20",
"fastexcel>=0.12.0",
"fastapi-cache2>=0.2.1",
"fastexcel>=0.13.0",
"inline-snapshot>=0.17.0",
"dirty-equals>=0.8.0",
"polyfactory>=2.18.1",
"granian>=1.7.0",
"apscheduler[redis,sqlalchemy]>=4.0.0a5",
"pendulum @ git+https://github.com/sdispater/pendulum.git@develop"
]
]
[tool.uv]
dev-dependencies = [
"ruff>=0.9.10",
"devtools[pygments]>=0.12.2",
"pyupgrade>=3.19.1",
"ipython>=9.0.2",
"sqlacodegen>=3.0.0",
"tryceratops>=2.4.1",
"locust>=2.33.0"
]
[tool.mypy]
strict = true
exclude = ["venv", ".venv", "alembic"]
[tool.ruff]
target-version = "py313"
exclude = ["alembic"]
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # pyflakes
"I", # isort
"B", # flake8-bugbear
"C4", # flake8-comprehensions
"UP", # pyupgrade
"ARG001", # unused arguments in functions
]
ignore = [
"E501", # line too long, handled by black
"B008", # do not perform function calls in argument defaults
"W191", # indentation contains tabs
"B904", # Allow raising exceptions without from e, for HTTPException
]
[tool.ruff.lint.pyupgrade]
# Preserve types, even if a file imports `from __future__ import annotations`.
keep-runtime-typing = true

View File

@ -1,9 +1,9 @@
import pytest
from httpx import AsyncClient
from starlette import status
import jwt
import pytest
from dirty_equals import IsPositiveFloat, IsStr, IsUUID
from httpx import AsyncClient
from inline_snapshot import snapshot
from dirty_equals import IsStr, IsUUID, IsPositiveFloat
from starlette import status
pytestmark = pytest.mark.anyio

View File

@ -1,6 +1,5 @@
from anyio import Path
import pytest
from anyio import Path
from fastapi import status
from httpx import AsyncClient

View File

@ -1,9 +1,8 @@
import pytest
from dirty_equals import IsUUID
from fastapi import status
from httpx import AsyncClient
from inline_snapshot import snapshot
from dirty_equals import IsUUID
from polyfactory.factories.pydantic_factory import ModelFactory
from app.schemas.stuff import StuffSchema

View File

@ -1,5 +1,8 @@
from collections.abc import AsyncGenerator
from typing import Any
import pytest
from httpx import AsyncClient, ASGITransport
from httpx import ASGITransport, AsyncClient
from app.database import engine
from app.main import app
@ -28,13 +31,11 @@ async def start_db():
@pytest.fixture(scope="session")
async def client(start_db) -> AsyncClient:
async def client(start_db) -> AsyncGenerator[AsyncClient, Any]: # noqa: ARG001
transport = ASGITransport(
app=app,
)
async with AsyncClient(
# app=app,
base_url="http://testserver/v1",
headers={"Content-Type": "application/json"},
transport=transport,

839
uv.lock generated

File diff suppressed because it is too large Load Diff