Skip to content

Commit

Permalink
doc/refactor(asgi): Start fleshing out docstrings, tweak doc refs, re…
Browse files Browse the repository at this point in the history
…move add_error_handler shimming (#1643)

This patch is a little bit of a grab bag of items that I noticed and
fixed while starting work on fleshing out the docs for the new ASGI
implementation.

Note that there is still quite a bit of work to do in order to integrate
ASGI into the docs. Many docs need to be combed and updated to work with
ASGI and WSGI side-by-side, and there are still plenty of missing docs for
the new ASGI classes and functions.

Co-authored-by: Vytautas Liuolia <vytautas.liuolia@gmail.com>
Co-authored-by: John Vrbanac <john.vrbanac@linux.com>
  • Loading branch information
3 people committed Jan 26, 2020
1 parent 14a73be commit 00e5c9c
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 111 deletions.
13 changes: 13 additions & 0 deletions docs/_static/custom.css
Expand Up @@ -164,6 +164,13 @@ pre {
font-size: smaller;
}


/* NOTE(kgriffs): Make sure that characters in plain text blocks line up correctly. */
.highlight-none .highlight pre {
line-height: 1em;
font-family: monospace;
}

/* Fix drifting to the left in some parameters lists. */
.field-body li .highlight pre {
float: right;
Expand Down Expand Up @@ -254,3 +261,9 @@ dl.field-list.simple dd ul li p:not(:first-child) {
dl.attribute > dd > p {
margin: 0;
}

/* NOTE(kgriffs): Fix spacing issue with embedded Note blocks, and make
things generally more readable by spacing the paragraphs. */
.field-list p {
margin-bottom: 1em;
}
18 changes: 15 additions & 3 deletions docs/api/app.rst
Expand Up @@ -3,18 +3,30 @@
The App Class
=============

Falcon's App class is a WSGI "application" that you can host with any
standard-compliant WSGI server.
Falcon supports both the WSGI (:class:`falcon.App`) and
ASGI (:class:`falcon.asgi.App`) protocols. This is done
by instantiating the respective ``App`` class to create a
callable WSGI or ASGI "application".

Because Falcon's ``App`` classes are built on
`WSGI <https://www.python.org/dev/peps/pep-3333/>`_ and
`ASGI <https://asgi.readthedocs.io/en/latest/>`_,
you can host them with any standard-compliant server.

.. code:: python
import falcon
import falcon.asgi
app = falcon.App()
wsgi_app = falcon.App()
asgi_app = falcon.asgi.App()
.. autoclass:: falcon.App
:members:

.. autoclass:: falcon.asgi.App
:members:

.. autoclass:: falcon.RequestOptions
:members:

Expand Down
5 changes: 3 additions & 2 deletions docs/api/cookies.rst
Expand Up @@ -99,8 +99,9 @@ the request.
When running your application in a development environment, you can
disable this default behavior by setting
:py:attr:`~.ResponseOptions.secure_cookies_by_default` to ``False``
via :any:`App.resp_options`. This lets you test your app locally
without having to set up TLS. You can make this option configurable to
via :py:attr:`falcon.App.resp_options` or
:py:attr:`falcon.asgi.App.resp_options`. This lets you test your app
locally without having to set up TLS. You can make this option configurable to
easily switch between development and production environments.

See also: `RFC 6265, Section 4.1.2.5`_
Expand Down
2 changes: 1 addition & 1 deletion docs/api/errors.rst
Expand Up @@ -63,7 +63,7 @@ All classes are available directly in the ``falcon`` package namespace::
Note also that any exception (not just instances of
:class:`~.HTTPError`) can be caught, logged, and otherwise handled
at the global level by registering one or more custom error handlers.
See also :meth:`~.App.add_error_handler` to learn more about this
See also :meth:`~.falcon.App.add_error_handler` to learn more about this
feature.

.. note::
Expand Down
2 changes: 1 addition & 1 deletion docs/api/middleware.rst
Expand Up @@ -158,7 +158,7 @@ which case the framework will use the latter exception to update the

By default, the framework installs two handlers, one for
:class:`~.HTTPError` and one for :class:`~.HTTPStatus`. These can
be overridden via :meth:`~.App.add_error_handler`.
be overridden via :meth:`~.falcon.App.add_error_handler`.

Regardless, the framework will continue unwinding the middleware
stack. For example, if *mob2.process_request* were to raise an
Expand Down
12 changes: 6 additions & 6 deletions docs/api/routing.rst
Expand Up @@ -58,9 +58,9 @@ Default Router
Falcon's default routing engine is based on a decision tree that is
first compiled into Python code, and then evaluated by the runtime.

The :meth:`~.App.add_route` method is used to associate a URI template
with a resource. Falcon then maps incoming requests to resources
based on these templates.
The :meth:`falcon.App.add_route` and :meth:`falcon.asgi.App.add_route` methods
are used to associate a URI template with a resource. Falcon then maps incoming
requests to resources based on these templates.

Falcon's default router uses Python classes to represent resources. In
practice, these classes act as controllers in your application. They
Expand All @@ -69,7 +69,7 @@ compose a response back to the client based on the results of those
actions. (See also:
:ref:`Tutorial: Creating Resources <tutorial_resources>`)

.. code::
.. code:: none
┌────────────┐
request → │ │
Expand Down Expand Up @@ -115,8 +115,8 @@ data to hooks and middleware methods.
object, a responder may raise an instance of either
:class:`~.HTTPError` or :class:`~.HTTPStatus`. Falcon will
convert these exceptions to appropriate HTTP responses.
Alternatively, you can handle them youself via
:meth:`~.App.add_error_handler`.
Alternatively, you can handle them yourself via
:meth:`~.falcon.App.add_error_handler`.

In addition to the standard `req` and `resp` parameters, if the
route's template contains field expressions, any responder that
Expand Down
4 changes: 2 additions & 2 deletions docs/user/tutorial.rst
Expand Up @@ -421,7 +421,7 @@ now look like this:
├── __init__.py
└── test_app.py
Falcon supports :ref:`testing <testing>` its :class:`~.App` object by
Falcon supports :ref:`testing <testing>` its :class:`~falcon.App` object by
simulating HTTP requests.

Tests can either be written using Python's standard :mod:`unittest`
Expand Down Expand Up @@ -1403,7 +1403,7 @@ for each error type.
for logging and otherwise handling exceptions raised by
responders, hooks, and middleware components.

See also: :meth:`~.App.add_error_handler`.
See also: :meth:`~.falcon.App.add_error_handler`.

Let's see a quick example of how this works. Try requesting an invalid
image name from your application:
Expand Down
97 changes: 50 additions & 47 deletions falcon/app.py
Expand Up @@ -19,7 +19,8 @@
import re
import traceback

from falcon import app_helpers as helpers, DEFAULT_MEDIA_TYPE, routing
from falcon import app_helpers as helpers, routing
from falcon.constants import DEFAULT_MEDIA_TYPE
from falcon.http_error import HTTPError
from falcon.http_status import HTTPStatus
from falcon.middlewares import CORSMiddleware
Expand All @@ -46,10 +47,11 @@


class App:
"""This class is the main entry point into a Falcon-based app.
"""This class is the main entry point into a Falcon-based WSGI app.
Each App instance provides a callable WSGI interface and a routing
engine.
Each App instance provides a callable
`WSGI <https://www.python.org/dev/peps/pep-3333/>`_ interface
and a routing engine.
Note:
The ``API`` class was renamed to ``App`` in Falcon 3.0. The
Expand All @@ -58,11 +60,11 @@ class App:
release.
Keyword Arguments:
media_type (str): Default media type to use as the
value for the Content-Type header on responses (default
'application/json'). The ``falcon`` module provides a
number of constants for common media types, such as
``falcon.MEDIA_MSGPACK``, ``falcon.MEDIA_YAML``,
media_type (str): Default media type to use when initializing
:py:class:`~.RequestOptions` and
:py:class:`~.ResponseOptions`. The ``falcon``
module provides a number of constants for common media types,
such as ``falcon.MEDIA_MSGPACK``, ``falcon.MEDIA_YAML``,
``falcon.MEDIA_XML``, etc.
middleware: Either a single middleware component object or an iterable
of objects (instantiated classes) that implement the following
Expand Down Expand Up @@ -125,15 +127,15 @@ def process_response(self, req, resp, resource, req_succeeded)
(See also: :ref:`Middleware <middleware>`)
request_type (Request): ``Request``-like class to use instead
request_type: ``Request``-like class to use instead
of Falcon's default class. Among other things, this feature
affords inheriting from ``falcon.request.Request`` in order
to override the ``context_type`` class variable.
(default ``falcon.request.Request``)
affords inheriting from :class:`falcon.Request` in order
to override the ``context_type`` class variable
(default: :class:`falcon.Request`)
response_type (Response): ``Response``-like class to use
instead of Falcon's default class. (default
``falcon.response.Response``)
response_type: ``Response``-like class to use
instead of Falcon's default class (default:
:class:`falcon.Response`)
router (object): An instance of a custom router
to use in lieu of the default engine.
Expand Down Expand Up @@ -238,32 +240,6 @@ def __init__(self, media_type=DEFAULT_MEDIA_TYPE,
self.add_error_handler(falcon.HTTPError, self._http_error_handler)
self.add_error_handler(falcon.HTTPStatus, self._http_status_handler)

def add_middleware(self, middleware):
"""Add one or more additional middleware components.
Arguments:
middleware: Either a single middleware component or an iterable
of components to add. The component(s) will be invoked, in
order, as if they had been appended to the original middleware
list passed to the class initializer.
"""

# NOTE(kgriffs): Since this is called by the initializer, there is
# the chance that middleware may be None.
if middleware:
try:
self._unprepared_middleware += middleware
except TypeError: # middleware is not iterable; assume it is just one bare component
self._unprepared_middleware.append(middleware)

# NOTE(kgriffs): Even if middleware is None or an empty list, we still
# need to make sure self._middleware is initialized if this is the
# first call to add_middleware().
self._middleware = self._prepare_middleware(
self._unprepared_middleware,
independent_middleware=self._independent_middleware
)

def __call__(self, env, start_response): # noqa: C901
"""WSGI `app` method.
Expand Down Expand Up @@ -414,6 +390,32 @@ def __call__(self, env, start_response): # noqa: C901
def router_options(self):
return self._router.options

def add_middleware(self, middleware):
"""Add one or more additional middleware components.
Arguments:
middleware: Either a single middleware component or an iterable
of components to add. The component(s) will be invoked, in
order, as if they had been appended to the original middleware
list passed to the class initializer.
"""

# NOTE(kgriffs): Since this is called by the initializer, there is
# the chance that middleware may be None.
if middleware:
try:
self._unprepared_middleware += middleware
except TypeError: # middleware is not iterable; assume it is just one bare component
self._unprepared_middleware.append(middleware)

# NOTE(kgriffs): Even if middleware is None or an empty list, we still
# need to make sure self._middleware is initialized if this is the
# first call to add_middleware().
self._middleware = self._prepare_middleware(
self._unprepared_middleware,
independent_middleware=self._independent_middleware
)

def add_route(self, uri_template, resource, **kwargs):
"""Associate a templatized URI path with a resource.
Expand Down Expand Up @@ -447,7 +449,7 @@ def add_route(self, uri_template, resource, **kwargs):
taken to ensure the template does not mask any sink
patterns, if any are registered.
(See also: :meth:`~.add_sink`)
(See also: :meth:`~.App.add_sink`)
resource (instance): Object which represents a REST
resource. Falcon will pass GET requests to ``on_get()``,
Expand Down Expand Up @@ -613,7 +615,7 @@ def add_error_handler(self, exception, handler=None):
For example, suppose we register error handlers as follows::
app = falcon.API()
app = App()
app.add_error_handler(falcon.HTTPNotFound, custom_handle_not_found)
app.add_error_handler(falcon.HTTPError, custom_handle_http_error)
app.add_error_handler(Exception, custom_handle_uncaught_exception)
Expand Down Expand Up @@ -662,9 +664,10 @@ def handle(req, resp, ex, params):
a single type, the handler must be explicitly specified.
.. versionchanged:: 3.0
Breaking change: error handler now selected by most specific
matching error class, rather than most recently registered matching
error class.
The error handler is now selected by the most-specific matching
error class, rather than the most-recently registered matching error
class.
"""
def wrap_old_handler(old_handler):
Expand Down

0 comments on commit 00e5c9c

Please sign in to comment.