Commit 0798fde
committed
feat(python-flask): add opt-in Connexion 3 support
Adds a new `useConnexion3` boolean generator option (default: false) to
the python-flask server generator, addressing #17303. Connexion 3 has
been out since 2023, but requirements.mustache explicitly pinned
`connexion<=2.14.2` and `Flask==2.1.1` to avoid it, blocking users from
picking up newer Flask/Werkzeug (one comment on the issue specifically
cited this as blocking a CVE fix in werkzeug). The maintainer has
repeatedly invited a contribution on the thread since Dec 2023, and
several community members had already prototyped working fixes in the
comments.
Kept as an opt-in flag rather than a default bump, following this
repo's existing convention for breaking generator-output changes
(useJackson3, useSpringBoot3/4).
What changes under the flag, and why:
- requirements.mustache / setup.mustache: swap the Connexion 2/Flask
2.1.1 pins for `connexion[flask,swagger-ui,uvicorn]>=3.3.0,<4.0.0` +
`Flask>=2.2.0,<4.0.0`. The uvicorn extra is required because
Connexion 3's `FlaskApp.run()` launches via uvicorn even for Flask
apps -- confirmed by actually running the generated server, which
fails at startup without it. The connexion floor is 3.3.0 (not just
the first 3.0.0 release) because that's the only version we've
actually run and verified, and it's also the first release with
official Python 3.13/3.14 support per Connexion's own release notes.
swagger-ui-bundle is bumped to >=1.1.0 to match the floor Connexion's
own swagger-ui extra already silently requires.
- __main__.mustache: `connexion.App` -> `connexion.FlaskApp`, and the
JSON encoder moves from a `Flask.json_encoder` attribute assignment
(removed in v3) to a `Jsonifier(cls=...)` passed into `add_api()`.
- encoder.mustache: the generated `JSONEncoder` becomes a plain
`json.JSONEncoder` subclass instead of extending Connexion 2's
`FlaskJSONEncoder` (removed in v3); the `default()` body handling
`Model.to_dict()` conversion is unchanged. Verified end-to-end (not
just unit-level): patched a controller to return a real nested
Pet/Category model instance, ran the actual generated server
(uvicorn + Flask + Connexion 3) in Docker, and curled it -- got back
correctly serialized JSON with attribute_map key translation intact
(photo_urls -> photoUrls).
- __init__test.mustache: Connexion 3's `app.test_client()` returns an
httpx/Starlette-based client, not Flask's WSGI test client -- so a
small `_FlaskStyleTestClient`/`_FlaskStyleResponse` adapter is added
to keep the existing `self.client.open(...)`/`self.assert200(...)`
calling convention in controller_test.mustache working unchanged.
Also drops `flask_testing.TestCase`, which is unmaintained since 2020
and not Flask-3-compatible.
- test-requirements.mustache: drops the `Flask-Testing` pin under the
flag, since Flask's own test client no longer needs it.
- CORS support (featureCORS): `flask_cors.CORS(app.app)` does not work
under Connexion 3, because Connexion 3 wraps the Flask app in its own
ASGI middleware stack and can route/short-circuit requests before
they ever reach the inner WSGI app flask-cors is watching. Confirmed
this was actually broken (zero Access-Control-* headers on a real
request with an Origin header) before fixing it. Replaced with
Connexion 3's own documented pattern --
`app.add_middleware(CORSMiddleware, ...)` from
`starlette.middleware.cors`, which comes for free as a connexion
dependency, no separate package needed. flask-cors itself is no
longer installed at all under useConnexion3. Reverified afterwards:
Access-Control-Allow-Origin and Access-Control-Allow-Credentials both
present on the response.
One unconditional (non-flag-gated) fix: controller.mustache swaps
`connexion.request.is_json`/`.get_json()` for Flask's own
`from flask import request`. Connexion 3's `connexion.request` is now a
Starlette Request and no longer exposes those Flask-specific methods.
Flask's own `request` object behaves identically under Connexion 2 and
3, so this is applied to both, and is a dependency-reduction as a side
effect. This is the only part of the diff that touches the existing
default sample output.
Also fixes #15062 (python-flask docs never explained *why* the
generator uses Connexion instead of vanilla Flask) via a `getHelp()`
override scoped to the Flask subclass, bundled in here since it's a
one-line addition to a PR already touching this generator's docs.
#21294 (operationId double-underscore breaking Connexion's resolver)
is a different root cause and is intentionally left for its own PR.
Security motivation, checked concretely rather than assumed: ran
pip-audit against the full resolved dependency tree for both paths.
v2 (current default, Flask==2.1.1/Werkzeug==2.2.3 as resolved): 10 real
CVEs across Flask+Werkzeug, including the exact one raised in the issue
thread (CVE-2024-34069) plus several more recent ones. v3
(useConnexion3, Flask==3.1.3/Werkzeug==3.1.8 as resolved): zero
vulnerabilities in any actual application dependency (the only
pip-audit hits in either scan are in pip/wheel themselves -- base image
tooling, not part of the app's declared dependencies, identical in both
scans).
Also surfaced, but explicitly NOT fixed here (separate, pre-existing,
unrelated to Connexion version -- reproduces even with
`useConnexion3: false`): generating python-flask through a config file
that has any `additionalProperties` block changes
`postProcessOperationsWithModels`'s `*/*`-consumes skip-marking
behavior for a couple of operations. Worth its own issue/investigation,
out of scope for this PR.
CI coverage: added bin/configs/python-flask-connexion3.yaml alongside
the existing bin/configs/python-flask.yaml, following the same pattern
used for other flag-gated variants (e.g. the *-jackson3.yaml configs),
so the "Samples up-to-date" job continuously verifies the new flag's
generated output.
Verification performed locally (this environment has no JDK/toolchain
installed, so all of the below ran inside Docker containers rather
than bare-metal):
- `mvn -pl modules/openapi-generator -am package` -- compiles clean.
- The full repo's CI "Unit tests" job command, run verbatim
(`mvn clean --no-snapshot-updates --batch-mode --quiet --fail-at-end
test`) across the whole reactor: 4493 tests run, 0 failures, 0
errors, 7 skipped (confirmed via real surefire report files, not just
the process exit code).
- Full `bin/generate-samples.sh` (all ~766 generators, no args) run
twice in a row, mirroring the "Samples up-to-date" CI job exactly:
zero diff outside the files intentionally touched by this change.
- Docs regenerated via `bin/utils/export_generator.sh python-flask`
(not the full "Docs up-to-date" job across every generator, since
this change only touches python-flask's own CliOptions/getHelp()).
- Built and ran the generated `useConnexion3: true` sample's own
Dockerfile; server starts, serves `/v2/openapi.json`, returns a
correct 401 on an auth-protected route without credentials, and
correctly serializes a real returned model object end-to-end.
- Established a genuine Connexion-2 baseline in a separate
Python-3.11 container (the generated Dockerfile's `python:3-alpine`
base floats to Python 3.14, which breaks even the *unmodified* v2
sample for unrelated reasons -- old Werkzeug's routing code hits a
removed `ast.Str` API) to get a fair v2-vs-v3 comparison of the
generated test suite. Both pass the same 8 real operations; the v3
run's extra failures are either pre-existing test-fixture/spec bugs
unrelated to Connexion version (confirmed present on the v2 baseline
too), or a documented Connexion 3 behavior change where operations
with multiple response content types require the handler to specify
which one to return -- inherent to Connexion 3's stricter response
handling for auto-generated placeholder stubs, not something
template-level codegen changes can paper over.
Known gap, not fixed here: no CI job actually installs/runs the
generated python-flask server (the existing samples-python-server.yaml
workflow only covers python-aiohttp-srclayout) or the full "Docs
up-to-date" job across every generator, so ongoing regression coverage
for this flag's *runtime* behavior relies on the manual verification
above, not on CI.1 parent 0f12a09 commit 0798fde
55 files changed
Lines changed: 3898 additions & 36 deletions
File tree
- bin/configs
- docs/generators
- modules/openapi-generator/src/main
- java/org/openapitools/codegen/languages
- resources/python-flask
- samples/server/petstore
- python-flask-connexion3
- .openapi-generator
- openapi_server
- controllers
- models
- openapi
- test
- python-flask/openapi_server/controllers
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| 38 | + | |
38 | 39 | | |
39 | 40 | | |
40 | 41 | | |
| |||
Lines changed: 33 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| 19 | + | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
| |||
28 | 29 | | |
29 | 30 | | |
30 | 31 | | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
31 | 36 | | |
32 | 37 | | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
33 | 66 | | |
34 | 67 | | |
35 | 68 | | |
| |||
Lines changed: 55 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
2 | 5 | | |
3 | 6 | | |
| 7 | + | |
4 | 8 | | |
5 | 9 | | |
6 | 10 | | |
| |||
14 | 18 | | |
15 | 19 | | |
16 | 20 | | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
Lines changed: 36 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
4 | 12 | | |
5 | 13 | | |
6 | 14 | | |
| 15 | + | |
7 | 16 | | |
8 | 17 | | |
9 | 18 | | |
10 | 19 | | |
11 | 20 | | |
| 21 | + | |
12 | 22 | | |
13 | 23 | | |
14 | 24 | | |
15 | 25 | | |
16 | 26 | | |
17 | | - | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
18 | 50 | | |
| 51 | + | |
19 | 52 | | |
20 | 53 | | |
21 | | - | |
22 | 54 | | |
| 55 | + | |
| 56 | + | |
23 | 57 | | |
24 | 58 | | |
25 | 59 | | |
| |||
Lines changed: 15 additions & 15 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
73 | 73 | | |
74 | 74 | | |
75 | 75 | | |
76 | | - | |
77 | | - | |
| 76 | + | |
| 77 | + | |
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
84 | 84 | | |
85 | | - | |
86 | | - | |
| 85 | + | |
| 86 | + | |
87 | 87 | | |
88 | 88 | | |
89 | | - | |
90 | | - | |
| 89 | + | |
| 90 | + | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
94 | | - | |
| 93 | + | |
| 94 | + | |
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
100 | 100 | | |
101 | | - | |
102 | | - | |
| 101 | + | |
| 102 | + | |
103 | 103 | | |
104 | 104 | | |
105 | | - | |
106 | | - | |
| 105 | + | |
| 106 | + | |
107 | 107 | | |
108 | 108 | | |
109 | | - | |
110 | | - | |
| 109 | + | |
| 110 | + | |
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
| |||
Lines changed: 23 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
| |||
17 | 18 | | |
18 | 19 | | |
19 | 20 | | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
Lines changed: 22 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
1 | 2 | | |
2 | 3 | | |
3 | 4 | | |
| |||
7 | 8 | | |
8 | 9 | | |
9 | 10 | | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
10 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
11 | 26 | | |
| 27 | + | |
12 | 28 | | |
13 | 29 | | |
14 | 30 | | |
15 | 31 | | |
| 32 | + | |
16 | 33 | | |
| 34 | + | |
17 | 35 | | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
Lines changed: 6 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
17 | 23 | | |
18 | 24 | | |
19 | 25 | | |
| |||
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
| 13 | + | |
13 | 14 | | |
| 15 | + | |
0 commit comments