Skip to content

Commit

Permalink
Merge branch 'master' into SimulateMultipartFile#1010
Browse files Browse the repository at this point in the history
  • Loading branch information
vytas7 committed May 7, 2024
2 parents 3af2fcc + 9b27c71 commit 6ae4d06
Show file tree
Hide file tree
Showing 63 changed files with 368 additions and 175 deletions.
27 changes: 14 additions & 13 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ jobs:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.12"
os:
- "ubuntu-latest"
toxenv:
- "pep8"
- "blue"
- "pep8-examples"
- "pep8-docstrings"
- "pep8-examples"
- "ruff"
- "mypy"
- "mypy_tests"
- "py310"
- "py310_sans_msgpack"
- "py310_cython"
- "py312"
- "py312_sans_msgpack"
- "py312_cython"
- "docs"
- "towncrier"
- "look"
Expand Down Expand Up @@ -82,12 +82,12 @@ jobs:
- python-version: "3.12"
os: ubuntu-latest
toxenv: py312_cython
- python-version: "3.10"
- python-version: "3.12"
os: macos-latest
toxenv: py310_nocover
- python-version: "3.10"
toxenv: py312_nocover
- python-version: "3.12"
os: windows-latest
toxenv: py310_nocover
toxenv: py312_nocover
# These env require 3.8 and 20.04, see tox.ini
- python-version: "3.8"
os: ubuntu-20.04
Expand Down Expand Up @@ -133,14 +133,15 @@ jobs:
run: tox -e ${{ matrix.toxenv }}

- name: Combine coverage
if: ${{ matrix.toxenv == 'py310' || matrix.toxenv == 'py310_sans_msgpack' }}
if: ${{ matrix.toxenv == 'py312' || matrix.toxenv == 'py312_sans_msgpack' }}
run: |
coverage --version
coverage combine
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
if: ${{ matrix.toxenv == 'py310' || matrix.toxenv == 'py310_sans_msgpack' }}
uses: codecov/codecov-action@v4
if: ${{ matrix.toxenv == 'py312' || matrix.toxenv == 'py312_sans_msgpack' }}
with:
env_vars: PYTHON
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,16 @@ Please note that all contributors and maintainers of this project are subject to

Before submitting a pull request, please ensure you have added or updated tests as appropriate,
and that all existing tests still pass with your changes.
Please also ensure that your coding style follows PEP 8 and the ``blue`` formatting style.
Please also ensure that your coding style follows PEP 8 and the ``ruff`` formatting style.

In order to reformat your code with ``blue``, simply issue:
In order to reformat your code with ``ruff``, simply issue:

```bash
$ pip install -U blue
$ blue .
$ pip install -U ruff
$ ruff format
```

You can check all this by running ``tox`` from within the Falcon project directory. Your environment must be based on CPython 3.8, 3.10 or 3.11:
You can check all this by running ``tox`` from within the Falcon project directory. Your environment must be based on CPython 3.8, 3.10, 3.11 or 3.12:

```bash
$ pip install -U tox
Expand Down
7 changes: 2 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
>
</a>

|Build Status| |Docs| |codecov.io| |Blue|
|Build Status| |Docs| |codecov.io|

The Falcon Web Framework
========================
Expand Down Expand Up @@ -1027,7 +1027,7 @@ See also: `CONTRIBUTING.md <https://github.com/falconry/falcon/blob/master/CONTR
Legal
-----

Copyright 2013-2023 by Individual and corporate contributors as
Copyright 2013-2024 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 All @@ -1049,6 +1049,3 @@ limitations under the License.
:target: https://github.com/falconry/falcon/actions?query=workflow%3A%22Run+tests%22
.. |codecov.io| image:: https://codecov.io/gh/falconry/falcon/branch/master/graphs/badge.svg
:target: http://codecov.io/gh/falconry/falcon
.. |Blue| image:: https://img.shields.io/badge/code%20style-blue-blue.svg
:target: https://blue.readthedocs.io/
:alt: code style: blue
4 changes: 4 additions & 0 deletions docs/_newsfragments/2066.newandimproved.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
In Python 3.13, the ``cgi`` module is removed entirely from the stdlib,
including its ``parse_header()`` method. Falcon addresses the issue by shipping
an own implementation; :func:`falcon.parse_header` can also be used in your projects
affected by the removal.
5 changes: 5 additions & 0 deletions docs/api/util.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ HTTP Status
.. autofunction:: falcon.code_to_http_status
.. autofunction:: falcon.get_http_status

Media types
-----------

.. autofunction:: falcon.parse_header

Async
-----

Expand Down
2 changes: 1 addition & 1 deletion docs/changes/4.0.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Changes to Supported Platforms
------------------------------

- CPython 3.11 is now fully supported. (`#2072 <https://github.com/falconry/falcon/issues/2072>`__)
- CPython 3.12 will be fully supported. (`#2196 <https://github.com/falconry/falcon/issues/2196>`__)
- CPython 3.12 is now fully supported. (`#2196 <https://github.com/falconry/falcon/issues/2196>`__)
- End-of-life Python 3.5 & 3.6 are no longer supported. (`#2074 <https://github.com/falconry/falcon/pull/2074>`__)
- Python 3.7 is no longer actively supported, but the framework should still
continue to install from source. We may remove the support for 3.7 altogether
Expand Down
3 changes: 1 addition & 2 deletions docs/user/recipes/pretty-json.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ implemented with a :ref:`custom media handler <custom-media-handler-type>`:

.. code:: python
import cgi
import json
import falcon
Expand All @@ -66,7 +65,7 @@ implemented with a :ref:`custom media handler <custom-media-handler-type>`:
return json.loads(data.decode())
def serialize(self, media, content_type):
_, params = cgi.parse_header(content_type)
_, params = falcon.parse_header(content_type)
indent = params.get('indent')
if indent is not None:
try:
Expand Down
53 changes: 49 additions & 4 deletions docs/user/tutorial-asgi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,12 @@ We can now implement a basic async image store. Save the following code as
class Image:
def __init__(self, config, image_id, size):
self._config = config
self.image_id = image_id
self.size = size
self.modified = datetime.datetime.utcnow()
self.modified = datetime.datetime.now(datetime.timezone.utc)
@property
def path(self):
Expand All @@ -206,7 +205,6 @@ We can now implement a basic async image store. Save the following code as
class Store:
def __init__(self, config):
self._config = config
self._images = {}
Expand Down Expand Up @@ -272,7 +270,6 @@ of images. Place the code below in a file named ``images.py``:
class Images:
def __init__(self, config, store):
self._config = config
self._store = store
Expand Down Expand Up @@ -967,6 +964,54 @@ adding ``--cov-fail-under=100`` (or any other percent threshold) to our
tests in multiple environments would most probably involve running
``coverage`` directly, and combining results.

Debugging ASGI Applications
---------------------------
(This section also applies to WSGI applications)

While developing and testing ASGI applications, understanding how to configure
and utilize logging can be helpful, especially when you encounter unexpected
issues or behaviors.

By default, Falcon does not set up logging for you,
but Python's built-in :mod:`logging` module provides a flexible framework for
emitting and capturing log messages. Here's how you can set up basic logging in
your ASGI Falcon application:

.. code:: python
import falcon
import logging
logging.basicConfig(level=logging.INFO)
class ErrorResource:
def on_get(self, req, resp):
raise Exception('Something went wrong!')
app = falcon.App()
app.add_route('/error', ErrorResource())
When the above route is accessed, Falcon will catch the unhandled exception and
automatically log an error message. Below is an example of what the log output
might look like:

.. code-block:: none
ERROR:falcon.asgi.app:Unhandled exception in ASGI application
Traceback (most recent call last):
File "path/to/falcon/app.py", line 123, in __call__
resp = resource.on_get(req, resp)
File "/path/to/your/app.py", line 7, in on_get
raise Exception("Something went wrong!")
Exception: Something went wrong!
.. note::
While logging is helpful for development and debugging, be mindful of logging
sensitive information. Ensure that log files are stored securely and are not
accessible to unauthorized users.

What Now?
---------

Expand Down
2 changes: 1 addition & 1 deletion examples/asgilook/asgilook/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, config, image_id, size):

self.image_id = image_id
self.size = size
self.modified = datetime.datetime.utcnow()
self.modified = datetime.datetime.now(datetime.timezone.utc)

@property
def path(self):
Expand Down
1 change: 0 additions & 1 deletion examples/look/look/images.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ def on_post(self, req, resp):


class ImageStore:

_CHUNK_SIZE_BYTES = 4096

# Note the use of dependency injection for standard library
Expand Down
1 change: 0 additions & 1 deletion examples/things_advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ def handle(ex, req, resp, params):


class SinkAdapter:

engines = {
'ddg': 'https://duckduckgo.com',
'y': 'https://search.yahoo.com/search',
Expand Down
1 change: 0 additions & 1 deletion examples/things_advanced_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ async def handle(ex, req, resp, params):


class SinkAdapter:

engines = {
'ddg': 'https://duckduckgo.com',
'y': 'https://search.yahoo.com/search',
Expand Down
1 change: 1 addition & 0 deletions falcon/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
from falcon.util import IS_64_BITS
from falcon.util import is_python_func
from falcon.util import misc
from falcon.util import parse_header
from falcon.util import reader
from falcon.util import runs_sync
from falcon.util import secure_filename
Expand Down
5 changes: 2 additions & 3 deletions falcon/asgi/multipart.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@

"""ASGI multipart form media handler components."""

import cgi

from falcon.asgi.reader import BufferedReader
from falcon.errors import DelimiterError
from falcon.media import multipart
from falcon.util.mediatypes import parse_header

_ALLOWED_CONTENT_HEADERS = multipart._ALLOWED_CONTENT_HEADERS
_CRLF = multipart._CRLF
Expand Down Expand Up @@ -54,7 +53,7 @@ async def get_media(self):
return self._media

async def get_text(self):
content_type, options = cgi.parse_header(self.content_type)
content_type, options = parse_header(self.content_type)
if content_type != 'text/plain':
return None

Expand Down
1 change: 0 additions & 1 deletion falcon/asgi/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -384,7 +384,6 @@ class Request(request.Request):
_wsgi_errors = None

def __init__(self, scope, receive, first_event=None, options=None):

# =====================================================================
# Prepare headers
# =====================================================================
Expand Down
1 change: 0 additions & 1 deletion falcon/bench/nuts/nuts/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ def create():


def setup_app(config):

return make_app(
config.app.root,
static_root=config.app.static_root,
Expand Down
2 changes: 1 addition & 1 deletion falcon/cmd/inspect_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"""
Script that prints out the routes of an App instance.
"""

import argparse
import importlib
import os
Expand Down Expand Up @@ -59,7 +60,6 @@ def make_parser():


def load_app(parser, args):

try:
module, instance = args.app_module.split(':', 1)
except ValueError:
Expand Down
6 changes: 4 additions & 2 deletions falcon/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
# limitations under the License.

"""Inspect utilities for falcon applications."""

from functools import partial
import inspect
from typing import Callable # NOQA: F401
Expand Down Expand Up @@ -89,8 +90,9 @@ def inspect_my_router(router):
def wraps(fn):
if router_class in _supported_routers:
raise ValueError(
'Another function is already registered'
' for the router {}'.format(router_class)
'Another function is already registered for the router {}'.format(
router_class
)
)
_supported_routers[router_class] = fn
return fn
Expand Down

0 comments on commit 6ae4d06

Please sign in to comment.