From 45b374f24f6793a6ef2ff8bfdea17e6def89d105 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 22 Apr 2010 14:57:27 -0400 Subject: [PATCH] First stab at implementing threads... seems to work okay, then suddenly segfaults --- apithreads.py | 110 +++++++++++++++++++++++++++++++++++++++++++ mytwitter.py | 116 ++++++++++++---------------------------------- twitterwidgets.py | 2 + 3 files changed, 141 insertions(+), 87 deletions(-) create mode 100644 apithreads.py diff --git a/apithreads.py b/apithreads.py new file mode 100644 index 0000000..b0fcb56 --- /dev/null +++ b/apithreads.py @@ -0,0 +1,110 @@ +# Python module that handles calls to the twitter API as a separate thread + +import re +from threading import Thread + +class GetTweets(Thread): + def __init__(self, api, list_name, pane, num_entries, username): + Thread.__init__(self) + self.api = api + self.list_name = list_name + self.pane = pane + self.num_entries = num_entries + self.username = username + + + def run(self): + print 'debug: GetTweets.run(): start' + + # 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) + + # 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)) + + # Direct Messages should match like /Home, above + elif self.list_name == self.username + '/Direct Messages': + statuses = dms_to_statuses(self.api.GetDirectMessages()) + + # 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) + + # 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)) + + self.pane.update_window(statuses) + + print 'debug: GetTweets.run(): finished' + + +### End class GetTweets + + + +# We use these classes to emulate a Status object when we need +# one to be built out of something else. +class Status(): + def __init__(self): + self.user = User() + self.id = None + self.created_at = None + self.in_reply_to_screen_name = None + self.in_reply_to_status_id = None + + +class User(): + def __init__(self): + self.screen_name = None + self.name = None + + +# To keep things simple elsewhere and improve code reuse +# we'll build a list of home-cooked Status objects out of results. +# Why is this even necessary? +# Why can't we have more consistency out of the Twitter API? +def results_to_statuses(results): + statuses = [] + for result in results.results: + status = Status() + status.id = result.id + status.user = User() + status.user.screen_name = result.from_user + status.user.name = "" + status.in_reply_to_screen_name = result.to_user + # The Twitter Search API has different timestamps than the + # REST API... balls + # fixme: + # Gotta be a cleaner way to do this, but I can't think of it + # right now + created_at = re.sub(',', '', result.created_at) + created_split = re.split(' ', created_at) + status.created_at = created_split[0] + ' ' + created_split[2] + ' ' + created_split[1] + ' ' + created_split[4] + ' ' + created_split[5] + ' ' + created_split[3] + status.text = result.text + statuses.append(status) + return statuses + + +def dms_to_statuses(direct_messages): + statuses = [] + for dm in direct_messages: + status = Status() + status.id = dm.id + status.user = User() + status.user.screen_name = dm.sender_screen_name + status.user.name = dm.sender.name + status.created_at = dm.created_at + status.text = dm.text + statuses.append(status) + return statuses diff --git a/mytwitter.py b/mytwitter.py index 126d8ab..0d8c342 100755 --- a/mytwitter.py +++ b/mytwitter.py @@ -7,6 +7,7 @@ import twitter import gtk, gtk.glade, gobject from urllib2 import HTTPError from twitterwidgets import TweetPane +import apithreads class MyTwitter(): @@ -135,6 +136,7 @@ class MyTwitter(): gobject.timeout_add(self.refresh_time * 1000, self.update_windows) + # Spawns a thread for each pane, which updates that pane. def update_windows(self): for i in range(0, self.tweet_notebook.get_n_pages()): pane = self.tweet_notebook.get_nth_page(i) @@ -144,41 +146,36 @@ class MyTwitter(): if pane.get_single_tweet() is not None: continue - # username/Home entries need to load the appropriate Home feed - elif re.search(r'/Home', list_name): - account = self.accounts[re.sub(r'/Home', r'', list_name)] - statuses = account.GetHomeTimeline(count=self.num_entries) + # Determine username and appropriate account to use + found = False - # For @username, we need to check if it is one of our usernames, or - # just needs to be searched on - elif re.match('@', list_name): - if self.accounts.has_key(re.sub('@', '', list_name)): - account = self.accounts[re.sub(r'@', r'', list_name)] - statuses = account.GetMentions(count=self.num_entries) - else: - statuses = self.results_to_statuses(self.api.Search(list_name, rpp=self.num_entries)) - - # Direct Messages should match like /Home, above - elif re.search(r'/Direct Messages', list_name): - account = self.accounts[re.sub(r'/Direct Messages', r'', list_name)] - statuses = self.dms_to_statuses(account.GetDirectMessages()) - - # User lookups go straight to the user - elif re.match(r'user: ', list_name): - statuses = self.api.GetUserTimeline(re.sub(r'^user: ', r'', list_name), count=self.num_entries) - - # Lists load the appropriate list from the appropriate account - elif re.match(r'list: ', list_name): - username = re.sub(r'list: (.*)/.*', r'\1', list_name) - real_list = re.sub(r'list: .*/(.*)', r'\1', list_name) + username = re.sub('/Home', '', list_name) + if self.accounts.has_key(username): account = self.accounts[username] - statuses = account.GetListStatuses(real_list, per_page=self.num_entries) + found = True - # Everything else is a straight search - else: - statuses = self.results_to_statuses(self.api.Search(list_name, rpp=self.num_entries)) + if not found: + username = re.sub('@', '', list_name) + if self.accounts.has_key(username): + account = self.accounts[username] + found = True - pane.update_window(statuses) + if not found: + username = re.sub('/Direct Messages', '', list_name) + if self.accounts.has_key(username): + account = self.accounts[username] + found = True + + if not found: + account = self.api + username = self.username + + apithreads.GetTweets(api=account, + list_name=list_name, + pane=pane, + num_entries=self.num_entries, + username=username + ).start() # We have to return true, so the timeout_add event will keep happening return True @@ -440,69 +437,14 @@ class MyTwitter(): self.username = new_user self.api = self.accounts[self.username] - - # To keep things simple elsewhere and improve code reuse - # we'll build a list of home-cooked Status objects out of results. - # Why is this even necessary? - # Why can't we have more consistency out of the Twitter API? - def results_to_statuses(self, results): - statuses = [] - for result in results.results: - status = Status() - status.id = result.id - status.user = User() - status.user.screen_name = result.from_user - status.user.name = "" - status.in_reply_to_screen_name = result.to_user - # The Twitter Search API has different timestamps than the - # REST API... balls - # fixme: - # Gotta be a cleaner way to do this, but I can't think of it - # right now - created_at = re.sub(',', '', result.created_at) - created_split = re.split(' ', created_at) - status.created_at = created_split[0] + ' ' + created_split[2] + ' ' + created_split[1] + ' ' + created_split[4] + ' ' + created_split[5] + ' ' + created_split[3] - status.text = result.text - statuses.append(status) - return statuses - - - def dms_to_statuses(self, direct_messages): - statuses = [] - for dm in direct_messages: - status = Status() - status.id = dm.id - status.user = User() - status.user.screen_name = dm.sender_screen_name - status.user.name = dm.sender.name - status.created_at = dm.created_at - status.text = dm.text - statuses.append(status) - return statuses - ### end class MyTwitter -# We use these classes to emulate a Status object when we need -# one to be built out of something else. -class Status(): - def __init__(self): - self.user = User() - self.id = None - self.created_at = None - self.in_reply_to_screen_name = None - self.in_reply_to_status_id = None - -class User(): - def __init__(self): - self.screen_name = None - self.name = None - - # main parser = optparse.OptionParser() parser.add_option('-c' ,'--config', dest="filename", default="~/.mytwitter.conf") (options, args) = parser.parse_args() my_twitter = MyTwitter(options.filename) +gtk.gdk.threads_init() gtk.main() diff --git a/twitterwidgets.py b/twitterwidgets.py index 2077dd8..7b251db 100644 --- a/twitterwidgets.py +++ b/twitterwidgets.py @@ -70,6 +70,8 @@ class TweetPane(gtk.ScrolledWindow): def update_window(self, statuses): + print 'debug: TweetPane.update_window(): ' + self.list_name + if self.updated_once is False: self.updated_once = True