From 61c1047db2ef02556da9c28cc42c813b52db3aa9 Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Wed, 16 Oct 2024 06:42:24 +0200 Subject: [PATCH 1/7] docs(changes): prepare Falcon `4.0.0rc1` (#2374) * docs(changes): describe a known typing issue * docs(changes): improve wording: "known typing limitations" * chore: prepare Falcon `4.0.0rc1` * docs(changes): stress that typing limitations do not affect runtime --- docs/changes/4.0.0.rst | 62 ++++++++++++++++++++++++++++++++---------- falcon/version.py | 2 +- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/docs/changes/4.0.0.rst b/docs/changes/4.0.0.rst index c3e9d426a..98e1634df 100644 --- a/docs/changes/4.0.0.rst +++ b/docs/changes/4.0.0.rst @@ -4,12 +4,11 @@ Changelog for Falcon 4.0.0 Summary ------- -Falcon ``4.0.0b4`` is hopefully the final beta release before moving forward to -a release candidate. +The first release candidate of Falcon 4.0 is here! -As Falcon 4.0 is now feature-complete, we would really be thankful if you -could test this beta release with your apps, and -:ref:`let us know if you run into any issues `! +As Falcon 4.0 is now nearing a stable release, in preparation for this +milestone, we would really be thankful if you could test this release candidate +with your apps, and :ref:`let us know if you run into any issues `! Please also check the list of **breaking changes** below. If you make use of type annotations in your Falcon app, please run your type @@ -17,9 +16,9 @@ checker of choice without any *typeshed* extensions for Falcon, and :ref:`report back to us ` how it went! As always, you can grab the new release -`from PyPI `__:: +`from PyPI `__:: - pip install falcon==4.0.0b4 + pip install falcon==4.0.0rc1 (Alternatively, continue reading these docs for more :ref:`installation options `.) @@ -50,7 +49,8 @@ Changes to Supported Platforms later 4.x release, especially if we are faced with incompatible ecosystem changes in typing, Cython, etc. -Typing support + +Typing Support -------------- Type checking support was introduced in version 4.0. While most of the library is @@ -79,6 +79,46 @@ runtime behavior, but may surface new or different errors with type checkers. Also, make sure to :ref:`let us know ` which essential aliases are missing from the public interface! +Known typing limitations +^^^^^^^^^^^^^^^^^^^^^^^^ + +Falcon's emphasis on flexibility and performance has presented certain +challenges when it comes to adding type annotations to the existing code base. +One notable limitation involves using custom :class:`~falcon.Request` and/or +:class:`~falcon.Response` types in callbacks that are passed back +to the framework, such as when adding an +:meth:`error handler `. + +For instance, the following application might unexpectedly not pass type +checking: + +.. code-block:: python + + from typing import Any + + from falcon import App, HTTPInternalServerError, Request, Response + + + class MyRequest(Request): + ... + + + def handle_os_error(req: MyRequest, resp: Response, ex: Exception, + params: dict[str, Any]) -> None: + raise HTTPInternalServerError(title='OS error!') from ex + + + app = App(request_type=MyRequest) + app.add_error_handler(OSError, handle_os_error) + +(Please also see the following GitHub issue: +`#2372 `__.) + +.. important:: + This is only a typing limitation that has no effect outside of type + checking -- the above ``app`` will run just fine! + + Breaking Changes ---------------- @@ -407,12 +447,6 @@ Misc Contributors to this Release ---------------------------- -.. note:: - If we missed you below, don’t worry! - - We will refresh the full list of contributors before the 4.0.0 final - release. - Many thanks to all of our talented and stylish contributors for this release! - `aarcex3 `__ diff --git a/falcon/version.py b/falcon/version.py index a10f53d7a..e123e18a1 100644 --- a/falcon/version.py +++ b/falcon/version.py @@ -14,5 +14,5 @@ """Falcon version.""" -__version__ = '4.0.0b4' +__version__ = '4.0.0rc1' """Current version of Falcon.""" From 53512207659097c348980f7ed16a9632be0b6127 Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Fri, 18 Oct 2024 18:55:36 +0200 Subject: [PATCH 2/7] docs(changes): prepare 4.0.0 stable (#2378) * docs(4.0.0): write a paragraph on community contributions * docs(changes): prepare 4.0.0 stable --- docs/changes/4.0.0.rst | 65 ++++++++++++++++++++++++++---------------- docs/user/install.rst | 2 +- falcon/version.py | 2 +- 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/docs/changes/4.0.0.rst b/docs/changes/4.0.0.rst index 98e1634df..ac078fad5 100644 --- a/docs/changes/4.0.0.rst +++ b/docs/changes/4.0.0.rst @@ -4,29 +4,45 @@ Changelog for Falcon 4.0.0 Summary ------- -The first release candidate of Falcon 4.0 is here! - -As Falcon 4.0 is now nearing a stable release, in preparation for this -milestone, we would really be thankful if you could test this release candidate -with your apps, and :ref:`let us know if you run into any issues `! -Please also check the list of **breaking changes** below. - -If you make use of type annotations in your Falcon app, please run your type -checker of choice without any *typeshed* extensions for Falcon, and -:ref:`report back to us ` how it went! - -As always, you can grab the new release -`from PyPI `__:: - - pip install falcon==4.0.0rc1 - -(Alternatively, continue reading these docs for more -:ref:`installation options `.) - -This release would have not been possible without contributions from the -fantastic group of 30 community members and maintainers. - -Thank You! +We are happy to present Falcon 4.0, a new major version of the framework that +brings a couple of commonly requested features including support for matching +multiple path segments (using :class:`~falcon.routing.PathConverter`), and +a fully typed codebase. (Please read more about typing in the notes below.) + +The timeframe for Falcon 4.0 was challenging due to the need to balance our +high standards with the CPython 3.13 timeline. We aimed to deliver the main +development branch in this release, without resorting to another compatibility +micro update (as we did with Falcon 3.1.1-3.1.3). Following community feedback, +we also want to improve our overall release schedule by shipping smaller +increments more often. +To support this goal, we have made several tooling and testing improvements: +the build process for :ref:`binary wheels ` has been simplified +using `cibuildwheel `__, and our test suite now +only requires ``pytest`` as a hard dependency. Additionally, you can run +``pytest`` against our tests from any directory. We hope that these changes +should also benefit packaging Falcon in Linux distributions. + +As with every SemVer major release, we have removed a number of previously +deprecated functions, classes, compatibility shims, as well as made other +potentially breaking changes that we could not risk in a minor version. +If you have been paying attention the deprecation warnings from the 3.x series, +the impact should be minimal, but please do take a look at the list of breaking +changes below. + +This release would not have been possible without the numerous contributions +from our community. This release alone comprises a number of pull requests +submitted by a group of 30 talented individuals. What is more, we were +particularly impressed by the high-quality discussions and code submissions +during our +`EuroPython 2024 Sprint `__. +Some notable sprint contributions include CHIPS support, and a new +:ref:`WebSocket Tutorial `, among others. +In fact, according to the +`statistics on GitHub `__, +we are thrilled to report that the total number of Falcon +contributors has now exceeded 200. We find it fascinating that our framework +has become a collaborative effort involving so many individuals, and would like +to thank everyone who has made this release possible! Changes to Supported Platforms @@ -42,7 +58,8 @@ Changes to Supported Platforms (`#2074 `__, `#2273 `__) - End-of-life Python 3.8 is no longer actively supported, but - the framework should still continue to install from source and function. + the framework should still continue to install from the pure-Python wheel or + source distribution, and function normally. - The Falcon 4.x series is guaranteed to support CPython 3.10 and PyPy3.10 (v7.3.16). This means that we may drop the support for Python 3.8 & 3.9 altogether in a diff --git a/docs/user/install.rst b/docs/user/install.rst index 9c5ae3e20..8de6b3690 100644 --- a/docs/user/install.rst +++ b/docs/user/install.rst @@ -127,7 +127,7 @@ Binary Wheels ^^^^^^^^^^^^^ Binary Falcon wheels are automatically built for many CPython platforms, -courtesy of `cibuildwheel `__. +courtesy of `cibuildwheel `__. .. wheels:: .github/workflows/cibuildwheel.yaml diff --git a/falcon/version.py b/falcon/version.py index e123e18a1..c3971dba6 100644 --- a/falcon/version.py +++ b/falcon/version.py @@ -14,5 +14,5 @@ """Falcon version.""" -__version__ = '4.0.0rc1' +__version__ = '4.0.0' """Current version of Falcon.""" From f7189d7af8ebcb85cc8fad8ce3f21969462f67b5 Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Sat, 19 Oct 2024 19:09:27 +0200 Subject: [PATCH 3/7] chore: open 4.1.0.dev1 development --- docs/changes/4.1.0.rst | 25 +++++++++++++++++++++++++ docs/changes/index.rst | 1 + falcon/version.py | 2 +- pyproject.toml | 2 +- 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 docs/changes/4.1.0.rst diff --git a/docs/changes/4.1.0.rst b/docs/changes/4.1.0.rst new file mode 100644 index 000000000..c9a6f005a --- /dev/null +++ b/docs/changes/4.1.0.rst @@ -0,0 +1,25 @@ +Changelog for Falcon 4.1.0 +========================== + +Summary +------- + +Falcon 4.1 is in development. The progress is tracked via the +`Version 4.1 milestone `__ +on GitHub. + + +Changes to Supported Platforms +------------------------------ + +.. NOTE(vytas): No changes to the supported platforms (yet). + + +.. towncrier release notes start + +Contributors to this Release +---------------------------- + +Many thanks to all of our talented and stylish contributors for this release! + +- `vytas7 `__ diff --git a/docs/changes/index.rst b/docs/changes/index.rst index 06371e311..aa1d0ace7 100644 --- a/docs/changes/index.rst +++ b/docs/changes/index.rst @@ -3,6 +3,7 @@ Changelogs .. toctree:: + 4.1.0 <4.1.0> 4.0.0 <4.0.0> 3.1.3 <3.1.3> 3.1.2 <3.1.2> diff --git a/falcon/version.py b/falcon/version.py index c3971dba6..21a355c2c 100644 --- a/falcon/version.py +++ b/falcon/version.py @@ -14,5 +14,5 @@ """Falcon version.""" -__version__ = '4.0.0' +__version__ = '4.1.0.dev1' """Current version of Falcon.""" diff --git a/pyproject.toml b/pyproject.toml index 4fe30640a..a848647dc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,7 +116,7 @@ exclude = ["examples", "tests"] [tool.towncrier] package = "falcon" package_dir = "" - filename = "docs/changes/4.0.0.rst" + filename = "docs/changes/4.1.0.rst" directory = "docs/_newsfragments" issue_format = "`#{issue} `__" From 1bfb81920ab424f7aafaf45c5251f9e7faa3f51f Mon Sep 17 00:00:00 2001 From: Akshay Awate Date: Sat, 19 Oct 2024 22:51:24 +0530 Subject: [PATCH 4/7] Enabled pdf build format (#2371) --- .readthedocs.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 5d3177879..20221e8d0 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -13,7 +13,8 @@ build: # Build documentation in the docs/ directory with Sphinx sphinx: configuration: docs/conf.py - +formats: + - pdf # We recommend specifying your dependencies to enable reproducible builds: # https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: From 2727fef685d64155473e232c94b16b846c3e8bb0 Mon Sep 17 00:00:00 2001 From: EricGoulart Date: Sun, 20 Oct 2024 14:19:19 -0300 Subject: [PATCH 5/7] refactor: request-id to use contextvars Migrate the request-id handling from threading.local() to contextvars to improve compatibility with async coroutines and avoid issues with threading. This change ensures that request-id is properly tracked in asynchronous environments, providing more robust handling in both sync and async contexts. Previously, threading.local() was used, which does not handle coroutines effectively. By using contextvars, we ensure that the request-id remains consistent across async calls. Closes #2260 --- docs/user/recipes/request-id.rst | 6 +++--- examples/recipes/request_id_context.py | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/user/recipes/request-id.rst b/docs/user/recipes/request-id.rst index 9274a5ad8..7a69b8336 100644 --- a/docs/user/recipes/request-id.rst +++ b/docs/user/recipes/request-id.rst @@ -10,14 +10,14 @@ 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 `thread-local`_ context object to store +normal request context, you can use a `contextvars`_ object to store the request ID: .. literalinclude:: ../../../examples/recipes/request_id_context.py :language: python Then, you can create a :ref:`middleware ` class to generate a -unique ID for each request, persisting it in the thread local: +unique ID for each request, persisting it in the `contextvars` object: .. literalinclude:: ../../../examples/recipes/request_id_middleware.py :language: python @@ -48,4 +48,4 @@ In a pinch, you can also output the request ID directly: .. literalinclude:: ../../../examples/recipes/request_id_log.py :language: python -.. _thread-local: https://docs.python.org/3/library/threading.html#thread-local-data +.. _contextvars: https://docs.python.org/3/library/contextvars.html diff --git a/examples/recipes/request_id_context.py b/examples/recipes/request_id_context.py index d071c904d..a7e36ddc3 100644 --- a/examples/recipes/request_id_context.py +++ b/examples/recipes/request_id_context.py @@ -1,19 +1,19 @@ # context.py -import threading +import contextvars class _Context: def __init__(self): - self._thread_local = threading.local() + self._request_id_var = contextvars.ContextVar('request_id', default=None) @property def request_id(self): - return getattr(self._thread_local, 'request_id', None) + return self._request_id_var.get() @request_id.setter def request_id(self, value): - self._thread_local.request_id = value + self._request_id_var.set(value) ctx = _Context() From c5c12475dd93548598533c1ffd08e65bb841f78d Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Tue, 22 Oct 2024 19:35:10 +0200 Subject: [PATCH 6/7] fix(dist): only detect `falcon*` as packages (#2385) * fix(dist): only detect `falcon*` as packages * fix: fix test-dist action * fix(workflows): one more fix for test-dist... * chore: run the new gate only on master merge --- .github/workflows/test-dist.yaml | 46 +++++++++++++++++++++++++++++ .github/workflows/tox-sdist.yaml | 2 +- docs/_newsfragments/2384.bugfix.rst | 4 +++ pyproject.toml | 2 +- tools/test_dist.py | 33 +++++++++++++++++---- 5 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/test-dist.yaml create mode 100644 docs/_newsfragments/2384.bugfix.rst diff --git a/.github/workflows/test-dist.yaml b/.github/workflows/test-dist.yaml new file mode 100644 index 000000000..5f64e176a --- /dev/null +++ b/.github/workflows/test-dist.yaml @@ -0,0 +1,46 @@ +# Test source distribution and pure-Python wheel. +name: test-dist + +on: + push: + branches: + - "*" + +jobs: + test-dist: + name: test-${{ matrix.build }} + runs-on: ubuntu-latest + strategy: + matrix: + build: + - sdist + - wheel + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Build dist + env: + FALCON_DISABLE_CYTHON: "Y" + run: | + pip install --upgrade pip + pip install --upgrade build + python -m build --${{ matrix.build }} + + - name: Test sdist + if: matrix.build == 'sdist' + run: | + tools/test_dist.py dist/*.tar.gz + + - name: Test pure-Python wheel + if: matrix.build == 'wheel' + run: | + tools/test_dist.py dist/*.whl diff --git a/.github/workflows/tox-sdist.yaml b/.github/workflows/tox-sdist.yaml index d890223f1..bcccd9e5e 100644 --- a/.github/workflows/tox-sdist.yaml +++ b/.github/workflows/tox-sdist.yaml @@ -9,7 +9,7 @@ on: jobs: run_tox: - name: tox (default envlist on ${{matrix.python-version}}) + name: tox (python${{ matrix.python-version }}) runs-on: "ubuntu-latest" strategy: fail-fast: false diff --git a/docs/_newsfragments/2384.bugfix.rst b/docs/_newsfragments/2384.bugfix.rst new file mode 100644 index 000000000..7f724e80c --- /dev/null +++ b/docs/_newsfragments/2384.bugfix.rst @@ -0,0 +1,4 @@ +Installing Falcon 4.0.0 unexpectly copies many unintended directories from the +source tree to the venv's ``site-packages``. This issue has been rectified, and +our CI has been extended with new tests (that verify what is actually installed +from the distribution) to make sure this regression does not resurface. diff --git a/pyproject.toml b/pyproject.toml index a848647dc..ac71c10c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,7 +78,7 @@ zip-safe = false version = {attr = "falcon.version.__version__"} [tool.setuptools.packages.find] -exclude = ["examples", "tests"] +include = ["falcon*"] [tool.mypy] exclude = [ diff --git a/tools/test_dist.py b/tools/test_dist.py index a0dc967b5..6b35f6e9b 100755 --- a/tools/test_dist.py +++ b/tools/test_dist.py @@ -15,22 +15,43 @@ REQUIREMENTS = FALCON_ROOT / 'requirements' / 'cibwtest' TESTS = FALCON_ROOT / 'tests' +EXPECTED_SCRIPTS = set({'falcon-bench', 'falcon-inspect-app', 'falcon-print-routes'}) +EXPECTED_PACKAGES = set({'falcon'}) + def test_package(package): with tempfile.TemporaryDirectory() as tmpdir: venv = pathlib.Path(tmpdir) / 'venv' + venv_bin = venv / 'bin' + venv_pip = venv_bin / 'pip' subprocess.check_call((sys.executable, '-m', 'venv', venv)) logging.info(f'Created a temporary venv in {venv}.') - subprocess.check_call((venv / 'bin' / 'pip', 'install', '--upgrade', 'pip')) - subprocess.check_call((venv / 'bin' / 'pip', 'install', '-r', REQUIREMENTS)) + subprocess.check_call((venv_pip, 'install', '--upgrade', 'pip')) + subprocess.check_call((venv_pip, 'install', '-r', REQUIREMENTS)) logging.info(f'Installed test requirements in {venv}.') - subprocess.check_call( - (venv / 'bin' / 'pip', 'install', package), - ) + + (venv_site_pkg,) = venv.glob('lib/python*/site-packages') + bin_before = {path.name for path in venv_bin.iterdir()} + pkg_before = {path.name for path in venv_site_pkg.iterdir()} + + subprocess.check_call((venv_pip, 'install', package)) logging.info(f'Installed {package} into {venv}.') - subprocess.check_call((venv / 'bin' / 'pytest', TESTS), cwd=venv) + bin_after = {path.name for path in venv_bin.iterdir()} + assert bin_after - bin_before == EXPECTED_SCRIPTS, ( + f'Unexpected scripts installed in {venv_bin} from {package}: ' + f'{bin_after - bin_before - EXPECTED_SCRIPTS}' + ) + pkg_after = { + path.name for path in venv_site_pkg.iterdir() if path.suffix != '.dist-info' + } + assert pkg_after - pkg_before == EXPECTED_PACKAGES, ( + f'Unexpected packages installed in {venv_site_pkg} from {package}: ' + f'{pkg_after - pkg_before - EXPECTED_PACKAGES}' + ) + + subprocess.check_call((venv_bin / 'pytest', TESTS), cwd=venv) logging.info(f'{package} passes tests.') From 4d04e9b0fbf6a057aad48ccfd612a82e18b97c53 Mon Sep 17 00:00:00 2001 From: Vytautas Liuolia Date: Tue, 22 Oct 2024 21:20:25 +0200 Subject: [PATCH 7/7] chore: incorporate Falcon 4.0.1 release notes (#2386) --- docs/_newsfragments/2384.bugfix.rst | 4 ---- docs/changes/4.0.1.rst | 17 +++++++++++++++++ docs/changes/index.rst | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) delete mode 100644 docs/_newsfragments/2384.bugfix.rst create mode 100644 docs/changes/4.0.1.rst diff --git a/docs/_newsfragments/2384.bugfix.rst b/docs/_newsfragments/2384.bugfix.rst deleted file mode 100644 index 7f724e80c..000000000 --- a/docs/_newsfragments/2384.bugfix.rst +++ /dev/null @@ -1,4 +0,0 @@ -Installing Falcon 4.0.0 unexpectly copies many unintended directories from the -source tree to the venv's ``site-packages``. This issue has been rectified, and -our CI has been extended with new tests (that verify what is actually installed -from the distribution) to make sure this regression does not resurface. diff --git a/docs/changes/4.0.1.rst b/docs/changes/4.0.1.rst new file mode 100644 index 000000000..2541b0c53 --- /dev/null +++ b/docs/changes/4.0.1.rst @@ -0,0 +1,17 @@ +Changelog for Falcon 4.0.1 +========================== + +Summary +------- + +This is a minor point release addressing a Python distribution issue in +Falcon 4.0.0. + + +Fixed +----- + +- Installing Falcon 4.0.0 unexpectedly copies many unintended directories from the + source tree to the venv's ``site-packages``. This issue has been rectified, and + our CI has been extended with new tests (that verify what is actually installed + from the distribution) to make sure this regression does not resurface. (`#2384 `__) diff --git a/docs/changes/index.rst b/docs/changes/index.rst index aa1d0ace7..cec3621d2 100644 --- a/docs/changes/index.rst +++ b/docs/changes/index.rst @@ -4,6 +4,7 @@ Changelogs .. toctree:: 4.1.0 <4.1.0> + 4.0.1 <4.0.1> 4.0.0 <4.0.0> 3.1.3 <3.1.3> 3.1.2 <3.1.2>