Merge pull request #238 from grillazz/rotoger-one-more-time

rotoger-one-more-time
This commit is contained in:
Ordinary Hobbit
2026-03-09 18:11:03 +01:00
committed by GitHub
6 changed files with 54 additions and 16 deletions
+21 -2
View File
@@ -1,4 +1,8 @@
from collections.abc import Callable
from typing import Annotated, Any
from fastapi import APIRouter, Depends, HTTPException, Request, status from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import ValidationError, WrapValidator
from rotoger import get_logger from rotoger import get_logger
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
@@ -22,12 +26,26 @@ async def create_random_stuff(
return {"id": str(random_stuff.id)} return {"id": str(random_stuff.id)}
failed_items: list[dict] = [] # Global or pass via context
def catch_invalid(v: Any, handler: Callable[[Any], Any] ) -> Any:
try:
return handler(v)
except ValidationError:
failed_items.append(v) # Intercept here!
return None # Or raise if needed
@router.post("/add_many", status_code=status.HTTP_201_CREATED) @router.post("/add_many", status_code=status.HTTP_201_CREATED)
async def create_multi_stuff( async def create_multi_stuff(
payload: list[StuffSchema], db_session: AsyncSession = Depends(get_db) payload: list[Annotated[StuffSchema, WrapValidator(catch_invalid)]], db_session: AsyncSession = Depends(get_db)
): ):
await logger.ainfo(f">>>{failed_items}")
try: try:
stuff_instances = [Stuff(**stuff.model_dump()) for stuff in payload] await logger.ainfo(f">>>{failed_items}")
await logger.ainfo(f">>>{payload}")
stuff_instances = [
Stuff(**stuff.model_dump()) for stuff in payload if stuff is not None
]
db_session.add_all(stuff_instances) db_session.add_all(stuff_instances)
await db_session.commit() await db_session.commit()
except SQLAlchemyError as ex: except SQLAlchemyError as ex:
@@ -39,6 +57,7 @@ async def create_multi_stuff(
await logger.ainfo( await logger.ainfo(
f"{len(stuff_instances)} Stuff instances inserted into the database." f"{len(stuff_instances)} Stuff instances inserted into the database."
) )
return {"inserted": len(stuff_instances)}
return True return True
+3 -3
View File
@@ -21,12 +21,12 @@ from app.middleware.profiler import ProfilingMiddleware
from app.redis import get_redis from app.redis import get_redis
from app.services.auth import AuthBearer from app.services.auth import AuthBearer
logger = get_logger()
templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates") templates = Jinja2Templates(directory=Path(__file__).parent.parent / "templates")
@asynccontextmanager @asynccontextmanager
async def lifespan(app: FastAPI): async def lifespan(app: FastAPI):
app.logger = get_logger()
app.redis = await get_redis() app.redis = await get_redis()
postgres_dsn = global_settings.postgres_url.unicode_string() postgres_dsn = global_settings.postgres_url.unicode_string()
try: try:
@@ -35,12 +35,12 @@ async def lifespan(app: FastAPI):
min_size=5, min_size=5,
max_size=20, max_size=20,
) )
await logger.ainfo( await app.logger.ainfo(
"Postgres pool created", idle_size=app.postgres_pool.get_idle_size() "Postgres pool created", idle_size=app.postgres_pool.get_idle_size()
) )
yield yield
except Exception as e: except Exception as e:
await logger.aerror("Error during app startup", error=repr(e)) await app.logger.aerror("Error during app startup", error=repr(e))
raise raise
finally: finally:
await app.redis.close() await app.redis.close()
-1
View File
@@ -5,7 +5,6 @@ from pydantic import BaseModel, ConfigDict, Field
config = ConfigDict(from_attributes=True) config = ConfigDict(from_attributes=True)
class RandomStuff(BaseModel): class RandomStuff(BaseModel):
chaos: dict[str, Any] = Field( chaos: dict[str, Any] = Field(
..., description="Pretty chaotic JSON data can be added here..." ..., description="Pretty chaotic JSON data can be added here..."
+20
View File
@@ -0,0 +1,20 @@
from granian import Granian
def startup():
print("Server starting up...")
def shutdown():
print("Server shutting down...")
server = Granian(
"main:app",
host="0.0.0.0", # Bind to all interfaces
port=8000,
workers=4,
interface="asgi",
blocking_threads=8 # Optional: threads per worker for blocking ops
)
server.on_startup(startup)
server.on_shutdown(shutdown)
server.serve_forever()
+2 -2
View File
@@ -22,14 +22,14 @@ dependencies = [
"redis==7.1.0", "redis==7.1.0",
"bcrypt==5.0.0", "bcrypt==5.0.0",
"polars[pyarrow]==1.36.1", "polars[pyarrow]==1.36.1",
"python-multipart==0.0.20", "python-multipart==0.0.22",
"fastexcel==0.18.0", "fastexcel==0.18.0",
"inline-snapshot==0.31.1", "inline-snapshot==0.31.1",
"dirty-equals==0.11", "dirty-equals==0.11",
"polyfactory==3.1.0", "polyfactory==3.1.0",
"granian==2.6.0", "granian==2.6.0",
"apscheduler[redis,sqlalchemy]>=4.0.0a6", "apscheduler[redis,sqlalchemy]>=4.0.0a6",
"rotoger==0.2.1", "rotoger==0.3.0",
"pyinstrument>=5.1.2", "pyinstrument>=5.1.2",
] ]
Generated
+8 -8
View File
@@ -438,10 +438,10 @@ requires-dist = [
{ name = "pyjwt", specifier = "==2.10.1" }, { name = "pyjwt", specifier = "==2.10.1" },
{ name = "pytest", specifier = "==9.0.2" }, { name = "pytest", specifier = "==9.0.2" },
{ name = "pytest-cov", specifier = "==7.0.0" }, { name = "pytest-cov", specifier = "==7.0.0" },
{ name = "python-multipart", specifier = "==0.0.20" }, { name = "python-multipart", specifier = "==0.0.22" },
{ name = "redis", specifier = "==7.1.0" }, { name = "redis", specifier = "==7.1.0" },
{ name = "rich", specifier = "==14.2.0" }, { name = "rich", specifier = "==14.2.0" },
{ name = "rotoger", specifier = "==0.2.1" }, { name = "rotoger", specifier = "==0.3.0" },
{ name = "sqlalchemy", specifier = "==2.0.45" }, { name = "sqlalchemy", specifier = "==2.0.45" },
{ name = "uvicorn", extras = ["standard"], specifier = "==0.38.0" }, { name = "uvicorn", extras = ["standard"], specifier = "==0.38.0" },
{ name = "uvloop", specifier = "==0.22.1" }, { name = "uvloop", specifier = "==0.22.1" },
@@ -1075,11 +1075,11 @@ wheels = [
[[package]] [[package]]
name = "python-multipart" name = "python-multipart"
version = "0.0.20" version = "0.0.22"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/87/f44d7c9f274c7ee665a29b885ec97089ec5dc034c7f3fafa03da9e39a09e/python_multipart-0.0.20.tar.gz", hash = "sha256:8dd0cab45b8e23064ae09147625994d090fa46f5b0d1e13af944c331a7fa9d13", size = 37158, upload-time = "2024-12-16T19:45:46.972Z" } sdist = { url = "https://files.pythonhosted.org/packages/94/01/979e98d542a70714b0cb2b6728ed0b7c46792b695e3eaec3e20711271ca3/python_multipart-0.0.22.tar.gz", hash = "sha256:7340bef99a7e0032613f56dc36027b959fd3b30a787ed62d310e951f7c3a3a58", size = 37612, upload-time = "2026-01-25T10:15:56.219Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/45/58/38b5afbc1a800eeea951b9285d3912613f2603bdf897a4ab0f4bd7f405fc/python_multipart-0.0.20-py3-none-any.whl", hash = "sha256:8a62d3a8335e06589fe01f2a3e178cdcc632f3fbe0d492ad9ee0ec35aab1f104", size = 24546, upload-time = "2024-12-16T19:45:44.423Z" }, { url = "https://files.pythonhosted.org/packages/1b/d0/397f9626e711ff749a95d96b7af99b9c566a9bb5129b8e4c10fc4d100304/python_multipart-0.0.22-py3-none-any.whl", hash = "sha256:2b2cd894c83d21bf49d702499531c7bafd057d730c201782048f7945d82de155", size = 24579, upload-time = "2026-01-25T10:15:54.811Z" },
] ]
[[package]] [[package]]
@@ -1196,7 +1196,7 @@ wheels = [
[[package]] [[package]]
name = "rotoger" name = "rotoger"
version = "0.2.1" version = "0.3.0"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "attrs" }, { name = "attrs" },
@@ -1204,9 +1204,9 @@ dependencies = [
{ name = "structlog" }, { name = "structlog" },
{ name = "whenever" }, { name = "whenever" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/9d/ad/75a22ddd259505547fd47c36ea984688e3b56d9cbc49c0f98bb95c84c01b/rotoger-0.2.1.tar.gz", hash = "sha256:823bb39c781d6038d2aae1c2c3f6d74c0abb1e9f07b257c079028d6ae3f2589d", size = 1647, upload-time = "2025-11-13T16:12:27.833Z" } sdist = { url = "https://files.pythonhosted.org/packages/06/40/475df92ef562d22489a1b07bc41cf1094dce1d4d03475f59112b97070560/rotoger-0.3.0.tar.gz", hash = "sha256:e407e3f4cf4948886bd26b35bd54e635f58703db406f5a706e4c4579dc273241", size = 2714, upload-time = "2026-03-01T17:08:46.78Z" }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/e7/da/9422061c62499eaafcf90c4adf3e13c51031d4b593af899451825fa85a7b/rotoger-0.2.1-py3-none-any.whl", hash = "sha256:849ed131068ab724991c38c32fb63e4904efb79e29bf084f37ec11a31ec0c703", size = 2603, upload-time = "2025-11-13T16:12:26.895Z" }, { url = "https://files.pythonhosted.org/packages/72/85/2164d61cff7594366d5797cc6f33784a05e83a39841701860bbbc41631dc/rotoger-0.3.0-py3-none-any.whl", hash = "sha256:08d3c239f05c0551a9cdb682332f4c1e981844b1b0afa7b1e04c50730bbe2098", size = 3458, upload-time = "2026-03-01T17:08:47.684Z" },
] ]
[[package]] [[package]]