Skip to content

bump project deps, inline-snapshot config, add templates #206

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ USER panettone
WORKDIR /panettone
COPY /app/ app/
COPY /tests/ tests/
COPY /templates/ templates/
COPY .env app/
COPY alembic.ini app/
COPY alembic/ app/alembic/
COPY logging-uvicorn.json /panettone/logging-uvicorn.json
COPY pyproject.toml /panettone/pyproject.toml

RUN python -V
RUN python -Im site
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ docker-test: ## Run project tests

.PHONY: docker-test-snapshot
docker-test-snapshot: ## Run project tests with inline snapshot
docker compose -f compose.yml -f test-compose.yml run --rm app pytest --inline-snapshot=create
docker compose -f compose.yml -f test-compose.yml run --rm app pytest tests --inline-snapshot=fix

.PHONY: safety
safety: ## Check project and dependencies with safety https://github.com/pyupio/safety
Expand Down
40 changes: 27 additions & 13 deletions app/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
from contextlib import asynccontextmanager
from pathlib import Path

import asyncpg
from apscheduler import AsyncScheduler
from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
from apscheduler.eventbrokers.redis import RedisEventBroker
from fastapi import Depends, FastAPI

# from apscheduler import AsyncScheduler
# from apscheduler.datastores.sqlalchemy import SQLAlchemyDataStore
# from apscheduler.eventbrokers.redis import RedisEventBroker
from fastapi import Depends, FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates

from app.api.health import router as health_router
from app.api.ml import router as ml_router
Expand All @@ -13,14 +17,18 @@
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.database import engine
from app.redis import get_redis
from app.services.auth import AuthBearer
from app.services.scheduler import SchedulerMiddleware

# from app.services.scheduler import SchedulerMiddleware
from app.utils.logging import AppLogger

logger = AppLogger().get_logger()

templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates")


@asynccontextmanager
async def lifespan(_app: FastAPI):
Expand All @@ -46,7 +54,7 @@ async def lifespan(_app: FastAPI):
await _app.postgres_pool.close()


app = FastAPI(title="Stuff And Nonsense API", version="0.18.0", lifespan=lifespan)
app = FastAPI(title="Stuff And Nonsense API", version="0.19.0", lifespan=lifespan)

app.include_router(stuff_router)
app.include_router(nonsense_router)
Expand All @@ -63,13 +71,19 @@ async def lifespan(_app: FastAPI):
dependencies=[Depends(AuthBearer())],
)

_scheduler_data_store = SQLAlchemyDataStore(engine, schema="scheduler")
_scheduler_event_broker = RedisEventBroker(
client_or_url=global_settings.redis_url.unicode_string()
)
_scheduler_himself = AsyncScheduler(_scheduler_data_store, _scheduler_event_broker)

app.add_middleware(SchedulerMiddleware, scheduler=_scheduler_himself)
@app.get("/index", response_class=HTMLResponse)
def get_index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})


# _scheduler_data_store = SQLAlchemyDataStore(engine, schema="scheduler")
# _scheduler_event_broker = RedisEventBroker(
# client_or_url=global_settings.redis_url.unicode_string()
# )
# _scheduler_himself = AsyncScheduler(_scheduler_data_store, _scheduler_event_broker)
#
# app.add_middleware(SchedulerMiddleware, scheduler=_scheduler_himself)


# TODO: every not GET meth should reset cache
Expand Down
4 changes: 3 additions & 1 deletion compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ services:
--reload --log-level debug
"
volumes:
- .:/home/code
- ./app:/panettone/app
- ./tests:/panettone/tests
- ./templates:/panettone/templates
ports:
- "8080:8080"
depends_on:
Expand Down
41 changes: 26 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,45 +1,45 @@
[project]
name = "fastapi-sqlalchemy-asyncpg"
version = "0.18.0"
version = "0.19.0"
description = "A modern FastAPI application with SQLAlchemy 2.0 and AsyncPG for high-performance async database operations. Features include JWT authentication with Redis token storage, password hashing, connection pooling, data processing with Polars, Rich logging, task scheduling with APScheduler, and Shakespeare datasets integration."
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"fastapi[all]>=0.115.12",
"pydantic[email]>=2.11.4",
"pydantic[email]>=2.11.5",
"pydantic-settings>=2.9.1",
"sqlalchemy>=2.0.40",
"uvicorn[standard]>=0.34.2",
"sqlalchemy>=2.0.41",
"uvicorn[standard]>=0.34.3",
"asyncpg>=0.30.0",
"alembic>=1.15.2",
"alembic>=1.16.1",
"httpx>=0.28.1",
"pytest>=8.3.5",
"pytest>=8.4.0",
"pytest-cov>=6.1.1",
"uvloop>=0.21.0",
"httptools>=0.6.4",
"rich>=14.0.0",
"pyjwt>=2.10.1",
"redis>=6.0.0",
"redis>=6.2.0",
"bcrypt>=4.3.0",
"polars>=1.29.0",
"polars>=1.30.0",
"python-multipart>=0.0.20",
"fastexcel>=0.14.0",
"inline-snapshot>=0.23.0",
"inline-snapshot>=0.23.2",
"dirty-equals>=0.9.0",
"polyfactory>=2.21.0",
"granian>=2.2.5",
"granian>=2.3.2",
"apscheduler[redis,sqlalchemy]>=4.0.0a6",
]

[tool.uv]
dev-dependencies = [
"ruff>=0.11.8",
"ruff>=0.11.13",
"devtools[pygments]>=0.12.2",
"pyupgrade>=3.19.1",
"ipython>=9.2.0",
"pyupgrade>=3.20.0",
"ipython>=9.3.0",
"sqlacodegen>=3.0.0",
"tryceratops>=2.4.1",
"locust>=2.36.2"
"locust>=2.37.9"

]

Expand Down Expand Up @@ -72,4 +72,15 @@ ignore = [

[tool.ruff.lint.pyupgrade]
# Preserve types, even if a file imports `from __future__ import annotations`.
keep-runtime-typing = true
keep-runtime-typing = true

[tool.inline-snapshot]
hash-length=15
default-flags=["report"]
default-flags-tui=["create", "review"]
show-updates=false
format-command="ruff format --stdin-filename {filename}"

[tool.inline-snapshot.shortcuts]
review=["review"]
fix=["create","fix"]
106 changes: 106 additions & 0 deletions templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Tabler Demo</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/@tabler/core@1.2.0/dist/css/tabler.min.css" />
</head>

<body>
<header class="navbar navbar-expand-md d-print-none">
<div class="container-xl">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbar-menu" aria-controls="navbar-menu"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<!-- BEGIN NAVBAR LOGO --><a href="../../.."
class="navbar-brand navbar-brand-autodark me-3"><svg
xmlns="http://www.w3.org/2000/svg" width="110" height="32"
viewBox="0 0 232 68" class="navbar-brand-image">
<path
d="M64.6 16.2C63 9.9 58.1 5 51.8 3.4 40 1.5 28 1.5 16.2 3.4 9.9 5 5 9.9 3.4 16.2 1.5 28 1.5 40 3.4 51.8 5 58.1 9.9 63 16.2 64.6c11.8 1.9 23.8 1.9 35.6 0C58.1 63 63 58.1 64.6 51.8c1.9-11.8 1.9-23.8 0-35.6zM33.3 36.3c-2.8 4.4-6.6 8.2-11.1 11-1.5.9-3.3.9-4.8.1s-2.4-2.3-2.5-4c0-1.7.9-3.3 2.4-4.1 2.3-1.4 4.4-3.2 6.1-5.3-1.8-2.1-3.8-3.8-6.1-5.3-2.3-1.3-3-4.2-1.7-6.4s4.3-2.9 6.5-1.6c4.5 2.8 8.2 6.5 11.1 10.9 1 1.4 1 3.3.1 4.7zM49.2 46H37.8c-2.1 0-3.8-1-3.8-3s1.7-3 3.8-3h11.4c2.1 0 3.8 1 3.8 3s-1.7 3-3.8 3z"
fill="#066fd1" style="fill: var(--tblr-primary, #066fd1)" />
<path
d="M105.8 46.1c.4 0 .9.2 1.2.6s.6 1 .6 1.7c0 .9-.5 1.6-1.4 2.2s-2 .9-3.2.9c-2 0-3.7-.4-5-1.3s-2-2.6-2-5.4V31.6h-2.2c-.8 0-1.4-.3-1.9-.8s-.9-1.1-.9-1.9c0-.7.3-1.4.8-1.8s1.2-.7 1.9-.7h2.2v-3.1c0-.8.3-1.5.8-2.1s1.3-.8 2.1-.8 1.5.3 2 .8.8 1.3.8 2.1v3.1h3.4c.8 0 1.4.3 1.9.8s.8 1.2.8 1.9-.3 1.4-.8 1.8-1.2.7-1.9.7h-3.4v13c0 .7.2 1.2.5 1.5s.8.5 1.4.5c.3 0 .6-.1 1.1-.2.5-.2.8-.3 1.2-.3zm28-20.7c.8 0 1.5.3 2.1.8.5.5.8 1.2.8 2.1v20.3c0 .8-.3 1.5-.8 2.1-.5.6-1.2.8-2.1.8s-1.5-.3-2-.8-.8-1.2-.8-2.1c-.8.9-1.9 1.7-3.2 2.4-1.3.7-2.8 1-4.3 1-2.2 0-4.2-.6-6-1.7-1.8-1.1-3.2-2.7-4.2-4.7s-1.6-4.3-1.6-6.9c0-2.6.5-4.9 1.5-6.9s2.4-3.6 4.2-4.8c1.8-1.1 3.7-1.7 5.9-1.7 1.5 0 3 .3 4.3.8 1.3.6 2.5 1.3 3.4 2.1 0-.8.3-1.5.8-2.1.5-.5 1.2-.7 2-.7zm-9.7 21.3c2.1 0 3.8-.8 5.1-2.3s2-3.4 2-5.7-.7-4.2-2-5.8c-1.3-1.5-3-2.3-5.1-2.3-2 0-3.7.8-5 2.3-1.3 1.5-2 3.5-2 5.8s.6 4.2 1.9 5.7 3 2.3 5.1 2.3zm32.1-21.3c2.2 0 4.2.6 6 1.7 1.8 1.1 3.2 2.7 4.2 4.7s1.6 4.3 1.6 6.9-.5 4.9-1.5 6.9-2.4 3.6-4.2 4.8c-1.8 1.1-3.7 1.7-5.9 1.7-1.5 0-3-.3-4.3-.9s-2.5-1.4-3.4-2.3v.3c0 .8-.3 1.5-.8 2.1-.5.6-1.2.8-2.1.8s-1.5-.3-2.1-.8c-.5-.5-.8-1.2-.8-2.1V18.9c0-.8.3-1.5.8-2.1.5-.6 1.2-.8 2.1-.8s1.5.3 2.1.8c.5.6.8 1.3.8 2.1v10c.8-1 1.8-1.8 3.2-2.5 1.3-.7 2.8-1 4.3-1zm-.7 21.3c2 0 3.7-.8 5-2.3s2-3.5 2-5.8-.6-4.2-1.9-5.7-3-2.3-5.1-2.3-3.8.8-5.1 2.3-2 3.4-2 5.7.7 4.2 2 5.8c1.3 1.6 3 2.3 5.1 2.3zm23.6 1.9c0 .8-.3 1.5-.8 2.1s-1.3.8-2.1.8-1.5-.3-2-.8-.8-1.3-.8-2.1V18.9c0-.8.3-1.5.8-2.1s1.3-.8 2.1-.8 1.5.3 2 .8.8 1.3.8 2.1v29.7zm29.3-10.5c0 .8-.3 1.4-.9 1.9-.6.5-1.2.7-2 .7h-15.8c.4 1.9 1.3 3.4 2.6 4.4 1.4 1.1 2.9 1.6 4.7 1.6 1.3 0 2.3-.1 3.1-.4.7-.2 1.3-.5 1.8-.8.4-.3.7-.5.9-.6.6-.3 1.1-.4 1.6-.4.7 0 1.2.2 1.7.7s.7 1 .7 1.7c0 .9-.4 1.6-1.3 2.4-.9.7-2.1 1.4-3.6 1.9s-3 .8-4.6.8c-2.7 0-5-.6-7-1.7s-3.5-2.7-4.6-4.6-1.6-4.2-1.6-6.6c0-2.8.6-5.2 1.7-7.2s2.7-3.7 4.6-4.8 3.9-1.7 6-1.7 4.1.6 6 1.7 3.4 2.7 4.5 4.7c.9 1.9 1.5 4.1 1.5 6.3zm-12.2-7.5c-3.7 0-5.9 1.7-6.6 5.2h12.6v-.3c-.1-1.3-.8-2.5-2-3.5s-2.5-1.4-4-1.4zm30.3-5.2c1 0 1.8.3 2.4.8.7.5 1 1.2 1 1.9 0 1-.3 1.7-.8 2.2-.5.5-1.1.8-1.8.7-.5 0-1-.1-1.6-.3-.2-.1-.4-.1-.6-.2-.4-.1-.7-.1-1.1-.1-.8 0-1.6.3-2.4.8s-1.4 1.3-1.9 2.3-.7 2.3-.7 3.7v11.4c0 .8-.3 1.5-.8 2.1-.5.6-1.2.8-2.1.8s-1.5-.3-2.1-.8c-.5-.6-.8-1.3-.8-2.1V28.8c0-.8.3-1.5.8-2.1.5-.6 1.2-.8 2.1-.8s1.5.3 2.1.8c.5.6.8 1.3.8 2.1v.6c.7-1.3 1.8-2.3 3.2-3 1.3-.7 2.8-1 4.3-1z"
fill-rule="evenodd" clip-rule="evenodd" fill="#4a4a4a" />
</svg></a><!-- END NAVBAR LOGO -->
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="#">
<span class="nav-link-icon">
<!-- Download SVG icon from http://tabler.io/icons/icon/home -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="icon icon-1">
<path d="M5 12l-2 0l9 -9l9 9l-2 0" />
<path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7" />
<path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6" />
</svg>
</span>
<span class="nav-link-title"> Home </span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span
class="nav-link-icon"><!-- Download SVG icon from http://tabler.io/icons/icon/checkbox -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="icon icon-1">
<path d="M9 11l3 3l8 -8" />
<path
d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9" />
</svg>
</span>
<span class="nav-link-title"> Profile </span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">
<span
class="nav-link-icon"><!-- Download SVG icon from http://tabler.io/icons/icon/checkbox -->
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="icon icon-1">
<path d="M9 11l3 3l8 -8" />
<path
d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9" />
</svg>
</span>
<span class="nav-link-title"> Settings </span>
</a>
</li>
</ul>
<div class="navbar-nav flex-row order-md-last ms-auto">
<div class="nav-item dropdown">
<a href="#" class="nav-link d-flex lh-1 text-reset"
data-bs-toggle="dropdown" aria-label="Open user menu">
<span class="avatar avatar-sm"
style="background-image: url(/static/avatars/044m.jpg)"></span>
<div class="d-none d-xl-block ps-2">
<div>Paweł Kuna</div>
<div class="mt-1 small text-secondary">UI Designer</div>
</div>
</a>
<div class="dropdown-menu dropdown-menu-end dropdown-menu-arrow">
<a href="#" class="dropdown-item">Status</a>
<a href="./profile.html" class="dropdown-item">Profile</a>
<a href="#" class="dropdown-item">Feedback</a>
<div class="dropdown-divider"></div>
<a href="./settings.html" class="dropdown-item">Settings</a>
<a href="./sign-in.html" class="dropdown-item">Logout</a>
</div>
</div>
</div>
</div>
</header>
</body>

</html>
Loading