cache_control.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. from __future__ import annotations
  2. from .mixins import ImmutableDictMixin
  3. from .mixins import UpdateDictMixin
  4. def cache_control_property(key, empty, type):
  5. """Return a new property object for a cache header. Useful if you
  6. want to add support for a cache extension in a subclass.
  7. .. versionchanged:: 2.0
  8. Renamed from ``cache_property``.
  9. """
  10. return property(
  11. lambda x: x._get_cache_value(key, empty, type),
  12. lambda x, v: x._set_cache_value(key, v, type),
  13. lambda x: x._del_cache_value(key),
  14. f"accessor for {key!r}",
  15. )
  16. class _CacheControl(UpdateDictMixin, dict):
  17. """Subclass of a dict that stores values for a Cache-Control header. It
  18. has accessors for all the cache-control directives specified in RFC 2616.
  19. The class does not differentiate between request and response directives.
  20. Because the cache-control directives in the HTTP header use dashes the
  21. python descriptors use underscores for that.
  22. To get a header of the :class:`CacheControl` object again you can convert
  23. the object into a string or call the :meth:`to_header` method. If you plan
  24. to subclass it and add your own items have a look at the sourcecode for
  25. that class.
  26. .. versionchanged:: 2.1.0
  27. Setting int properties such as ``max_age`` will convert the
  28. value to an int.
  29. .. versionchanged:: 0.4
  30. Setting `no_cache` or `private` to boolean `True` will set the implicit
  31. none-value which is ``*``:
  32. >>> cc = ResponseCacheControl()
  33. >>> cc.no_cache = True
  34. >>> cc
  35. <ResponseCacheControl 'no-cache'>
  36. >>> cc.no_cache
  37. '*'
  38. >>> cc.no_cache = None
  39. >>> cc
  40. <ResponseCacheControl ''>
  41. In versions before 0.5 the behavior documented here affected the now
  42. no longer existing `CacheControl` class.
  43. """
  44. no_cache = cache_control_property("no-cache", "*", None)
  45. no_store = cache_control_property("no-store", None, bool)
  46. max_age = cache_control_property("max-age", -1, int)
  47. no_transform = cache_control_property("no-transform", None, None)
  48. def __init__(self, values=(), on_update=None):
  49. dict.__init__(self, values or ())
  50. self.on_update = on_update
  51. self.provided = values is not None
  52. def _get_cache_value(self, key, empty, type):
  53. """Used internally by the accessor properties."""
  54. if type is bool:
  55. return key in self
  56. if key in self:
  57. value = self[key]
  58. if value is None:
  59. return empty
  60. elif type is not None:
  61. try:
  62. value = type(value)
  63. except ValueError:
  64. pass
  65. return value
  66. return None
  67. def _set_cache_value(self, key, value, type):
  68. """Used internally by the accessor properties."""
  69. if type is bool:
  70. if value:
  71. self[key] = None
  72. else:
  73. self.pop(key, None)
  74. else:
  75. if value is None:
  76. self.pop(key, None)
  77. elif value is True:
  78. self[key] = None
  79. else:
  80. if type is not None:
  81. self[key] = type(value)
  82. else:
  83. self[key] = value
  84. def _del_cache_value(self, key):
  85. """Used internally by the accessor properties."""
  86. if key in self:
  87. del self[key]
  88. def to_header(self):
  89. """Convert the stored values into a cache control header."""
  90. return http.dump_header(self)
  91. def __str__(self):
  92. return self.to_header()
  93. def __repr__(self):
  94. kv_str = " ".join(f"{k}={v!r}" for k, v in sorted(self.items()))
  95. return f"<{type(self).__name__} {kv_str}>"
  96. cache_property = staticmethod(cache_control_property)
  97. class RequestCacheControl(ImmutableDictMixin, _CacheControl):
  98. """A cache control for requests. This is immutable and gives access
  99. to all the request-relevant cache control headers.
  100. To get a header of the :class:`RequestCacheControl` object again you can
  101. convert the object into a string or call the :meth:`to_header` method. If
  102. you plan to subclass it and add your own items have a look at the sourcecode
  103. for that class.
  104. .. versionchanged:: 2.1.0
  105. Setting int properties such as ``max_age`` will convert the
  106. value to an int.
  107. .. versionadded:: 0.5
  108. In previous versions a `CacheControl` class existed that was used
  109. both for request and response.
  110. """
  111. max_stale = cache_control_property("max-stale", "*", int)
  112. min_fresh = cache_control_property("min-fresh", "*", int)
  113. only_if_cached = cache_control_property("only-if-cached", None, bool)
  114. class ResponseCacheControl(_CacheControl):
  115. """A cache control for responses. Unlike :class:`RequestCacheControl`
  116. this is mutable and gives access to response-relevant cache control
  117. headers.
  118. To get a header of the :class:`ResponseCacheControl` object again you can
  119. convert the object into a string or call the :meth:`to_header` method. If
  120. you plan to subclass it and add your own items have a look at the sourcecode
  121. for that class.
  122. .. versionchanged:: 2.1.1
  123. ``s_maxage`` converts the value to an int.
  124. .. versionchanged:: 2.1.0
  125. Setting int properties such as ``max_age`` will convert the
  126. value to an int.
  127. .. versionadded:: 0.5
  128. In previous versions a `CacheControl` class existed that was used
  129. both for request and response.
  130. """
  131. public = cache_control_property("public", None, bool)
  132. private = cache_control_property("private", "*", None)
  133. must_revalidate = cache_control_property("must-revalidate", None, bool)
  134. proxy_revalidate = cache_control_property("proxy-revalidate", None, bool)
  135. s_maxage = cache_control_property("s-maxage", None, int)
  136. immutable = cache_control_property("immutable", None, bool)
  137. # circular dependencies
  138. from .. import http