Skip to content

Commit 0798fde

Browse files
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

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
generatorName: python-flask
2+
outputDir: samples/server/petstore/python-flask-connexion3
3+
inputSpec: modules/openapi-generator/src/test/resources/3_0/python-flask/petstore.yaml
4+
templateDir: modules/openapi-generator/src/main/resources/python-flask
5+
additionalProperties:
6+
useConnexion3: "true"

docs/generators/python-flask.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ title: Documentation for the python-flask Generator
1212
| generator language | Python | |
1313
| generator language version | 3.5.2+ | |
1414
| generator default templating engine | mustache | |
15-
| helpTxt | Generates a Python server library using the Connexion project. By default, it will also generate service classes -- which you can disable with the `-Dnoservice` environment variable. | |
15+
| helpTxt | Generates a Python Flask server library using the Connexion project. Connexion is used (instead of hand-written Flask routes) because it maps operations in an OpenAPI/Swagger spec directly to Python functions, handling request routing, payload validation and parameter binding automatically. By default, it will also generate service classes -- which you can disable with the `-Dnoservice` environment variable. | |
1616

1717
## CONFIG OPTIONS
1818
These options may be applied as additional-properties (cli) or configOptions (plugins). Refer to [configuration docs](https://openapi-generator.tech/docs/configuration) for more details.
@@ -35,6 +35,7 @@ These options may be applied as additional-properties (cli) or configOptions (pl
3535
|sortModelPropertiesByRequiredFlag|Sort model properties to place required parameters before optional parameters.| |true|
3636
|sortParamsByRequiredFlag|Sort method arguments to place required parameters before optional parameters.| |true|
3737
|testsUsePythonSrcRoot|generates test under the pythonSrcRoot folder.| |false|
38+
|useConnexion3|Use Connexion 3.x instead of Connexion 2.x. This changes the pinned connexion/Flask/Flask-Testing dependency versions and switches the generated encoder, __main__ and test bootstrap code to Connexion 3's APIs. This is a breaking change to the generated output, so it defaults to false to preserve existing Connexion 2.x behavior.| |false|
3839
|useNose|use the nose test framework| |false|
3940
|usePythonSrcRootInImports|include pythonSrcRoot in import namespaces.| |false|
4041

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonFlaskConnexionServerCodegen.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.openapitools.codegen.languages;
1818

19+
import org.openapitools.codegen.CliOption;
1920
import org.openapitools.codegen.SupportingFile;
2021
import org.slf4j.Logger;
2122
import org.slf4j.LoggerFactory;
@@ -28,8 +29,40 @@
2829
public class PythonFlaskConnexionServerCodegen extends AbstractPythonConnexionServerCodegen {
2930
private final Logger LOGGER = LoggerFactory.getLogger(PythonFlaskConnexionServerCodegen.class);
3031

32+
public static final String USE_CONNEXION_3 = "useConnexion3";
33+
34+
protected boolean useConnexion3 = false;
35+
3136
public PythonFlaskConnexionServerCodegen() {
3237
super("python-flask", false);
38+
39+
cliOptions.add(CliOption.newBoolean(USE_CONNEXION_3,
40+
"Use Connexion 3.x instead of Connexion 2.x. This changes the pinned "
41+
+ "connexion/Flask/Flask-Testing dependency versions and switches the "
42+
+ "generated encoder, __main__ and test bootstrap code to Connexion 3's "
43+
+ "APIs. This is a breaking change to the generated output, so it defaults "
44+
+ "to false to preserve existing Connexion 2.x behavior.",
45+
useConnexion3).defaultValue(Boolean.toString(useConnexion3)));
46+
}
47+
48+
@Override
49+
public void processOpts() {
50+
super.processOpts();
51+
52+
if (additionalProperties.containsKey(USE_CONNEXION_3)) {
53+
this.useConnexion3 = Boolean.parseBoolean(additionalProperties.get(USE_CONNEXION_3).toString());
54+
}
55+
additionalProperties.put(USE_CONNEXION_3, useConnexion3);
56+
}
57+
58+
@Override
59+
public String getHelp() {
60+
return "Generates a Python Flask server library using the Connexion project. Connexion is "
61+
+ "used (instead of hand-written Flask routes) because it maps operations in an "
62+
+ "OpenAPI/Swagger spec directly to Python functions, handling request routing, "
63+
+ "payload validation and parameter binding automatically. By default, it will also "
64+
+ "generate service classes -- which you can disable with the `-Dnoservice` "
65+
+ "environment variable.";
3366
}
3467

3568
/**

modules/openapi-generator/src/main/resources/python-flask/__init__test.mustache

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import logging
2+
{{#useConnexion3}}
3+
import unittest
4+
{{/useConnexion3}}
25

36
import connexion
7+
{{^useConnexion3}}
48
from flask_testing import TestCase
59

610
from {{packageName}}.encoder import JSONEncoder
@@ -14,3 +18,54 @@ class BaseTestCase(TestCase):
1418
app.app.json_encoder = JSONEncoder
1519
app.add_api('openapi.yaml', pythonic_params=True)
1620
return app.app
21+
{{/useConnexion3}}
22+
{{#useConnexion3}}
23+
from connexion.jsonifier import Jsonifier
24+
25+
from {{packageName}}.encoder import JSONEncoder
26+
27+
28+
class _FlaskStyleResponse:
29+
"""Adapts Connexion 3's httpx/Starlette test response to the
30+
status_code/data attributes the generated controller tests expect."""
31+
32+
def __init__(self, response):
33+
self._response = response
34+
self.status_code = response.status_code
35+
self.data = response.content
36+
37+
38+
class _FlaskStyleTestClient:
39+
"""Adapts Connexion 3's `app.test_client()` (httpx/Starlette-based) to
40+
the Flask-style `.open(path, method=, headers=, data=, content_type=,
41+
query_string=)` calling convention the generated controller tests use."""
42+
43+
def __init__(self, client):
44+
self._client = client
45+
46+
def open(self, path, method='GET', headers=None, data=None,
47+
content_type=None, query_string=None):
48+
headers = dict(headers or {})
49+
if content_type:
50+
headers['Content-Type'] = content_type
51+
response = self._client.request(
52+
method, path, headers=headers, content=data, params=query_string)
53+
return _FlaskStyleResponse(response)
54+
55+
56+
class BaseTestCase(unittest.TestCase):
57+
58+
def setUp(self):
59+
logging.getLogger('connexion.operation').setLevel('ERROR')
60+
app = connexion.FlaskApp(__name__, specification_dir='../openapi/')
61+
app.add_api('openapi.yaml', pythonic_params=True,
62+
jsonifier=Jsonifier(cls=JSONEncoder))
63+
self._raw_client_cm = app.test_client()
64+
self.client = _FlaskStyleTestClient(self._raw_client_cm.__enter__())
65+
66+
def tearDown(self):
67+
self._raw_client_cm.__exit__(None, None, None)
68+
69+
def assert200(self, response, message=None):
70+
self.assertEqual(200, response.status_code, message)
71+
{{/useConnexion3}}

modules/openapi-generator/src/main/resources/python-flask/__main__.mustache

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,59 @@
11
#!/usr/bin/env python3
22

33
import connexion
4+
{{#useConnexion3}}
5+
from connexion.jsonifier import Jsonifier
6+
{{#featureCORS}}
7+
from connexion.middleware import MiddlewarePosition
8+
from starlette.middleware.cors import CORSMiddleware
9+
{{/featureCORS}}
10+
{{/useConnexion3}}
11+
{{^useConnexion3}}
412
{{#featureCORS}}
513
from flask_cors import CORS
614
{{/featureCORS}}
15+
{{/useConnexion3}}
716

817
from {{packageName}} import encoder
918

1019

1120
def main():
21+
{{^useConnexion3}}
1222
app = connexion.App(__name__, specification_dir='./openapi/')
1323
app.app.json_encoder = encoder.JSONEncoder
1424
app.add_api('openapi.yaml',
1525
arguments={'title': '{{appName}}'},
1626
pythonic_params=True)
17-
27+
{{/useConnexion3}}
28+
{{#useConnexion3}}
29+
app = connexion.FlaskApp(__name__, specification_dir='./openapi/')
30+
{{#featureCORS}}
31+
# add CORS support. Connexion 3 wraps the Flask app in its own ASGI
32+
# middleware stack, so flask-cors (which only sees requests after
33+
# they reach the inner WSGI app) never runs -- CORS has to be added
34+
# as ASGI middleware instead.
35+
app.add_middleware(
36+
CORSMiddleware,
37+
position=MiddlewarePosition.BEFORE_EXCEPTION,
38+
allow_origins=["*"],
39+
allow_credentials=True,
40+
allow_methods=["*"],
41+
allow_headers=["*"],
42+
)
43+
{{/featureCORS}}
44+
app.add_api('openapi.yaml',
45+
arguments={'title': '{{appName}}'},
46+
pythonic_params=True,
47+
jsonifier=Jsonifier(cls=encoder.JSONEncoder))
48+
{{/useConnexion3}}
49+
{{^useConnexion3}}
1850
{{#featureCORS}}
51+
1952
# add CORS support
2053
CORS(app.app)
21-
2254
{{/featureCORS}}
55+
{{/useConnexion3}}
56+
2357
app.run(port={{serverPort}})
2458

2559

modules/openapi-generator/src/main/resources/python-flask/controller.mustache

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import connexion
1+
from flask import request
22
from typing import Dict
33
from typing import Tuple
44
from typing import Union
@@ -73,41 +73,41 @@ def {{operationId}}({{#allParams}}{{^isBodyParam}}{{paramName}}{{/isBodyParam}}{
7373
{{^isPrimitiveType}}
7474
{{^isFile}}
7575
{{^isUuid}}
76-
if connexion.request.is_json:
77-
{{paramName}} = {{baseType}}{{^baseType}}{{#dataType}} {{.}}{{/dataType}}{{/baseType}}.from_dict(connexion.request.get_json()) # noqa: E501
76+
if request.is_json:
77+
{{paramName}} = {{baseType}}{{^baseType}}{{#dataType}} {{.}}{{/dataType}}{{/baseType}}.from_dict(request.get_json()) # noqa: E501
7878
{{/isUuid}}
7979
{{/isFile}}
8080
{{/isPrimitiveType}}
8181
{{/isContainer}}
8282
{{#isArray}}
8383
{{#items}}
8484
{{#isDate}}
85-
if connexion.request.is_json:
86-
{{paramName}} = [util.deserialize_date(s) for s in connexion.request.get_json()] # noqa: E501
85+
if request.is_json:
86+
{{paramName}} = [util.deserialize_date(s) for s in request.get_json()] # noqa: E501
8787
{{/isDate}}
8888
{{#isDateTime}}
89-
if connexion.request.is_json:
90-
{{paramName}} = [util.deserialize_datetime(s) for s in connexion.request.get_json()] # noqa: E501
89+
if request.is_json:
90+
{{paramName}} = [util.deserialize_datetime(s) for s in request.get_json()] # noqa: E501
9191
{{/isDateTime}}
9292
{{#complexType}}
93-
if connexion.request.is_json:
94-
{{paramName}} = [{{complexType}}.from_dict(d) for d in connexion.request.get_json()] # noqa: E501
93+
if request.is_json:
94+
{{paramName}} = [{{complexType}}.from_dict(d) for d in request.get_json()] # noqa: E501
9595
{{/complexType}}
9696
{{/items}}
9797
{{/isArray}}
9898
{{#isMap}}
9999
{{#items}}
100100
{{#isDate}}
101-
if connexion.request.is_json:
102-
{{paramName}} = {k: util.deserialize_date(v) for k, v in connexion.request.get_json().items()} # noqa: E501
101+
if request.is_json:
102+
{{paramName}} = {k: util.deserialize_date(v) for k, v in request.get_json().items()} # noqa: E501
103103
{{/isDate}}
104104
{{#isDateTime}}
105-
if connexion.request.is_json:
106-
{{paramName}} = {k: util.deserialize_datetime(v) for k, v in connexion.request.get_json().items()} # noqa: E501
105+
if request.is_json:
106+
{{paramName}} = {k: util.deserialize_datetime(v) for k, v in request.get_json().items()} # noqa: E501
107107
{{/isDateTime}}
108108
{{#complexType}}
109-
if connexion.request.is_json:
110-
{{paramName}} = {k: {{baseType}}.from_dict(v) for k, v in connexion.request.get_json().items()} # noqa: E501
109+
if request.is_json:
110+
{{paramName}} = {k: {{baseType}}.from_dict(v) for k, v in request.get_json().items()} # noqa: E501
111111
{{/complexType}}
112112
{{/items}}
113113
{{/isMap}}

modules/openapi-generator/src/main/resources/python-flask/encoder.mustache

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{{^useConnexion3}}
12
from connexion.apps.flask_app import FlaskJSONEncoder
23

34
from {{modelPackage}}.base_model import Model
@@ -17,3 +18,25 @@ class JSONEncoder(FlaskJSONEncoder):
1718
dikt[attr] = value
1819
return dikt
1920
return FlaskJSONEncoder.default(self, o)
21+
{{/useConnexion3}}
22+
{{#useConnexion3}}
23+
import json
24+
25+
from {{modelPackage}}.base_model import Model
26+
27+
28+
class JSONEncoder(json.JSONEncoder):
29+
include_nulls = False
30+
31+
def default(self, o):
32+
if isinstance(o, Model):
33+
dikt = {}
34+
for attr in o.openapi_types:
35+
value = getattr(o, attr)
36+
if value is None and not self.include_nulls:
37+
continue
38+
attr = o.attribute_map[attr]
39+
dikt[attr] = value
40+
return dikt
41+
return json.JSONEncoder.default(self, o)
42+
{{/useConnexion3}}

modules/openapi-generator/src/main/resources/python-flask/requirements.mustache

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{{^useConnexion3}}
12
connexion[swagger-ui] >= 2.6.0; python_version>="3.6"
23
# 2.3 is the last version that supports python 3.4-3.5
34
connexion[swagger-ui] <= 2.3.0; python_version=="3.5" or python_version=="3.4"
@@ -7,11 +8,32 @@ connexion[swagger-ui] <= 2.14.2; python_version>"3.4"
78
# we must peg werkzeug versions below to fix connexion
89
# https://github.com/zalando/connexion/pull/1044
910
werkzeug == 0.16.1; python_version=="3.5" or python_version=="3.4"
11+
{{/useConnexion3}}
12+
{{#useConnexion3}}
13+
# pinned to 3.3.0+ (not just the first 3.0.0 release) since that's the
14+
# earliest version verified to work with the Jsonifier/test_client setup
15+
# below, and the first to officially support Python 3.13/3.14
16+
connexion[flask,swagger-ui,uvicorn] >= 3.3.0,<4.0.0
17+
{{/useConnexion3}}
18+
{{^useConnexion3}}
1019
swagger-ui-bundle >= 0.0.2
20+
{{/useConnexion3}}
21+
{{#useConnexion3}}
22+
# connexion's own swagger-ui extra already requires this floor; pinned
23+
# explicitly here too so requirements.txt is self-documenting.
24+
swagger-ui-bundle >= 1.1.0
25+
{{/useConnexion3}}
1126
python_dateutil >= 2.6.0
27+
{{^useConnexion3}}
1228
{{#featureCORS}}
1329
# should support both Python 2 and Python 3
1430
flask-cors >= 3.0.10
1531
{{/featureCORS}}
32+
{{/useConnexion3}}
1633
setuptools >= 21.0.0
34+
{{^useConnexion3}}
1735
Flask == 2.1.1
36+
{{/useConnexion3}}
37+
{{#useConnexion3}}
38+
Flask >= 2.2.0,<4.0.0
39+
{{/useConnexion3}}

modules/openapi-generator/src/main/resources/python-flask/setup.mustache

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@ VERSION = "{{packageVersion}}"
1212
# http://pypi.python.org/pypi/setuptools
1313

1414
REQUIRES = [
15+
{{^useConnexion3}}
1516
"connexion>=2.0.2",
1617
"swagger-ui-bundle>=0.0.2",
18+
{{/useConnexion3}}
19+
{{#useConnexion3}}
20+
"connexion[flask,uvicorn]>=3.3.0,<4.0.0",
21+
"swagger-ui-bundle>=1.1.0",
22+
{{/useConnexion3}}
1723
"python_dateutil>=2.6.0"
1824
]
1925

modules/openapi-generator/src/main/resources/python-flask/test-requirements.mustache

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,6 @@ pytest~=7.1.0
1010
pytest-cov>=2.8.1
1111
pytest-randomly>=1.2.3
1212
{{/useNose}}
13+
{{^useConnexion3}}
1314
Flask-Testing==0.8.1
15+
{{/useConnexion3}}

0 commit comments

Comments
 (0)