Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/werkzeug/datastructures/cache_control.py: 60%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

87 statements  

1from __future__ import annotations 

2 

3import collections.abc as cabc 

4import typing as t 

5from inspect import cleandoc 

6 

7from ..http import dump_header 

8from ..http import parse_dict_header 

9from .mixins import ImmutableDictMixin 

10from .structures import CallbackDict 

11 

12if t.TYPE_CHECKING: 

13 import typing_extensions as te 

14 

15 

16def cache_control_property( 

17 key: str, empty: t.Any, type: type[t.Any] | None, *, doc: str | None = None 

18) -> t.Any: 

19 """Return a new property object for a cache header. Useful if you 

20 want to add support for a cache extension in a subclass. 

21 

22 :param key: The attribute name present in the parsed cache-control header dict. 

23 :param empty: The value to use if the key is present without a value. 

24 :param type: The type to convert the string value to instead of a string. If 

25 conversion raises a ``ValueError``, the returned value is ``None``. 

26 :param doc: The docstring for the property. If not given, it is generated 

27 based on the other params. 

28 

29 .. versionchanged:: 3.1 

30 Added the ``doc`` param. 

31 

32 .. versionchanged:: 2.0 

33 Renamed from ``cache_property``. 

34 """ 

35 if doc is None: 

36 parts = [f"The ``{key}`` attribute."] 

37 

38 if type is bool: 

39 parts.append("A ``bool``, either present or not.") 

40 else: 

41 if type is None: 

42 parts.append("A ``str``,") 

43 else: 

44 parts.append(f"A ``{type.__name__}``,") 

45 

46 if empty is not None: 

47 parts.append(f"``{empty!r}`` if present with no value,") 

48 

49 parts.append("or ``None`` if not present.") 

50 

51 doc = " ".join(parts) 

52 

53 return property( 

54 lambda x: x._get_cache_value(key, empty, type), 

55 lambda x, v: x._set_cache_value(key, v, type), 

56 lambda x: x._del_cache_value(key), 

57 doc=cleandoc(doc), 

58 ) 

59 

60 

61class _CacheControl(CallbackDict[str, str | None]): 

62 """Subclass of a dict that stores values for a Cache-Control header. It 

63 has accessors for all the cache-control directives specified in RFC 2616. 

64 The class does not differentiate between request and response directives. 

65 

66 Because the cache-control directives in the HTTP header use dashes the 

67 python descriptors use underscores for that. 

68 

69 To get a header of the :class:`CacheControl` object again you can convert 

70 the object into a string or call the :meth:`to_header` method. If you plan 

71 to subclass it and add your own items have a look at the sourcecode for 

72 that class. 

73 

74 .. versionchanged:: 3.2 

75 The ``on_update`` parameter was removed. 

76 

77 .. versionchanged:: 3.1 

78 Dict values are always ``str | None``. Setting properties will 

79 convert the value to a string. Setting a non-bool property to 

80 ``False`` is equivalent to setting it to ``None``. Getting typed 

81 properties will return ``None`` if conversion raises 

82 ``ValueError``, rather than the string. 

83 

84 .. versionchanged:: 2.1 

85 Setting int properties such as ``max_age`` will convert the 

86 value to an int. 

87 

88 .. versionchanged:: 0.4 

89 Setting ``no_cache`` or ``private`` to ``True`` will set the 

90 implicit value ``"*"``. 

91 """ 

92 

93 no_store: bool = cache_control_property("no-store", None, bool) 

94 max_age: int | None = cache_control_property("max-age", None, int) 

95 no_transform: bool = cache_control_property("no-transform", None, bool) 

96 stale_if_error: int | None = cache_control_property("stale-if-error", None, int) 

97 

98 def __init__( 

99 self, 

100 values: cabc.Mapping[str, t.Any] 

101 | cabc.Iterable[tuple[str, t.Any]] 

102 | None = None, 

103 ): 

104 super().__init__(values) 

105 self.provided = values is not None 

106 

107 def _get_cache_value( 

108 self, key: str, empty: t.Any, type: type[t.Any] | None 

109 ) -> t.Any: 

110 """Used internally by the accessor properties.""" 

111 if type is bool: 

112 return key in self 

113 

114 if key not in self: 

115 return None 

116 

117 if (value := self[key]) is None: 

118 return empty 

119 

120 if type is not None: 

121 try: 

122 value = type(value) 

123 except ValueError: 

124 return None 

125 

126 return value 

127 

128 def _set_cache_value( 

129 self, key: str, value: t.Any, type: type[t.Any] | None 

130 ) -> None: 

131 """Used internally by the accessor properties.""" 

132 if type is bool: 

133 if value: 

134 self[key] = None 

135 else: 

136 self.pop(key, None) 

137 elif value is None or value is False: 

138 self.pop(key, None) 

139 elif value is True: 

140 self[key] = None 

141 else: 

142 if type is not None: 

143 value = type(value) 

144 

145 self[key] = str(value) 

146 

147 def _del_cache_value(self, key: str) -> None: 

148 """Used internally by the accessor properties.""" 

149 if key in self: 

150 del self[key] 

151 

152 @classmethod 

153 def from_header(cls, value: str | None) -> te.Self: 

154 """Parse a ``Cache-Control`` header value and create an instance of this class. 

155 

156 .. versionadded:: 3.2 

157 """ 

158 if not value: 

159 return cls() 

160 

161 return cls(parse_dict_header(value)) 

162 

163 def to_header(self) -> str: 

164 """Convert to a ``Cache-Control`` header value.""" 

165 return dump_header(self) 

166 

167 def __str__(self) -> str: 

168 return self.to_header() 

169 

170 def __repr__(self) -> str: 

171 kv_str = " ".join(f"{k}={v!r}" for k, v in sorted(self.items())) 

172 return f"<{type(self).__name__} {kv_str}>" 

173 

174 cache_property = staticmethod(cache_control_property) 

175 

176 

177class RequestCacheControl(ImmutableDictMixin[str, str | None], _CacheControl): # type: ignore[misc] 

178 """A cache control for requests. This is immutable and gives access 

179 to all the request-relevant cache control headers. 

180 

181 To get a header of the :class:`RequestCacheControl` object again you can 

182 convert the object into a string or call the :meth:`to_header` method. If 

183 you plan to subclass it and add your own items have a look at the sourcecode 

184 for that class. 

185 

186 .. versionchanged:: 3.1 

187 Dict values are always ``str | None``. Setting properties will 

188 convert the value to a string. Setting a non-bool property to 

189 ``False`` is equivalent to setting it to ``None``. Getting typed 

190 properties will return ``None`` if conversion raises 

191 ``ValueError``, rather than the string. 

192 

193 .. versionchanged:: 3.1 

194 ``max_age`` is ``None`` if present without a value, rather 

195 than ``-1``. 

196 

197 .. versionchanged:: 3.1 

198 ``no_cache`` is a boolean, it is ``True`` instead of ``"*"`` 

199 when present. 

200 

201 .. versionchanged:: 3.1 

202 ``max_stale`` is ``True`` if present without a value, rather 

203 than ``"*"``. 

204 

205 .. versionchanged:: 3.1 

206 ``no_transform`` is a boolean. Previously it was mistakenly 

207 always ``None``. 

208 

209 .. versionchanged:: 3.1 

210 ``min_fresh`` is ``None`` if present without a value, rather 

211 than ``"*"``. 

212 

213 .. versionchanged:: 2.1 

214 Setting int properties such as ``max_age`` will convert the 

215 value to an int. 

216 

217 .. versionadded:: 0.5 

218 Response-only properties are not present on this request class. 

219 """ 

220 

221 no_cache: bool = cache_control_property("no-cache", None, bool) 

222 max_stale: int | t.Literal[True] | None = cache_control_property( 

223 "max-stale", 

224 True, 

225 int, 

226 ) 

227 min_fresh: int | None = cache_control_property("min-fresh", None, int) 

228 only_if_cached: bool = cache_control_property("only-if-cached", None, bool) 

229 

230 

231class ResponseCacheControl(_CacheControl): 

232 """A cache control for responses. Unlike :class:`RequestCacheControl` 

233 this is mutable and gives access to response-relevant cache control 

234 headers. 

235 

236 To get a header of the :class:`ResponseCacheControl` object again you can 

237 convert the object into a string or call the :meth:`to_header` method. If 

238 you plan to subclass it and add your own items have a look at the sourcecode 

239 for that class. 

240 

241 .. versionchanged:: 3.1 

242 Dict values are always ``str | None``. Setting properties will 

243 convert the value to a string. Setting a non-bool property to 

244 ``False`` is equivalent to setting it to ``None``. Getting typed 

245 properties will return ``None`` if conversion raises 

246 ``ValueError``, rather than the string. 

247 

248 .. versionchanged:: 3.1 

249 ``no_cache`` is ``True`` if present without a value, rather than 

250 ``"*"``. 

251 

252 .. versionchanged:: 3.1 

253 ``private`` is ``True`` if present without a value, rather than 

254 ``"*"``. 

255 

256 .. versionchanged:: 3.1 

257 ``no_transform`` is a boolean. Previously it was mistakenly 

258 always ``None``. 

259 

260 .. versionchanged:: 3.1 

261 Added the ``must_understand``, ``stale_while_revalidate``, and 

262 ``stale_if_error`` properties. 

263 

264 .. versionchanged:: 2.1.1 

265 ``s_maxage`` converts the value to an int. 

266 

267 .. versionchanged:: 2.1 

268 Setting int properties such as ``max_age`` will convert the 

269 value to an int. 

270 

271 .. versionadded:: 0.5 

272 Request-only properties are not present on this response class. 

273 """ 

274 

275 # https://httpwg.org/specs/rfc9111.html#cache-response-directive.no-cache 

276 # This can be with or without a value, not mentioned on MDN. 

277 no_cache: str | t.Literal[True] | None = cache_control_property( 

278 "no-cache", True, None 

279 ) 

280 public: bool = cache_control_property("public", None, bool) 

281 # https://httpwg.org/specs/rfc9111.html#cache-response-directive.private 

282 # This can be with or without a value, not mentioned on MDN. 

283 private: str | t.Literal[True] | None = cache_control_property( 

284 "private", True, None 

285 ) 

286 must_revalidate: bool = cache_control_property("must-revalidate", None, bool) 

287 proxy_revalidate: bool = cache_control_property("proxy-revalidate", None, bool) 

288 s_maxage: int | None = cache_control_property("s-maxage", None, int) 

289 immutable: bool = cache_control_property("immutable", None, bool) 

290 must_understand: bool = cache_control_property("must-understand", None, bool) 

291 stale_while_revalidate: int | None = cache_control_property( 

292 "stale-while-revalidate", None, int 

293 )