From 00f3812c8c5e6a0e06a78ee53d4a31abb08b99e0 Mon Sep 17 00:00:00 2001 From: Josh Roesslein Date: Thu, 30 Jul 2009 02:21:57 -0500 Subject: [PATCH] Implemented in-memory cache. Tied in cache system into binder. --- tweepy/__init__.py | 1 + tweepy/api.py | 4 ++- tweepy/binder.py | 24 ++++++++++---- tweepy/cache.py | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 7 deletions(-) create mode 100644 tweepy/cache.py diff --git a/tweepy/__init__.py b/tweepy/__init__.py index ce3e0ef..ab4d5eb 100644 --- a/tweepy/__init__.py +++ b/tweepy/__init__.py @@ -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() diff --git a/tweepy/api.py b/tweepy/api.py index c36072f..8880c65 100644 --- a/tweepy/api.py +++ b/tweepy/api.py @@ -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 diff --git a/tweepy/binder.py b/tweepy/binder.py index e4d82f1..48addde 100644 --- a/tweepy/binder.py +++ b/tweepy/binder.py @@ -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 index 0000000..bc6b11f --- /dev/null +++ b/tweepy/cache.py @@ -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() + -- 2.25.1