Merge remote-tracking branch 'origin/switch-logger-to-rotoger' into switch-logger-to-rotoger

# Conflicts:
#	pyproject.toml
#	uv.lock
This commit is contained in:
grillazz
2025-11-16 15:18:11 +01:00
17 changed files with 43 additions and 140 deletions

View File

@@ -12,7 +12,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: [ "3.13" ]
python-version: [ "3.14" ]
env:
PYTHONDONTWRITEBYTECODE: 1

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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()

View File

@@ -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()

View File

@@ -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")

View File

@@ -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")

View File

@@ -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(),

View File

@@ -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)

View File

@@ -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")

View File

@@ -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):

View File

@@ -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):

View File

@@ -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():

View File

@@ -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

View File

@@ -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

View File

@@ -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"