Repository: pallets/werkzeug (python)

Issue:
`ResponseCacheControl.s_maxage` raises error when set to an int
`Response().cache_control.s_maxage` errors with `None` is not callable

```
from werkzeug.datastructures import ResponseCacheControl

cc = ResponseCacheControl()
cc.s_maxage = 1

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [13], in <cell line: 1>()
----> 1 cc.s_maxage = 1

File /usr/local/lib/python3.10/site-packages/werkzeug/datastructures.py:1958, in cache_control_property.<locals>.<lambda>(x, v)
   1949 def cache_control_property(key, empty, type):
   1950     """Return a new property object for a cache header. Useful if you
   1951     want to add support for a cache extension in a subclass.
   1952 
   1953     .. versionchanged:: 2.0
   1954         Renamed from ``cache_property``.
   1955     """
   1956     return property(
   1957         lambda x: x._get_cache_value(key, empty, type),
-> 1958         lambda x, v: x._set_cache_value(key, v, type),
   1959         lambda x: x._del_cache_value(key),
   1960         f"accessor for {key!r}",
   1961     )

File /usr/local/lib/python3.10/site-packages/werkzeug/datastructures.py:2039, in _CacheControl._set_cache_value(self, key, value, type)
   2037     self[key] = None
   2038 else:
-> 2039     self[key] = type(value)

TypeError: 'NoneType' object is not callable
```

2.0.3 is OK


Flask use case
```
from flask import Response

response = Response()
response.cache_control.s_maxage = 1
```

Environment:

- Python version: 3.10.4
- Werkzeug version: 2.1.0


Pull request:
`s_maxage` converts to an int
- fixes #2364 

<!--
Ensure each step in CONTRIBUTING.rst is complete by adding an "x" to
each box below.

If only docs were changed, these aren't relevant and can be removed.
-->

Checklist:

- [x] Add tests that demonstrate the correct behavior of the change. Tests should fail without the change.
- [ ] Add or update relevant docs, in the docs folder and in code.
- [x] Add an entry in `CHANGES.rst` summarizing the change and linking to the issue.
- [x] Add `.. versionchanged::` entries in any relevant code docs.
- [x] Run `pre-commit` hooks and fix any issues.
- [x] Run `pytest` and `tox`, no tests failed.


Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 96445280..c4025cbf 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,9 @@ Version 2.1.1
 
 Unreleased
 
+-   ``ResponseCacheControl.s_maxage`` converts its value to an int, like
+    ``max_age``. :issue:`2364`
+
 
 Version 2.1.0
 -------------
diff --git a/src/werkzeug/datastructures.py b/src/werkzeug/datastructures.py
index cdb578f3..d0da331d 100644
--- a/src/werkzeug/datastructures.py
+++ b/src/werkzeug/datastructures.py
@@ -2090,6 +2090,9 @@ class ResponseCacheControl(_CacheControl):
     you plan to subclass it and add your own items have a look at the sourcecode
     for that class.
 
+    .. versionchanged:: 2.1.1
+        ``s_maxage`` converts the value to an int.
+
     .. versionchanged:: 2.1.0
         Setting int properties such as ``max_age`` will convert the
         value to an int.
@@ -2103,7 +2106,7 @@ class ResponseCacheControl(_CacheControl):
     private = cache_control_property("private", "*", None)
     must_revalidate = cache_control_property("must-revalidate", None, bool)
     proxy_revalidate = cache_control_property("proxy-revalidate", None, bool)
-    s_maxage = cache_control_property("s-maxage", None, None)
+    s_maxage = cache_control_property("s-maxage", None, int)
     immutable = cache_control_property("immutable", None, bool)
 
 


Test patch:
diff --git a/tests/test_http.py b/tests/test_http.py
index dfa6dd11..5936bfa5 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -119,6 +119,9 @@ class TestHTTPUtility:
         c.max_age = 3.1
         assert c.max_age == 3
         del c.max_age
+        c.s_maxage = 3.1
+        assert c.s_maxage == 3
+        del c.s_maxage
         assert c.to_header() == "no-cache"
 
     def test_csp_header(self):