summaryrefslogtreecommitdiff
path: root/requests/cache.py
diff options
context:
space:
mode:
authorKenneth Reitz <me@kennethreitz.com>2012-10-16 13:39:00 -0700
committerKenneth Reitz <me@kennethreitz.com>2012-10-16 13:39:00 -0700
commite3c51e4182c54ffc494a7327e69004342844b0a4 (patch)
tree6f8a68415b5adc4109cd63b56d134e48535288b3 /requests/cache.py
parent706f88fedd7c526bc17ec9341a75ace1486d0a60 (diff)
downloadpython-requests-cache.tar.gz
brain dumpcache
Diffstat (limited to 'requests/cache.py')
-rw-r--r--requests/cache.py150
1 files changed, 144 insertions, 6 deletions
diff --git a/requests/cache.py b/requests/cache.py
index 5be1dfbe..373e53c3 100644
--- a/requests/cache.py
+++ b/requests/cache.py
@@ -7,7 +7,11 @@ requests.cache
Requests caching layer.
"""
+
+import time
import hashlib
+import calendar
+import email
from .packages.cachecore import SimpleCache
DEFAULT_CACHE = SimpleCache
@@ -25,20 +29,16 @@ def expand_cache(c):
if c is False:
return Cache(backend=False)
-def request_hash(r, type=''):
- """Returns a SHA256(type-method-url) for cache keys."""
-
- s = '{0}-{1}-{2}'.format(type, r.request.method, r.request.full_url)
- return hashlib.sha256(s).hexdigest()
class Cache(object):
"""A Cache session."""
- def __init__(self, backend=None, conditional=None, content=True):
+ def __init__(self, backend=None, conditional=None, content=True, handler=None):
self.conditional = None
self.content = None
+ self.handler = handler
# Default to backend if True.
if not backend is None:
@@ -54,3 +54,141 @@ class Cache(object):
if content is not True:
self.content = content
+ if handler is None:
+ self.handler = CacheHandler()
+
+
+
+class CacheHandler(object):
+
+
+ @staticmethod
+ def request_hash(r, type=''):
+ """Returns a SHA256(type-method-url) for cache keys."""
+
+ s = '{0}-{1}-{2}'.format(type, r.request.method, r.request.full_url)
+ return hashlib.sha256(s).hexdigest()
+
+
+ @staticmethod
+ def _parse_cache_control(headers):
+ """
+ Parse the cache control headers returning a dictionary with values
+ for the different directives.
+ """
+ retval = {}
+ if 'cache-control' in headers:
+ parts = headers['cache-control'].split(',')
+ parts_with_args = [
+ tuple([x.strip().lower() for x in part.split("=", 1)])
+ for part in parts if -1 != part.find("=")]
+ parts_wo_args = [(name.strip().lower(), 1)
+ for name in parts if -1 == name.find("=")]
+ retval = dict(parts_with_args + parts_wo_args)
+ return retval
+
+ def cache_request(self, r, cache):
+ """See if we should use a cached response."""
+
+ return True
+ cache_hash = self.request_hash(r)
+ cc = self._parse_cache_control(r.headers)
+
+ # non-caching states
+ no_cache = False
+ if 'no-cache' in cc:
+ no_cache = True
+ if 'max-age' in cc and cc['max-age'] == 0:
+ no_cache = True
+
+ # see if it is in the cache anyways
+ in_cache = cache.get(cache_hash)
+ if no_cache or not in_cache:
+ return False
+
+ # It is in the cache, so lets see if it is going to be
+ # fresh enough
+ resp = cache.get(cache_hash)
+ now = time.time()
+ date = calendar.timegm(
+ email.Utils.parsedate_tz(resp.headers['date']))
+ current_age = max(0, now - date)
+
+ resp_cc = self._parse_cache_control(resp.headers)
+
+
+ # determine freshness
+ freshness_lifetime = 0
+ if 'max-age' in resp_cc:
+ try:
+ freshness_lifetime = int(resp_cc['max-age'])
+ except ValueError:
+ pass
+ elif 'expires' in resp.headers:
+ expires = email.Utils.parsedate_tz(resp.headers['expires'])
+ if expires != None:
+ expire_time = calendar.timegm(expires) - date
+ freshness_lifetime = max(0, expire_time)
+
+ # determine if we are setting freshness limit in the req
+ if 'max-age' in cc:
+ try:
+ freshness_lifetime = int(cc['max-age'])
+ except ValueError:
+ freshness_lifetime = 0
+
+ if 'min-fresh' in cc:
+ try:
+ min_fresh = int(cc['min-fresh'])
+ except ValueError:
+ min_fresh = 0
+ # adjust our current age by our min fresh
+ current_age += min_fresh
+
+ # see how fresh we actually are
+ fresh = (freshness_lifetime > current_age)
+
+ if fresh:
+ # make sure we set the from_cache to true
+ resp.from_cache = True
+ return resp
+
+ # we're not fresh, clean out the junk
+ cache.delete(cache_hash)
+
+ # return the original handler
+ return False
+
+
+ def cache_response(self, r, cache):
+ """Algorithm for caching requests."""
+
+ if r.status_code not in [200, 203]:
+ return
+
+ cc_req = self._parse_cache_control(r.request.headers)
+ cc = self._parse_cache_control(r.headers)
+
+ # cache_url = self.cache_url(r.request.full_url)
+ cache_hash = self.request_hash(r)
+
+ # Delete it from the cache if we happen to have it stored there
+ no_store = cc.get('no-store') or cc_req.get('no-store')
+ if no_store and cache.get(cache_hash):
+ cache.delete(cache_hash)
+
+ # Add to the cache if the response headers demand it. If there
+ # is no date header then we can't do anything about expiring
+ # the cache.
+ if 'date' in r.headers:
+
+ # cache when there is a max-age > 0
+ if cc and cc.get('max-age'):
+ if int(cc['max-age']) > 0:
+ cache.set(cache_hash, r)
+
+ # If the request can expire, it means we should cache it
+ # in the meantime.
+ elif 'expires' in r.headers:
+ if int(r.headers['expires']) > 0:
+ cache.set(cache_hash, r)