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

docs(recipes): clean up recent recipe additions (+misc touchups) #2425

Merged
merged 5 commits into from
Jan 2, 2025
Merged
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
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
Copy link
Member Author

@vytas7 vytas7 Jan 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file was originally contributed with CRLF line endings, whereas we use LF. Hence the total diff.

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
Loading