Skip to content

Commit a2c3165

Browse files
committed
fix: upgrade dependencies on v2 branch
The main goal was upgrading werkzeug for CVE-2024-34069. After switching to python 3.12, it proved more difficult with changes to setuptools, etc. I decided to pull the pyproject from the main, and utilize that, alone with updated dependencies. Small changes were needed in various api changes, notably: - flask change of request_ctx - swagger_ui_bundle version change, default_template_dir change - aiohttp middleware api slightly changed - flask json change, using flask.json.provider I believe these changes will have minimal impact to users, but the changes are likely breaking for some, specifically, the move to latest flask. fixes #1969 Signed-off-by: Mike Marchetti <[email protected]>
1 parent 6a859b3 commit a2c3165

12 files changed

+3213
-85
lines changed

Diff for: README.md

+284
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,284 @@
1+
<a id="top"></a>
2+
<p align="center">
3+
<img src="https://raw.githubusercontent.com/spec-first/connexion/main/docs/images/logo_banner.svg" width="100%"/>
4+
</p>
5+
<p align="center">
6+
<a href="https://pypi.org/project/connexion"><img alt="coveralls" src="https://img.shields.io/pypi/status/connexion.svg?style=flat-square&color=brightgreen"></a>
7+
<a href="https://pypi.org/project/connexion"><img alt="PyPI version" src="https://img.shields.io/pypi/v/connexion?color=brightgreen&style=flat-square"></a>
8+
<a href="https://pypistats.org/packages/connexion"><img alt="PyPI" src="https://img.shields.io/pypi/dm/connexion?style=flat-square&color=brightgreen"></a>
9+
<a href="https://github.com/spec-first/connexion/blob/main/LICENSE"><img alt="License" src="https://img.shields.io/pypi/l/connexion?style=flat-square&color=brightgreen"></a>
10+
<a href="https://github.com/spec-first/connexion/actions/workflows/pipeline.yml"><img alt="GitHub Workflow Status" src="https://img.shields.io/github/actions/workflow/status/spec-first/connexion/pipeline.yml?style=flat-square"></a>
11+
<a href="https://coveralls.io/github/spec-first/connexion?branch=main"><img alt="Coveralls" src="https://img.shields.io/coverallsCoverage/github/spec-first/connexion?style=flat-square"></a>
12+
<br>
13+
<br>
14+
<a href="https://connexion.readthedocs.io/en/stable/"><strong>Explore the docs »</strong></a>
15+
</p>
16+
17+
---
18+
19+
Connexion is a modern Python web framework that makes spec-first and api-first development easy.
20+
You describe your API in an [OpenAPI][OpenAPI] (or [Swagger][Swagger]) specification with as much
21+
detail as you want and Connexion will guarantee that it works as you specified.
22+
23+
It works either standalone, or in combination with any ASGI or WSGI-compatible framework!
24+
25+
<p align="center">
26+
<br>
27+
<a href="https://connexion.readthedocs.io/en/latest/v3.html"><strong>📢 Connexion 3 was recently released! Read about the changes here »</strong></a>
28+
<br>
29+
<br>
30+
</p>
31+
32+
## ✨ Features
33+
34+
Connexion provides the following functionality **based on your specification**:
35+
36+
- 🚏 **Automatic route registration**, no ``@route`` decorators needed
37+
- 🔒 **Authentication**, split from your application logic
38+
- 🔎 **Request and response validation** of headers, parameters, and body
39+
- 📬 **Parameter parsing and injection**, no request object needed
40+
- 📨 **Response serialization**, you can return regular Python objects
41+
- 📺 **A Swagger UI console** with live documentation and ‘try it out’ feature
42+
- 🧩 **Pluggability**, in all dimensions
43+
44+
Connexion also **helps you write your OpenAPI specification** and develop against it by providing a command line interface which lets you test and mock your specification.
45+
46+
```shell
47+
connexion run openapi.yaml
48+
```
49+
50+
<p align="right">(<a href="#top">back to top</a>)</p>
51+
52+
53+
## 🫶 Sponsors
54+
55+
<a href="https://www.ml6.eu"><img src="https://raw.githubusercontent.com/spec-first/connexion/main/docs/images/sponsors/ML6.png" title=ML6 height="100"></a>
56+
<a href="https://www.devmark.ai/fern/?utm_source=connexion&utm_loc=readme&utm_type=logo"><img src="https://raw.githubusercontent.com/spec-first/connexion/main/docs/images/sponsors/Fern.png" title=Fern height="100"></a>
57+
58+
Sponsors help us dedicate time to maintain Connexion. Want to help?
59+
60+
<a href="https://github.com/sponsors/spec-first"><strong>Explore the options »</strong></a>
61+
62+
<p align="right">(<a href="#top">back to top</a>)</p>
63+
64+
## 🪤 Why Connexion
65+
66+
With Connexion, you write the spec first. Connexion then calls your Python
67+
code, handling the mapping from the specification to the code. This
68+
incentivizes you to write the specification so that all of your
69+
developers can understand what your API does, even before you write a
70+
single line of code.
71+
72+
If multiple teams depend on your APIs, you can use Connexion to easily
73+
send them the documentation of your API. This guarantees that your API will
74+
follow the specification that you wrote. This is a different process from
75+
the one offered by most frameworks, which generate a specification
76+
*after* you've written the code.
77+
Some disadvantages of generating specifications based on code is that
78+
they often end up lacking details or mix your documentation with the implementation
79+
logic of your application.
80+
81+
<p align="right">(<a href="#top">back to top</a>)</p>
82+
83+
## ⚒️ How to Use
84+
85+
### Installation
86+
87+
You can install connexion using pip:
88+
89+
```shell
90+
$ pip install connexion
91+
```
92+
93+
Connexion provides 'extras' with optional dependencies to unlock additional features:
94+
95+
- `swagger-ui`: Enables a Swagger UI console for your application.
96+
- `uvicorn`: Enables to run the your application using `app.run()` for
97+
development instead of using an external ASGI server.
98+
- `flask`: Enables the `FlaskApp` to build applications compatible with the Flask
99+
ecosystem.
100+
101+
You can install them as follows:
102+
103+
```shell
104+
$ pip install connexion[swagger-ui]
105+
$ pip install connexion[swagger-ui,uvicorn]
106+
```
107+
108+
<p align="right">(<a href="#top">back to top</a>)</p>
109+
110+
### Creating your application
111+
112+
Connexion can be used either as a standalone application or as a middleware wrapping an existing
113+
ASGI (or WSGI) application written using a different framework. The standalone application can be
114+
built using either the `AsyncApp` or `FlaskApp`.
115+
116+
- The `AsyncApp` is a lightweight application with native asynchronous support. Use it if you
117+
are starting a new project and have no specific reason to use one of the other options.
118+
119+
```Python
120+
from connexion import AsyncApp
121+
122+
app = AsyncApp(__name__)
123+
```
124+
125+
- The `FlaskApp` leverages the `Flask` framework, which is useful if you're migrating from
126+
connexion 2.X or you want to leverage the `Flask` ecosystem.
127+
128+
```python
129+
from connexion import FlaskApp
130+
131+
app = FlaskApp(__name__)
132+
```
133+
134+
- The `ConnexionMiddleware` can be wrapped around any existing ASGI or WSGI application.
135+
Use it if you already have an application written in a different framework and want to add
136+
functionality provided by connexion
137+
138+
```python
139+
from asgi_framework import App
140+
from connexion import ConnexionMiddleware
141+
142+
app = App(__name__)
143+
app = ConnexionMiddleware(app)
144+
```
145+
146+
<p align="right">(<a href="#top">back to top</a>)</p>
147+
148+
### Registering an API
149+
150+
While you can register individual routes on your application, Connexion really shines when you
151+
register an API defined by an OpenAPI (or Swagger) specification.
152+
The operation described in your specification is automatically linked to your Python view function via the ``operationId``
153+
154+
**run.py**
155+
156+
```python
157+
def post_greeting(name: str, greeting: str): # Paramaeters are automatically unpacked
158+
return f"{greeting} {name}", 200 # Responses are automatically serialized
159+
160+
app.add_api("openapi.yaml")
161+
```
162+
163+
**openapi.yaml**
164+
165+
```yaml
166+
...
167+
paths:
168+
/greeting/{name}:
169+
post:
170+
operationId: run.post_greeting
171+
responses:
172+
200:
173+
content:
174+
text/plain:
175+
schema:
176+
type: string
177+
parameters:
178+
- name: name
179+
in: path
180+
required: true
181+
schema:
182+
type: string
183+
- name: greeting
184+
in: query
185+
required: true
186+
schema:
187+
type: string
188+
```
189+
190+
<p align="right">(<a href="#top">back to top</a>)</p>
191+
192+
### Running your application
193+
194+
If you installed connexion using `connexion[uvicorn]`, you can run it using the
195+
`run` method. This is only recommended for development:
196+
197+
```python
198+
app.run()
199+
```
200+
201+
In production, run your application using an ASGI server such as `uvicorn`. If you defined your
202+
`app` in a python module called `run.py`, you can run it as follows:
203+
204+
```shell
205+
$ uvicorn run:app
206+
```
207+
208+
Or with gunicorn:
209+
210+
```shell
211+
$ gunicorn -k uvicorn.workers.UvicornWorker run:app
212+
```
213+
214+
----
215+
216+
Now you're able to run and use Connexion!
217+
218+
See the [examples][examples] folder for more examples.
219+
220+
<p align="right">(<a href="#top">back to top</a>)</p>
221+
222+
## 📜 Changes
223+
224+
A full changelog is maintained on the [GitHub releases page][Releases].
225+
226+
<p align="right">(<a href="#top">back to top</a>)</p>
227+
228+
## 🤲 Contributing
229+
230+
We welcome your ideas, issues, and pull requests. Just follow the
231+
usual/standard GitHub practices.
232+
233+
For easy development, install connexion using poetry with all extras, and
234+
install the pre-commit hooks to automatically run black formatting and static analysis checks.
235+
236+
```shell
237+
pip install poetry
238+
poetry install --all-extras
239+
pre-commit install
240+
```
241+
242+
You can find out more about how Connexion works and where to apply your changes by having a look
243+
at our [architecture][Architecture].
244+
245+
Unless you explicitly state otherwise in advance, any non trivial
246+
contribution intentionally submitted for inclusion in this project by you
247+
to the steward of this repository shall be under the
248+
terms and conditions of Apache License 2.0 written below, without any
249+
additional copyright information, terms or conditions.
250+
251+
<p align="right">(<a href="#top">back to top</a>)</p>
252+
253+
## 🙏 Thanks
254+
255+
We'd like to thank all of Connexion's contributors for working on this
256+
project, Swagger/OpenAPI for their support, and Zalando for originally developing and releasing Connexion.
257+
258+
## 📚 Recommended Resources
259+
260+
About the advantages of working spec-first:
261+
262+
* [Blog Atlassian][Blog Atlassian]
263+
* [API guidelines Zalando][API guidelines Zalando]
264+
* [Blog ML6][Blog ML6]
265+
* [Blog Zalando][Blog Zalando]
266+
267+
Tools to help you work spec-first:
268+
269+
* [Online swagger editor][Online swagger editor]
270+
* [VS Code plugin][VS Code plugin]
271+
* [Pycharm plugin][Pycharm plugin]
272+
273+
[OpenAPI]: https://openapis.org/
274+
[Swagger]: http://swagger.io/open-source-integrations/
275+
[Blog atlassian]: https://www.atlassian.com/blog/technology/spec-first-api-development
276+
[Blog ML6]: https://blog.ml6.eu/why-we-decided-to-help-maintain-connexion-c9f449877083
277+
[Blog Zalando]: https://engineering.zalando.com/posts/2016/12/crafting-effective-microservices-in-python.html
278+
[API guidelines Zalando]: https://opensource.zalando.com/restful-api-guidelines/#api-first
279+
[Online swagger editor]: https://editor.swagger.io/
280+
[VS Code plugin]: https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi
281+
[Pycharm plugin]: https://plugins.jetbrains.com/plugin/14837-openapi-swagger-editor
282+
[examples]: https://github.com/spec-first/connexion/blob/main/examples
283+
[Releases]: https://github.com/spec-first/connexion/releases
284+
[Architecture]: https://github.com/spec-first/connexion/blob/main/docs/images/architecture.png

Diff for: connexion/apis/flask_api.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Any
1010

1111
import flask
12+
from flask.globals import request_ctx
1213
import werkzeug.exceptions
1314
from werkzeug.local import LocalProxy
1415

@@ -36,7 +37,7 @@ def _set_base_path(self, base_path):
3637

3738
def _set_blueprint(self):
3839
logger.debug('Creating API blueprint: %s', self.base_path)
39-
endpoint = flask_utils.flaskify_endpoint(self.base_path)
40+
endpoint = flask_utils.flaskify_endpoint(self.base_path) or "/"
4041
self.blueprint = flask.Blueprint(endpoint, __name__, url_prefix=self.base_path,
4142
template_folder=str(self.options.openapi_console_ui_from_dir))
4243

@@ -233,7 +234,7 @@ def get_request(cls, *args, **params):
233234
:rtype: ConnexionRequest
234235
"""
235236
context_dict = {}
236-
setattr(flask._request_ctx_stack.top, 'connexion_context', context_dict)
237+
setattr(request_ctx, "connexion_context", context_dict)
237238
flask_request = flask.request
238239
request = ConnexionRequest(
239240
flask_request.url,
@@ -265,7 +266,7 @@ def _set_jsonifier(cls):
265266

266267

267268
def _get_context():
268-
return getattr(flask._request_ctx_stack.top, 'connexion_context')
269+
return getattr(request_ctx, "connexion_context")
269270

270271

271272
context = LocalProxy(_get_context)

Diff for: connexion/apps/flask_app.py

+27-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55
import datetime
66
import logging
77
import pathlib
8+
import json
89
from decimal import Decimal
910
from types import FunctionType # NOQA
1011

1112
import flask
1213
import werkzeug.exceptions
13-
from flask import json, signals
14+
from flask import signals
15+
from flask.json.provider import DefaultJSONProvider
1416

1517
from ..apis.flask_api import FlaskApi
1618
from ..exceptions import ProblemException
@@ -33,7 +35,7 @@ def __init__(self, import_name, server='flask', extra_files=None, **kwargs):
3335

3436
def create_app(self):
3537
app = flask.Flask(self.import_name, **self.server_args)
36-
app.json_encoder = FlaskJSONEncoder
38+
app.json = FlaskJSONProvider(app)
3739
app.url_map.converters['float'] = NumberConverter
3840
app.url_map.converters['int'] = IntegerConverter
3941
return app
@@ -150,6 +152,29 @@ def run(self,
150152
raise Exception(f'Server {self.server} not recognized')
151153

152154

155+
class FlaskJSONProvider(DefaultJSONProvider):
156+
def __init__(self, app):
157+
super().__init__(app)
158+
159+
def default(self, o):
160+
if isinstance(o, datetime.datetime):
161+
if o.tzinfo:
162+
# eg: '2015-09-25T23:14:42.588601+00:00'
163+
return o.isoformat("T")
164+
else:
165+
# No timezone present - assume UTC.
166+
# eg: '2015-09-25T23:14:42.588601Z'
167+
return o.isoformat("T") + "Z"
168+
169+
if isinstance(o, datetime.date):
170+
return o.isoformat()
171+
172+
if isinstance(o, Decimal):
173+
return float(o)
174+
175+
return super().default(o)
176+
177+
153178
class FlaskJSONEncoder(json.JSONEncoder):
154179
def default(self, o):
155180
if isinstance(o, datetime.datetime):

Diff for: connexion/options.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
from typing import Optional # NOQA
77

88
try:
9-
from swagger_ui_bundle import swagger_ui_2_path, swagger_ui_3_path
9+
from swagger_ui_bundle import swagger_ui_path as default_template_dir
1010
except ImportError:
11-
swagger_ui_2_path = swagger_ui_3_path = None
11+
default_template_dir = None
1212

1313
from connexion.decorators.uri_parsing import AbstractURIParser
1414

@@ -28,10 +28,10 @@ def __init__(self, options=None, oas_version=(2,)):
2828
self.oas_version = oas_version
2929
if self.oas_version >= (3, 0, 0):
3030
self.openapi_spec_name = '/openapi.json'
31-
self.swagger_ui_local_path = swagger_ui_3_path
31+
self.swagger_ui_local_path = default_template_dir
3232
else:
3333
self.openapi_spec_name = '/swagger.json'
34-
self.swagger_ui_local_path = swagger_ui_2_path
34+
self.swagger_ui_local_path = default_template_dir
3535

3636
if options:
3737
self._options.update(filter_values(options))

0 commit comments

Comments
 (0)