Spaces:
Sleeping
Sleeping
from __future__ import annotations | |
from .mixins import ImmutableDictMixin | |
from .mixins import UpdateDictMixin | |
def cache_control_property(key, empty, type): | |
"""Return a new property object for a cache header. Useful if you | |
want to add support for a cache extension in a subclass. | |
.. versionchanged:: 2.0 | |
Renamed from ``cache_property``. | |
""" | |
return property( | |
lambda x: x._get_cache_value(key, empty, type), | |
lambda x, v: x._set_cache_value(key, v, type), | |
lambda x: x._del_cache_value(key), | |
f"accessor for {key!r}", | |
) | |
class _CacheControl(UpdateDictMixin, dict): | |
"""Subclass of a dict that stores values for a Cache-Control header. It | |
has accessors for all the cache-control directives specified in RFC 2616. | |
The class does not differentiate between request and response directives. | |
Because the cache-control directives in the HTTP header use dashes the | |
python descriptors use underscores for that. | |
To get a header of the :class:`CacheControl` object again you can convert | |
the object into a string or call the :meth:`to_header` method. If you plan | |
to subclass it and add your own items have a look at the sourcecode for | |
that class. | |
.. versionchanged:: 2.1.0 | |
Setting int properties such as ``max_age`` will convert the | |
value to an int. | |
.. versionchanged:: 0.4 | |
Setting `no_cache` or `private` to boolean `True` will set the implicit | |
none-value which is ``*``: | |
>>> cc = ResponseCacheControl() | |
>>> cc.no_cache = True | |
>>> cc | |
<ResponseCacheControl 'no-cache'> | |
>>> cc.no_cache | |
'*' | |
>>> cc.no_cache = None | |
>>> cc | |
<ResponseCacheControl ''> | |
In versions before 0.5 the behavior documented here affected the now | |
no longer existing `CacheControl` class. | |
""" | |
no_cache = cache_control_property("no-cache", "*", None) | |
no_store = cache_control_property("no-store", None, bool) | |
max_age = cache_control_property("max-age", -1, int) | |
no_transform = cache_control_property("no-transform", None, None) | |
def __init__(self, values=(), on_update=None): | |
dict.__init__(self, values or ()) | |
self.on_update = on_update | |
self.provided = values is not None | |
def _get_cache_value(self, key, empty, type): | |
"""Used internally by the accessor properties.""" | |
if type is bool: | |
return key in self | |
if key in self: | |
value = self[key] | |
if value is None: | |
return empty | |
elif type is not None: | |
try: | |
value = type(value) | |
except ValueError: | |
pass | |
return value | |
return None | |
def _set_cache_value(self, key, value, type): | |
"""Used internally by the accessor properties.""" | |
if type is bool: | |
if value: | |
self[key] = None | |
else: | |
self.pop(key, None) | |
else: | |
if value is None: | |
self.pop(key, None) | |
elif value is True: | |
self[key] = None | |
else: | |
if type is not None: | |
self[key] = type(value) | |
else: | |
self[key] = value | |
def _del_cache_value(self, key): | |
"""Used internally by the accessor properties.""" | |
if key in self: | |
del self[key] | |
def to_header(self): | |
"""Convert the stored values into a cache control header.""" | |
return http.dump_header(self) | |
def __str__(self): | |
return self.to_header() | |
def __repr__(self): | |
kv_str = " ".join(f"{k}={v!r}" for k, v in sorted(self.items())) | |
return f"<{type(self).__name__} {kv_str}>" | |
cache_property = staticmethod(cache_control_property) | |
class RequestCacheControl(ImmutableDictMixin, _CacheControl): | |
"""A cache control for requests. This is immutable and gives access | |
to all the request-relevant cache control headers. | |
To get a header of the :class:`RequestCacheControl` object again you can | |
convert the object into a string or call the :meth:`to_header` method. If | |
you plan to subclass it and add your own items have a look at the sourcecode | |
for that class. | |
.. versionchanged:: 2.1.0 | |
Setting int properties such as ``max_age`` will convert the | |
value to an int. | |
.. versionadded:: 0.5 | |
In previous versions a `CacheControl` class existed that was used | |
both for request and response. | |
""" | |
max_stale = cache_control_property("max-stale", "*", int) | |
min_fresh = cache_control_property("min-fresh", "*", int) | |
only_if_cached = cache_control_property("only-if-cached", None, bool) | |
class ResponseCacheControl(_CacheControl): | |
"""A cache control for responses. Unlike :class:`RequestCacheControl` | |
this is mutable and gives access to response-relevant cache control | |
headers. | |
To get a header of the :class:`ResponseCacheControl` object again you can | |
convert the object into a string or call the :meth:`to_header` method. If | |
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. | |
.. versionadded:: 0.5 | |
In previous versions a `CacheControl` class existed that was used | |
both for request and response. | |
""" | |
public = cache_control_property("public", None, bool) | |
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, int) | |
immutable = cache_control_property("immutable", None, bool) | |
# circular dependencies | |
from .. import http | |