Implemented user name caching, so we can efficiently pull data for Direct Messages and Searches

This commit is contained in:
Anna 2010-05-23 12:42:19 -04:00
parent 964358c1f2
commit d7893fce37
4 changed files with 163 additions and 77 deletions

View File

@ -6,7 +6,7 @@ from threading import Thread,RLock
import twitter_pb2
from oauthtwitter import OAuthApi
from urllib2 import HTTPError,URLError
import avcache
import usercache
import webbrowser
from time import sleep
@ -107,10 +107,11 @@ class GetTweets(ApiThread):
except (HTTPError, URLError):
statuses = None
# For each user id present, populate the AvCache with an image
# For each user id present, populate the UserCaches
if statuses:
for status in statuses:
avcache.add_to_cache(status.user)
usercache.add_to_av_cache(status.user)
usercache.add_to_name_cache(status.user, self.api)
gtk.gdk.threads_enter()
try:
@ -140,7 +141,8 @@ class GetSingleTweet(ApiThread):
# In case we've never seen this user, grab their profile image
for status in statuses:
avcache.add_to_cache(status.user)
usercache.add_to_av_cache(status.user)
usercache.add_to_name_cache(status.user)
gtk.gdk.threads_enter()
try:
@ -184,7 +186,8 @@ class GetConversation(ApiThread):
# In case we've never seen some of these users, grab their profile images and cache them
for status in statuses:
avcache.add_to_cache(status.user)
usercache.add_to_av_cache(status.user)
usercache.add_to_name_cache(status.user)
statuses.reverse()
@ -235,7 +238,8 @@ class GetUserInfo(ApiThread):
try:
with self.api.lock:
user = self.api.GetUser(screen_name)
avcache.add_to_cache(user)
usercache.add_to_av_cache(user)
usercache.add_to_name_cache(user)
except (HTTPError, URLError):
verified = False
user = None

View File

@ -1,65 +0,0 @@
from threading import RLock
from urllib2 import URLError,urlopen
import gtk
class AvCache:
"""
Store a cache of avatar images we've already downloaded.
This cache will be accessed by a number of threads, so it includes
a lock as well.
"""
class __impl:
""" Implementation of the singleton interface """
def __init__(self):
self.lock = RLock()
self.map = {}
# storage for the instance reference
__instance = None
def __init__(self):
""" Create singleton instance """
# Check whether we already have an instance
if AvCache.__instance is None:
# Create and remember instance
AvCache.__instance = AvCache.__impl()
# Store instance reference as the only member in the handle
self.__dict__['_AvCache__instance'] = AvCache.__instance
def __getattr__(self, attr):
""" Delegate access to implementation """
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
""" Delegate access to implementation """
return setattr(self.__instance, attr, value)
# end class AvCache
def add_to_cache(user):
"""
This helping function takes a python-twitter User object, grabs the image
data from the image_url and adds it to the AvCache with the screen_name as
the key.
"""
with AvCache().lock:
hit = AvCache().map.has_key(user.screen_name)
if not hit:
try:
url = urlopen(user.profile.image_url)
data = url.read()
loader = gtk.gdk.PixbufLoader()
loader.write(str(data))
image = loader.get_pixbuf()
loader.close()
with AvCache().lock:
AvCache().map[user.screen_name] = image
except (URLError, ValueError):
pass # Nothing needs be done, just catch & ignore

View File

@ -2,7 +2,7 @@ import re
import datetime, dateutil.tz
import gtk, gobject
from threading import RLock
from avcache import AvCache
from usercache import AvCache,NameCache
import webbrowser
@ -377,11 +377,18 @@ class TweetBox(gtk.HBox):
if not self.is_user:
try:
with AvCache().lock:
self.avatar.set_from_pixbuf(AvCache().map[status.user.screen_name])
image = AvCache().map[status.user.screen_name]
self.avatar.set_from_pixbuf(image)
self.avatar.show()
except KeyError:
self.avatar.hide()
try:
with NameCache().lock:
name = NameCache().map[status.user.screen_name]
except KeyError:
name = status.user.name
self.set_read(read)
timezone = dateutil.tz.gettz()
@ -424,7 +431,7 @@ class TweetBox(gtk.HBox):
if self.is_user:
self.user_button.set_label('')
else:
self.user_button.set_label(user.name + " (" + user.screen_name + ") ")
self.user_button.set_label(name + " (" + user.screen_name + ") ")
# and the text
new_text = status.text
@ -591,15 +598,24 @@ class UserBox(gtk.VBox):
def update_info(self, user):
self.user_name = user.screen_name
self.name_label.set_text(user.name + ' (' + self.user_name + ')')
name = ''
try:
with AvCache().lock:
self.avatar.set_from_pixbuf(AvCache().map[user.screen_name])
image = AvCache().map[user.screen_name]
self.avatar.set_from_pixbuf(image)
self.avatar.show()
except KeyError:
self.avatar.hide()
try:
with NameCache().lock:
name = NameCache().map[user.screen_name]
except KeyError:
name = user.name
self.name_label.set_text(name + ' (' + self.user_name + ')')
with self.data_lock:
self.verified = user.verified
if self.verified:

131
usercache.py Normal file
View File

@ -0,0 +1,131 @@
from threading import RLock
from urllib2 import URLError,urlopen
import gtk
class AvCache:
"""
Store a cache of user data we've already downloaded.
This cache will be accessed by a number of threads, so it includes
a lock as well.
"""
class __impl:
""" Implementation of the singleton interface """
def __init__(self):
self.lock = RLock()
self.map = {}
# storage for the instance reference
__instance = None
def __init__(self):
""" Create singleton instance """
# Check whether we already have an instance
if AvCache.__instance is None:
# Create and remember instance
AvCache.__instance = AvCache.__impl()
# Store instance reference as the only member in the handle
self.__dict__['_AvCache__instance'] = AvCache.__instance
def __getattr__(self, attr):
""" Delegate access to implementation """
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
""" Delegate access to implementation """
return setattr(self.__instance, attr, value)
# end class AvCache
class NameCache:
"""
Store a cache of user names we've already downloaded.
This cache will be accessed by a number of threads, so it includes
a lock as well.
"""
class __impl:
""" Implementation of the singleton interface """
def __init__(self):
self.lock = RLock()
self.map = {}
# storage for the instance reference
__instance = None
def __init__(self):
""" Create singleton instance """
# Check whether we already have an instance
if NameCache.__instance is None:
# Create and remember instance
NameCache.__instance = NameCache.__impl()
# Store instance reference as the only member in the handle
self.__dict__['_NameCache__instance'] = NameCache.__instance
def __getattr__(self, attr):
""" Delegate access to implementation """
return getattr(self.__instance, attr)
def __setattr__(self, attr, value):
""" Delegate access to implementation """
return setattr(self.__instance, attr, value)
# end class NameCache
def add_to_av_cache(user):
"""
This helping function takes a python-twitter User object, grabs the image
and from the image_url and adds it to the cache, along with the user's
full name, with the screen_name as the key.
"""
with AvCache().lock:
hit = AvCache().map.has_key(user.screen_name)
if not hit:
try:
url = urlopen(user.profile.image_url)
data = url.read()
loader = gtk.gdk.PixbufLoader()
loader.write(str(data))
image = loader.get_pixbuf()
loader.close()
with AvCache().lock:
AvCache().map[user.screen_name] = image
except (URLError, ValueError):
pass
def add_to_name_cache(user, api=None):
"""
This helping function takes a python-twitter User object and saves the
user's full name to the cache. If an API is passed in and the name in
'user' is false, we can grab the UserInfo for the user and get the name
directly from that.
"""
with NameCache().lock:
hit = NameCache().map.has_key(user.screen_name)
if not hit:
NameCache().map[user.screen_name] = user.name
name = NameCache().map[user.screen_name]
# Now let's look at the name we've got, and see if we need a better one
if not name:
with api.lock:
try:
new_user = api.GetUser(user.screen_name)
except (HTTPError, URLError):
new_user = None
if new_user is not None:
with NameCache().lock:
NameCache().map[user.screen_name] = new_user.name