mirror of
https://github.com/grillazz/fastapi-sqlalchemy-asyncpg.git
synced 2026-01-17 11:40:39 +03:00
Merge remote-tracking branch 'origin/switch-logger-to-rotoger' into switch-logger-to-rotoger
# Conflicts: # pyproject.toml # uv.lock
This commit is contained in:
2
.github/workflows/build-and-test.yml
vendored
2
.github/workflows/build-and-test.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: [ "3.13" ]
|
||||
python-version: [ "3.14" ]
|
||||
|
||||
env:
|
||||
PYTHONDONTWRITEBYTECODE: 1
|
||||
|
||||
37
Dockerfile
37
Dockerfile
@@ -1,32 +1,28 @@
|
||||
FROM ubuntu:oracular AS build
|
||||
FROM ubuntu:25.10 AS base
|
||||
|
||||
RUN apt-get update -qy && apt-get install -qyy \
|
||||
RUN apt-get update -qy \
|
||||
&& apt-get install -qyy \
|
||||
-o APT::Install-Recommends=false \
|
||||
-o APT::Install-Suggests=false \
|
||||
build-essential \
|
||||
ca-certificates \
|
||||
python3-setuptools \
|
||||
python3.13-dev \
|
||||
git
|
||||
python3.14-dev
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
|
||||
|
||||
ENV UV_LINK_MODE=copy \
|
||||
UV_COMPILE_BYTECODE=1 \
|
||||
UV_PYTHON_DOWNLOADS=never \
|
||||
UV_PYTHON=python3.13 \
|
||||
UV_PYTHON=python3.14 \
|
||||
UV_PROJECT_ENVIRONMENT=/panettone
|
||||
|
||||
COPY pyproject.toml /_lock/
|
||||
COPY uv.lock /_lock/
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache
|
||||
RUN cd /_lock && uv sync \
|
||||
--locked \
|
||||
--no-dev \
|
||||
--no-install-project
|
||||
RUN cd /_lock && uv sync --locked --no-install-project
|
||||
##########################################################################
|
||||
FROM ubuntu:oracular
|
||||
FROM ubuntu:25.10
|
||||
|
||||
ENV PATH=/panettone/bin:$PATH
|
||||
|
||||
@@ -38,15 +34,14 @@ STOPSIGNAL SIGINT
|
||||
RUN apt-get update -qy && apt-get install -qyy \
|
||||
-o APT::Install-Recommends=false \
|
||||
-o APT::Install-Suggests=false \
|
||||
python3.13 \
|
||||
libpython3.13 \
|
||||
libpcre3 \
|
||||
libxml2
|
||||
python3.14 \
|
||||
libpython3.14 \
|
||||
libpcre3
|
||||
|
||||
RUN apt-get clean
|
||||
RUN rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||
|
||||
COPY --from=build --chown=panettone:panettone /panettone /panettone
|
||||
COPY --from=base --chown=panettone:panettone /panettone /panettone
|
||||
|
||||
USER panettone
|
||||
WORKDIR /panettone
|
||||
@@ -56,8 +51,4 @@ COPY /templates/ templates/
|
||||
COPY .env app/
|
||||
COPY alembic.ini /panettone/alembic.ini
|
||||
COPY /alembic/ /panettone/alembic/
|
||||
COPY pyproject.toml /panettone/pyproject.toml
|
||||
|
||||
RUN python -V
|
||||
RUN python -Im site
|
||||
RUN python -Ic 'import uvicorn'
|
||||
COPY pyproject.toml /panettone/pyproject.toml
|
||||
2
Makefile
2
Makefile
@@ -26,7 +26,7 @@ docker-up: ## Run project with compose
|
||||
docker-clean: ## Clean and reset project containers and volumes
|
||||
docker compose down -v --remove-orphans | true
|
||||
docker compose rm -f | true
|
||||
docker volume rm panettone_postgres_data | true
|
||||
docker volume ls -q | grep panettone_postgres_data | xargs -r docker volume rm | true
|
||||
|
||||
# ====================================================================================
|
||||
# DATABASE MIGRATIONS
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<ul>
|
||||
<li><a href="#make-will-help-you">Make will help you</a></li>
|
||||
<li><a href="#how-to-feed-database">How to feed database</a></li>
|
||||
<li><a href="#structured-&-asynchronous-logging-with-rotoger">Structured & Asynchronous Logging with Rotoger</a></li>
|
||||
<li><a href="#structured-asynchronous-logging-with-rotoger">Structured & Asynchronous Logging with Rotoger</a></li>
|
||||
<li><a href="#setup-user-auth">Setup user auth</a></li>
|
||||
<li><a href="#setup-local-env-with-uv">Setup local development with uv</a></li>
|
||||
<li><a href="#import-xlsx-files-with-polars-and-calamine">Import xlsx files with polars and calamine</a></li>
|
||||
|
||||
@@ -2,12 +2,12 @@ from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Query, Request, status
|
||||
from pydantic import EmailStr
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
from starlette.concurrency import run_in_threadpool
|
||||
|
||||
from app.services.smtp import SMTPEmailService
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Form
|
||||
from fastapi.responses import StreamingResponse
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
|
||||
from app.services.llm import get_llm_service
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -8,7 +8,7 @@ from app.models.stuff import RandomStuff, Stuff
|
||||
from app.schemas.stuff import RandomStuff as RandomStuffSchema
|
||||
from app.schemas.stuff import StuffResponse, StuffSchema
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
router = APIRouter(prefix="/v1/stuff")
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, HTTPException, Request, status
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import get_db
|
||||
@@ -9,7 +9,7 @@ from app.models.user import User
|
||||
from app.schemas.user import TokenResponse, UserLogin, UserResponse, UserSchema
|
||||
from app.services.auth import create_access_token
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
router = APIRouter(prefix="/v1/user")
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from collections.abc import AsyncGenerator
|
||||
|
||||
from fastapi.exceptions import ResponseValidationError
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
||||
|
||||
from app.config import settings as global_settings
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
engine = create_async_engine(
|
||||
global_settings.asyncpg_url.unicode_string(),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import orjson
|
||||
from attrs import define, field
|
||||
from fastapi import Request
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
@define(slots=True)
|
||||
|
||||
@@ -5,7 +5,7 @@ import asyncpg
|
||||
from fastapi import Depends, FastAPI, Request
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
|
||||
from app.api.health import router as health_router
|
||||
from app.api.ml import router as ml_router
|
||||
@@ -18,7 +18,7 @@ from app.exception_handlers import register_exception_handlers
|
||||
from app.redis import get_redis
|
||||
from app.services.auth import AuthBearer
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates")
|
||||
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@ from typing import Any
|
||||
|
||||
from asyncpg import UniqueViolationError
|
||||
from fastapi import HTTPException, status
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import DeclarativeBase, declared_attr
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
class Base(DeclarativeBase):
|
||||
|
||||
@@ -3,12 +3,12 @@ import time
|
||||
import jwt
|
||||
from fastapi import HTTPException, Request
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
|
||||
from app.config import settings as global_settings
|
||||
from app.models.user import User
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
async def get_from_redis(request: Request, key: str):
|
||||
|
||||
@@ -3,13 +3,13 @@ from datetime import datetime
|
||||
from apscheduler import AsyncScheduler
|
||||
from apscheduler.triggers.interval import IntervalTrigger
|
||||
from attrs import define
|
||||
from rotoger import get_logger
|
||||
from sqlalchemy import text
|
||||
from starlette.types import ASGIApp, Receive, Scope, Send
|
||||
|
||||
from app.database import AsyncSessionFactory
|
||||
from app.utils.logging import AppLogger
|
||||
|
||||
logger = AppLogger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
async def tick():
|
||||
|
||||
@@ -5,12 +5,12 @@ from email.mime.text import MIMEText
|
||||
from attrs import define, field
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from pydantic import EmailStr
|
||||
from rotoger import Rotoger
|
||||
from rotoger import get_logger
|
||||
|
||||
from app.config import settings as global_settings
|
||||
from app.utils.singleton import SingletonMetaNoArgs
|
||||
|
||||
logger = Rotoger().get_logger()
|
||||
logger = get_logger()
|
||||
|
||||
|
||||
@define
|
||||
|
||||
@@ -20,8 +20,10 @@ services:
|
||||
ports:
|
||||
- "8080:8080"
|
||||
depends_on:
|
||||
- postgres
|
||||
- redis
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
|
||||
postgres:
|
||||
container_name: panettone_postgres
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
[tool.poetry]
|
||||
name = "fastapi-sqlalchemy-asyncpg"
|
||||
version = "0.0.17"
|
||||
description = ""
|
||||
authors = ["Jakub Miazek <the@grillazz.com>"]
|
||||
packages = []
|
||||
license = "MIT"
|
||||
package-mode = false
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.13"
|
||||
fastapi = {version = "^0.115.6", extras = ["all"]}
|
||||
pydantic = {version = "^2.10.3", extras = ["email"]}
|
||||
pydantic-settings = "^2.7.0"
|
||||
sqlalchemy = "^2.0.36"
|
||||
uvicorn = { version = "^0.34.0", extras = ["standard"]}
|
||||
asyncpg = "^0.30.0"
|
||||
alembic = "^1.14.0"
|
||||
httpx = "^0.28.1"
|
||||
pytest = "^8.3.4"
|
||||
pytest-cov = "^6.0.0"
|
||||
uvloop = "^0.21.0"
|
||||
httptools = "^0.6.4"
|
||||
rich = "^13.9.4"
|
||||
pyjwt = {version = "^2.10.1", extras = ["cryptography"]}
|
||||
redis = "^5.2.1"
|
||||
bcrypt = "^4.2.1"
|
||||
polars = "^1.17.1"
|
||||
python-multipart = "^0.0.20"
|
||||
fastexcel = "^0.12.0"
|
||||
fastapi-cache2 = "^0.2.1"
|
||||
inline-snapshot = "^0.17.0"
|
||||
dirty-equals = "^0.8.0"
|
||||
polyfactory = "^2.18.1"
|
||||
granian = "^1.7.0"
|
||||
apscheduler = {version = "^4.0.0a5", extras = ["redis,sqlalchemy"]}
|
||||
pendulum = {git = "https://github.com/sdispater/pendulum.git", rev="develop"}
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
devtools = { extras = ["pygments"], version = "^0.12.2" }
|
||||
safety = "*"
|
||||
pyupgrade = "*"
|
||||
ipython = "^8.26.0"
|
||||
ruff = "^0.6.1"
|
||||
sqlacodegen = "^3.0.0rc5"
|
||||
tryceratops = "^2.3.3"
|
||||
locust = "^2.31.3"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
indent-width = 4
|
||||
|
||||
lint.select = ["E", "F", "UP", "N", "C", "B"]
|
||||
lint.ignore = ["E501"]
|
||||
|
||||
# Exclude a variety of commonly ignored directories.
|
||||
exclude = ["alembic",]
|
||||
# Assume Python 3.13
|
||||
target-version = "py313"
|
||||
|
||||
[tool.ruff.lint.flake8-quotes]
|
||||
docstring-quotes = "double"
|
||||
|
||||
[tool.ruff.lint.flake8-bugbear]
|
||||
extend-immutable-calls = ["fastapi.Depends",]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "-v --doctest-modules --doctest-glob=*.md --ignore=alembic"
|
||||
asyncio_mode = "strict"
|
||||
env_files = [".env"]
|
||||
|
||||
[tool.tryceratops]
|
||||
exclude = ["alembic",]
|
||||
|
||||
[tool.ruff.format]
|
||||
# Like Black, use double quotes for strings.
|
||||
quote-style = "double"
|
||||
|
||||
# Like Black, indent with spaces, rather than tabs.
|
||||
indent-style = "space"
|
||||
|
||||
# Like Black, respect magic trailing commas.
|
||||
skip-magic-trailing-comma = false
|
||||
|
||||
# Like Black, automatically detect the appropriate line ending.
|
||||
line-ending = "auto"
|
||||
Reference in New Issue
Block a user