Skip to content

Commit

Permalink
Merge branch 'master' into issue2382
Browse files Browse the repository at this point in the history
  • Loading branch information
vytas7 authored Dec 10, 2024
2 parents a3cba51 + 11076b8 commit e3bb626
Show file tree
Hide file tree
Showing 8 changed files with 62 additions and 36 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ jobs:
python-version: "3.13"
- env: py313_cython
python-version: "3.13"
- env: py314
python-version: "3.14.0-alpha.2 - 3.14.0"
- env: py312_nocover
os: macos-latest
platform-label: ' (macos)'
Expand Down
5 changes: 5 additions & 0 deletions docs/_newsfragments/2262.newandimproved.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Similar to :func:`~falcon.testing.create_environ`,
the :func:`~falcon.testing.create_scope` testing helper now preserves the raw URI path,
and propagates it to the created ASGI connection scope as the ``raw_path`` byte string
(according to the `ASGI specification
<https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope>`__).
17 changes: 5 additions & 12 deletions docs/api/cookies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,13 +155,6 @@ at least set this attribute to ``'Lax'`` in order to mitigate
Currently, :meth:`~falcon.Response.set_cookie` does not set `SameSite` by
default, although this may change in a future release.

.. note::

The standard ``http.cookies`` module does not support the `SameSite`
attribute in versions prior to Python 3.8. Therefore, Falcon performs a
simple monkey-patch on the standard library module to backport this
feature for apps running on older Python versions.

.. _RFC 6265, Section 4.1.2.5:
https://tools.ietf.org/html/rfc6265#section-4.1.2.5

Expand All @@ -172,7 +165,7 @@ by setting the 'samesite' kwarg.
The Partitioned Attribute
~~~~~~~~~~~~~~~~~~~~~~~~~

Starting from Q1 2024, Google Chrome will start to
Starting from Q1 2024, Google Chrome started to
`phase out support for third-party cookies
<https://developers.google.com/privacy-sandbox/3pcd/prepare/prepare-for-phaseout>`__.
If your site is relying on cross-site cookies, it might be necessary to set the
Expand All @@ -187,7 +180,7 @@ automatically depending on other attributes (like ``SameSite``),
although this may change in a future release.

.. note::
Similar to ``SameSite`` on older Python versions, the standard
:mod:`http.cookies` module does not support the ``Partitioned`` attribute
yet, and Falcon performs the same monkey-patching as it did for
``SameSite``.
The standard :mod:`http.cookies` module does not support the `Partitioned`
attribute in versions prior to Python 3.14. Therefore, Falcon performs a
simple monkey-patch on the standard library module to backport this
feature for apps running on older Python versions.
9 changes: 8 additions & 1 deletion falcon/testing/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -974,10 +974,16 @@ def create_scope(
iterable yielding a series of two-member (*name*, *value*)
iterables. Each pair of items provides the name and value
for the 'Set-Cookie' header.
.. versionadded:: 4.1
The raw (i.e., not URL-decoded) version of the provided `path` is now
preserved in the returned scope as the ``raw_path`` byte string.
According to the ASGI specification, ``raw_path`` **does not include**
any query string.
"""

http_version = _fixup_http_version(http_version)

raw_path, _, _ = path.partition('?')
path = uri.decode(path, unquote_plus=False)

# NOTE(kgriffs): Handles both None and ''
Expand All @@ -995,6 +1001,7 @@ def create_scope(
'http_version': http_version,
'method': method.upper(),
'path': path,
'raw_path': raw_path.encode(),
'query_string': query_string_bytes,
}

Expand Down
22 changes: 14 additions & 8 deletions falcon/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,23 @@
from falcon.util.sync import wrap_sync_to_async_unsafe
from falcon.util.time import TimezoneGMT

# NOTE(kgriffs): Backport support for the new 'SameSite' attribute
# for Python versions prior to 3.8. We do it this way because
# SimpleCookie does not give us a simple way to specify our own
# subclass of Morsel.
# NOTE(kgriffs, m-mueller): Monkey-patch support for the new 'Partitioned'
# attribute that will probably be added in Python 3.14.
# We do it this way because SimpleCookie does not give us a simple way to
# specify our own subclass of Morsel.
_reserved_cookie_attrs = http_cookies.Morsel._reserved # type: ignore
if 'samesite' not in _reserved_cookie_attrs: # pragma: no cover
_reserved_cookie_attrs['samesite'] = 'SameSite'
# NOTE(m-mueller): Same for the 'partitioned' attribute that will
# probably be added in Python 3.13 or 3.14.
if 'partitioned' not in _reserved_cookie_attrs: # pragma: no cover
_reserved_cookie_attrs['partitioned'] = 'Partitioned'
# NOTE(vytas): Morsel._reserved_defaults is a new optimization in Python 3.14+
# for faster initialization of Morsel instances.
# TODO(vytas): Remove this part of monkey-patching in the case CPython 3.14
# adds the Partitioned attribute to Morsel.
_reserved_cookie_defaults = getattr(http_cookies.Morsel, '_reserved_defaults', None)
if (
_reserved_cookie_defaults is not None
and 'partitioned' not in _reserved_cookie_defaults
): # pragma: no cover
_reserved_cookie_defaults['partitioned'] = ''


IS_64_BITS = sys.maxsize > 2**32
Expand Down
3 changes: 2 additions & 1 deletion requirements/tests
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ ujson
python-rapidjson; platform_python_implementation != 'PyPy' and platform_machine != 's390x' and platform_machine != 'aarch64'

# wheels are missing some EoL interpreters and non-x86 platforms; build would fail unless rust is available
orjson; platform_python_implementation != 'PyPy' and platform_machine != 's390x' and platform_machine != 'aarch64'
# (not available for CPython 3.14 either yet)
orjson; python_version < '3.14' and platform_python_implementation != 'PyPy' and platform_machine != 's390x' and platform_machine != 'aarch64'
18 changes: 18 additions & 0 deletions tests/asgi/test_testing_asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,21 @@ def test_immediate_disconnect():

with pytest.raises(ConnectionError):
client.simulate_get('/', asgi_disconnect_ttl=0)


@pytest.mark.parametrize(
'path, expected',
[
('/cache/http%3A%2F%2Ffalconframework.org/status', True),
(
'/cache/http%3A%2F%2Ffalconframework.org/status?param1=value1&param2=value2',
False,
),
],
)
def test_create_scope_preserve_raw_path(path, expected):
scope = testing.create_scope(path=path)
if expected:
assert scope['raw_path'] == path.encode()
else:
assert scope['raw_path'] != path.encode()
22 changes: 8 additions & 14 deletions tests/test_recipes.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,33 +115,27 @@ def test_optional_indent(self, util):


class TestRawURLPath:
def path_extras(self, asgi, url):
if asgi:
return {'raw_path': url.encode()}
return None

def test_raw_path(self, asgi, app_kind, util):
recipe = util.load_module(
'raw_url_path', parent_dir='examples/recipes', suffix=app_kind
)

# TODO(vytas): Improve TestClient to automatically add ASGI raw_path
# (as it does for WSGI): GH #2262.

url1 = '/cache/http%3A%2F%2Ffalconframework.org'
result1 = falcon.testing.simulate_get(
recipe.app, url1, extras=self.path_extras(asgi, url1)
)
result1 = falcon.testing.simulate_get(recipe.app, url1)
assert result1.status_code == 200
assert result1.json == {'url': 'http://falconframework.org'}

scope1 = falcon.testing.create_scope(url1)
assert scope1['raw_path'] == url1.encode()

url2 = '/cache/http%3A%2F%2Ffalconframework.org/status'
result2 = falcon.testing.simulate_get(
recipe.app, url2, extras=self.path_extras(asgi, url2)
)
result2 = falcon.testing.simulate_get(recipe.app, url2)
assert result2.status_code == 200
assert result2.json == {'cached': True}

scope2 = falcon.testing.create_scope(url2)
assert scope2['raw_path'] == url2.encode()


class TestRequestIDContext:
@pytest.fixture
Expand Down

0 comments on commit e3bb626

Please sign in to comment.