Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: GoogleCloudPlatform/functions-framework-python
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.8.3
Choose a base ref
...
head repository: GoogleCloudPlatform/functions-framework-python
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.9.0
Choose a head ref
  • 14 commits
  • 52 files changed
  • 4 contributors

Commits on May 16, 2025

  1. Configuration menu
    Copy the full SHA
    3fdeaa0 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    0ab5f03 View commit details
    Browse the repository at this point in the history

Commits on Jun 9, 2025

  1. fix: Pin cloudevent sdk version to support python3.7. (#373)

    * fix: Pin cloudevent sdk version to support python3.7.
    
    * Pin cloudevent version on examples.
    
    * Pin cloudevent versions.
    taeold authored Jun 9, 2025
    Configuration menu
    Copy the full SHA
    cc2b9b5 View commit details
    Browse the repository at this point in the history

Commits on Jun 10, 2025

  1. fix(ci): specify python version in tox environment (#375)

    * fix(ci): specify python version in tox environment
    
    * Enable shell for github workflows.
    taeold authored Jun 10, 2025
    Configuration menu
    Copy the full SHA
    37e0bf7 View commit details
    Browse the repository at this point in the history
  2. feat: add support for async functions (#364)

    * feat: Introduce functions_framework.aio submodule that support async function execution.
    
    * Remove httpx.
    
    * Update pyproject.toml to include extra async package.
    
    * Update test deps.
    
    * Improve test coverage.
    
    * Make linter happy.
    
    * Fix test harness to support py37.
    
    * Remove version filter in tox file.
    
    * Remove dependency-groups in pyproject.toml for now.
    
    * Use py3.8 compatible types.
    
    * Fix more incompatibility with python38
    
    * Pin cloudevent sdk to python37 compatible version.
    
    * Fix more py37 incompatibility.
    
    * fix: Prevent test_aio.py collection errors on Python 3.7
    
    Add pytest_ignore_collect hook to skip test_aio.py entirely on Python 3.7
    to prevent ImportError during test collection. The previous approach using
    only pytest_collection_modifyitems was too late in the process - the error
    occurred when pytest tried to import the module before skip markers could
    be applied.
    
    Both hooks are marked as safe to remove when Python 3.7 support is dropped.
    
    * style: Apply black formatting to conftest.py
    
    * fix: Use modern pytest collection_path parameter and return None
    
    - Replace deprecated 'path' parameter with 'collection_path' in pytest_ignore_collect
    - Return None instead of False to let pytest use default behavior
    - This should fix the issue where pytest was collecting tests from .tox/.pkg/
    
    * fix: Skip tests parametrized with None on Python 3.7
    
    Simplify the check to just skip any test parametrized with None value.
    On Python 3.7, create_asgi_app is always None due to the conditional
    import, so this catches all async-related parametrized tests.
    
    * fix: Replace asyncio.to_thread with Python 3.8 compatible code
    
    Use asyncio.get_event_loop().run_in_executor() instead of
    asyncio.to_thread() for Python 3.8 compatibility. Added TODO
    comments to switch back when Python 3.8 support is dropped.
    
    * fix: Improve async test detection for Python 3.7
    
    - Use a list of async keywords (async, asgi, aio, starlette)
    - Check for these keywords in test names, file paths, and parameters
    - This catches more async-related tests including those with "aio" prefix
    
    * fix: Handle Flask vs Starlette redirect behavior differences
    
    - Remove unnecessary follow_redirects=True from Starlette TestClient
    - Make test_http_function_request_url_empty_path aware of framework differences
    - Starlette TestClient normalizes empty path "" to "/" while Flask preserves it
    - Test now expects appropriate behavior for each framework
    
    * fix: Exclude aio module from coverage on Python 3.7
    
    Add special coverage configuration for Python 3.7 that excludes
    the aio module since it requires Python 3.8+ due to Starlette
    dependency. This prevents coverage failures on Python 3.7.
    
    * fix: Simplify conftest.py.
    
    * fix: Use full environment names for py37 coverage exclusion
    
    The tox environment names in GitHub Actions include the OS suffix
    (e.g., py37-ubuntu-22.04), so we need to match the full names.
    
    * fix: Explicitly list each py37 environment for coverage exclusion
    
    - List py37-ubuntu-22.04 and py37-macos-13 explicitly
    - Place py37 settings before general windows-latest setting
    - This should properly exclude aio module from coverage on Python 3.7
    
    * fix: Add Python 3.7 specific coverage configuration
    
    - Create .coveragerc-py37 to exclude aio module from coverage on Python 3.7
    - Use --cov-config flag to specify this file for py37 environments only
    - This prevents the aio module exclusion from affecting Python 3.8+ tests
    taeold authored Jun 10, 2025
    Configuration menu
    Copy the full SHA
    49f6985 View commit details
    Browse the repository at this point in the history

Commits on Jun 12, 2025

  1. feat: add flag to run functions framework in asgi stack (#376)

    * feat: add ASGI server support for async functions
    
    - Add uvicorn and uvicorn-worker to async optional dependencies
    - Refactor gunicorn.py with BaseGunicornApplication for shared config
    - Add UvicornApplication class for ASGI apps
    - Add StarletteApplication in asgi.py for development mode
    - Update HTTPServer to auto-detect Flask (WSGI) vs other (ASGI) apps
    - Add --gateway CLI flag to choose between wsgi and asgi
    - Update test_http.py to use Flask instance in tests
    
    * fix: apply black and isort formatting to source files
    
    * test: add comprehensive tests for ASGI server support
    
    - Add tests for HTTPServer ASGI/WSGI auto-detection
    - Add tests for StarletteApplication and UvicornApplication
    - Add tests for CLI --gateway flag functionality
    - Add integration tests for async functions with ASGI
    - Ensure 100% code coverage for new ASGI features
    - Apply black and isort formatting to test files
    
    * fix: skip async test files on Python 3.7
    
    * fix: exclude async code from Python 3.7 coverage
    
    * fix: Install uvicorn on windows.
    
    * fix: unable to use reload in starlette.
    
    * fix: add missing endpoint parameter to Route constructors in ASGI
    
    * feat: add async conformance tests with ASGI gateway
    
    * fix: set UvicornWorker class before parent init and update tests
    
    * fix: update asgi tests to remove reload option
    
    * feat: add async-specific conformance tests for ASGI mode
    
    * fix: apply black formatting to async files
    
    * fix: disable validateMapping for CloudEvent tests in ASGI mode
    
    ASGI mode does not support automatic conversion from legacy events to CloudEvents, so validateMapping must be false for CloudEvent conformance tests.
    
    * fix: avoid mutating options dict in Gunicorn applications
    
    Create a copy of the options dict before modifying it to prevent side effects when the same options dict is reused elsewhere. This could cause issues with timeout tests.
    
    * fix: add pragma comments for Python 3.7 coverage and fix options handling
    
    - Add pragma: no cover comments for ASGI-specific code paths that won't execute in Python 3.7
    - Fix options dict handling to use consistent variable names to avoid confusion
    
    * fix: revert to separate GunicornApplication and UvicornApplication classes
    
    Remove the BaseGunicornApplication abstraction as it was causing issues with the timeout mechanism. Each class now independently extends gunicorn.app.base.BaseApplication, which is cleaner and avoids the problems we were seeing with shared state and options handling.
    
    * refactor: restore GunicornApplication to match main branch
    
    Remove unnecessary changes to GunicornApplication class, keeping only
    the UvicornApplication addition for ASGI support.
    
    * chore: Untrack uv.lock
    
    * chore: rename confirmance test (asgi) github workflow
    
    * chore: cleanup .gitignore.
    
    * chore: clean up unncessary comments.
    taeold authored Jun 12, 2025
    Configuration menu
    Copy the full SHA
    268acf1 View commit details
    Browse the repository at this point in the history

Commits on Jun 17, 2025

  1. feat: add execution_id support for async stack (#377)

    * feat: add execution_id support for async stack
    
    - Add contextvars support to execution_id.py for async-safe context storage
    - Create AsgiMiddleware class to inject execution_id into ASGI requests
    - Add set_execution_context_async decorator for both sync and async functions
    - Update LoggingHandlerAddExecutionId to support both Flask g and contextvars
    - Integrate execution_id support in aio/__init__.py with proper exception handling
    - Add comprehensive async tests matching sync test functionality
    - Follow Starlette best practices for exception handling
    
    The implementation enables automatic execution_id injection and logging for async
    functions when LOG_EXECUTION_ID=true, matching the existing sync stack behavior.
    
    * refactor: move exception logging to crash handler for cleaner code
    
    - Remove try/catch blocks from wrapper functions
    - Centralize exception logging in _crash_handler
    - Extract execution_id directly from request headers in crash handler
    - Temporarily set context when logging exceptions to ensure execution_id is included
    - This approach is cleaner and more similar to Flask's centralized exception handling
    
    * refactor: improve code organization based on feedback
    
    - Move imports to top of file instead of inside functions
    - Extract common header parsing logic into _extract_context_from_headers helper
    - Reduce code duplication between sync and async decorators
    - Add comment explaining why crash handler needs to extract context from headers
    - This addresses the context reset issue where decorators clean up before exception handlers run
    
    * fix: preserve execution context for exception handlers
    
    - Don't reset context on exception, only on successful completion
    - This allows exception handlers to access execution_id naturally
    - Simplify crash handler since context is now available
    - Rely on Python's automatic contextvar cleanup when task completes
    - Each request runs in its own task, so no risk of context leakage
    
    This is more correct and follows the principle that context should be
    available throughout the entire request lifecycle, including error handling.
    
    * style: apply black and isort formatting
    
    - Format code with black for consistent style
    - Sort imports with isort for better organization
    - All linting checks now pass
    
    * refactor: clean up async tests and remove redundant comments
    
    * chore: remove uv.lock from version control
    
    * style: fix black formatting
    
    * fix: skip async execution_id tests on Python 3.7
    
    * refactor: reuse _enable_execution_id_logging from main module
    
    * chore: more cleanup.
    
    * test: remove unnecessary pragma no cover for sync_wrapper
    
    * test: improve coverage by removing unnecessary pragma no cover annotations
    
    * style: fix black formatting
    
    * style: fix isort import ordering
    
    * test: add back pragma no cover for genuinely hard-to-test edge cases
    
    * refactor: simplify async decorator by removing dead code branch
    
    * fix: exclude async-specific code from py37 coverage
    
    The AsgiMiddleware class and set_execution_context_async function in
    execution_id.py require Python 3.8+ due to async dependencies. These
    are now excluded from coverage calculations in Python 3.7 environments.
    
    * fix: improve async execution ID context propagation using contextvars
    
    - Use contextvars.copy_context() to properly propagate execution context in async functions
    - Implement AsyncExecutionIdHandler to handle JSON logging with execution_id
    - Redirect logging output from stderr to stdout for consistency
    - Add build dependency to dev dependencies
    - Update tests to reflect new logging output location
    
    * feat: Add execution ID logging for async functions
    
    Refactors the async logging implementation to align with the sync version, ensuring consistent execution ID logging across both stacks.
    
    * chore: clean up impl.
    
    * refactor: define custom exception handling middleware to avoid duplicate log of traceback.
    
    * style: run black
    
    * chore: clean up code a little more.
    
    * fix: propagate context in ce fns.
    
    * style: more nits.
    
    * chore: remove unncessary debug flag.
    
    * fix: respond to PR comments
    
    * fix: respond to more PR comments
    taeold authored Jun 17, 2025
    Configuration menu
    Copy the full SHA
    1123eea View commit details
    Browse the repository at this point in the history

Commits on Jun 18, 2025

  1. Configuration menu
    Copy the full SHA
    42b7fdd View commit details
    Browse the repository at this point in the history
  2. fix: set default log level for asgi logger to WARNING to match defaul…

    …t python behavior (#381)
    
    * fix: set default log level for asgi logger to WARNING to match default python behavior.
    
    * fix: fix broken test.
    taeold authored Jun 18, 2025
    Configuration menu
    Copy the full SHA
    a576a8f View commit details
    Browse the repository at this point in the history

Commits on Jun 23, 2025

  1. refactor: replace --gateway flag with --asgi boolean flag (#383)

    * refactor: replace --gateway flag with --asgi boolean flag
    
    Simplify the CLI by replacing `--gateway asgi` with `--asgi`. The new flag is more
    intuitive as WSGI remains the default and ASGI is opt-in. Also updates the environment
    variable to FUNCTION_USE_ASGI for clarity.
    
    * fix: update conformance tests to use --asgi flag
    taeold authored Jun 23, 2025
    Configuration menu
    Copy the full SHA
    58deaf1 View commit details
    Browse the repository at this point in the history

Commits on Jul 22, 2025

  1. fix: resolve CI failures for egress policies and Python 3.7 buildpack…

    … support (#388)
    
    * fix: add GitHub Actions CDN to egress allowlist
    
    The conformance workflow was failing with ECONNREFUSED errors when trying
    to download Python binaries from GitHub releases. This was caused by the
    harden-runner egress policy blocking connections to the GitHub Actions
    CDN IP addresses.
    
    Added *.actions.githubusercontent.com:443 to the allowed endpoints to
    fix Python setup for all versions (3.7, 3.8, etc).
    
    * fix: remove Python 3.7 from buildpack integration tests
    
    Google Cloud Buildpacks dropped Python 3.7 support for Ubuntu 22.04.
    The version is not available in their runtime manifest.
    
    Note: Functions Framework still supports Python 3.7, which is tested
    in unit and conformance tests using GitHub Actions with Ubuntu 20.04.
    
    * fix: use correct domain for GitHub release assets
    
    The Python binaries are actually hosted on release-assets.githubusercontent.com,
    not *.actions.githubusercontent.com
    
    * fix: add release-assets domain to unit and conformance-asgi workflows
    
    The same ECONNREFUSED issue was affecting multiple workflows with
    harden-runner egress policies
    taeold authored Jul 22, 2025
    Configuration menu
    Copy the full SHA
    2de6eec View commit details
    Browse the repository at this point in the history
  2. refactor: move async dependencies from optional to direct (#386)

    ## Summary
    This PR moves starlette, uvicorn, and uvicorn-worker from optional dependencies to direct dependencies, simplifying the installation process for users who need async functionality.
    
    ## Impact Analysis
    - **Installation size increase: < 1MB** (specifically 816KB, ~3.5% increase)
    - Base installation: 23MB → With async deps: 24MB
    - Removes the need for `pip install functions-framework[async]`
    - Maintains Python 3.8+ requirement for these dependencies
    
    ## Changes
    - Move async dependencies to direct dependencies in `pyproject.toml`
    - Remove `[async]` extra dependency configuration
    - Update imports to be direct instead of conditional
    - Remove "Starlette is not installed" error messages
    - Update tests to reflect direct dependency availability
    
    ## Test Plan
    All existing tests pass with 100% coverage. The async functionality remains unchanged, just the installation method is simplified.
    taeold authored Jul 22, 2025
    Configuration menu
    Copy the full SHA
    82ba117 View commit details
    Browse the repository at this point in the history

Commits on Jul 23, 2025

  1. feat: auto-detect ASGI mode for @aio decorated functions (#387)

    ## Summary
    - Implement automatic ASGI mode detection for functions decorated with `@aio.http` or `@aio.cloud_event`
    - The approach creates a Flask app first, loads the module within its context, then checks if ASGI is needed
    - This results in an unused Flask app for ASGI functions, but we accept this memory overhead as a trade-off
    - The `--asgi` CLI flag still works and skips the Flask app creation for optimization
    
    ## Implementation Details
    - Added `ASGI_FUNCTIONS` set to `_function_registry.py` to track functions that require ASGI
    - Updated `@aio.http` and `@aio.cloud_event` decorators to register functions in `ASGI_FUNCTIONS`
    - Modified `create_app()` to auto-detect ASGI requirements after module loading:
      1. Always creates a Flask app first
      2. Loads the user module within Flask app context
      3. Checks if target function is in `ASGI_FUNCTIONS` registry
      4. If ASGI is needed, delegates to `create_asgi_app_from_module()`
    - The `--asgi` CLI flag continues to work, bypassing Flask app creation entirely for performance
    
    ## Trade-offs
    - **Memory overhead**: ASGI functions will have an unused Flask app instance created during auto-detection
    - **Accepted trade-off**: This avoids loading modules twice which could cause side effects
    - **Optimization available**: Users can still use `--asgi` flag to skip Flask app creation entirely
    
    ## Test plan
    - [x] Added tests to verify decorators register functions in `ASGI_FUNCTIONS`
    - [x] Added CLI tests to verify auto-detection works for `@aio` decorated functions
    - [x] Added CLI tests to verify regular functions still use Flask/WSGI mode
    - [x] Added proper test isolation with registry cleanup fixtures
    - [x] All existing tests pass
    - [x] Linting passes
    taeold authored Jul 23, 2025
    Configuration menu
    Copy the full SHA
    ef48e70 View commit details
    Browse the repository at this point in the history
  2. chore(main): release 3.9.0 (#374)

    Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com>
    release-please[bot] authored Jul 23, 2025
    Configuration menu
    Copy the full SHA
    d5ac6b4 View commit details
    Browse the repository at this point in the history
Loading