This repository has been archived on 2026-02-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
shared/db/session.py
giles 668d9c7df8 feat: initial shared library extraction
Contains shared infrastructure for all coop services:
- shared/ (factory, urls, user_loader, context, internal_api, jinja_setup)
- models/ (User, Order, Calendar, Ticket, Product, Ghost CMS)
- db/ (SQLAlchemy async session, base)
- suma_browser/app/ (csrf, middleware, errors, authz, redis_cacher, payments, filters, utils)
- suma_browser/templates/ (shared base layouts, macros, error pages)
- static/ (CSS, JS, fonts, images)
- alembic/ (database migrations)
- config/ (app-config.yaml)
- editor/ (Lexical editor Node.js build)
- requirements.txt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 23:11:36 +00:00

100 lines
2.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import os
from contextlib import asynccontextmanager
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from quart import Quart, g
DATABASE_URL = (
os.getenv("DATABASE_URL_ASYNC")
or os.getenv("DATABASE_URL")
or "postgresql+asyncpg://localhost/coop"
)
_engine = create_async_engine(
DATABASE_URL,
future=True,
echo=False,
pool_pre_ping=True,
pool_size=-1 # ned to look at this!!!
)
_Session = async_sessionmaker(
bind=_engine,
class_=AsyncSession,
expire_on_commit=False,
)
@asynccontextmanager
async def get_session():
"""Always create a fresh AsyncSession for this block."""
sess = _Session()
try:
yield sess
finally:
await sess.close()
def register_db(app: Quart):
#@app.before_request
#async def _open_session():
# g.s = _Session()
# g.tx = await g.s.begin() # begin txn now (or begin_nested if you like)
#@app.after_request
#async def _commit_session(response):
# print('after request')
# return response
#@app.teardown_request
#async def _rollback_on_error(exc):
# print('teardown')
# # Quart calls this when an exception happened
# if exc is not None and hasattr(g, "tx"):
# await g.tx.rollback()
# if exc and hasattr(g, 'tx'):
# await g.tx.commit()
# if hasattr(g, "sess"):
# await g.s.close()
@app.before_request
async def open_session():
g.s = _Session()
g.tx = await g.s.begin()
g.had_error = False
@app.after_request
async def maybe_commit(response):
# Runs BEFORE bytes are sent.
if not g.had_error and 200 <= response.status_code < 400:
try:
if hasattr(g, "tx"):
await g.tx.commit()
except Exception as e:
print(f'commit failed {e}')
if hasattr(g, "tx"):
await g.tx.rollback()
from quart import make_response
return await make_response("Commit failed", 500)
return response
@app.teardown_request
async def finish(exc):
try:
# If an exception occurred OR we didnt commit (still in txn), roll back.
if hasattr(g, "s"):
if exc is not None or g.s.in_transaction():
if hasattr(g, "tx"):
await g.tx.rollback()
finally:
if hasattr(g, "s"):
await g.s.close()
@app.errorhandler(Exception)
async def mark_error(e):
g.had_error = True
raise