fix(python-aiohttp, python-blueplanet, python-flask): read header/cookie params from the request; stop overwriting non-body params from the body

KH
2d ago
+813-16

In the Connexion-based Python server generators (python-aiohttp, python-blueplanet, python-flask), generated handlers declare in: header and in: cookie parameters as function arguments. Connexion (1.x, 2.x and 3.x) never injects those: only path, query, body and files arguments are passed (see the request handling docs, 2.14.2 connexion/operations/abstract.py#get_arguments and 3.x connexion/decorators/parameter.py). As a result:

  • A required header or cookie makes every request fail with TypeError: <handler>() missing 1 required positional argument → HTTP 500. Reproduced with real servers on Connexion 3.3.0 and 2.14.2 (python-flask), 2.14.2 (python-aiohttp) and 1.1.15 (python-blueplanet).
  • An optional header or cookie is always None, even when the client sends it (for example, api_key in the petstore delete_pet).

Separately, python-flask and python-blueplanet deserialize non-body model and container parameters from request.get_json(), the request body. A $ref query parameter sent alongside an unrelated JSON body is overwritten by that body.

Changes

  • Header and cookie parameters are removed from handler signatures (vendor extension x-handler-params lists the remaining ones). They are read inside the handler from request.headers / request.cookies (flask.request, connexion.request or aiohttp's request) by their original name. baseName is restored from x-python-connexion-openapi-name.
  • Schema defaults are applied. Integer, number and boolean values are coerced. Array headers and cookies are split on , and their items coerced. content: application/json headers and cookies are decoded with json.loads before Model.from_dict.
  • get_json()-based conversion is limited to body parameters. Non-body model and container parameters are converted from the value Connexion passed.
  • Array defaults are rendered as Python literals (['foo', 'bar'], [True]). This override of toDefaultValue also affects model __init__ defaults: for example, the 7.26.0 output flags=[true] raises NameError when the model is imported, and now becomes [True].
  • Generated controller tests use the header's baseName (X-Req-Header), not the Python parameter name.

Migration note

For operations with header or cookie parameters, the generated handler signature changes. Those parameters are no longer function arguments and are read from the request inside the handler. Code that calls generated handlers directly and passes header or cookie arguments needs to provide them through the request instead. Before this change, Connexion never passed these arguments anyway: required ones failed and optional ones were always None.

Tests and verification

  • New spec src/test/resources/3_0/python/connexion-param-sources.yaml. New tests in AbstractPythonConnexionServerCodegenTest cover all three generators plus useConnexion3. Each fails without its fix.
  • Python*Test, AbstractPython*Test: 164 tests, 0 failures. Samples change only in the four petstore server pet_controller.py files, where delete_pet's api_key header is now read from the request.
  • I generated servers from the new spec and sent real HTTP requests to them on Connexion 3.3.0 / 2.14.2 (python-flask), 2.14.2 (python-aiohttp) and 1.1.15 (python-blueplanet). Header, cookie, query and body values arrive with the right types, both when sent and when defaults apply. A missing required header gets Connexion's own 400 Missing header parameter. The generated test_default_controller.py passes with pytest for a spec with a required header.

Known limitations (not changed here)

  • Connexion 3.3.0's request validation rejects type: integer/number/boolean header values such as '42' with a 400 before the handler runs. This is Connexion behavior, not something the generator controls.
  • Generated controller tests reference openapi_server.<Model>() without importing it (for example, samples/server/petstore/python-flask/openapi_server/test/test_store_controller.py), so those tests fail with NameError. This was already the case in 7.26.0.
  • python-blueplanet (Connexion 1.x) passes deepObject query parameters as raw strings, so they aren't converted to models.

Note on overlap

This touches the same controller-template blocks and AbstractPythonConnexionServerCodegenTest as #25352 and #25361. Whichever lands first, I'll rebase the others.

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    Ran the Python generator tests listed above and regenerated the samples for the three affected generators' configs. The resulting four sample changes are committed. Generator options and docs are unchanged.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.

cc @cbornet @tomplus @krjakbrjak @fa0311 (Python technical committee)

Parts of this change were drafted with AI coding assistants (Devin, Claude Code) and reviewed and verified by me.

🤖 Generated with Claude Code


Summary by cubic

Fixes header and cookie parameter handling in the python-aiohttp, python-blueplanet, and python-flask generators so generated servers receive these values instead of crashing or ignoring them.

  • Header and cookie parameters are no longer declared as handler arguments; they are read from the request inside the handler, with type coercion, defaults, JSON decoding, and CSV array splitting applied.
  • Non-body model and container parameters (e.g. $ref query parameters) are no longer overwritten from the request body; only body parameters use request.get_json().
  • Array schema defaults are emitted as valid Python literals, and controller tests send headers using the parameter's baseName.

Migration

  • Handler signatures change for operations with header or cookie parameters: those parameters are no longer function arguments. Code that calls generated handlers directly must provide them via the request instead. Previously, required header/cookie parameters always failed with HTTP 500, so such callers were already broken.