2
2
3
3
import collections .abc as cabc
4
4
import typing as t
5
+ from inspect import cleandoc
5
6
6
7
from .mixins import ImmutableDictMixin
7
8
from .structures import CallbackDict
8
9
9
10
10
- def cache_control_property (key : str , empty : t .Any , type : type [t .Any ] | None ) -> t .Any :
11
+ def cache_control_property (
12
+ key : str , empty : t .Any , type : type [t .Any ] | None , * , doc : str | None = None
13
+ ) -> t .Any :
11
14
"""Return a new property object for a cache header. Useful if you
12
15
want to add support for a cache extension in a subclass.
13
16
17
+ :param key: The attribute name present in the parsed cache-control header dict.
18
+ :param empty: The value to use if the key is present without a value.
19
+ :param type: The type to convert the string value to instead of a string. If
20
+ conversion raises a ``ValueError``, the returned value is ``None``.
21
+ :param doc: The docstring for the property. If not given, it is generated
22
+ based on the other params.
23
+
24
+ .. versionchanged:: 3.1
25
+ Added the ``doc`` param.
26
+
14
27
.. versionchanged:: 2.0
15
28
Renamed from ``cache_property``.
16
29
"""
30
+ if doc is None :
31
+ parts = [f"The ``{ key } `` attribute." ]
32
+
33
+ if type is bool :
34
+ parts .append ("A ``bool``, either present or not." )
35
+ else :
36
+ if type is None :
37
+ parts .append ("A ``str``," )
38
+ else :
39
+ parts .append (f"A ``{ type .__name__ } ``," )
40
+
41
+ if empty is not None :
42
+ parts .append (f"``{ empty !r} `` if present with no value," )
43
+
44
+ parts .append ("or ``None`` if not present." )
45
+
46
+ doc = " " .join (parts )
47
+
17
48
return property (
18
49
lambda x : x ._get_cache_value (key , empty , type ),
19
50
lambda x , v : x ._set_cache_value (key , v , type ),
20
51
lambda x : x ._del_cache_value (key ),
21
- f"accessor for { key !r } " ,
52
+ doc = cleandoc ( doc ) ,
22
53
)
23
54
24
55
25
- class _CacheControl (CallbackDict [str , t .Any ]):
56
+ class _CacheControl (CallbackDict [str , t .Optional [ str ] ]):
26
57
"""Subclass of a dict that stores values for a Cache-Control header. It
27
58
has accessors for all the cache-control directives specified in RFC 2616.
28
59
The class does not differentiate between request and response directives.
@@ -36,36 +67,25 @@ class _CacheControl(CallbackDict[str, t.Any]):
36
67
that class.
37
68
38
69
.. versionchanged:: 3.1
70
+ Dict values are always ``str | None``. Setting properties will
71
+ convert the value to a string. Setting a non-bool property to
72
+ ``False`` is equivalent to setting it to ``None``. Getting typed
73
+ properties will return ``None`` if conversion raises
74
+ ``ValueError``, rather than the string.
39
75
40
- ``no_transform`` is a boolean when present.
41
-
42
- .. versionchanged:: 2.1.0
76
+ .. versionchanged:: 2.1
43
77
Setting int properties such as ``max_age`` will convert the
44
78
value to an int.
45
79
46
80
.. versionchanged:: 0.4
47
-
48
- Setting `no_cache` or `private` to boolean `True` will set the implicit
49
- none-value which is ``*``:
50
-
51
- >>> cc = ResponseCacheControl()
52
- >>> cc.no_cache = True
53
- >>> cc
54
- <ResponseCacheControl 'no-cache'>
55
- >>> cc.no_cache
56
- '*'
57
- >>> cc.no_cache = None
58
- >>> cc
59
- <ResponseCacheControl ''>
60
-
61
- In versions before 0.5 the behavior documented here affected the now
62
- no longer existing `CacheControl` class.
81
+ Setting ``no_cache`` or ``private`` to ``True`` will set the
82
+ implicit value ``"*"``.
63
83
"""
64
84
65
- no_cache : str | bool | None = cache_control_property ("no-cache" , "*" , None )
66
85
no_store : bool = cache_control_property ("no-store" , None , bool )
67
- max_age : int | None = cache_control_property ("max-age" , - 1 , int )
86
+ max_age : int | None = cache_control_property ("max-age" , None , int )
68
87
no_transform : bool = cache_control_property ("no-transform" , None , bool )
88
+ stale_if_error : int | None = cache_control_property ("stale-if-error" , None , int )
69
89
70
90
def __init__ (
71
91
self ,
@@ -81,17 +101,20 @@ def _get_cache_value(
81
101
"""Used internally by the accessor properties."""
82
102
if type is bool :
83
103
return key in self
84
- if key in self :
85
- value = self [key ]
86
- if value is None :
87
- return empty
88
- elif type is not None :
89
- try :
90
- value = type (value )
91
- except ValueError :
92
- pass
93
- return value
94
- return None
104
+
105
+ if key not in self :
106
+ return None
107
+
108
+ if (value := self [key ]) is None :
109
+ return empty
110
+
111
+ if type is not None :
112
+ try :
113
+ value = type (value )
114
+ except ValueError :
115
+ return None
116
+
117
+ return value
95
118
96
119
def _set_cache_value (
97
120
self , key : str , value : t .Any , type : type [t .Any ] | None
@@ -102,16 +125,15 @@ def _set_cache_value(
102
125
self [key ] = None
103
126
else :
104
127
self .pop (key , None )
128
+ elif value is None or value is False :
129
+ self .pop (key , None )
130
+ elif value is True :
131
+ self [key ] = None
105
132
else :
106
- if value is None :
107
- self .pop (key , None )
108
- elif value is True :
109
- self [key ] = None
110
- else :
111
- if type is not None :
112
- self [key ] = type (value )
113
- else :
114
- self [key ] = value
133
+ if type is not None :
134
+ value = type (value )
135
+
136
+ self [key ] = str (value )
115
137
116
138
def _del_cache_value (self , key : str ) -> None :
117
139
"""Used internally by the accessor properties."""
@@ -132,7 +154,7 @@ def __repr__(self) -> str:
132
154
cache_property = staticmethod (cache_control_property )
133
155
134
156
135
- class RequestCacheControl (ImmutableDictMixin [str , t .Any ], _CacheControl ): # type: ignore[misc]
157
+ class RequestCacheControl (ImmutableDictMixin [str , t .Optional [ str ] ], _CacheControl ): # type: ignore[misc]
136
158
"""A cache control for requests. This is immutable and gives access
137
159
to all the request-relevant cache control headers.
138
160
@@ -142,21 +164,61 @@ class RequestCacheControl(ImmutableDictMixin[str, t.Any], _CacheControl): # typ
142
164
for that class.
143
165
144
166
.. versionchanged:: 3.1
145
- ``no_transform`` is a boolean when present.
167
+ Dict values are always ``str | None``. Setting properties will
168
+ convert the value to a string. Setting a non-bool property to
169
+ ``False`` is equivalent to setting it to ``None``. Getting typed
170
+ properties will return ``None`` if conversion raises
171
+ ``ValueError``, rather than the string.
172
+
173
+ .. versionchanged:: 3.1
174
+ ``max_age`` is ``None`` if not present, rather than ``-1``.
175
+
176
+ .. versionchanged:: 3.1
177
+ ``no_cache`` is a boolean, it is ``False`` instead of ``"*"``
178
+ when not present.
179
+
180
+ .. versionchanged:: 3.1
181
+ ``max_stale`` is an int, it is ``None`` instead of ``"*"`` if it is
182
+ present with no value. ``max_stale_any`` is a boolean indicating if
183
+ the property is present regardless of if it has a value.
146
184
147
185
.. versionchanged:: 3.1
148
- ``min_fresh`` is ``None`` if a value is not provided for the attribute.
186
+ ``no_transform`` is a boolean. Previously it was mistakenly
187
+ always ``None``.
149
188
150
- .. versionchanged:: 2.1.0
189
+ .. versionchanged:: 3.1
190
+ ``min_fresh`` is ``None`` if not present instead of ``"*"``.
191
+
192
+ .. versionchanged:: 2.1
151
193
Setting int properties such as ``max_age`` will convert the
152
194
value to an int.
153
195
154
196
.. versionadded:: 0.5
155
- In previous versions a `CacheControl` class existed that was used
156
- both for request and response.
197
+ Response-only properties are not present on this request class.
157
198
"""
158
199
159
- max_stale : str | int | None = cache_control_property ("max-stale" , "*" , int )
200
+ no_cache : bool = cache_control_property ("no-cache" , None , bool )
201
+ max_stale : int | None = cache_control_property (
202
+ "max-stale" ,
203
+ None ,
204
+ int ,
205
+ doc = """The ``max-stale`` attribute if it has a value. A ``int``, or
206
+ ``None`` if not present or no value.
207
+
208
+ This attribute can also be present without a value. To check that, use
209
+ :attr:`max_stale_any`.
210
+ """ ,
211
+ )
212
+ max_stale_any : bool = cache_control_property (
213
+ "max-stale" ,
214
+ None ,
215
+ bool ,
216
+ doc = """The ``max-stale`` attribute presence regardless of value. A
217
+ ``bool``, either present or not.
218
+
219
+ To check the value of the attribute if present, use :attr:`max_stale`.
220
+ """ ,
221
+ )
160
222
min_fresh : int | None = cache_control_property ("min-fresh" , None , int )
161
223
only_if_cached : bool = cache_control_property ("only-if-cached" , None , bool )
162
224
@@ -172,26 +234,38 @@ class ResponseCacheControl(_CacheControl):
172
234
for that class.
173
235
174
236
.. versionchanged:: 3.1
175
- ``no_transform`` is a boolean when present.
237
+ Dict values are always ``str | None``. Setting properties will
238
+ convert the value to a string. Setting a non-bool property to
239
+ ``False`` is equivalent to setting it to ``None``. Getting typed
240
+ properties will return ``None`` if conversion raises
241
+ ``ValueError``, rather than the string.
242
+
243
+ .. versionchanged:: 3.1
244
+ ``private`` is a boolean, it is ``False`` instead of ``"*"``
245
+ when not present.
246
+
247
+ .. versionchanged:: 3.1
248
+ ``no_transform`` is a boolean. Previously it was mistakenly always
249
+ ``None``.
176
250
177
251
.. versionchanged:: 3.1
178
252
Added the ``must_understand``, ``stale_while_revalidate``, and
179
- ``stale_if_error`` attributes .
253
+ ``stale_if_error`` properties .
180
254
181
255
.. versionchanged:: 2.1.1
182
256
``s_maxage`` converts the value to an int.
183
257
184
- .. versionchanged:: 2.1.0
258
+ .. versionchanged:: 2.1
185
259
Setting int properties such as ``max_age`` will convert the
186
260
value to an int.
187
261
188
262
.. versionadded:: 0.5
189
- In previous versions a `CacheControl` class existed that was used
190
- both for request and response.
263
+ Request-only properties are not present on this response class.
191
264
"""
192
265
266
+ no_cache : str | bool | None = cache_control_property ("no-cache" , "*" , None )
193
267
public : bool = cache_control_property ("public" , None , bool )
194
- private : str | None = cache_control_property ("private" , "*" , None )
268
+ private : bool = cache_control_property ("private" , None , bool )
195
269
must_revalidate : bool = cache_control_property ("must-revalidate" , None , bool )
196
270
proxy_revalidate : bool = cache_control_property ("proxy-revalidate" , None , bool )
197
271
s_maxage : int | None = cache_control_property ("s-maxage" , None , int )
@@ -200,7 +274,6 @@ class ResponseCacheControl(_CacheControl):
200
274
stale_while_revalidate : int | None = cache_control_property (
201
275
"stale-while-revalidate" , None , int
202
276
)
203
- stale_if_error : int | None = cache_control_property ("stale-if-error" , None , int )
204
277
205
278
206
279
# circular dependencies
0 commit comments