Skip to content

feat(python): optimize FastAPI deployment with direct uvicorn entrypoint#422

Merged
davideme merged 1 commit intomainfrom
claude/festive-vaughan
Feb 24, 2026
Merged

feat(python): optimize FastAPI deployment with direct uvicorn entrypoint#422
davideme merged 1 commit intomainfrom
claude/festive-vaughan

Conversation

@davideme
Copy link
Owner

Summary

  • Direct uvicorn entrypoint: Replaces launcher.py in the Procfile with uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4, following the Google Cloud Buildpacks recommended pattern for Python 3.13+; adds a main.py shim at the buildpack root to expose the app at the expected module path
  • Fix lifespan shutdown bugs: settings.use_postgres was a method reference (always truthy, missing ()); db_manager was captured as None at import time so the engine pool was never closed — fixed by reading dependencies.db_manager through the module at shutdown
  • Add httptools: C-level HTTP/1.1 parser replaces pure-Python h11; added to both pyproject.toml and requirements.txt since the buildpack uses requirements.txt for dependency installation
  • Fix pre-commit hook: Pass --force-exclude to ruff so lint.exclude in pyproject.toml is honoured when files are passed explicitly (was inadvertently linting generated/excluded files)

Test plan

  • Python CI passes (lint, unit tests, mode tests, schemathesis)
  • Smoke test locally: uvicorn main:app --port 8080 starts and GET /health returns {"status":"ok","storage":"memory"}
  • Cloud Build deployment uses Procfile entrypoint and starts with 4 workers

🤖 Generated with Claude Code

- Replace launcher.py Procfile entrypoint with direct uvicorn invocation
  following Google Cloud Buildpacks recommended pattern for Python 3.13+
- Add main.py shim at buildpack root to enable `uvicorn main:app` module path
- Run 4 workers for parallel request handling on Cloud Run instances
- Fix two bugs in lifespan shutdown: settings.use_postgres was referenced
  as a method object (always truthy) instead of being called, and db_manager
  was captured as None at import time so the connection pool was never closed;
  fix by calling settings.use_postgres() and reading dependencies.db_manager
  through the module at shutdown time
- Add httptools for C-level HTTP/1.1 parsing (replaces pure-Python h11);
  added to both pyproject.toml and requirements.txt since the buildpack
  uses requirements.txt for dependency installation
- Fix pre-commit hook to pass --force-exclude to ruff so lint.exclude in
  pyproject.toml is respected when files are explicitly specified

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 24, 2026 13:33
@davideme davideme merged commit e7fe2ce into main Feb 24, 2026
5 checks passed
@davideme davideme deleted the claude/festive-vaughan branch February 24, 2026 13:37
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes the FastAPI deployment for Google Cloud Buildpacks by replacing the Python launcher script with a direct uvicorn command in the Procfile, adding performance improvements via httptools, and fixing critical bugs in the application lifespan management.

Changes:

  • Replaced launcher.py with direct uvicorn main:app command in Procfile and added a root-level main.py shim for buildpack compatibility
  • Fixed two critical bugs in the lifespan shutdown: settings.use_postgres was missing () call, and db_manager was captured as None at import time
  • Added httptools dependency for C-level HTTP parsing performance
  • Fixed pre-commit hook to pass --force-exclude to ruff to honor exclusion configuration

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/python/src/openapi_server/main.py Fixed lifespan shutdown bugs: corrected use_postgres() method call and changed to module-level dependencies.db_manager access
src/python/main.py Added entrypoint shim to expose app at main:app path expected by buildpacks
src/python/Procfile Replaced launcher.py with direct uvicorn command using hardcoded port 8080 and 4 workers
src/python/pyproject.toml Added httptools dependency with >=0.5.0 constraint
src/python/requirements.txt Added httptools 0.7.1 for buildpack installation
src/python/poetry.lock Updated lock file with httptools 0.7.1 and updated content hash
scripts/hooks/python.sh Added --force-exclude flag to ruff commands to honor pyproject.toml exclusions

@@ -1 +1 @@
web: python launcher.py No newline at end of file
web: uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4 No newline at end of file
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded --port 8080 removes support for the PORT environment variable that was previously respected by launcher.py. Google Cloud Run and other platforms commonly use the PORT environment variable to dynamically assign ports. Consider using --port $PORT or ${PORT:-8080} to maintain backward compatibility while providing a sensible default. Uvicorn also respects the PORT environment variable automatically if --port is not specified, so you could remove the explicit port flag entirely.

Suggested change
web: uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
web: uvicorn main:app --host 0.0.0.0 --workers 4

Copilot uses AI. Check for mistakes.
fastapi = "^0.115.12"
pydantic = "^2.12.0"
uvicorn = "^0.27.1"
httptools = ">=0.5.0"
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version constraint >=0.5.0 is inconsistent with the locked version 0.7.1 in poetry.lock. Poetry's caret (^) constraints are more conventional for non-zero major versions and better express semantic versioning expectations. Consider using ^0.7.0 or ^0.5.0 instead of >=0.5.0 to prevent accidentally pulling in breaking changes from hypothetical future major versions while still allowing minor and patch updates.

Suggested change
httptools = ">=0.5.0"
httptools = "^0.7.0"

Copilot uses AI. Check for mistakes.
@@ -1 +1 @@
web: python launcher.py No newline at end of file
web: uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4 No newline at end of file
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded --workers 4 removes support for the WORKERS environment variable that was previously respected by launcher.py. Different deployment environments may need different worker counts based on available resources. Consider using --workers ${WORKERS:-4} to maintain backward compatibility while providing a sensible default, or allow the platform to configure this through environment variables at deployment time.

Suggested change
web: uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4
web: uvicorn main:app --host 0.0.0.0 --port 8080 --workers ${WORKERS:-4}

Copilot uses AI. Check for mistakes.
@@ -1 +1 @@
web: python launcher.py No newline at end of file
web: uvicorn main:app --host 0.0.0.0 --port 8080 --workers 4 No newline at end of file
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new Procfile hardcodes port 8080 and 4 workers, but the README.md still documents that PORT and WORKERS environment variables are supported via the launcher script (lines 172-173). Since the Procfile now uses direct uvicorn invocation instead of launcher.py, the documentation needs to be updated to reflect the current behavior. Either update the README to document the new hardcoded values, or modify the Procfile to continue supporting these environment variables.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants