Skip to content

fix(python-flask, python-aiohttp, python-blueplanet): don't HTML-escape names inside generated Python string literals - #25457

Open
khayashi4337 wants to merge 8 commits into
OpenAPITools:masterfrom
khayashi4337:fix/python-connexion-model-attribute-names
Open

khayashi4337 wants to merge 8 commits into
OpenAPITools:masterfrom
khayashi4337:fix/python-connexion-model-attribute-names

Conversation

@khayashi4337

@khayashi4337 khayashi4337 commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Mustache's {{...}} HTML-escapes its value. The Connexion server templates write property and parameter names into Python string literals that way, so a model property named a&b produces

self.attribute_map = {
    'ab': 'a&b'
}

and from_dict({'a&b': 'x'}) silently drops the value (ab is None). A ' in a name becomes '.

Changes

  • These names are now rendered with {{{...}}} from vendor extensions that hold them escaped for a single-quoted Python literal. The escaping is lossless: \, ', newline, CR, tab and other control characters. It is used for:
    • the model attribute_map (python-flask, python-aiohttp, python-blueplanet);
    • the query keys in the generated controller tests (python-flask, python-aiohttp), and the header names in python-blueplanet's generated tests.
  • Parameters get x-python-connexion-quoted-name, with the same name and meaning as in feat(python-flask, python-aiohttp): deserialize object-typed query and header parameters #25427. That PR covers the header/cookie lookups in the python-flask/python-aiohttp controllers and tests, so this PR leaves those lines alone to avoid conflicts.
  • The generated tests sent an apiKey under the security scheme's name instead of its header name (keyParamName). With scheme apiKeyAuth and header X-Api-Key, the generated test got 401. They now send keyParamName, and only for apiKeys in a header.

Verification

  • Python*Test / AbstractPython*Test pass. A new spec has properties a&b, o'k, back\slash, dq"x and unicodeé, a query parameter q&we's, a header X-A&B'Head, and an apiKey header X-A&B'Key; the test asserts the rendered names for all three generators, plus a unit test for the escaper. The tests fail without the template change.
  • For python-flask, python-aiohttp and python-blueplanet, all generated .py files pass python -m compileall. For python-flask, on Connexion 2.14.2, EscapedNames.from_dict({...}) keeps all five values, and before the change they were dropped.
  • bin/generate-samples.sh bin/configs/python*.yaml produces no diff; no sample uses such names.

Not changed (known)

  • apiKeys in a query parameter or a cookie are still not sent by the generated tests, as before.
  • info.title is still HTML-escaped where it goes into a Python literal (arguments={'title': ...}). This only affects the Swagger UI title.

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

Stops Mustache's HTML escaping from corrupting property, parameter, and apiKey names in generated Python Connexion servers. Names like a&b or o'k were written into Python string literals as a&b or o'k, so from_dict silently dropped such values and generated tests sent wrong keys. These names are now escaped losslessly for single-quoted Python literals via vendor extensions.

Bug Fixes

  • The model attribute_map in python-flask, python-aiohttp, and python-blueplanet now renders the escaped wire name; query keys, blueplanet header/cookie names in generated tests, and blueplanet controller lookups use x-python-connexion-quoted-name to match feat(python-flask, python-aiohttp): deserialize object-typed query and header parameters #25427.
  • Generated tests now send query params and apiKey tokens (in query or header) under their wire names instead of the security scheme name or the sanitized Python parameter name; apiKeys in a cookie are still not sent, and info.title remains HTML-escaped in the Swagger UI title.
  • Escaping is lossless for backslash, quote, and control characters via a new escapeForPythonSingleQuotedLiteral, covered by unit tests and generated-file assertions for all three generators.

Written for commit 2a344aa. Summary will update on new commits.

View guided diff

…pe names inside generated Python string literals

Mustache's {{...}} HTML-escapes its value. The Connexion server templates
wrote property and parameter names into Python string literals that way,
so a model property named 'a&b' produced attribute_map 'ab': 'a&b'
and from_dict() silently dropped its value. A ' in a name gave '&OpenAPITools#39;'.

These names are now rendered with {{{...}}} from vendor extensions that
hold them escaped for a single-quoted Python literal, losslessly
(backslash, quote, newline, CR, tab and other control characters):
- model attribute_map (python-flask, python-aiohttp, python-blueplanet);
- query keys in the generated controller tests (python-flask,
  python-aiohttp), and header names in python-blueplanet's generated
  tests. Parameters get x-python-connexion-quoted-name, the same name and
  meaning as in OpenAPITools#25427, which covers the header/cookie lookups in the
  python-flask/python-aiohttp controllers and tests.

The generated tests also sent an apiKey under the security scheme's name
instead of the header name (keyParamName). They now send keyParamName,
and only for apiKeys in a header. Query/cookie apiKeys are not sent, as
before.

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.

No issues found across 9 files

View guided diff | Re-trigger cubic

khayashi4337 and others added 2 commits November 2, 2026 17:23
…ion-model-attribute-names

# Conflicts:
#	modules/openapi-generator/src/main/resources/python-aiohttp/controller_test.mustache
#	modules/openapi-generator/src/main/resources/python-flask/controller_test.mustache
#	modules/openapi-generator/src/test/java/org/openapitools/codegen/python/AbstractPythonConnexionServerCodegenTest.java
Merge the two fromSecurity overrides produced by the upstream merge so
both the security-function-suffix and quoted-key-param-name extensions
are set. Emit the escaped wire name (openapi-name or baseName) for
query params and apiKey-in-query entries in x-test-query-string, and
update the escaped-names test to expect the apikey query credential
that the new upstream behavior correctly sends.

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.

All reported issues were addressed across 2 files (changes from recent commits).

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

View guided diff | Re-trigger cubic

khayashi4337 and others added 3 commits November 2, 2026 18:14
Mustache HTML-escapes {{name}}, so wire names containing &, ', <, > or "
were rendered as entities and the second query_string assignment sent the
escaped name instead of the real one. Use {{{name}}} like the other
string interpolations in these templates.

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

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

Resolve conflicts by combining both sides: keep this branch's lossless
escapeForPythonSingleQuotedLiteral (also applied to upstream's new
per-copy X_QUOTED_NAME loop so the value round-trips) and its
quoted-key-param-name extension, while adopting upstream's
quoted-name lookup for header params in the generated controller tests.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ames in the generated controller and test

The generated blueplanet controller read headers and cookies via
connexion.request.headers.get('{{baseName}}') / cookies.get('{{baseName}}'),
and the generated test sent query parameters under the sanitized
{{paramName}}. {{baseName}} is HTML-escaped by mustache, so names such as
"X-A&B'Head" or "X-C&oo'kie" were emitted as "X-A&amp;B&OpenAPITools#39;Head" and the
lookup could never match; the query test line sent the sanitized name
("qwes") instead of the wire name ("q&we's").

Use the x-python-connexion-quoted-name vendor extension (already populated
for every parameter by AbstractPythonConnexionServerCodegen) in all three
places, matching python-flask and python-aiohttp. Add a cookie parameter to
the escaped-names test spec, assert the generated controller and test emit
the original names escaped for single-quoted Python literals, and py_compile
the generated controller and test.

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

Copy link
Copy Markdown
Contributor Author

Follow-up commit d004e66 for the python-blueplanet server, which was still using HTML-escaped names where python-flask and python-aiohttp already emit the escaped Python literal:

  • controllers/controller.mustache: headers.get('{{baseName}}' / cookies.get('{{baseName}}' now use '{{{vendorExtensions.x-python-connexion-quoted-name}}}', like python-flask. A header named X-A&B'Head used to be looked up as X-A&amp;B&#39;Head and is now X-A&B\'Head.
  • test/controller_test.mustache: the generated test sent the sanitized parameter name as the query name; it now sends the wire name (q&we\'s).
  • escaped-names.yaml gained a cookie parameter (X-C&oo'kie); the escaped-names test now also checks the blueplanet controller and test, and runs python3 -m py_compile on them (the syntax check runs after the content assertions and is skipped when python3 is not available). With the template change reverted, the new assertion fails.

CI on the new head is green (11/11).

khayashi4337 and others added 2 commits November 3, 2026 15:11
…ion-model-attribute-names

Resolve controller_test.mustache conflicts by combining both intents:
keep the quoted wire names (x-python-connexion-quoted-name /
x-python-connexion-quoted-key-param-name, no HTML escaping) while taking
upstream's x-python-connexion-test-header-value conditionals and the new
cookie test blocks. Align upstream's added cookie/header lines in
python-blueplanet to the quoted-name form too, and drop the per-param
query iteration upstream replaced with x-test-query-string.

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

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

This branch has not been deployed

No deployments
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.

1 participant