[postr] Added error logging to every Twisted callback
- From: GermÃn Poà CaamaÃo <gpoo src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [postr] Added error logging to every Twisted callback
- Date: Thu, 4 Oct 2012 08:58:13 +0000 (UTC)
commit eadde207b344bd3778a3e331cef2c1276daa27d9
Author: GermÃn Poo-CaamaÃo <gpoo gnome org>
Date: Thu Oct 4 01:56:31 2012 -0700
Added error logging to every Twisted callback
- Error callbacks return failure to avoid silently miss errors
- Formatted the code to make it easier to read (chaining
callbacks in Twisted is not very readable).
Signed-off-by: GermÃn Poo-CaamaÃo <gpoo gnome org>
src/GroupSelector.py | 14 +++++++++-
src/LicenseCombo.py | 14 ++++-------
src/SetCombo.py | 12 ++++++++-
src/StatusBar.py | 9 ++++--
src/TagsEntry.py | 27 ++++++++++-----------
src/flickrest.py | 64 +++++++++++++++++++++++++++++++++----------------
src/postr.py | 48 +++++++++++++++++++++++++++----------
src/util.py | 13 ++++++----
8 files changed, 132 insertions(+), 69 deletions(-)
---
diff --git a/src/GroupSelector.py b/src/GroupSelector.py
index bf29365..6c32ac9 100644
--- a/src/GroupSelector.py
+++ b/src/GroupSelector.py
@@ -19,6 +19,8 @@ from gi.repository import Gtk, GObject, Pango, GdkPixbuf
from ErrorDialog import ErrorDialog
import util
+from twisted.python import log
+
(COL_SELECTED,
COL_ID,
COL_NAME,
@@ -82,7 +84,9 @@ class GroupSelector(Gtk.TreeView):
def update(self):
# TODO: block changed signals
- self.flickr.groups_pools_getGroups().addCallbacks(self.got_groups, self.twisted_error)
+ deferred = self.flickr.groups_pools_getGroups()
+ deferred.addCallback(self.got_groups)
+ deferred.addErrback(self.twisted_error)
def got_groups(self, rsp):
for group in rsp.findall("groups/group"):
@@ -92,13 +96,19 @@ class GroupSelector(Gtk.TreeView):
COL_NAME, group.get("name"))
def got_thumb(thumb, it):
self.model.set (it, COL_ICON, thumb)
- util.get_buddyicon(self.flickr, group, self.thumb_size).addCallback(got_thumb, it)
+
+ deferred = util.get_buddyicon(self.flickr, group, self.thumb_size)
+ deferred.addCallback(got_thumb, it)
+ deferred.addErrback(self.twisted_error)
def twisted_error(self, failure):
dialog = ErrorDialog()
dialog.set_from_failure(failure)
dialog.show_all()
+ log.err(failure, 'Exception in %s' % self.__gtype_name__)
+ return failure
+
def get_selected_groups(self):
return [row[COL_ID] for row in self.model if row[COL_SELECTED]]
diff --git a/src/LicenseCombo.py b/src/LicenseCombo.py
index a185eb4..5f30a1a 100644
--- a/src/LicenseCombo.py
+++ b/src/LicenseCombo.py
@@ -16,6 +16,7 @@
# St, Fifth Floor, Boston, MA 02110-1301 USA
from gi.repository import GObject, Gtk
+from twisted.python import log
class LicenseCombo(Gtk.ComboBox):
__gtype_name__ = 'LicenseCombo'
@@ -23,7 +24,7 @@ class LicenseCombo(Gtk.ComboBox):
def __init__(self):
Gtk.ComboBox.__init__(self)
self.flickr = None
-
+
self.model = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_INT)
self.model.append([_("Default"), -1])
self.set_model(self.model)
@@ -33,12 +34,6 @@ class LicenseCombo(Gtk.ComboBox):
self.pack_start(cell, True)
self.add_attribute(cell, "text", 0)
- def twisted_error(self, failure):
- from ErrorDialog import ErrorDialog
- dialog = ErrorDialog()
- dialog.set_from_failure(failure)
- dialog.show_all()
-
def __got_licenses(self, rsp):
"""Callback for the photos.licenses.getInfo call"""
for license in rsp.findall("licenses/license"):
@@ -46,8 +41,9 @@ class LicenseCombo(Gtk.ComboBox):
self.model.append([license.get("name"), license_id])
def update(self):
- self.flickr.photos_licenses_getInfo().addCallbacks(self.__got_licenses,
- self.twisted_error)
+ deferred = self.flickr.photos_licenses_getInfo()
+ deferred.addCallback(self.__got_licenses)
+ deferred.addErrback(log.err)
def get_license_for_iter(self, it):
if it is None: return None
diff --git a/src/SetCombo.py b/src/SetCombo.py
index 3b5cc1e..4cdc21c 100644
--- a/src/SetCombo.py
+++ b/src/SetCombo.py
@@ -20,6 +20,7 @@
import datetime
from gi.repository import GObject, Gtk, GdkPixbuf
from twisted.web.client import getPage
+from twisted.python import log
_NO_PHOTOSET_ID = "-1"
_NO_PHOTOSET_LABEL = _("None")
@@ -71,6 +72,9 @@ class SetCombo(Gtk.ComboBox):
dialog.set_from_failure(failure)
dialog.show_all()
+ log.err(failure, 'Exception in %s' % self.__gtype_name__)
+ return failure
+
def __got_set_thumb(self, page, it):
loader = GdkPixbuf.PixbufLoader()
loader.set_size (self.thumb_size, self.thumb_size)
@@ -87,10 +91,14 @@ class SetCombo(Gtk.ComboBox):
1, photoset.find("title").text)
url = "http://static.flickr.com/%s/%s_%s%s.jpg" % (photoset.get("server"), photoset.get("primary"), photoset.get("secret"), "_s")
- getPage (url).addCallback(self.__got_set_thumb, it).addErrback(self.twisted_error)
+ deferred = getPage(url)
+ deferred.addCallback(self.__got_set_thumb, it)
+ deferred.addErrback(self.twisted_error)
def update(self):
- self.flickr.photosets_getList().addCallbacks(self.__got_photosets, self.twisted_error)
+ deferred = self.flickr.photosets_getList()
+ deferred.addCallback(self.__got_photosets)
+ deferred.addErrback(self.twisted_error)
def get_id_for_iter(self, it):
if it is None: return None
diff --git a/src/StatusBar.py b/src/StatusBar.py
index d0f956a..32eee67 100644
--- a/src/StatusBar.py
+++ b/src/StatusBar.py
@@ -19,6 +19,7 @@ from gi.repository import Gtk, GObject
from ErrorDialog import ErrorDialog
from util import greek
from xml.sax.saxutils import escape
+from twisted.python import log
class StatusBar(Gtk.Label):
__gtype_name__ = 'StatusBar'
@@ -38,7 +39,7 @@ class StatusBar(Gtk.Label):
if self.flickr.get_username():
message = message + _("Logged in as <b>%s</b>. ") % escape(self.flickr.get_fullname() or self.flickr.get_username())
-
+
if self.quota and self.to_upload:
message = message + _("You can upload %(quota)s this month, and have %(to_upload)s to upload.") % self.__dict__
elif self.quota:
@@ -47,7 +48,7 @@ class StatusBar(Gtk.Label):
message = message + _("%(to_upload)s to upload.") % self.__dict__
self.set_markup(message)
-
+
def update_quota(self):
"""Call Flickr to get the current upload quota, and update the status bar."""
def got_quota(rsp):
@@ -61,7 +62,9 @@ class StatusBar(Gtk.Label):
dialog = ErrorDialog(self.get_toplevel())
dialog.set_from_failure(failure)
dialog.show_all()
- self.flickr.people_getUploadStatus().addCallbacks(got_quota, error)
+ deferred = self.flickr.people_getUploadStatus()
+ deferred.addCallback(got_quota)
+ deferred.addErrback(log.err)
def set_upload(self, to_upload):
"""Set the amount of data to be uploaded, and update the status bar."""
diff --git a/src/TagsEntry.py b/src/TagsEntry.py
index abf6341..79c5a64 100644
--- a/src/TagsEntry.py
+++ b/src/TagsEntry.py
@@ -16,6 +16,7 @@
# St, Fifth Floor, Boston, MA 02110-1301 USA
from gi.repository import Gtk, GObject
+from twisted.python import log
_USER_POPULAR_TAGS = 200
_HOTS_TAGS = 20
@@ -57,7 +58,7 @@ class TagsEntry(Gtk.Entry):
# add the matching word
current_text = '%s %s ' % (current_text, model[iter][_COL_TAG_NAME])
else:
- current_text = model[iter][_COL_TAG_NAME] +' '
+ current_text = model[iter][_COL_TAG_NAME] + ' '
# set back the whole text
self.set_text(current_text)
@@ -83,15 +84,20 @@ class TagsEntry(Gtk.Entry):
return modelstr.lower().startswith(key_string.lower())
def update(self):
-
self.completion_model.clear()
- self.flickr.tags_getListUserPopular(user_id=self.flickr.get_nsid(), \
- count=_USER_POPULAR_TAGS).addCallbacks(self.create_completion_model,
- self.twisted_error)
+ # In both cases, we do not call a dialog to throw an error
+ # because it is too invasive when we query Flickr constantly.
+ user_id = self.flickr.get_nsid()
+ deferred1 = self.flickr.tags_getListUserPopular(user_id=user_id,
+ count=_USER_POPULAR_TAGS)
+ deferred1.addCallback(self.create_completion_model)
+ deferred1.addErrback(log.err)
- self.flickr.tags_getHotList(user_id=self.flickr.get_nsid(), count=_HOTS_TAGS)\
- .addCallbacks(self.create_completion_model, self.twisted_error)
+ deferred2 = self.flickr.tags_getHotList(user_id=user_id,
+ count=_HOTS_TAGS)
+ deferred2.addCallback(self.create_completion_model)
+ deferred2.addErrback(log.err)
def create_completion_model(self, rsp):
'''
@@ -102,10 +108,3 @@ class TagsEntry(Gtk.Entry):
self.completion_model.append([tag.text])
self.completion.set_model(self.completion_model)
-
- def twisted_error(self, failure):
- #TODO: throw a message in a less invasive way
- from ErrorDialog import ErrorDialog
- dialog = ErrorDialog()
- dialog.set_from_failure(failure)
- dialog.show_all()
diff --git a/src/flickrest.py b/src/flickrest.py
index 9980dfd..9213591 100644
--- a/src/flickrest.py
+++ b/src/flickrest.py
@@ -19,6 +19,7 @@ import logging, os, mimetools, urllib
from gi.repository import Gio
from twisted.internet import defer
from twisted.python.failure import Failure
+from twisted.python import log
import proxyclient as client
try:
@@ -36,7 +37,7 @@ class FlickrError(Exception):
Exception.__init__(self)
self.code = int(code)
self.message = message
-
+
def __str__(self):
return "%d: %s" % (self.code, self.message)
@@ -48,7 +49,7 @@ class FlickrError(Exception):
class Flickr:
endpoint = "http://api.flickr.com/services/rest/?"
-
+
def __init__(self, api_key, secret, perms="read"):
self.__methods = {}
self.api_key = api_key
@@ -75,10 +76,10 @@ class Flickr:
if proxy and "://" not in proxy:
proxy = "http://" + proxy
self.proxy = proxy
-
+
def __repr__(self):
return "<FlickREST>"
-
+
def __getTokenFile(self):
"""Get the filename that contains the authentication token for the API key"""
return os.path.expanduser(os.path.join("~", ".flickr", self.api_key, "auth.xml"))
@@ -92,7 +93,7 @@ class Flickr:
if os.path.exists(token):
os.remove(token)
self.token = None
-
+
def __sign(self, kwargs):
kwargs['api_key'] = self.api_key
# If authenticating we don't yet have a token
@@ -112,7 +113,7 @@ class Flickr:
return client.getPage(Flickr.endpoint, proxy=self.proxy, method="POST",
headers={"Content-Type": "application/x-www-form-urlencoded"},
postdata=urllib.urlencode(kwargs))
-
+
def __cb(self, data, method):
self.logger.info("%s returned" % method)
xml = ElementTree.XML(data)
@@ -124,12 +125,16 @@ class Flickr:
else:
# Fake an error in this case
raise FlickrError(0, "Invalid response")
-
+
def __getattr__(self, method):
method = "flickr." + method.replace("_", ".")
if not self.__methods.has_key(method):
def proxy(method=method, **kwargs):
- return self.__call(method, kwargs).addCallback(self.__cb, method)
+ deferred = self.__call(method, kwargs)
+ deferred.addCallback(self.__cb, method)
+ deferred.addErrback(log.err)
+ return deferred
+
self.__methods[method] = proxy
return self.__methods[method]
@@ -161,7 +166,7 @@ class Flickr:
# Add final boundary.
lines.append("--" + boundary.encode("utf-8"))
return (boundary, '\r\n'.join(lines))
-
+
def upload(self, uri=None, imageData=None,
title=None, desc=None, tags=None,
is_public=None, is_family=None, is_friend=None,
@@ -194,23 +199,27 @@ class Flickr:
kwargs['content_type'] = content_type
self.__sign(kwargs)
self.logger.info("Upload args %s" % kwargs)
-
+
if imageData:
kwargs['photo'] = imageData
else:
kwargs['photo'] = Gio.File.new_for_uri(uri)
(boundary, form) = self.__encodeForm(kwargs)
- headers= {
+ headers = {
"Content-Type": "multipart/form-data; boundary=%s" % boundary,
"Content-Length": str(len(form))
}
self.logger.info("Calling upload")
- return client.upload("http://api.flickr.com/services/upload/",
- proxy=self.proxy, method="POST",
- headers=headers, postdata=form,
- progress_tracker=progress_tracker).addCallback(self.__cb, "upload")
+ deferred = client.upload("http://api.flickr.com/services/upload/",
+ proxy=self.proxy, method="POST",
+ headers=headers, postdata=form,
+ progress_tracker=progress_tracker)
+ deferred.addCallback(self.__cb, "upload")
+ deferred.addErrback(log.err)
+
+ return deferred
def authenticate_2(self, state):
def gotToken(e):
@@ -235,7 +244,11 @@ class Flickr:
# Callback to the user
return True
- return self.auth_getToken(frob=state['frob']).addCallback(gotToken)
+ deferred = self.auth_getToken(frob=state['frob'])
+ deferred.addCallback(gotToken)
+ deferred.addErrback(log.err)
+
+ return deferred
def __get_frob(self):
"""Make the getFrob() call."""
@@ -246,7 +259,12 @@ class Flickr:
self.__sign(keys)
url = "http://flickr.com/services/auth/?api_key=%(api_key)s&perms=%(perms)s&frob=%(frob)s&api_sig=%(api_sig)s" % keys
return {'url': url, 'frob': frob}
- return self.auth_getFrob().addCallback(gotFrob)
+
+ deferred = self.auth_getFrob()
+ deferred.addCallback(gotFrob)
+ deferred.addErrback(log.err)
+
+ return deferred
def authenticate_1(self):
"""Attempts to log in to Flickr. The return value is a Twisted Deferred
@@ -263,7 +281,7 @@ class Flickr:
try:
e = ElementTree.parse(filename).getroot()
self.token = e.find("auth/token").text
-
+
user = e.find("auth/user")
self.fullname = user.get("fullname")
self.username = user.get("username")
@@ -275,13 +293,17 @@ class Flickr:
# If checkToken() failed, we need to re-authenticate
self.clear_cached()
return self.__get_frob()
- return self.auth_checkToken().addCallbacks(reply, failed)
+ deferred = self.auth_checkToken()
+ deferred.addCallbacks(reply, failed)
+ deferred.addErrback(log.err)
+
+ return deferred
except:
# TODO: print the exception to stderr?
pass
-
+
return self.__get_frob()
-
+
@staticmethod
def get_photo_url(photo, size=SIZE_MEDIUM):
if photo is None:
diff --git a/src/postr.py b/src/postr.py
index 33dc81d..b6f4be2 100644
--- a/src/postr.py
+++ b/src/postr.py
@@ -21,6 +21,8 @@ import logging, os, urllib
from urlparse import urlparse
from os.path import basename
from tempfile import mkstemp
+from twisted.python import log
+from twisted.internet import reactor
from gi.repository import GObject, Gtk, GConf, GdkPixbuf, Gio, Gdk, GLib
@@ -216,7 +218,9 @@ class Postr(UniqueApp):
self.proxy_changed(client, 0, None, None)
# Connect to flickr, go go go
- self.flickr.authenticate_1().addCallbacks(self.auth_open_url, self.twisted_error)
+ deferred = self.flickr.authenticate_1()
+ deferred.addCallback(self.auth_open_url)
+ deferred.addErrback(self.twisted_error)
def open_uri(self, uri):
return self.send_message(self.commands['OPEN'], uri)
@@ -228,6 +232,9 @@ class Postr(UniqueApp):
dialog.set_from_failure(failure)
dialog.show_all()
+ log.err(failure, 'Exception in %s' % self.__gtype_name__)
+ return failure
+
def proxy_changed(self, client, cnxn_id, entry, something):
if client.get_bool("/system/http_proxy/use_http_proxy"):
host = client.get_string("/system/http_proxy/host")
@@ -274,7 +281,9 @@ class Postr(UniqueApp):
else:
dialog = AuthenticationDialog(self.window, state['url'])
if dialog.run() == Gtk.ResponseType.ACCEPT:
- self.flickr.authenticate_2(state).addCallbacks(self.connected, self.twisted_error)
+ deferred = self.flickr.authenticate_2(state)
+ deferred.addCallback(self.connected)
+ deferred.addErrback(self.twisted_error)
dialog.destroy()
def connected(self, connected):
@@ -320,18 +329,24 @@ class Postr(UniqueApp):
Update the avatar displayed at the top of the window. Called when
authentication is completed.
"""
- def getinfo_cb(rsp):
+ def getinfo_cb(rsp, user_id):
p = rsp.find("person")
data = {
- "nsid": self.flickr.get_nsid(),
+ "nsid": user_id,
"iconfarm": p.get("iconfarm"),
"iconserver": p.get("iconserver")
}
def get_buddyicon_cb(icon):
self.avatar_image.set_from_pixbuf(icon)
- get_buddyicon(self.flickr, data).addCallbacks(get_buddyicon_cb, self.twisted_error)
+ deferred = get_buddyicon(self.flickr, data)
+ deferred.addCallback(get_buddyicon_cb)
+ deferred.addErrback(log.err)
+
# Need to call people.getInfo to get the iconserver/iconfarm
- self.flickr.people_getInfo(user_id=self.flickr.get_nsid()).addCallbacks(getinfo_cb, self.twisted_error)
+ user_id = self.flickr.get_nsid()
+ deferred = self.flickr.people_getInfo(user_id=user_id)
+ deferred.addCallback(getinfo_cb, user_id)
+ deferred.addErrback(log.err)
def on_field_changed(self, widget, column):
"""Callback when the entry fields are changed."""
@@ -499,8 +514,7 @@ class Postr(UniqueApp):
for gfile in self.temporary_files:
gfile.delete(None)
- import twisted.internet.reactor
- twisted.internet.reactor.stop()
+ reactor.stop()
def on_save_session_activate(self, widget):
"""Callback from File->Save session."""
@@ -558,7 +572,9 @@ class Postr(UniqueApp):
def on_switch_activate(self, menuitem):
"""Callback from File->Switch User."""
self.flickr.clear_cached()
- self.flickr.authenticate_1().addCallbacks(self.auth_open_url, self.twisted_error)
+ deferred = self.flickr.authenticate_1()
+ deferred.addCallback(self.auth_open_url)
+ deferred.addErrback(self.twisted_error)
def on_upload_activate(self, menuitem):
"""Callback from File->Upload."""
@@ -1103,7 +1119,8 @@ class Postr(UniqueApp):
progress_tracker=self.upload_progress_tracker)
else:
print "No filename or pixbuf stored"
- except Gio.Error, (error):
+ except GLib.GError, e:
+ print e
# save the iterator and continue uploading process
self.list_failed_it.append(it)
self.current_upload_it = None
@@ -1112,29 +1129,34 @@ class Postr(UniqueApp):
if set_id:
d.addCallback(self.add_to_set, set_id)
+ d.addErrback(log.err)
else:
self.upload_progress_tracker.complete_extra_step(EXTRA_STEP_SET_ID)
if groups:
d.addCallback(self.add_to_groups, groups)
+ d.addErrback(log.err)
else:
self.upload_progress_tracker.complete_extra_step(EXTRA_STEP_GROUPS)
if license is not None: # 0 is a valid license.
d.addCallback(self.set_license, license)
+ d.addErrback(log.err)
else:
self.upload_progress_tracker.complete_extra_step(EXTRA_STEP_LICENSE)
# creating a new photoset has implications on subsequent uploads,
# so this has to finish before starting the next upload
if new_photoset_name:
d.addCallback(self.create_photoset_then_continue, new_photoset_name)
+ d.addErrback(log.err)
else:
d.addCallbacks(self.upload, self.upload_error)
+ d.addErrback(self.log.err)
self.upload_progress_tracker.complete_extra_step(EXTRA_STEP_NEW_SET)
def create_photoset_then_continue(self, rsp, photoset_name):
photo_id = rsp.find("photoid").text
- create_photoset = self.flickr.photosets_create(primary_photo_id=photo_id, title=photoset_name)
- create_photoset.addCallback(self._process_photoset_creation, photoset_name)
- create_photoset.addErrback(self.upload_error)
+ deferred = self.flickr.photosets_create(primary_photo_id=photo_id, title=photoset_name)
+ deferred.addCallback(self._process_photoset_creation, photoset_name)
+ deferred.addErrback(self.upload_error)
return rsp
def _process_photoset_creation(self, create_rsp, photoset_name):
diff --git a/src/util.py b/src/util.py
index 5974ae1..eed535a 100644
--- a/src/util.py
+++ b/src/util.py
@@ -18,6 +18,9 @@
import os
from gi.repository import Gtk, GdkPixbuf
import bsddb3
+from twisted.web.client import getPage
+from twisted.internet import defer
+from twisted.python import log
def greek(size):
"""Take a quantity (like 1873627) and display it in a human-readable rounded
@@ -72,7 +75,6 @@ __buddy_cache = None
def get_buddyicon(flickr, data, size=48):
"""Lookup the buddyicon from the data in @data using @flickr and resize it
to @size pixels."""
- from twisted.web.client import getPage
global __buddy_cache
if __buddy_cache is None:
@@ -104,11 +106,12 @@ def get_buddyicon(flickr, data, size=48):
url = "http://www.flickr.com/images/buddyicon.jpg"
if __buddy_cache.has_key(url):
- from twisted.internet import defer
- return defer.succeed(load_thumb(__buddy_cache[url], size))
+ return defer.execute(load_thumb, __buddy_cache[url], size)
else:
- return getPage(url).addCallback(got_data, url, size)
-
+ deferred = getPage(url)
+ deferred.addCallback(got_data, url, size)
+ deferred.addErrback(log.err)
+ return deferred
def get_cache_path():
"""Return the location of the XDG cache directory."""
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]