Added locking and moved work around / reduced repeat work at startup. Should clear up segfaults and improve startup time

This commit is contained in:
Anna 2010-05-04 11:03:32 -04:00
parent 7cce83c0f3
commit 6044fa27d2
3 changed files with 61 additions and 39 deletions

6
TODO
View File

@ -14,6 +14,6 @@ features:
bugs:
* Direct Messages have no names, only screen names (may not be fixable without
considerable tweets to python-twitter)
* Can't always kill tabs
* New segfault at start that can't be reproduced with gdb. Must be a timing issue... adding locked access to some data in TweetPane didn't help... need locks to go with api objects.
considerable tweaks to python-twitter)
* Can't always kill tabs - open tab list not always correctly populated?
* Can't always kill application

View File

@ -2,7 +2,19 @@
import re
import gtk
from threading import Thread
from threading import Thread,RLock
from twitter import Api
class SafeApi(Api):
''' This is just a Twitter API with an RLock for multi-threaded access '''
def __init__(self, username, password):
Api.__init__(self, username, password)
self.lock = RLock()
# End class SafeApi
class GetTweets(Thread):
def __init__(self, api, list_name, pane, num_entries, username):
@ -15,33 +27,35 @@ class GetTweets(Thread):
def run(self):
# username/Home entries need to load the appropriate Home feed
if self.list_name == self.username + '/Home':
statuses = self.api.GetHomeTimeline(count=self.num_entries)
with self.api.lock:
# For @username, check if it is one of our usernames, or
# just needs to be searched on
elif self.list_name == '@' + self.username:
statuses = self.api.GetMentions(count=self.num_entries)
elif re.match('@', self.list_name):
statuses = results_to_statuses(self.api.Search(self.list_name, rpp=self.num_entries))
# username/Home entries need to load the appropriate Home feed
if self.list_name == self.username + '/Home':
statuses = self.api.GetHomeTimeline(count=self.num_entries)
# Direct Messages should match like /Home, above
elif self.list_name == self.username + '/Direct Messages':
statuses = dms_to_statuses(self.api.GetDirectMessages())
# For @username, check if it is one of our usernames, or
# just needs to be searched on
elif self.list_name == '@' + self.username:
statuses = self.api.GetMentions(count=self.num_entries)
elif re.match('@', self.list_name):
statuses = results_to_statuses(self.api.Search(self.list_name, rpp=self.num_entries))
# User lookups go straight to the user
elif re.match(r'user: ', self.list_name):
statuses = self.api.GetUserTimeline(re.sub(r'^user: ', r'', self.list_name), count=self.num_entries)
# Direct Messages should match like /Home, above
elif self.list_name == self.username + '/Direct Messages':
statuses = dms_to_statuses(self.api.GetDirectMessages())
# Lists load the appropriate list from the appropriate account
elif re.match(r'list: ', self.list_name):
real_list = re.sub(r'list: .*/(.*)', r'\1', self.list_name)
statuses = self.api.GetListStatuses(real_list, per_page=self.num_entries)
# User lookups go straight to the user
elif re.match(r'user: ', self.list_name):
statuses = self.api.GetUserTimeline(re.sub(r'^user: ', r'', self.list_name), count=self.num_entries)
# Everything else is a straight search
else:
statuses = results_to_statuses(self.api.Search(self.list_name, rpp=self.num_entries))
# Lists load the appropriate list from the appropriate account
elif re.match(r'list: ', self.list_name):
real_list = re.sub(r'list: .*/(.*)', r'\1', self.list_name)
statuses = self.api.GetListStatuses(real_list, per_page=self.num_entries)
# Everything else is a straight search
else:
statuses = results_to_statuses(self.api.Search(self.list_name, rpp=self.num_entries))
gtk.gdk.threads_enter()
try:
@ -64,7 +78,8 @@ class GetSingleTweet(Thread):
def run(self):
statuses = []
statuses.append(self.api.GetStatus(self.single_tweet))
with self.api.lock:
statuses.append(self.api.GetStatus(self.single_tweet))
gtk.gdk.threads_enter()
try:
@ -89,7 +104,8 @@ class GetFollowing(Thread):
screen_name = re.sub('user: ', '', self.user)
try:
relationship = self.api.ShowFriendships(target_screen_name=screen_name)
with self.api.lock:
relationship = self.api.ShowFriendships(target_screen_name=screen_name)
following = relationship.source.following
except HTTPError:
following = false
@ -112,7 +128,8 @@ class GetVerified(Thread):
screen_name = re.sub('user: ', '', self.user)
try:
user = self.api.GetUser(screen_name)
with self.api.lock:
user = self.api.GetUser(screen_name)
verified = user.verified
except HTTPError:
verified = false

View File

@ -3,7 +3,6 @@
# Custom twitter client... mostly for learning Python
import sys, ConfigParser, os, re, optparse, shelve
import twitter
import gtk, gtk.glade, gobject
from urllib2 import HTTPError
from twitterwidgets import TweetPane
@ -47,7 +46,7 @@ class MyTwitter():
for item in config.sections():
if (re.match(r'account', item)):
username = config.get(item, 'username')
self.accounts[username] = twitter.Api(username=username, password=config.get(item, 'password'))
self.accounts[username] = apithreads.SafeApi(username=username, password=config.get(item, 'password'))
self.username = self.accounts.keys()[0]
self.api = self.accounts[self.username]
@ -106,8 +105,9 @@ class MyTwitter():
# Add the tabs from last session to the notebook
page_num = self.db['active_page']
for tab, single_tweet in self.db['open_tabs']:
self.add_to_notebook(tab, single_tweet)
self.add_to_notebook(tab, single_tweet, update=False)
self.tweet_notebook.set_current_page(page_num)
self.update_windows()
# Put Home, @user, Direct Messages, and lists in the View menu for
# each user
@ -188,7 +188,8 @@ class MyTwitter():
def update_status(self):
text = self.update_entry.get_text()
self.update_entry.set_text("")
self.api.PostUpdate(text, in_reply_to_status_id=self.reply_id)
with self.api.lock:
self.api.PostUpdate(text, in_reply_to_status_id=self.reply_id)
self.reply_id = None
self.update_status_bar('Tweet Posted')
@ -224,7 +225,8 @@ class MyTwitter():
def on_retweet(self, widget, data):
self.api.PostRetweet(data['id'])
with self.api.lock:
self.api.PostRetweet(data['id'])
def on_reply_to(self, widget, data):
@ -262,7 +264,7 @@ class MyTwitter():
self.remove_view(name, single_tweet)
def add_to_notebook(self, name, single_tweet=None):
def add_to_notebook(self, name, single_tweet=None, update=True):
# If it already exists, don't add it, just switch to it
for i in range(self.tweet_notebook.get_n_pages()):
pane = self.tweet_notebook.get_nth_page(i)
@ -308,8 +310,9 @@ class MyTwitter():
pane=new_pane,
single_tweet=single_tweet).start()
self.update_windows()
self.tweet_notebook.set_current_page(-1) # switch to the new pane
if update:
self.update_windows()
self.tweet_notebook.set_current_page(-1) # switch to the new pane
def on_tab_change(self, event, page, page_num):
@ -356,10 +359,12 @@ class MyTwitter():
current_pane = self.tweet_notebook.get_nth_page(self.tweet_notebook.get_current_page())
user_name = re.sub('^user: ', '', current_pane.get_list_name())
if current_pane.get_following():
self.api.DestroyFriendship(user_name)
with self.api.lock:
self.api.DestroyFriendship(user_name)
current_pane.set_following(False)
else:
self.api.CreateFriendship(user_name)
with self.api.lock:
self.api.CreateFriendship(user_name)
current_pane.set_following(True)
self.update_follow_button(current_pane)