Skip to content

Commit

Permalink
docs(recipes): clean up recent recipe additions (+misc touchups) (#2425)
Browse files Browse the repository at this point in the history
* chore(AUTHORS): aggregate 4.1.0 contributors so far

* docs: happy new year

* chore: remove carriage-returns (we only use LF for indentation)

* docs(recipes): improve the plain text media handler recipe

* docs(recipes): clean up request-id log recipes
  • Loading branch information
vytas7 authored Jan 2, 2025
1 parent 7621942 commit ead6535
Show file tree
Hide file tree
Showing 27 changed files with 125 additions and 90 deletions.
2 changes: 2 additions & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ listed below by date of first contribution:
* Akshay Awate (AkshayAwate)
* Jasper Spaans (jap)
* Alessandro Chitarrini (chitvs)
* Eric Goulart (EricGoulart)
* Diego (diegomirandap)

(et al.)

Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,7 @@ See also: `CONTRIBUTING.md <https://github.com/falconry/falcon/blob/master/CONTR
Legal
-----

Copyright 2013-2024 by Individual and corporate contributors as
Copyright 2013-2025 by Individual and corporate contributors as
noted in the individual source files.

Licensed under the Apache License, Version 2.0 (the "License"); you may
Expand Down
5 changes: 5 additions & 0 deletions docs/changes/4.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,9 @@ Contributors to this Release

Many thanks to all of our talented and stylish contributors for this release!

- `aarcex3 <https://github.com/aarcex3>`__
- `bssyousefi <https://github.com/bssyousefi>`__
- `diegomirandap <https://github.com/diegomirandap>`__
- `EricGoulart <https://github.com/EricGoulart>`__
- `jap <https://github.com/jap>`__
- `vytas7 <https://github.com/vytas7>`__
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2014-2024 by Falcon Contributors.
# Copyright 2014-2025 by Falcon Contributors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion docs/ext/cibuildwheel.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2024 by Vytautas Liuolia.
# Copyright 2024-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 2 additions & 0 deletions docs/user/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,8 @@ types:
Response Handling
~~~~~~~~~~~~~~~~~

.. _resp_media_data_text:

When would I use media, data, text, and stream?
-----------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion docs/user/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Now, if you do make changes to Falcon itself, please consider contributing your
Falcon License
--------------

Copyright 2012-2024 by Rackspace Hosting, Inc. and other contributors,
Copyright 2012-2025 by Rackspace Hosting, Inc. and other contributors,
as noted in the individual source code files.

Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
71 changes: 40 additions & 31 deletions docs/user/recipes/plain-text-handler.rst
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
.. _plain_text_handler_recipe:

Handling Plain Text as Media
============================

This example demonstrates how to create a custom handler in Falcon to
process the ``text/plain`` media type. The handler implements serialization
and deserialization of textual content, respecting the charset specified
in the ``Content-Type`` header (or defaulting to ``utf-8`` when no charset is provided).

.. literalinclude:: ../../../examples/recipes/plain_text_main.py
:language: python

To use this handler, register it in the Falcon application's media
options for both request and response:

.. code:: python
import falcon
from your_module import TextHandler
app = falcon.App()
app.req_options.media_handlers['text/plain'] = TextHandler()
app.resp_options.media_handlers['text/plain'] = TextHandler()
With this setup, the application can handle textual data directly
as ``text/plain``, ensuring support for various character encodings as needed.

.. warning::
Be sure to validate and limit the size of incoming data when
working with textual content to prevent server overload or denial-of-service attacks.
.. _plain_text_handler_recipe:

Handling Plain Text as Media
============================

Normally, it is easiest to render a plain text response by setting
:attr:`resp.text <falcon.Response.text>`
(see also: :ref:`resp_media_data_text`).
However, if plain text is just one of
the :ref:`Internet media types <media>` that your application speaks, it may be
useful to generalize handling plain text as :ref:`media <media>` too.

This recipe demonstrates how to create a
:ref:`custom media handler <custom-media-handler-type>` to process the
``text/plain`` media type. The handler implements serialization and
deserialization of textual content, respecting the charset specified in the
``Content-Type`` header
(or defaulting to ``utf-8`` when no charset is provided).

.. literalinclude:: ../../../examples/recipes/plain_text_main.py
:language: python

To use this handler, :ref:`register <custom_media_handlers>` it in the Falcon
application's media options for both request and response:

.. code:: python
import falcon
from your_module import TextHandler
app = falcon.App()
app.req_options.media_handlers['text/plain'] = TextHandler()
app.resp_options.media_handlers['text/plain'] = TextHandler()
With this setup, the application can handle textual data directly
as ``text/plain``, ensuring support for various character encodings as needed.

.. warning::
Be sure to validate and limit the size of incoming data when working with
textual content to prevent server overload or denial-of-service attacks.
14 changes: 9 additions & 5 deletions docs/user/recipes/request-id.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,29 @@ to every log entry.

If you wish to trace each request throughout your application, including
from within components that are deeply nested or otherwise live outside of the
normal request context, you can use a `contextvars`_ object to store
the request ID:
normal request context, you can use a :class:`~contextvars.ContextVar` object
to store the request ID:

.. literalinclude:: ../../../examples/recipes/request_id_context.py
:caption: *context.py*
:language: python

Then, you can create a :ref:`middleware <middleware>` class to generate a
unique ID for each request, persisting it in the `contextvars` object:

.. literalinclude:: ../../../examples/recipes/request_id_middleware.py
:caption: *middleware.py*
:language: python

Alternatively, if all of your application logic has access to the :ref:`request
<request>`, you can simply use the `context` object to store the ID:
Alternatively, if all of your application logic has access to the
:ref:`request <request>`, you can simply use the
:attr:`req.context <falcon.Request.context>` object to store the ID:

.. literalinclude:: ../../../examples/recipes/request_id_structlog.py
:caption: *middleware.py*
:language: python

.. note::

If your app is deployed behind a reverse proxy that injects a request ID
header, you can easily adapt this recipe to use the upstream ID rather than
generating a new one. By doing so, you can provide traceability across the
Expand All @@ -46,6 +49,7 @@ or by using a third-party logging library such as
In a pinch, you can also output the request ID directly:

.. literalinclude:: ../../../examples/recipes/request_id_log.py
:caption: *some_other_module.py*
:language: python

.. _contextvars: https://docs.python.org/3/library/contextvars.html
2 changes: 1 addition & 1 deletion e2e-tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020-2024 by Vytautas Liuolia.
# Copyright 2020-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion e2e-tests/test_e2e.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020-2024 by Vytautas Liuolia.
# Copyright 2020-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 0 additions & 2 deletions examples/recipes/request_id_context.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# context.py

import contextvars


Expand Down
5 changes: 2 additions & 3 deletions examples/recipes/request_id_log.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# some_other_module.py

import logging

from context import ctx
# Import the above context.py
from my_app.context import ctx


def create_widget_object(name: str):
Expand Down
6 changes: 2 additions & 4 deletions examples/recipes/request_id_middleware.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# middleware.py

from uuid import uuid4

from context import ctx
# Import the above context.py
from my_app.context import ctx


class RequestIDMiddleware:
def process_request(self, req, resp):
request_id = str(uuid4())
ctx.request_id = request_id
req.context.request_id = request_id

# It may also be helpful to include the ID in the response
def process_response(self, req, resp, resource, req_succeeded):
Expand Down
4 changes: 1 addition & 3 deletions examples/recipes/request_id_structlog.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# middleware.py

from uuid import uuid4

# Optional logging package (pip install structlog)
Expand All @@ -10,7 +8,7 @@ class RequestIDMiddleware:
def process_request(self, req, resp):
request_id = str(uuid4())

# Using Falcon 2.0 syntax
# Using Falcon 2.0+ context style
req.context.request_id = request_id

# Or if your logger has built-in support for contexts
Expand Down
2 changes: 1 addition & 1 deletion falcon/_typing.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2021-2024 by Vytautas Liuolia.
# Copyright 2021-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/asgi/_asgi_helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020-2024 by Vytautas Liuolia.
# Copyright 2020-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/asgi/multipart.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2024 by Vytautas Liuolia.
# Copyright 2019-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/asgi/reader.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2024 by Vytautas Liuolia.
# Copyright 2019-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/cyutil/misc.pyx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020-2024 by Vytautas Liuolia.
# Copyright 2020-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/cyutil/reader.pyx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2024 by Vytautas Liuolia.
# Copyright 2019-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/cyutil/uri.pyx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2024 by Vytautas Liuolia.
# Copyright 2019-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/media/multipart.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2024 by Vytautas Liuolia.
# Copyright 2019-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/util/mediatypes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2023-2024 by Vytautas Liuolia.
# Copyright 2023-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
2 changes: 1 addition & 1 deletion falcon/util/reader.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2019-2024 by Vytautas Liuolia.
# Copyright 2019-2025 by Vytautas Liuolia.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand Down
19 changes: 16 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,31 @@ def load_module(filename, parent_dir=None, suffix=None, module_name=None):
if suffix is not None:
path = path.with_name(f'{path.stem}_{suffix}.py')
prefix = '.'.join(filename.parent.parts)
sys_module_name = module_name
module_name = module_name or f'{prefix}.{path.stem}'

spec = importlib.util.spec_from_file_location(module_name, path)
assert spec is not None, f'could not load module from {path}'
module = importlib.util.module_from_spec(spec)
if sys_module_name:
sys.modules[sys_module_name] = module
spec.loader.exec_module(module)
return module


@pytest.fixture()
def register_module():
"""Temporarily monkey-patch a module into sys.modules."""

def _register(name, module):
sys.modules[name] = module
patched_modules.add(name)

patched_modules = set()

yield _register

for name in patched_modules:
sys.modules.pop(name, None)


@pytest.fixture(scope='session')
def util():
return _SuiteUtils()
Expand Down
Loading

0 comments on commit ead6535

Please sign in to comment.