Implemented in-memory cache. Tied in cache system into binder.
authorJosh Roesslein <jroesslein@gmail.com>
Thu, 30 Jul 2009 07:21:57 +0000 (02:21 -0500)
committerJosh Roesslein <jroesslein@gmail.com>
Thu, 30 Jul 2009 07:21:57 +0000 (02:21 -0500)
tweepy/__init__.py
tweepy/api.py
tweepy/binder.py
tweepy/cache.py [new file with mode: 0644]

index ce3e0ef99422fd5f50aa376c40bfbbe2e9dd88c8..ab4d5eb3bd908aed965edfcf5a307178a5c0fbf3 100644 (file)
@@ -10,6 +10,7 @@ __version__ = '1.0'
 from models import *
 from error import TweepError
 from api import API
+from cache import *
 
 # Global, unauthenticated instance of API
 api = API()
index c36072fd892ae39fea1fcf5be6c57adb76190652..8880c653f80d6cbe3c3eec5c9679e35aec16f474 100644 (file)
@@ -12,7 +12,8 @@ from error import TweepError
 """Twitter API"""
 class API(object):
 
-  def __init__(self, username=None, password=None, host='twitter.com', secure=False,
+  def __init__(self, username=None, password=None, host='twitter.com',
+                cache=None, secure=False,
                 classes={'user': User, 'status': Status,
                 'direct_message': DirectMessage, 'friendship': Friendship,
                 'saved_search': SavedSearch, 'search_result': SearchResult}):
@@ -21,6 +22,7 @@ class API(object):
     else:
       self._b64up = None
     self.host = host
+    self.cache = cache
     self.secure = secure
     self.classes = classes
     self.username = username
index e4d82f1d59a4841c3d7949e9ceaeba8178ec470c..48addde26b87cc0105f3fd9aaf9a3f38adb4ff70 100644 (file)
@@ -21,6 +21,20 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
     else:
       parameters = None
 
+    # Build url with parameters
+    if parameters:
+      url = '%s?%s' % (path, urllib.urlencode(parameters))
+    else:
+      url = path
+
+    # Check cache if caching enabled and method is GET
+    if api.cache and method == 'GET':
+      cache_result = api.cache.get(url)
+      if cache_result:
+        # if cache result found and not expired, return it
+        print 'hit!'
+        return cache_result
+
     # Open connection
     if host:
       _host = host
@@ -31,12 +45,6 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
     else:
       conn = httplib.HTTPConnection(_host)
 
-    # Build url with parameters
-    if parameters:
-      url = '%s?%s' % (path, urllib.urlencode(parameters))
-    else:
-      url = path
-
     # Assemble headers
     headers = {
       'User-Agent': 'tweepy'
@@ -57,6 +65,10 @@ def bind_api(path, parser, allowed_param=None, method='GET', require_auth=False,
     # Pass returned body into parser and return parser output
     out =  parser(resp.read(), api)
 
+    # store result in cache
+    if api.cache and method == 'GET':
+      api.cache.store(url, out)
+
     # close connection and return data
     conn.close()
     return out
diff --git a/tweepy/cache.py b/tweepy/cache.py
new file mode 100644 (file)
index 0000000..bc6b11f
--- /dev/null
@@ -0,0 +1,79 @@
+# Tweepy
+# Copyright 2009 Joshua Roesslein
+# See LICENSE
+
+import time
+import threading
+
+"""Cache interface"""
+class Cache(object):
+
+  def __init__(self, timeout=60):
+    """Init the cache
+        timeout: number of seconds to keep a cached entry
+    """
+    self.timeout = timeout
+
+  def store(self, key, value):
+    """Add new record to cache
+        key: entry key
+        value: data of entry
+    """
+    raise NotImplemented
+
+  def get(self, key):
+    """Get cached entry if exists and not expired
+        key: which entry to get
+    """
+    raise NotImplemented
+
+  def cleanup(self):
+    """Delete any expired entries in cache."""
+    raise NotImplemented
+
+  def flush(self):
+    """Delete all cached entries"""
+    raise NotImplemented
+
+"""In-memory cache"""
+class MemoryCache(Cache):
+
+  def __init__(self, timeout=60):
+    Cache.__init__(self, timeout)
+    self._entries = {}
+    self.lock = threading.Lock()
+
+  def _is_expired(self, entry):
+    return (time.time() - entry[0]) >= self.timeout
+
+  def store(self, key, value):
+    with self.lock:
+      self._entries[key] = (time.time(), value)
+
+  def get(self, key):
+    with self.lock:
+      # check to see if we have this key
+      entry = self._entries.get(key)
+      if not entry:
+        # no hit, return nothing
+        return None
+
+      # make sure entry is not expired
+      if self._is_expired(entry):
+        # entry expired, delete and return nothing
+        del self._entries[key]
+        return None
+
+      # entry found and not expired, return it
+      return entry[1]
+
+  def cleanup(self):
+    with self.lock:
+      for k,v in self._entries.items():
+        if self._is_expired(v):
+          del self._entries[k]
+
+  def flush(self):
+    with self.lock:
+      self._entries.clear()
+