Skip to content

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

Merged
wing328 merged 7 commits into
OpenAPITools:masterfrom
khayashi4337:fix/python-connexion-param-sources
Oct 26, 2026
Merged

wing328 merged 7 commits into
OpenAPITools:masterfrom
khayashi4337:fix/python-connexion-param-sources

Conversation

@khayashi4337

@khayashi4337 khayashi4337 commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

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.

Written for commit cf71763. Summary will update on new commits.

View guided diff

khayashi4337 and others added 3 commits October 22, 2026 15:42
…ams from request and avoid overwriting non-body model params

Connexion only binds path, query, body, files, and context arguments to
controller handlers and never passes header parameters, causing required
header parameters in generated controller signatures to fail with
TypeError at runtime. In addition, python-blueplanet and python-flask
unconditionally deserialized non-container model parameters from
request.get_json(), overwriting non-body model parameters such as $ref
query parameters with the request body.

Exclude header parameters from controller signatures via
x-non-header-params and read them from request headers inside the
handler, and scope request.get_json() deserialization to body parameters
while deserializing non-body model parameters from their dict value.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…arams, header coercion/defaults, and non-body container params

Address Codex review findings on Connexion parameter handling:
- Scope request.get_json() deserialization for array and map parameters
  in python-flask and python-blueplanet to body parameters, deserializing
  non-body list/dict arguments from their passed value.
- Convert integer, number,boolean, and JSON-content header/cookie
  parameters read from request headers/cookies and honor schema default
  values when omitted.
- Use baseName instead of paramName for header keys in generated
  controller_test.mustache across python-aiohttp, python-blueplanet, and
  python-flask so required headers pass Connexion validation.
- Exclude cookie parameters from controller signatures and read them
  from request.cookies.get('{{baseName}}') inside the handler.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…faults as Python literals and support array/JSON cookies

- Format array schema defaults as valid Python list literals (quoting
  strings and capitalizing True/False for both Jackson ArrayNode and
  List defaults) so header/cookie array defaults do not emit bare
  identifiers or lowercase booleans.
- Split non-JSON array header and cookie values on ',' and coerce
  integer/number/boolean array items, and decode JSON-content cookies
  via json.loads before model deserialization.
- Guard non-body date, datetime, and model array comprehensions in
  python-aiohttp with isinstance(..., list).
- Rename the x-non-header-params vendor extension to x-handler-params to
  reflect that both header and cookie parameters are excluded.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1 issue found across 13 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache">

<violation number="1" location="modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache:64">
P2: Header/cookie lookups leave non-JSON complex array parameters as raw strings, so valid simple-style arrays of model items never become `List[Model]`. Parse the parameter’s wire representation before the model-item conversion, or retain Connexion’s converted value.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

"""
{{#allParams}}
{{#isHeaderParam}}
{{paramName}} = request.headers.get('{{baseName}}'{{#defaultValue}}, {{{defaultValue}}}{{/defaultValue}})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Header/cookie lookups leave non-JSON complex array parameters as raw strings, so valid simple-style arrays of model items never become List[Model]. Parse the parameter’s wire representation before the model-item conversion, or retain Connexion’s converted value.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At modules/openapi-generator/src/main/resources/python-aiohttp/controller.mustache, line 64:

<comment>Header/cookie lookups leave non-JSON complex array parameters as raw strings, so valid simple-style arrays of model items never become `List[Model]`. Parse the parameter’s wire representation before the model-item conversion, or retain Connexion’s converted value.</comment>

<file context>
@@ -57,6 +60,44 @@ async def {{operationId}}(request: web.Request, {{#allParams}}{{paramName}}{{^re
     """
     {{#allParams}}
+        {{#isHeaderParam}}
+    {{paramName}} = request.headers.get('{{baseName}}'{{#defaultValue}}, {{{defaultValue}}}{{/defaultValue}})
+        {{/isHeaderParam}}
+        {{#isCookieParam}}
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Partially addressed in cf71763. Arrays whose items reference an enum or a primitive alias (which also have a complexType but aren't models) are now split on , like other primitive arrays before conversion.

For arrays of object models I've intentionally left the raw string alone. OpenAPI's parameter serialization (style: simple for headers, style: form for cookies) defines primitives, arrays of primitives and a single flat object, but no wire format for an array of objects; those need content: application/json, which is already decoded with json.loads before from_dict. Splitting such a value on , would produce List[str], and Model.from_dict(str) returns an empty model, which silently discards the data. Happy to change this if you'd prefer a different behavior.

… params, format nested array defaults, and split enum-ref array headers

Address cubic-dev-ai review findings on PR OpenAPITools#25362:
- Guard non-container date and date-time deserialization with
  `if <param> is not None:` across python-aiohttp, python-blueplanet,
  and python-flask so omitted optional date/date-time headers or cookies
  do not pass None into dateutil.parser.parse().
- Recursively format object and nested array elements in array schema
  defaults as Python dict and list literals instead of calling
  JsonNode.asText() (which returns '' for container nodes).
- Split non-JSON array header/cookie values when items are non-object
  referenced schemas (such as $ref enum models where !items.isModel) while
  keeping object-model arrays unsplit unless JSON content is used.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
khayashi4337 and others added 3 commits October 26, 2026 13:22
…ion-param-sources

Picks up OpenAPITools#25372 (pytest-aiohttp loop fixture fix) so the Python Server CI
samples run against current master.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ct.from_dict for free-form object params

A free-form 'type: object' (or untyped) query/header parameter has the
builtin 'object' as its type, so the non-body conversion rendered
'object.from_dict(x)'. Connexion passes {} for an absent object query
parameter, so every request to such an operation failed with
AttributeError (500). Skip the conversion when the parameter's dataType is
'object'; named models (including an allOf model without a top-level
type, which is flagged isAnyType) are still converted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion-param-sources

Resolves conflicts with OpenAPITools#25361 (free-form params skip from_dict): keep the
x-python-connexion-skip-from-dict guard from master on both the body and
the non-body conversion, drop this branch's duplicate of that check, and
update the composed-query test to expect query params to be converted from
their own value rather than from the request body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@wing328
wing328 merged commit 1f711a2 into OpenAPITools:master Oct 26, 2026
17 checks passed
@wing328 wing328 added this to the 7.27.0 milestone Oct 26, 2026
khayashi4337 added a commit to khayashi4337/openapi-generator that referenced this pull request Oct 28, 2026
…k-param-typing

Resolves conflicts with OpenAPITools#25361/OpenAPITools#25362:
- postProcessConnexionOperationMap (introduced here so webhooks share the
  Connexion post-processing) now also receives the enclosing map, because
  OpenAPITools#25362 sets the hasJsonHeaderParam flag on it; this merged silently but
  did not compile.
- The webhook controller test now expects header parameters to be read
  from the request (OpenAPITools#25362) instead of being handler arguments.
- Keep both sets of new tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
khayashi4337 added a commit to khayashi4337/openapi-generator that referenced this pull request Oct 28, 2026
…ion-header-content

Resolves conflicts with OpenAPITools#25362: master now splits the body and non-body
model conversion; keep that structure and apply this branch's dataType
fallback (for a null baseType) to the body line as well. The blueplanet
JSON-header test now expects the header to be read from the request
headers (OpenAPITools#25362) instead of the request body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
khayashi4337 added a commit to khayashi4337/openapi-generator that referenced this pull request Oct 28, 2026
…ion-test-example-names

Resolves the test-file conflict with OpenAPITools#25362 by keeping both new tests.

Also covers the defaultValue path of setParameterExampleValue (cubic
review): a model-typed parameter whose schema has a default keeps the
default as its example instead of being turned into a constructor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
khayashi4337 added a commit to khayashi4337/openapi-generator that referenced this pull request Oct 28, 2026
…xion-object-params

OpenAPITools#25362 was squash-merged, so its files conflict with this branch's copy of
the original commits. Master's versions of the conflicting files are
identical to OpenAPITools#25362's head (cd3fe31), so this branch's versions are
kept; the diff against master is now only the object-parameter commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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