Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 'Apps and APIs' documentation #1702

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion connexion/apps/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ def __init__(
:param import_name: The name of the package or module that this object belongs to. If you
are using a single module, __name__ is always the correct value. If you however are
using a package, it’s usually recommended to hardcode the name of your package there.
:param lifespan: A lifespan context function, which can be used to perform startup and
:param middlewares: The list of middlewares to wrap around the application. Defaults to
:obj:`middleware.main.ConnexionmMiddleware.default_middlewares`
:obj:`middleware.main.ConnexionMiddleware.default_middlewares`
:param specification_dir: The directory holding the specification(s). The provided path
should either be absolute or relative to the root path of the application. Defaults to
the root path.
Expand Down
4 changes: 3 additions & 1 deletion connexion/apps/asynchronous.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,10 @@ def __init__(
:param import_name: The name of the package or module that this object belongs to. If you
are using a single module, __name__ is always the correct value. If you however are
using a package, it’s usually recommended to hardcode the name of your package there.
:param lifespan: A lifespan context function, which can be used to perform startup and
shutdown tasks.
:param middlewares: The list of middlewares to wrap around the application. Defaults to
:obj:`middleware.main.ConnexionmMiddleware.default_middlewares`
:obj:`middleware.main.ConnexionMiddleware.default_middlewares`
:param specification_dir: The directory holding the specification(s). The provided path
should either be absolute or relative to the root path of the application. Defaults to
the root path.
Expand Down
4 changes: 3 additions & 1 deletion connexion/apps/flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def __init__(
:param lifespan: A lifespan context function, which can be used to perform startup and
shutdown tasks.
:param middlewares: The list of middlewares to wrap around the application. Defaults to
:obj:`middleware.main.ConnexionmMiddleware.default_middlewares`
:obj:`middleware.main.ConnexionMiddleware.default_middlewares`
:param server_args: Arguments to pass to the Flask application.
:param specification_dir: The directory holding the specification(s). The provided path
should either be absolute or relative to the root path of the application. Defaults to
Expand All @@ -221,6 +221,8 @@ def __init__(
start.
:param strict_validation: When True, extra form or query parameters not defined in the
specification result in a validation error. Defaults to False.
:param swagger_ui_options: A :class:`options.ConnexionOptions` instance with configuration
options for the swagger ui.
:param uri_parser_class: Class to use for uri parsing. See :mod:`uri_parsing`.
:param validate_responses: Whether to validate responses against the specification. This has
an impact on performance. Defaults to False.
Expand Down
7 changes: 7 additions & 0 deletions connexion/middleware/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,13 @@ def add_api(
def add_error_handler(
self, code_or_exception: t.Union[int, t.Type[Exception]], function: t.Callable
) -> None:
"""
Register a callable to handle application errors.

:param code_or_exception: An exception class or the status code of HTTP exceptions to
handle.
:param function: Callable that will handle exception.
"""
if self.middleware_stack is not None:
raise RuntimeError(
"Cannot add error handler after an application has started"
Expand Down
245 changes: 245 additions & 0 deletions docs/apps_and_apis.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
Apps & APIs
===========

Connexion can be used either as a standalone application or as a middleware wrapping an existing
ASGI (or WSGI) application written using a different framework. The standalone application can be
built using either the :code:`AsyncApp` or :code:`FlaskApp`.

- The :code:`AsyncApp` is a lightweight application with native asynchronous support. Use it if you
are starting a new project and have no specific reason to use one of the other options.
- The :code:`FlaskApp` leverages the `Flask` framework, which is useful if you're migrating from
connexion 2.X or you want to leverage the `Flask` ecosystem.
- The :code:`ConnexionMiddleware` can be wrapped around any existing ASGI or WSGI application.
Use it if you already have an application written in a different framework and want to add
functionality provided by connexion.

Creating your application
-------------------------

The first step is to create your application:

.. tab-set::

.. tab-item:: AsyncApp
:sync: AsyncApp

.. code-block:: python

from connexion import AsyncApp

app = AsyncApp(__name__)

.. dropdown:: View a detailed reference of the options accepted by the :code:`AsyncApp`
:icon: eye

.. autoclass:: connexion.AsyncApp
:noindex:

.. tab-item:: FlaskApp
:sync: FlaskApp

.. note::
To leverage the :code:`FlaskApp`, make sure you install connexion using the
:code:`flask` extra.

.. code-block:: python

from connexion import FlaskApp

app = FlaskApp(__name__)

.. dropdown:: View a detailed reference of the options accepted by the :code:`FlaskApp`
:icon: eye

.. autoclass:: connexion.FlaskApp
:noindex:

.. tab-item:: ConnexionMiddleware
:sync: ConnexionMiddleware

.. code-block:: python

from asgi_framework import App
from connexion import ConnexionMiddleware

app = App(__name__)
app = ConnexionMiddleware(app)


You can also wrap a WSGI application leveraging the :code:`a2wsgi.WSGIMiddleware`:

.. code-block:: python

from wsgi_framework import App
from connexion import ConnexionMiddleware
from a2wsgi import WSGIMiddleware

wsgi_app = App(__name__)
asgi_app = WSGIMiddleware(wsgi_app)
app = ConnexionMiddleware(app)

.. dropdown:: View a detailed reference of the options accepted by the
:code:`ConnexionMiddleware`
:icon: eye

.. autoclass:: connexion.ConnexionMiddleware
:noindex:

Registering an API
------------------

While you can register individual routes on your application, connexion really shines when you
register an API defined by an OpenAPI (or Swagger) specification.

.. grid::
:padding: 0

.. grid-item:: **run.py**

.. code-block:: python

def post_greeting(name: str):
return f"Hello {name}", 200

app.add_api("openapi.yaml")

.. grid-item:: **openapi.yaml**

.. code-block:: yaml

openapi: "3.0.0"
...
paths:
/greeting/{name}:
post:
operationId: run.post_greeting
responses:
200:
content:
text/plain:
schema:
type: string
parameters:
- name: name
in: path
required: true
schema:
type: string

The operation described in your specification is automatically linked to your Python view function
via the :code:`operationId`. You can change this behavior using different :code:`Resolvers`, see
:doc:`routing`. When the endpoint is called, connexion will take care of routing, security,
request body and parameter parsing, and response serialization. All based on the specification.

You can add as many APIs as you want to a single application. The :code:`add_api()` method
provides a lot of configuration options. When an option is provided both to the App and the API,
the API value will take precedence.

.. dropdown:: View a detailed reference of the options accepted by the :code:`add_api()` method
:icon: eye

.. tab-set::

.. tab-item:: AsyncApp
:sync: AsyncApp

.. autofunction:: connexion.AsyncApp.add_api
:noindex:

.. tab-item:: FlaskApp
:sync: FlaskApp

.. autofunction:: connexion.FlaskApp.add_api
:noindex:

.. tab-item:: ConnexionMiddleware
:sync: ConnexionMiddleware

.. autofunction:: connexion.ConnexionMiddleware.add_api
:noindex:

Running your application
------------------------

You can run your application using an ASGI server such as `uvicorn`. If you defined your
:code:`app` in a python module called :code:`run.py`, you can run it as follows:

.. code-block:: bash

$ uvicorn run:app

or if you installed connexion using :code:`connexion[uvicorn]`, you can run it using the
:code:`run` method. This is only recommended for development:

.. code-block:: python

app.run()

To leverage automatic reloading of your application, you need to provide the application as an
import string. In most cases, this can be achieved as follows:

.. code-block:: python

from pathlib import Path

app.run(f"{Path(__file__).stem}:app")

.. dropdown:: View a detailed reference of the options accepted by the :code:`run()` method
:icon: eye

.. tab-set::

.. tab-item:: AsyncApp
:sync: AsyncApp

.. autofunction:: connexion.AsyncApp.run
:noindex:

.. tab-item:: FlaskApp
:sync: FlaskApp

.. autofunction:: connexion.FlaskApp.run
:noindex:

.. tab-item:: ConnexionMiddleware
:sync: ConnexionMiddleware

.. autofunction:: connexion.ConnexionMiddleware.run
:noindex:

Full class reference
--------------------

.. tab-set::

.. tab-item:: AsyncApp
:sync: AsyncApp

.. dropdown:: View a detailed reference of the :code:`AsyncApp`
:icon: eye

.. autoclass:: connexion.AsyncApp
:members:
:undoc-members:
:inherited-members:

.. tab-item:: FlaskApp
:sync: FlaskApp

.. dropdown:: View a detailed reference of the :code:`FlaskApp`
:icon: eye

.. autoclass:: connexion.FlaskApp
:members:
:undoc-members:
:inherited-members:

.. tab-item:: ConnexionMiddleware
:sync: ConnexionMiddleware

.. dropdown:: View a detailed reference of the :code:`ConnexionMiddleware`
:icon: eye

.. autoclass:: connexion.ConnexionMiddleware
:members:
:undoc-members:
18 changes: 5 additions & 13 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,13 @@
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['autoapi.extension']

autoapi_type = 'python'
autoapi_options = ['members',
'inherited-members',
'undoc-members',
'show-inheritance',
'show-module-summary',
'special-members',
'imported-members']
autoapi_python_class_content = 'both'
autoapi_dirs = [
'../connexion'
extensions = [
'sphinx.ext.autodoc',
'sphinx_design',
]

autoclass_content = 'both'

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Documentation
:maxdepth: 2

quickstart
apps_and_apis
cli
routing
request
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ pytest-cov = "~2.12.1"
sphinx = "5.3.0"
sphinx-autoapi = "2.0.1"
sphinx-rtd-theme = "1.2.0"
sphinx_design = "0.4.1"

[build-system]
requires = ["poetry-core>=1.2.0"]
Expand Down