From 14c5d990215dc90fa92132ee7dc56efa2c0f67c2 Mon Sep 17 00:00:00 2001 From: Cal Paterson Date: Mon, 9 Sep 2024 15:25:56 +0300 Subject: [PATCH] Add support for RFC5861 Cache-Control headers stale-while-revalidate and stale-if-error --- CHANGES.rst | 2 ++ docs/datastructures.rst | 22 ++++--------------- src/werkzeug/datastructures/cache_control.py | 5 ++++- src/werkzeug/datastructures/cache_control.pyi | 12 ++++++++++ tests/test_datastructures.py | 12 ++++++++++ 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 3fcd0ae4a..4c21d209e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,8 @@ Unreleased - ``CacheControl.no_transform`` is a boolean when present. ``min_fresh`` is ``None`` when not present. Added the ``must_understand`` attribute. Fixed some typing issues on cache control. :issue:`2881` +- Add ``stale_while_revalidate`` and ``stale_if_error`` properties to + ``ResponseCacheControl``. :issue:`2948` - Add 421 ``MisdirectedRequest`` HTTP exception. :issue:`2850` diff --git a/docs/datastructures.rst b/docs/datastructures.rst index 01432f413..92c969932 100644 --- a/docs/datastructures.rst +++ b/docs/datastructures.rst @@ -69,26 +69,12 @@ HTTP Related .. autoclass:: LanguageAccept .. autoclass:: RequestCacheControl - :members: - - .. autoattribute:: no_cache - - .. autoattribute:: no_store - - .. autoattribute:: max_age - - .. autoattribute:: no_transform + :members: + :inherited-members: .. autoclass:: ResponseCacheControl - :members: - - .. autoattribute:: no_cache - - .. autoattribute:: no_store - - .. autoattribute:: max_age - - .. autoattribute:: no_transform + :members: + :inherited-members: .. autoclass:: ETags :members: diff --git a/src/werkzeug/datastructures/cache_control.py b/src/werkzeug/datastructures/cache_control.py index 6ff4eceeb..fa7ed0a71 100644 --- a/src/werkzeug/datastructures/cache_control.py +++ b/src/werkzeug/datastructures/cache_control.py @@ -165,7 +165,8 @@ class ResponseCacheControl(_CacheControl): ``no_transform`` is a boolean when present. .. versionchanged:: 3.1 - Added the ``must_understand`` attribute. + Added the ``must_understand``, ``stale_while_revalidate``, and + ``stale_if_error`` attributes. .. versionchanged:: 2.1.1 ``s_maxage`` converts the value to an int. @@ -186,6 +187,8 @@ class ResponseCacheControl(_CacheControl): s_maxage = cache_control_property("s-maxage", None, int) immutable = cache_control_property("immutable", None, bool) must_understand = cache_control_property("must-understand", None, bool) + stale_while_revalidate = cache_control_property("stale-while-revalidate", None, int) + stale_if_error = cache_control_property("stale-if-error", None, int) # circular dependencies diff --git a/src/werkzeug/datastructures/cache_control.pyi b/src/werkzeug/datastructures/cache_control.pyi index 4c9f4df37..93c595db8 100644 --- a/src/werkzeug/datastructures/cache_control.pyi +++ b/src/werkzeug/datastructures/cache_control.pyi @@ -116,3 +116,15 @@ class ResponseCacheControl(_CacheControl): def must_understand(self, value: bool | None) -> None: ... @must_understand.deleter def must_understand(self) -> None: ... + @property + def stale_while_revalidate(self) -> int | None: ... + @stale_while_revalidate.setter + def stale_while_revalidate(self, value: int | None) -> None: ... + @stale_while_revalidate.deleter + def stale_while_revalidate(self) -> None: ... + @property + def stale_if_error(self) -> int | None: ... + @stale_if_error.setter + def stale_if_error(self, value: int | None) -> None: ... + @stale_if_error.deleter + def stale_if_error(self) -> None: ... diff --git a/tests/test_datastructures.py b/tests/test_datastructures.py index 830dfefd5..a681c022b 100644 --- a/tests/test_datastructures.py +++ b/tests/test_datastructures.py @@ -973,6 +973,18 @@ def test_must_understand(self): cc = ds.ResponseCacheControl() assert cc.must_understand is False + def test_stale_while_revalidate(self): + cc = ds.ResponseCacheControl([("stale-while-revalidate", "1")]) + assert cc.stale_while_revalidate == 1 + cc = ds.ResponseCacheControl() + assert cc.stale_while_revalidate is None + + def test_stale_if_error(self): + cc = ds.ResponseCacheControl([("stale-if-error", "1")]) + assert cc.stale_if_error == 1 + cc = ds.ResponseCacheControl() + assert cc.stale_while_revalidate is None + class TestContentSecurityPolicy: def test_construct(self):