Skip to content

Commit 4ac5ec6

Browse files
authored
chore: prepare Falcon 4.0.2 (in-tree) (#2392)
* chore: prepare 4.0.2 in-tree (WiP) * chore: relabel #2387 as bugfix * chore: aggregate 4.0.2 contributors * chore: bump up version to `4.0.2` * docs: slightly tweak newsfragment for #2387 * chore: aggregate contributors, +docs tweaks * docs(ASGI): clean up docs on logging setup * chore: render out newsfragments * docs(typing); describe another limitation/inconsistency
1 parent ab2ce4c commit 4ac5ec6

12 files changed

+155
-117
lines changed

AUTHORS

+3
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,9 @@ listed below by date of first contribution:
199199
* Agustin Arce (aarcex3)
200200
* Christian Grossmüller (chgad)
201201
* Sai Prathik R (prathik2401)
202+
* Akshay Awate (AkshayAwate)
203+
* Jasper Spaans (jap)
204+
* Alessandro Chitarrini (chitvs)
202205

203206
(et al.)
204207

docs/_newsfragments/2365.misc.rst

-1
This file was deleted.

docs/_newsfragments/2387.misc.rst

-7
This file was deleted.

docs/api/typing.rst

+12-4
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ Known Limitations
3333

3434
Falcon's emphasis on flexibility and performance presents certain
3535
challenges when it comes to adding type annotations to the existing code base.
36+
3637
One notable limitation involves using custom :class:`~falcon.Request` and/or
3738
:class:`~falcon.Response` types in callbacks that are passed back
3839
to the framework, such as when adding an
3940
:meth:`error handler <falcon.App.add_error_handler>`.
40-
4141
For instance, the following application might unexpectedly not pass type
4242
checking:
4343

@@ -60,12 +60,20 @@ checking:
6060
app = App(request_type=MyRequest)
6161
app.add_error_handler(OSError, handle_os_error)
6262
63-
(Please also see the following GitHub issue:
63+
(We are working on addressing this limitation at the time of writing --
64+
please see the following GitHub issue for the progress, and possible solutions:
6465
`#2372 <https://github.com/falconry/falcon/issues/2372>`__.)
6566

67+
Another known inconsistency is the typing of the
68+
:class:`converter interface <falcon.routing.BaseConverter>`, where certain
69+
subclasses (such as :class:`~falcon.routing.PathConverter`) declare a different
70+
input type than the base ``convert()`` method.
71+
(See also the discussions and possible solutions on the GitHub issue
72+
`#2396 <https://github.com/falconry/falcon/issues/2396>`__.)
73+
6674
.. important::
67-
This is only a typing limitation that has no effect outside of type
68-
checking -- the above ``app`` will run just fine!
75+
The above issues are only typing limitations that have no effect outside of
76+
type checking -- applications will work just fine at runtime!
6977

7078

7179
Public Type Aliases

docs/changes/4.0.2.rst

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
Changelog for Falcon 4.0.2
2+
==========================
3+
4+
Summary
5+
-------
6+
7+
This is a minor point release to fix some missed re-exports for type checkers.
8+
In addition, we have also included a couple of documentation improvements.
9+
10+
11+
Fixed
12+
-----
13+
14+
- Running Mypy on code that uses parts of ``falcon.testing``
15+
would previously lead to errors like::
16+
17+
Name "falcon.testing.TestClient" is not defined
18+
19+
This has been fixed by explicitly exporting the names that are
20+
imported into the ``falcon.testing`` namespace. (`#2387 <https://github.com/falconry/falcon/issues/2387>`__)
21+
22+
23+
Misc
24+
----
25+
26+
- The printable PDF version of our documentation was enabled on Read the Docs. (`#2365 <https://github.com/falconry/falcon/issues/2365>`__)
27+
28+
29+
Contributors to this Release
30+
----------------------------
31+
32+
Many thanks to those who contributed to this bugfix release:
33+
34+
- `AkshayAwate <https://github.com/AkshayAwate>`__
35+
- `CaselIT <https://github.com/CaselIT>`__
36+
- `chitvs <https://github.com/chitvs>`__
37+
- `jap <https://github.com/jap>`__
38+
- `vytas7 <https://github.com/vytas7>`__

docs/changes/4.1.0.rst

-25
This file was deleted.

docs/changes/index.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ Changelogs
33

44
.. toctree::
55

6-
4.1.0 <4.1.0>
6+
4.0.2 <4.0.2>
77
4.0.1 <4.0.1>
88
4.0.0 <4.0.0>
99
3.1.3 <3.1.3>

docs/user/faq.rst

+23-26
Original file line numberDiff line numberDiff line change
@@ -1348,47 +1348,44 @@ To include multiple values, simply use ``"; "`` to separate each name-value
13481348
pair. For example, if you were to pass ``{'Cookie': 'xxx=yyy; hello=world'}``,
13491349
you would get ``{'cookies': {'xxx': 'yyy', 'hello': 'world'}}``.
13501350

1351-
Why do I not see error tracebacks in ASGI applications?
1352-
-------------------------------------------------------
1351+
Why do I see no error tracebacks in my ASGI application?
1352+
--------------------------------------------------------
13531353

1354-
When using Falcon with ASGI servers like Uvicorn,
1355-
you might notice that server errors do not display a traceback by default.
1356-
This behavior differs from WSGI applications, where errors are logged to `stderr`,
1357-
providing detailed tracebacks.
1354+
When using Falcon with an ASGI server like Uvicorn,
1355+
you might notice that server errors do not include any traceback by default.
1356+
This behavior differs from WSGI, where the PEP-3333 specification defines the
1357+
`wsgi.errors <https://peps.python.org/pep-3333/#environ-variables>`__ stream
1358+
(which Falcon utilizes to log unhandled
1359+
:class:`internal server errors <falcon.HTTPInternalServerError>`).
13581360

1359-
The reason for this is that ASGI does not define a standardized way to log errors back to the application server,
1360-
unlike WSGI. Therefore, you need to configure logging manually to see these tracebacks.
1361+
Since there is no standardized way to log errors back to the ASGI server,
1362+
the framework simply opts to log them using the ``falcon``
1363+
:class:`logger <logging.Logger>`.
13611364

1362-
Here’s how to set up logging in your ASGI Falcon application to capture error tracebacks:
1365+
The easiest way to get started is configuring the root logger via
1366+
:func:`logging.basicConfig`:
13631367

13641368
.. code:: python
13651369
13661370
import logging
1371+
13671372
import falcon
13681373
import falcon.asgi
13691374
13701375
logging.basicConfig(
1371-
format="%(asctime)s [%(levelname)s] %(message)s",
1372-
level=logging.INFO
1373-
)
1376+
format="%(asctime)s [%(levelname)s] %(message)s", level=logging.INFO)
1377+
13741378
1375-
class ThingsResource:
1379+
class FaultyResource:
13761380
async def on_get(self, req, resp):
13771381
raise ValueError('foo')
13781382
1379-
app = falcon.asgi.App()
1380-
things = ThingsResource()
1381-
app.add_route('/things', things)
1382-
1383-
By adding the above logging configuration, you will see tracebacks like this in your console:
13841383
1385-
.. code-block:: none
1384+
app = falcon.asgi.App()
1385+
app.add_route('/things', FaultyResource())
13861386
1387-
[ERROR] [FALCON] Unhandled exception in ASGI app
1388-
Traceback (most recent call last):
1389-
File "<...>", line 12, in on_get
1390-
raise ValueError('foo')
1391-
ValueError: foo
1387+
By adding the above logging configuration, you should now see tracebacks logged
1388+
to :any:`stderr <sys.stderr>` when accessing ``/things``.
13921389

1393-
For additional details on this topic,
1394-
please refer to :ref:`debugging-asgi-applications`.
1390+
For additional details on this topic,
1391+
please refer to :ref:`debugging_asgi_applications`.

docs/user/tutorial-asgi.rst

+62-48
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,68 @@ Woohoo, it works!!!
115115

116116
Well, sort of. Onwards to adding some real functionality!
117117

118+
.. _debugging_asgi_applications:
119+
120+
Debugging ASGI Applications
121+
---------------------------
122+
123+
While developing and testing a Falcon ASGI application along the lines of this
124+
tutorial, you may encounter unexpected issues or behaviors, be it a copy-paste
125+
mistake, an idea that didn't work out, or unusual input where validation falls
126+
outside of the scope of this tutorial.
127+
128+
Unlike WSGI, the ASGI specification has no standard mechanism for logging
129+
errors back to the application server, so Falcon falls back to the stdlib's
130+
:mod:`logging` (using the ``falcon`` :class:`logger <logging.Logger>`).
131+
132+
As a well-behaved library, Falcon does not configure any loggers since that
133+
might interfere with the user's logging setup.
134+
Here's how you can set up basic logging in your ASGI Falcon application via
135+
:func:`logging.basicConfig`:
136+
137+
.. code:: python
138+
139+
import logging
140+
141+
import falcon
142+
143+
logging.basicConfig(level=logging.INFO)
144+
145+
146+
class ErrorResource:
147+
def on_get(self, req, resp):
148+
raise Exception('Something went wrong!')
149+
150+
151+
app = falcon.App()
152+
app.add_route('/error', ErrorResource())
153+
154+
When the above route is accessed, Falcon will catch the unhandled exception and
155+
automatically log an error message. Below is an example of what the log output
156+
might look like:
157+
158+
.. code-block:: none
159+
160+
ERROR:falcon.asgi.app:Unhandled exception in ASGI application
161+
Traceback (most recent call last):
162+
File "/path/to/your/app.py", line 123, in __call__
163+
resp = resource.on_get(req, resp)
164+
File "/path/to/your/app.py", line 7, in on_get
165+
raise Exception("Something went wrong!")
166+
Exception: Something went wrong!
167+
168+
.. note::
169+
While logging is helpful for development and debugging, be mindful of
170+
logging sensitive information. Ensure that log files are stored securely
171+
and are not accessible to unauthorized users.
172+
173+
.. note::
174+
Unhandled errors are only logged automatically by Falcon's default error
175+
handler for :class:`Exception`. If you
176+
:meth:`replace this handler <falcon.asgi.App.add_error_handler>` with your
177+
own generic :class:`Exception` handler, you are responsible for logging or
178+
reporting these errors yourself.
179+
118180
.. _asgi_tutorial_config:
119181

120182
Configuration
@@ -963,54 +1025,6 @@ adding ``--cov-fail-under=100`` (or any other percent threshold) to our
9631025
tests in multiple environments would most probably involve running
9641026
``coverage`` directly, and combining results.
9651027

966-
.. _debugging-asgi-applications:
967-
968-
Debugging ASGI Applications
969-
---------------------------
970-
(This section also applies to WSGI applications)
971-
972-
While developing and testing ASGI applications, understanding how to configure
973-
and utilize logging can be helpful, especially when you encounter unexpected
974-
issues or behaviors.
975-
976-
By default, Falcon does not set up logging for you,
977-
but Python's built-in :mod:`logging` module provides a flexible framework for
978-
emitting and capturing log messages. Here's how you can set up basic logging in
979-
your ASGI Falcon application:
980-
981-
.. code:: python
982-
983-
import logging
984-
import falcon
985-
986-
logging.basicConfig(level=logging.INFO)
987-
988-
class ErrorResource:
989-
def on_get(self, req, resp):
990-
raise Exception('Something went wrong!')
991-
992-
app = falcon.App()
993-
app.add_route('/error', ErrorResource())
994-
995-
When the above route is accessed, Falcon will catch the unhandled exception and
996-
automatically log an error message. Below is an example of what the log output
997-
might look like:
998-
999-
.. code-block:: none
1000-
1001-
ERROR:falcon.asgi.app:Unhandled exception in ASGI application
1002-
Traceback (most recent call last):
1003-
File "path/to/falcon/app.py", line 123, in __call__
1004-
resp = resource.on_get(req, resp)
1005-
File "/path/to/your/app.py", line 7, in on_get
1006-
raise Exception("Something went wrong!")
1007-
Exception: Something went wrong!
1008-
1009-
.. note::
1010-
While logging is helpful for development and debugging, be mindful of logging
1011-
sensitive information. Ensure that log files are stored securely and are not
1012-
accessible to unauthorized users.
1013-
10141028
What Now?
10151029
---------
10161030

falcon/typing.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,28 @@
2121
from falcon.asgi import SSEvent
2222

2323
Headers = Dict[str, str]
24-
"""Headers dictionary returned by the framework."""
24+
"""Headers dictionary returned by the framework.
25+
26+
.. versionadded:: 4.0
27+
"""
2528

2629

2730
# WSGI
2831
class ReadableIO(Protocol):
29-
"""File-like protocol that defines only a read method."""
32+
"""File-like protocol that defines only a read method.
33+
34+
.. versionadded:: 4.0
35+
"""
3036

3137
def read(self, n: Optional[int] = ..., /) -> bytes: ...
3238

3339

3440
# ASGI
3541
class AsyncReadableIO(Protocol):
36-
"""Async file-like protocol that defines only a read method, and is iterable."""
42+
"""Async file-like protocol that defines only a read method, and is iterable.
43+
44+
.. versionadded:: 4.0
45+
"""
3746

3847
async def read(self, n: Optional[int] = ..., /) -> bytes: ...
3948
def __aiter__(self) -> AsyncIterator[bytes]: ...
@@ -42,4 +51,6 @@ def __aiter__(self) -> AsyncIterator[bytes]: ...
4251
SSEEmitter = AsyncIterator[Optional['SSEvent']]
4352
"""Async generator or iterator over Server-Sent Events
4453
(instances of :class:`falcon.asgi.SSEvent`).
54+
55+
.. versionadded:: 4.0
4556
"""

falcon/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414

1515
"""Falcon version."""
1616

17-
__version__ = '4.1.0.dev1'
17+
__version__ = '4.0.2'
1818
"""Current version of Falcon."""

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ include = ["falcon*"]
116116
[tool.towncrier]
117117
package = "falcon"
118118
package_dir = ""
119-
filename = "docs/changes/4.1.0.rst"
119+
filename = "docs/changes/4.0.2.rst"
120120
directory = "docs/_newsfragments"
121121
issue_format = "`#{issue} <https://github.com/falconry/falcon/issues/{issue}>`__"
122122

0 commit comments

Comments
 (0)