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

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

79 statements  

1from __future__ import annotations 

2 

3import collections.abc as cabc 

4import typing as t 

5from inspect import cleandoc 

6 

7from .mixins import ImmutableDictMixin 

8from .structures import CallbackDict 

9 

10 

11def cache_control_property( 

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

13) -> t.Any: 

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

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

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 

27 .. versionchanged:: 2.0 

28 Renamed from ``cache_property``. 

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 

48 return property( 

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

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

51 lambda x: x._del_cache_value(key), 

52 doc=cleandoc(doc), 

53 ) 

54 

55 

56class _CacheControl(CallbackDict[str, t.Optional[str]]): 

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

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

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

60 

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

62 python descriptors use underscores for that. 

63 

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

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

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

67 that class. 

68 

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. 

75 

76 .. versionchanged:: 2.1 

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

78 value to an int. 

79 

80 .. versionchanged:: 0.4 

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

82 implicit value ``"*"``. 

83 """ 

84 

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

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

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) 

89 

90 def __init__( 

91 self, 

92 values: cabc.Mapping[str, t.Any] | cabc.Iterable[tuple[str, t.Any]] | None = (), 

93 on_update: cabc.Callable[[_CacheControl], None] | None = None, 

94 ): 

95 super().__init__(values, on_update) 

96 self.provided = values is not None 

97 

98 def _get_cache_value( 

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

100 ) -> t.Any: 

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

102 if type is bool: 

103 return key in self 

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 

118 

119 def _set_cache_value( 

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

121 ) -> None: 

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

123 if type is bool: 

124 if value: 

125 self[key] = None 

126 else: 

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 

132 else: 

133 if type is not None: 

134 value = type(value) 

135 

136 self[key] = str(value) 

137 

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

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

140 if key in self: 

141 del self[key] 

142 

143 def to_header(self) -> str: 

144 """Convert the stored values into a cache control header.""" 

145 return http.dump_header(self) 

146 

147 def __str__(self) -> str: 

148 return self.to_header() 

149 

150 def __repr__(self) -> str: 

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

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

153 

154 cache_property = staticmethod(cache_control_property) 

155 

156 

157class RequestCacheControl(ImmutableDictMixin[str, t.Optional[str]], _CacheControl): # type: ignore[misc] 

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

159 to all the request-relevant cache control headers. 

160 

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

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

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

164 for that class. 

165 

166 .. versionchanged:: 3.1 

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 present without a value, rather 

175 than ``-1``. 

176 

177 .. versionchanged:: 3.1 

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

179 when present. 

180 

181 .. versionchanged:: 3.1 

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

183 than ``"*"``. 

184 

185 .. versionchanged:: 3.1 

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

187 always ``None``. 

188 

189 .. versionchanged:: 3.1 

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

191 than ``"*"``. 

192 

193 .. versionchanged:: 2.1 

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

195 value to an int. 

196 

197 .. versionadded:: 0.5 

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

199 """ 

200 

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

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

203 "max-stale", 

204 True, 

205 int, 

206 ) 

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

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

209 

210 

211class ResponseCacheControl(_CacheControl): 

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

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

214 headers. 

215 

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

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

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

219 for that class. 

220 

221 .. versionchanged:: 3.1 

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

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

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

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

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

227 

228 .. versionchanged:: 3.1 

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

230 ``"*"``. 

231 

232 .. versionchanged:: 3.1 

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

234 ``"*"``. 

235 

236 .. versionchanged:: 3.1 

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

238 always ``None``. 

239 

240 .. versionchanged:: 3.1 

241 Added the ``must_understand``, ``stale_while_revalidate``, and 

242 ``stale_if_error`` properties. 

243 

244 .. versionchanged:: 2.1.1 

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

246 

247 .. versionchanged:: 2.1 

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

249 value to an int. 

250 

251 .. versionadded:: 0.5 

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

253 """ 

254 

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

256 "no-cache", True, None 

257 ) 

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

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

260 "private", True, None 

261 ) 

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

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

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

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

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

267 stale_while_revalidate: int | None = cache_control_property( 

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

269 ) 

270 

271 

272# circular dependencies 

273from .. import http