Skip to content

fix(python-flask, python-aiohttp): name handler arguments the way Connexion passes them - #25450

Merged
wing328 merged 5 commits into
OpenAPITools:masterfrom
khayashi4337:fix/python-connexion-builtin-param-names
Nov 3, 2026
Merged

wing328 merged 5 commits into
OpenAPITools:masterfrom
khayashi4337:fix/python-connexion-builtin-param-names

Conversation

@khayashi4337

@khayashi4337 khayashi4337 commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

With pythonic_params=True, which the generated python-flask and python-aiohttp servers use, Connexion renames parameters with snake_and_shadow(): snake_case, then a trailing _ if the name is a Python builtin or, since Connexion 2.10, a keyword. The generator named the handler arguments differently. It added a leading _ for its own reserved words and left builtins unchanged. As a result:

  • a path parameter id produced def get_thing(id, ...) while Connexion calls get_thing(id_=...), so every request fails with TypeError (500);
  • query parameters such as filter, type or format are silently dropped.

Changes

AbstractPythonConnexionServerCodegen.toParamName():

  • appends _ to Python builtins (a fixed list of the lowercase dir(builtins) names; it matches Python 3.11 and 3.14 exactly) and to keywords, like Connexion;
  • keeps leading underscores (_from stays _from) and passes callback through unchanged, as Connexion does;
  • keeps the previous naming for header and cookie parameters. Connexion never passes these to the handler (they are read from the request), so they keep a name such as _json that doesn't shadow modules like json.

python-blueplanet opts out (usesPythonicParams() returns false). Its generated app doesn't set pythonic_params, so Connexion 1.x passes the original names.

The wire names don't change: x-python-connexion-openapi-name restores them in the generated openapi.yaml, now also for parameters defined under components/parameters. The generated controller tests send form data under the wire names too.

A side effect: names that the generator reserved but that are neither builtins nor keywords (date, self, schema, ...) now become date instead of _date, which is what Connexion passes.

Verification

  • Python*Test / AbstractPython*Test pass. The new tests cover flask, aiohttp and blueplanet signatures, _from, callback, date/self, a JSON header named json, a $ref parameter, and form keys.
  • Generated python-flask and python-aiohttp servers from a spec with path id and query filter, type, format, from, pageSize, plus _from, callback, date, self and a JSON header json. On Connexion 2.14.2, the requests return 200 with every value delivered (id_, filter_, type_, format_, from_, page_size, _from, callback, date, self, _json). With the generator from master, the same GET /probe/abc fails with TypeError: unexpected keyword argument 'id_'.
  • bin/generate-samples.sh bin/configs/python*.yaml produces no diff; none of the sample specs use such names.

Known remaining differences / not in this PR

  • Names that Connexion's inflection.underscore() and the generator's underscore() still convert differently: acronyms (HTTPId), names with dots or other separators, and names starting with a digit. A spec with both id and id_ can't work either, because Connexion maps both to id_.
  • The builtins list follows current Pythons: aiter/anext (3.10+) and breakpoint (3.7+) would still mismatch on older interpreters.
  • Pre-existing and unchanged: application/x-www-form-urlencoded request bodies fail on master too (missing 1 required positional argument). Connexion passes the form as a single body argument, while the generated handler expects one argument per field.

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
    
    Regenerated all bin/configs/python*.yaml samples: no diff. 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, with reviews by Codex and Fable) and reviewed and verified by me.

🤖 Generated with Claude Code


Summary by cubic

Fixes generated python-flask and python-aiohttp handlers to name arguments the way Connexion actually passes them (pythonic_params=True). Previously, a path param id became id in the signature while Connexion sent id_, causing a TypeError (500) on every request; query params like filter, type, and format were silently dropped because the handler argument names didn't match.

  • Builtins and keywords now get a trailing underscore (id_, filter_, from_), matching Connexion's snake_and_shadow().
  • Header and cookie params keep the previous naming, since Connexion reads them from the request and never passes them to the handler.
  • Wire names in the generated openapi.yaml are preserved via the existing x-python-connexion-openapi-name extension, now also for parameters under components/parameters.
  • Generated controller tests send form data under the wire names.
  • python-blueplanet opts out of the new naming because its generated app doesn't set pythonic_params (Connexion 1.x).

Written for commit 0090f1f. Summary will update on new commits.

View guided diff

…nexion passes them

With pythonic_params=True, which the generated python-flask and
python-aiohttp servers use, Connexion renames parameters with
snake_and_shadow(): snake_case, then a trailing '_' if the name is a
Python builtin (or, since Connexion 2.10, a keyword). The generator named
the handler arguments differently: a leading '_' for its own reserved
words, and builtins unchanged. A path parameter 'id' therefore produced
'def get_thing(id, ...)' while Connexion called it with 'id_=...', so
every request failed with a TypeError (500). Query parameters such as
'filter', 'type' or 'format' were silently dropped.

AbstractPythonConnexionServerCodegen.toParamName() now:
- appends '_' to Python builtins (a fixed list of the lowercase
  dir(builtins) names) and keywords, like Connexion;
- keeps leading underscores ('_from' stays '_from') and passes 'callback'
  through, as Connexion does;
- keeps the previous naming for header and cookie parameters, which
  Connexion never passes to the handler (they are read from the request),
  so that e.g. a JSON header named 'json' doesn't shadow the json module.
python-blueplanet opts out (usesPythonicParams() = false): its generated
app doesn't set pythonic_params, so Connexion 1.x passes the original
names.

The wire names are unchanged: x-python-connexion-openapi-name restores
them in the generated openapi.yaml, now also for parameters under
components/parameters. Generated controller tests send form data under
the wire names.

Remaining known differences from Connexion's naming: acronyms (HTTPId),
names with dots or other separators, names starting with a digit, and
specs that have both 'id' and 'id_' (Connexion can't tell them apart
either).

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.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 7 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/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java:58">
P2: The hardcoded builtin list is newer than the supported Python floor: Python 3.5 does not define `aiter`, `anext`, or `breakpoint`, but this generator still emits suffixed handler names that Connexion will not pass. Keep the reserved-name set compatible with the supported interpreter floor or derive it from the same runtime/version constraints as Connexion.</violation>
</file>

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

View guided diff | Re-trigger cubic

public abstract class AbstractPythonConnexionServerCodegen extends AbstractPythonCodegen implements CodegenConfig {
protected static final Set<String> PYTHON_BUILTINS = Set.of(
"abs", "aiter", "all", "anext", "any", "ascii", "bin", "bool",
"breakpoint", "bytearray", "bytes", "callable", "chr", "classmethod",

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: The hardcoded builtin list is newer than the supported Python floor: Python 3.5 does not define aiter, anext, or breakpoint, but this generator still emits suffixed handler names that Connexion will not pass. Keep the reserved-name set compatible with the supported interpreter floor or derive it from the same runtime/version constraints as Connexion.

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/java/org/openapitools/codegen/languages/AbstractPythonConnexionServerCodegen.java, line 58:

<comment>The hardcoded builtin list is newer than the supported Python floor: Python 3.5 does not define `aiter`, `anext`, or `breakpoint`, but this generator still emits suffixed handler names that Connexion will not pass. Keep the reserved-name set compatible with the supported interpreter floor or derive it from the same runtime/version constraints as Connexion.</comment>

<file context>
@@ -50,8 +50,22 @@
 public abstract class AbstractPythonConnexionServerCodegen extends AbstractPythonCodegen implements CodegenConfig {
+    protected static final Set<String> PYTHON_BUILTINS = Set.of(
+            "abs", "aiter", "all", "anext", "any", "ascii", "bin", "bool",
+            "breakpoint", "bytearray", "bytes", "callable", "chr", "classmethod",
+            "compile", "complex", "copyright", "credits", "delattr", "dict",
+            "dir", "divmod", "enumerate", "eval", "exec", "exit", "filter",
</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.

Valid, but I've left the list as is on purpose. Connexion decides at runtime, from the builtins of the Python it's running on, so a fixed list can't match every interpreter: whichever version I pick, some other version disagrees. Tracking the oldest Python would make aiter/anext (3.10+) and breakpoint (3.7+) mismatch on every current interpreter instead.

I followed current Pythons, because the generated servers depend on Connexion >= 2.6, which needs Python >= 3.6. Parameters with these three names are also very unlikely. The PR description already listed aiter/anext as a known difference, and I've added breakpoint there too.

khayashi4337 and others added 4 commits November 2, 2026 17:18
…ion-builtin-param-names

# Conflicts:
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/python/AbstractPythonConnexionServerCodegenTest.java
The merge commit accidentally staged the developer-local .mvn/maven.config
which hard-codes /mnt/dev-env paths and breaks CI environments.

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

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

Resolve the conflict in PythonFlaskConnexionServerCodegenTest by keeping
both sides' new tests: this branch's builtin/keyword parameter name test
and upstream's object-typed parameter tests.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ion-builtin-param-names

Resolve conflicts by keeping both intents: the header/cookie paramName
reassignment for builtin-shadowing safety alongside upstream's extracted
isJsonContentParam helper, and the shared generateOutputDir overload in the
test (identical implementations, upstream signature kept).

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@wing328
wing328 merged commit 78199b1 into OpenAPITools:master Nov 3, 2026
15 checks passed
@wing328 wing328 added this to the 7.28.0 milestone Nov 3, 2026
wing328 pushed a commit that referenced this pull request Nov 4, 2026
…lerTestModelExampleUsesImportedName (#25552)

The test was disabled in #25519 because its query_string assertion
expected the pre-#25485 rendering ('filter', Filter()). Object-typed
query/header parameters are now sent in their OpenAPI wire style
(form/explode pairs for the query object, a simple-style value for the
header), and the body parameter is renamed to filter_ per #25450.
Update the expectations to the current generated output while keeping
the test's purpose: model examples still use the imported class name
([Filter()], {'key': Filter()}, filter_ = Filter()), never the
package-qualified openapi_server.Filter()/openapi_server.Trace().

Co-authored-by: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants