123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- 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
|