From ee4e91d31816918cba7c2744c01a2373dfde439a Mon Sep 17 00:00:00 2001 From: "Michael J. Cohen" Date: Tue, 3 Apr 2012 19:34:21 -0400 Subject: [PATCH 1/2] Default exception handler no longer assumes all remaining exceptions take (errno, string) pairs. Previously any exception that made it all the way up to the default exception handler would be expected to take (errno, string) pairs, as is the python standard for exceptions thrown by system calls. All exceptions that don't take enough arguments throw a ValueError. Based on the errno tested, it appears that this code is meant to silently ignore when a socket receives a SIGINT (from e.g. a timeout.) This seems to be the only instance where handling EINTR in this manner is desired - though having this bubble up this far seems odd. The existing code would also handle any other EINTR, though, which includes those raised by OSError, WindowsError, and anything that subclasses EnvironmentError, barring KeyboardError because it is handled separately. This is a bug as there is already some use of the signals module elsewhere in CouchPotato.py to trap SIGINT and SIGTERM outside of system calls, and most of these other EINTRs should be handled by code lower down the stack. A default exception handler is also added, so that unhandled exceptions will be logged, and raised. --- CouchPotato.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/CouchPotato.py b/CouchPotato.py index 3bc7916f..57f6461c 100755 --- a/CouchPotato.py +++ b/CouchPotato.py @@ -4,6 +4,7 @@ from os.path import dirname import logging import os import signal +import socket import subprocess import sys import traceback @@ -121,9 +122,20 @@ if __name__ == '__main__': pass except SystemExit: raise - except Exception as (nr, msg): + except socket.error as (nr, msg): + # log when socket receives SIGINT, but continue. + # previous code would have skipped over other types of IO errors too. if nr != 4: try: l.log.critical(traceback.format_exc()) except: print traceback.format_exc() + raise + except: + try: + # if this fails we will have two tracebacks + # one for failing to log, and one for the exception that got us here. + l.log.critical(traceback.format_exc()) + except: + print traceback.format_exc() + raise \ No newline at end of file From e99cf6757e31802e25b104c8b87f7b238fec147d Mon Sep 17 00:00:00 2001 From: "Michael J. Cohen" Date: Tue, 3 Apr 2012 20:35:25 -0400 Subject: [PATCH 2/2] Throw a useful error in the log when FileBrowser fails to load because of missing pywin32 This is a quick hack so that anyone else who runs this from source doesn't have to spend the time I spent tracking down why directory.list failed silently. There are two options that are much cleaner that come to mind: - Subclass ImportException so as to differentiate missing requirements from parse errors etc. - Provide a method for plugins to list their requirements, so that the loader can be the one to use imp.find_module(). Using imp.find_module() seems wise, either way. --- couchpotato/core/loader.py | 9 +++++++-- couchpotato/core/plugins/browser/main.py | 10 +++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/couchpotato/core/loader.py b/couchpotato/core/loader.py index a5816cc2..ee7afcb1 100644 --- a/couchpotato/core/loader.py +++ b/couchpotato/core/loader.py @@ -51,8 +51,13 @@ class Loader(object): did_save += self.loadSettings(m, module_name, save = False) self.loadPlugins(m, plugin.get('name')) - except ImportError: - log.debug('Import error, remove the empty folder: %s' % plugin.get('module')) + except ImportError as e: + # todo:: subclass ImportError for missing requirements. + if (e.message.lower().startswith("missing")): + log.error(e.message) + pass + # todo:: this needs to be more descriptive. + log.error('Import error, remove the empty folder: %s' % plugin.get('module')) except: log.error('Can\'t import %s: %s' % (module_name, traceback.format_exc())) diff --git a/couchpotato/core/plugins/browser/main.py b/couchpotato/core/plugins/browser/main.py index 21d3b4b7..887edc30 100644 --- a/couchpotato/core/plugins/browser/main.py +++ b/couchpotato/core/plugins/browser/main.py @@ -6,7 +6,15 @@ import os import string if os.name == 'nt': - import win32file + import imp + try: + imp.find_module('win32file') + except: + # todo:: subclass ImportError for missing dependencies, vs. broken plugins? + raise ImportError("Missing the win32file module, which is a part of the prerequisite \ + pywin32 package. You can get it from http://sourceforge.net/projects/pywin32/files/pywin32/"); + else: + import win32file class FileBrowser(Plugin):