Repository: pallets/werkzeug

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



Discussion:
I think the change would be something like

```
s_maxage = cache_control_property("s-maxage", None, int)
```
Related to the change in https://github.com/pallets/werkzeug/pull/2254, which missed testing `ResponseCacheControl`.

Resolving patch:
diff --git a/src/werkzeug/datastructures.py b/src/werkzeug/datastructures.py
--- 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
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -119,6 +119,9 @@ def test_cache_control_header(self):
         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):