pyxle-db + pyxle-auth 0.3.0 — middleware + pure-ASGI streaming compatibility#1
Merged
Conversation
…ic CLI - PyxleDbMiddleware injects a request-scoped `request.state.db` and runs each unsafe-method request in one auto-transaction, committing or rolling back on the response status (the dispatcher swallows action errors, so commit cannot key off an escaping exception). Opt out per-action with @no_auto_transaction. - Optional SQLAlchemy async ORM path behind the [sqlalchemy] extra: an async engine + session factory wired to the same database, with the middleware managing request.state.session. - A `pyxle-db` CLI (argparse, base-dep-light): migrate/status plus alembic-init/revision/upgrade/downgrade/current/history for the ORM path. - Migrator.status() + portable migration helpers. Ships an examples/crud-notes app. Bumped to 0.3.0.
… endpoints - AuthSessionMiddleware populates request.user (zero-DB when no cookie) and serves the endpoints the client useAuth hook talks to: GET /me, POST /login, /signup, /logout. Publishes the user + endpoint map on scope['pyxle.auth'] for the SSR seed. Guards reuse the cached request.user so a guarded loader never re-resolves. - OAuth (Google/GitHub/Discord, [oauth] extra): PKCE S256, a signed single-use HttpOnly state cookie, verified-email-only account linking, same-origin-only next, env-only secrets. - JWT (pyjwt, [jwt] extra): HS256 access tokens + opaque rotating refresh tokens with reuse -> family revoke; bearer_user/authenticate guards (session->JWT->PAT). - login_required/permission_required guard aliases. AuthService.verify_credentials (extracted) + start_session + create_external_user. Migrations 0002/0003. Ships an examples/auth-app. Bumped to 0.3.0.
PyxleDbMiddleware (pyxle-db) and AuthSessionMiddleware + OAuthMiddleware (pyxle-auth) were Starlette BaseHTTPMiddleware, which buffers the whole response — defeating streaming SSR (a <Suspense> page arrives all at once) and surfacing a mid-stream disconnect as 'RuntimeError: No response returned'. Rewritten as pure-ASGI: forward every body chunk through, capture status from http.response.start, and decide commit/rollback (db) or pass-through (auth) after the response completes. Endpoints, cookies, request.user seeding, and the auto-transaction contract are unchanged. pyxle-db additionally: a mid-stream disconnect now ROLLS BACK the partial write (it was committing — anyio swallows the disconnect so the 200 was already captured; commit now requires the terminal body frame); a commit failure after the response is sent is logged rather than escaping as an unhandled ASGI error, with the ORM session finalized in a finally; ruff per-file-ignore for the pytest.importorskip E402 in the dialect tests. Part of the unreleased 0.3.0; CHANGELOGs note the streaming-safe property. Tests: pyxle-db 313 passed, pyxle-auth 389 passed.
The 0.3.0 ORM feature added tests/orm, whose conftest imports pyxle_db.orm and fails collection (ConfigurationError) unless SQLAlchemy is installed. The CI install line and the README dev-setup command only pulled in [postgres,mysql,dev], so every test-matrix job errored at collection. Add the sqlalchemy extra in both places.
The live-backend conformance suite's teardown dropped a fixed table list that predated the 0.3.0 migrations, so oauth_identities (0002) and jwt_refresh_tokens (0003) leaked between tests. On PostgreSQL the leaked FK back to users blocked the users drop (DependentObjectsStillExist); on MySQL the surviving index made the next run's bare CREATE INDEX fail (Duplicate key name). Add both tables to the drop list and update the idempotency assertion to expect all three applied migrations. These tests run only against the live PG/MySQL service containers, so the regression was invisible locally (the live params skip without the engine URLs) and only surfaced once the sqlalchemy-extra fix let the job reach the pyxle-auth step.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
pyxle-db+pyxle-auth0.3.0Brings both official plugins to 0.3.0 (PyPI is still at 0.2.0) and makes their middleware streaming-SSR-compatible.
Features (0.3.0)
request.state.db), automatic transactions (commit-on-2xx / rollback-otherwise keyed on the response, not an escaping exception), an optional SQLAlchemy ORM path (request.state.session), and an Alembic-backed migration CLI.AuthSessionMiddleware(populatesrequest.user, serves/auth/{me,login,signup,logout,token,token/refresh}), sliding sessions, OAuth 2.0/OIDC (Google/GitHub/Discord, PKCE), JWT issue/refresh, and theuseAuthclient endpoints.Fix: pure-ASGI middleware (streaming-SSR compatible)
All three middlewares (
PyxleDbMiddleware,AuthSessionMiddleware,OAuthMiddleware) were StarletteBaseHTTPMiddleware, which buffers the whole response — that defeats streaming SSR (a<Suspense>page arrives all at once) and surfaces a mid-stream client disconnect asRuntimeError: No response returned. Rewritten as pure-ASGI: forward every body chunk straight through, capture status fromhttp.response.start, and decide commit/rollback (db) or pass-through (auth) after the response completes. Endpoints, cookies,request.userseeding, and the auto-transaction contract are unchanged.pyxle-db additionally:
finally.Tests: pyxle-db 313 passed, pyxle-auth 389 passed.