codekingpro/portable-devtools
114k
1from __future__ import annotations2 3from .mixins import ImmutableDictMixin4from .mixins import UpdateDictMixin5 6 7def cache_control_property(key, empty, type):8 """Return a new property object for a cache header. Useful if you9 want to add support for a cache extension in a subclass.10 11 .. versionchanged:: 2.012 Renamed from ``cache_property``.13 """14 return property(15 lambda x: x._get_cache_value(key, empty, type),16 lambda x, v: x._set_cache_value(key, v, type),17 lambda x: x._del_cache_value(key),18 f"accessor for {key!r}",19 )20 21 22class _CacheControl(UpdateDictMixin, dict):23 """Subclass of a dict that stores values for a Cache-Control header. It24 has accessors for all the cache-control directives specified in RFC 2616.25 The class does not differentiate between request and response directives.26 27 Because the cache-control directives in the HTTP header use dashes the28 python descriptors use underscores for that.29 30 To get a header of the :class:`CacheControl` object again you can convert31 the object into a string or call the :meth:`to_header` method. If you plan32 to subclass it and add your own items have a look at the sourcecode for33 that class.34 35 .. versionchanged:: 2.1.036 Setting int properties such as ``max_age`` will convert the37 value to an int.38 39 .. versionchanged:: 0.440 41 Setting `no_cache` or `private` to boolean `True` will set the implicit42 none-value which is ``*``:43 44 >>> cc = ResponseCacheControl()45 >>> cc.no_cache = True46 >>> cc47 <ResponseCacheControl 'no-cache'>48 >>> cc.no_cache49 '*'50 >>> cc.no_cache = None51 >>> cc52 <ResponseCacheControl ''>53 54 In versions before 0.5 the behavior documented here affected the now55 no longer existing `CacheControl` class.56 """57 58 no_cache = cache_control_property("no-cache", "*", None)59 no_store = cache_control_property("no-store", None, bool)60 max_age = cache_control_property("max-age", -1, int)61 no_transform = cache_control_property("no-transform", None, None)62 63 def __init__(self, values=(), on_update=None):64 dict.__init__(self, values or ())65 self.on_update = on_update66 self.provided = values is not None67 68 def _get_cache_value(self, key, empty, type):69 """Used internally by the accessor properties."""70 if type is bool:71 return key in self72 if key in self:73 value = self[key]74 if value is None:75 return empty76 elif type is not None:77 try:78 value = type(value)79 except ValueError:80 pass81 return value82 return None83 84 def _set_cache_value(self, key, value, type):85 """Used internally by the accessor properties."""86 if type is bool:87 if value:88 self[key] = None89 else:90 self.pop(key, None)91 else:92 if value is None:93 self.pop(key, None)94 elif value is True:95 self[key] = None96 else:97 if type is not None:98 self[key] = type(value)99 else:100 self[key] = value101 102 def _del_cache_value(self, key):103 """Used internally by the accessor properties."""104 if key in self:105 del self[key]106 107 def to_header(self):108 """Convert the stored values into a cache control header."""109 return http.dump_header(self)110 111 def __str__(self):112 return self.to_header()113 114 def __repr__(self):115 kv_str = " ".join(f"{k}={v!r}" for k, v in sorted(self.items()))116 return f"<{type(self).__name__} {kv_str}>"117 118 cache_property = staticmethod(cache_control_property)119 120 121class RequestCacheControl(ImmutableDictMixin, _CacheControl):122 """A cache control for requests. This is immutable and gives access123 to all the request-relevant cache control headers.124 125 To get a header of the :class:`RequestCacheControl` object again you can126 convert the object into a string or call the :meth:`to_header` method. If127 you plan to subclass it and add your own items have a look at the sourcecode128 for that class.129 130 .. versionchanged:: 2.1.0131 Setting int properties such as ``max_age`` will convert the132 value to an int.133 134 .. versionadded:: 0.5135 In previous versions a `CacheControl` class existed that was used136 both for request and response.137 """138 139 max_stale = cache_control_property("max-stale", "*", int)140 min_fresh = cache_control_property("min-fresh", "*", int)141 only_if_cached = cache_control_property("only-if-cached", None, bool)142 143 144class ResponseCacheControl(_CacheControl):145 """A cache control for responses. Unlike :class:`RequestCacheControl`146 this is mutable and gives access to response-relevant cache control147 headers.148 149 To get a header of the :class:`ResponseCacheControl` object again you can150 convert the object into a string or call the :meth:`to_header` method. If151 you plan to subclass it and add your own items have a look at the sourcecode152 for that class.153 154 .. versionchanged:: 2.1.1155 ``s_maxage`` converts the value to an int.156 157 .. versionchanged:: 2.1.0158 Setting int properties such as ``max_age`` will convert the159 value to an int.160 161 .. versionadded:: 0.5162 In previous versions a `CacheControl` class existed that was used163 both for request and response.164 """165 166 public = cache_control_property("public", None, bool)167 private = cache_control_property("private", "*", None)168 must_revalidate = cache_control_property("must-revalidate", None, bool)169 proxy_revalidate = cache_control_property("proxy-revalidate", None, bool)170 s_maxage = cache_control_property("s-maxage", None, int)171 immutable = cache_control_property("immutable", None, bool)172 173 174# circular dependencies175from .. import http176 