Source

CherryPy / cherrypy / lib / caching.py

Diff from to

cherrypy/lib/caching.py

 
 class Cache(object):
     """Base class for Cache implementations."""
-    
+
     def get(self):
         """Return the current variant if in the cache, else None."""
         raise NotImplemented
-    
+
     def put(self, obj, size):
         """Store the current variant in the cache."""
         raise NotImplemented
-    
+
     def delete(self):
         """Remove ALL cached variants of the current resource."""
         raise NotImplemented
-    
+
     def clear(self):
         """Reset the cache to its initial, empty state."""
         raise NotImplemented
 
 class AntiStampedeCache(dict):
     """A storage system for cached items which reduces stampede collisions."""
-    
+
     def wait(self, key, timeout=5, debug=False):
         """Return the cached value for the given key, or None.
-        
+
         If timeout is not None, and the value is already
         being calculated by another thread, wait until the given timeout has
         elapsed. If the value is available before the timeout expires, it is
         returned. If not, None is returned, and a sentinel placed in the cache
         to signal other threads to wait.
-        
+
         If timeout is None, no waiting is performed nor sentinels used.
         """
         value = self.get(key)
                 if debug:
                     cherrypy.log('No timeout', 'TOOLS.CACHING')
                 return None
-            
+
             # Wait until it's done or times out.
             if debug:
                 cherrypy.log('Waiting up to %s seconds' % timeout, 'TOOLS.CACHING')
             e = threading.Event()
             e.result = None
             dict.__setitem__(self, key, e)
-            
+
             return None
         elif value is None:
             # Stick an Event in the slot so other threads wait
             e.result = None
             dict.__setitem__(self, key, e)
         return value
-    
+
     def __setitem__(self, key, value):
         """Set the cached value for the given key."""
         existing = self.get(key)
 
 class MemoryCache(Cache):
     """An in-memory cache for varying response content.
-    
+
     Each key in self.store is a URI, and each value is an AntiStampedeCache.
     The response for any given URI may vary based on the values of
     "selecting request headers"; that is, those named in the Vary
     response header. We assume the list of header names to be constant
     for each URI throughout the lifetime of the application, and store
     that list in ``self.store[uri].selecting_headers``.
-    
+
     The items contained in ``self.store[uri]`` have keys which are tuples of
     request header values (in the same order as the names in its
     selecting_headers), and values which are the actual responses.
     """
-    
+
     maxobjects = 1000
     """The maximum number of cached objects; defaults to 1000."""
-    
+
     maxobj_size = 100000
     """The maximum size of each cached object in bytes; defaults to 100 KB."""
-    
+
     maxsize = 10000000
     """The maximum size of the entire cache in bytes; defaults to 10 MB."""
-    
+
     delay = 600
     """Seconds until the cached content expires; defaults to 600 (10 minutes)."""
-    
+
     antistampede_timeout = 5
     """Seconds to wait for other threads to release a cache lock."""
-    
+
     expire_freq = 0.1
     """Seconds to sleep between cache expiration sweeps."""
-    
+
     debug = False
-    
+
     def __init__(self):
         self.clear()
-        
+
         # Run self.expire_cache in a separate daemon thread.
         t = threading.Thread(target=self.expire_cache, name='expire_cache')
         self.expiration_thread = t
         set_daemon(t, True)
         t.start()
-    
+
     def clear(self):
         """Reset the cache to its initial, empty state."""
         self.store = {}
         self.tot_expires = 0
         self.tot_non_modified = 0
         self.cursize = 0
-    
+
     def expire_cache(self):
         """Continuously examine cached objects, expiring stale ones.
-        
+
         This function is designed to be run in its own daemon thread,
         referenced at ``self.expiration_thread``.
         """
                             pass
                     del self.expirations[expiration_time]
             time.sleep(self.expire_freq)
-    
+
     def get(self):
         """Return the current variant if in the cache, else None."""
         request = cherrypy.serving.request
         self.tot_gets += 1
-        
+
         uri = cherrypy.url(qs=request.query_string)
         uricache = self.store.get(uri)
         if uricache is None:
             return None
-        
+
         header_values = [request.headers.get(h, '')
                          for h in uricache.selecting_headers]
         variant = uricache.wait(key=tuple(sorted(header_values)),
         if variant is not None:
             self.tot_hist += 1
         return variant
-    
+
     def put(self, variant, size):
         """Store the current variant in the cache."""
         request = cherrypy.serving.request
         response = cherrypy.serving.response
-        
+
         uri = cherrypy.url(qs=request.query_string)
         uricache = self.store.get(uri)
         if uricache is None:
             uricache.selecting_headers = [
                 e.value for e in response.headers.elements('Vary')]
             self.store[uri] = uricache
-        
+
         if len(self.store) < self.maxobjects:
             total_size = self.cursize + size
-            
+
             # checks if there's space for the object
             if (size < self.maxobj_size and total_size < self.maxsize):
                 # add to the expirations list
                 expiration_time = response.time + self.delay
                 bucket = self.expirations.setdefault(expiration_time, [])
                 bucket.append((size, uri, uricache.selecting_headers))
-                
+
                 # add to the cache
                 header_values = [request.headers.get(h, '')
                                  for h in uricache.selecting_headers]
                 uricache[tuple(sorted(header_values))] = variant
                 self.tot_puts += 1
                 self.cursize = total_size
-    
+
     def delete(self):
         """Remove ALL cached variants of the current resource."""
         uri = cherrypy.url(qs=cherrypy.serving.request.query_string)
 
 def get(invalid_methods=("POST", "PUT", "DELETE"), debug=False, **kwargs):
     """Try to obtain cached output. If fresh enough, raise HTTPError(304).
-    
+
     If POST, PUT, or DELETE:
         * invalidates (deletes) any cached response for this resource
         * sets request.cached = False
         * sets request.cacheable = False
-    
+
     else if a cached copy exists:
         * sets request.cached = True
         * sets request.cacheable = False
           if necessary.
         * sets response.status and response.body to the cached values
         * returns True
-    
+
     otherwise:
         * sets request.cached = False
         * sets request.cacheable = True
     """
     request = cherrypy.serving.request
     response = cherrypy.serving.response
-    
+
     if not hasattr(cherrypy, "_cache"):
         # Make a process-wide Cache object.
         cherrypy._cache = kwargs.pop("cache_class", MemoryCache)()
-        
+
         # Take all remaining kwargs and set them on the Cache object.
         for k, v in kwargs.items():
             setattr(cherrypy._cache, k, v)
         cherrypy._cache.debug = debug
-    
+
     # POST, PUT, DELETE should invalidate (delete) the cached copy.
     # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.10.
     if request.method in invalid_methods:
         request.cached = False
         request.cacheable = False
         return False
-    
+
     if 'no-cache' in [e.value for e in request.headers.elements('Pragma')]:
         request.cached = False
         request.cacheable = True
         return False
-    
+
     cache_data = cherrypy._cache.get()
     request.cached = bool(cache_data)
     request.cacheable = not request.cached
                 request.cached = False
                 request.cacheable = True
                 return False
-        
+
         if debug:
             cherrypy.log('Reading response from cache', 'TOOLS.CACHING')
         s, h, b, create_time = cache_data
             request.cached = False
             request.cacheable = True
             return False
-        
+
         # Copy the response headers. See http://www.cherrypy.org/ticket/721.
         response.headers = rh = httputil.HeaderMap()
         for k in h:
             dict.__setitem__(rh, k, dict.__getitem__(h, k))
-        
+
         # Add the required Age header
         response.headers["Age"] = str(age)
-        
+
         try:
             # Note that validate_since depends on a Last-Modified header;
             # this was put into the cached copy, and should have been
             if x.status == 304:
                 cherrypy._cache.tot_non_modified += 1
             raise
-        
+
         # serve it & get out from the request
         response.status = s
         response.body = b
 def tee_output():
     """Tee response output to cache storage. Internal."""
     # Used by CachingTool by attaching to request.hooks
-    
+
     request = cherrypy.serving.request
     if 'no-store' in request.headers.values('Cache-Control'):
         return
-    
+
     def tee(body):
         """Tee response.body into a list."""
         if ('no-cache' in response.headers.values('Pragma') or
             for chunk in body:
                 yield chunk
             return
-        
+
         output = []
         for chunk in body:
             output.append(chunk)
             yield chunk
-        
+
         # save the cache data
         body = ntob('').join(output)
         cherrypy._cache.put((response.status, response.headers or {},
                              body, response.time), len(body))
-    
+
     response = cherrypy.serving.response
     response.body = tee(response.body)
 
         expire. The 'Expires' header will be set to response.time + secs.
         If secs is zero, the 'Expires' header is set one year in the past, and
         the following "cache prevention" headers are also set:
-        
+
             * Pragma: no-cache
             * Cache-Control': no-cache, must-revalidate
 
     force
         If False, the following headers are checked:
-        
+
             * Etag
             * Last-Modified
             * Age
             * Expires
-        
+
         If any are already present, none of the above response headers are set.
-    
+
     """
-    
+
     response = cherrypy.serving.response
     headers = response.headers
-    
+
     cacheable = False
     if not force:
         # some header names that indicate that the response can be cached
             if indicator in headers:
                 cacheable = True
                 break
-    
+
     if not cacheable and not force:
         if debug:
             cherrypy.log('request is not cacheable', 'TOOLS.EXPIRES')
             cherrypy.log('request is cacheable', 'TOOLS.EXPIRES')
         if isinstance(secs, datetime.timedelta):
             secs = (86400 * secs.days) + secs.seconds
-        
+
         if secs == 0:
             if force or ("Pragma" not in headers):
                 headers["Pragma"] = "no-cache"