← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~trb143/openlp/refactor into lp:openlp

 

Tim Bentley has proposed merging lp:~trb143/openlp/refactor into lp:openlp.

Requested reviews:
  Raoul Snyman (raoul-snyman)

For more details, see:
https://code.launchpad.net/~trb143/openlp/refactor/+merge/194022

This is work in progress and not complete.

This is a start to add trace functionality and clean up logging.

Move de_hump as it has more uses than theme!

add register decorator.

Add some mixins!
Add a common Error handling mixin
Change some invalid error handling as exception should be called only within a except block.

Registry needs to be moved and split up,  Need to agree direction before moving code.
-- 
https://code.launchpad.net/~trb143/openlp/refactor/+merge/194022
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/common/__init__.py'
--- openlp/core/common/__init__.py	2013-10-13 20:36:42 +0000
+++ openlp/core/common/__init__.py	2013-11-05 22:12:59 +0000
@@ -27,17 +27,20 @@
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
 """
-The :mod:`common` module contains most of the components and libraries that make
-OpenLP work.
+The :mod:`common` module contains most of the components and libraries that make OpenLP work.
 """
 import os
 import logging
+import re
 import sys
 
 from PyQt4 import QtCore
 
 log = logging.getLogger(__name__)
 
+FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
+SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])')
+
 
 def check_directory_exists(directory, do_not_log=False):
     """
@@ -94,6 +97,14 @@
     return qt_translate(context, text, comment, encoding, n)
 
 
+def de_hump(name):
+    """
+    Change any Camel Case string to python string
+    """
+    sub_name = FIRST_CAMEL_REGEX.sub(r"\1_\2", name)
+    return SECOND_CAMEL_REGEX.sub(r"\1_\2", sub_name).lower()
+
+
 class SlideLimits(object):
     """
     Provides an enumeration for behaviour of OpenLP at the end limits of each service item when pressing the up/down
@@ -103,7 +114,8 @@
     Wrap = 2
     Next = 3
 
+from .errormixin import ErrorMixin
+from .logger import trace
 from .uistrings import UiStrings
 from .settings import Settings
 from .applocation import AppLocation
-

=== added file 'openlp/core/common/errormixin.py'
--- openlp/core/common/errormixin.py	1970-01-01 00:00:00 +0000
+++ openlp/core/common/errormixin.py	2013-11-05 22:12:59 +0000
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+Provide Error Handling Services
+"""
+import traceback
+import logging
+
+
+class ErrorMixin(object):
+    """
+    Base Calling object for OpenLP classes.
+    """
+    def trace_error_handler(self):
+        """
+        Log the calling path of an exception
+        """
+        log = logging.getLogger(self.__module__)
+        for tb in traceback.extract_stack():
+            log.error('Called by ' + tb[3] + ' at line ' + str(tb[1]) + ' in ' + tb[0])
+
+    def log_error(self, message):
+        """
+        Common log exception handler which prints the calling path
+        """
+        log = logging.getLogger(self.__module__)
+        self.trace_error_handler()
+        log.error(message)
+

=== added file 'openlp/core/common/logger.py'
--- openlp/core/common/logger.py	1970-01-01 00:00:00 +0000
+++ openlp/core/common/logger.py	2013-11-05 22:12:59 +0000
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+This class contains the core logger functions.
+"""
+
+import inspect
+import logging
+
+# Events to never be logged.  Usually timer events which will flood the log!
+DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent']
+
+
+def log_wrapper(func):
+    """
+    Code to added debug wrapper to work on called functions within a decorated class.
+    """
+    def wrapped(*args, **kwargs):
+        log = logging.getLogger(args[0].__module__)
+        if log.getEffectiveLevel() == logging.DEBUG:
+            log.debug("Entering %s" % func.__name__)
+        try:
+            return func(*args, **kwargs)
+        except Exception as e:
+            if log.getEffectiveLevel() <= logging.ERROR:
+                log.error('Exception in %s : %s' % (func.__name__, e))
+            raise e
+    return wrapped
+
+
+def trace(cls):
+    """
+    Decorator to add log_wrapper to all methods in calls unless they are banned!
+    """
+    for name, m in inspect.getmembers(cls, inspect.isfunction):
+        if name not in DO_NOT_TRACE_EVENTS:
+            setattr(cls, name, log_wrapper(m))
+    return cls

=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2013-10-13 20:36:42 +0000
+++ openlp/core/lib/__init__.py	2013-11-05 22:12:59 +0000
@@ -329,7 +329,7 @@
         return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (string_list[0], merged)
 
 
-from .registry import Registry
+from .registry import Registry, RegistryMixin, RegistryApplicationMixin, register
 from .screen import ScreenList
 from .listwidgetwithdnd import ListWidgetWithDnD
 from .treewidgetwithdnd import TreeWidgetWithDnD

=== modified file 'openlp/core/lib/imagemanager.py'
--- openlp/core/lib/imagemanager.py	2013-08-31 18:17:38 +0000
+++ openlp/core/lib/imagemanager.py	2013-11-05 22:12:59 +0000
@@ -39,7 +39,8 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.lib import Registry, ScreenList, resize_image, image_to_byte
+from openlp.core.common import trace
+from openlp.core.lib import Registry, ScreenList, resize_image, image_to_byte, register
 
 log = logging.getLogger(__name__)
 
@@ -172,19 +173,18 @@
         if (image.priority, image.secondary_priority, image) in self.queue:
             self.queue.remove((image.priority, image.secondary_priority, image))
 
-
+@trace
 class ImageManager(QtCore.QObject):
     """
     Image Manager handles the conversion and sizing of images.
     """
     log.info('Image Manager loaded')
-
+    @register
     def __init__(self):
         """
         Constructor for the image manager.
         """
         super(ImageManager, self).__init__()
-        Registry().register('image_manager', self)
         current_screen = ScreenList().current
         self.width = current_screen['size'].width()
         self.height = current_screen['size'].height()
@@ -198,7 +198,6 @@
         """
         Screen has changed size so rebuild the cache to new size.
         """
-        log.debug('update_display')
         current_screen = ScreenList().current
         self.width = current_screen['size'].width()
         self.height = current_screen['size'].height()
@@ -210,7 +209,6 @@
         """
         Border has changed so update all the images affected.
         """
-        log.debug('update_images_border')
         # Mark the images as dirty for a rebuild by setting the image and byte stream to None.
         for image in list(self._cache.values()):
             if image.source == source:
@@ -221,7 +219,6 @@
         """
         Border has changed so update the image affected.
         """
-        log.debug('update_image_border')
         # Mark the image as dirty for a rebuild by setting the image and byte stream to None.
         image = self._cache[(path, source)]
         if image.source == source:
@@ -302,7 +299,6 @@
         """
         Controls the processing called from a ``QtCore.QThread``.
         """
-        log.debug('_process - started')
         while not self._conversion_queue.empty() and not self.stop_manager:
             self._process_cache()
         log.debug('_process - ended')
@@ -311,7 +307,6 @@
         """
         Actually does the work.
         """
-        log.debug('_processCache')
         image = self._conversion_queue.get()[2]
         # Generate the QImage for the image.
         if image.image is None:

=== modified file 'openlp/core/lib/pluginmanager.py'
--- openlp/core/lib/pluginmanager.py	2013-10-13 13:51:13 +0000
+++ openlp/core/lib/pluginmanager.py	2013-11-05 22:12:59 +0000
@@ -34,7 +34,7 @@
 import logging
 import imp
 
-from openlp.core.lib import Plugin, PluginStatus, Registry
+from openlp.core.lib import Plugin, PluginStatus, Registry, register
 from openlp.core.common import AppLocation
 
 log = logging.getLogger(__name__)
@@ -47,13 +47,13 @@
     """
     log.info('Plugin manager loaded')
 
+    @register
     def __init__(self):
         """
         The constructor for the plugin manager. Passes the controllers on to
         the plugins for them to interact with via their ServiceItems.
         """
         log.info('Plugin manager Initialising')
-        Registry().register('plugin_manager', self)
         Registry().register_function('bootstrap_initialise', self.bootstrap_initialise)
         self.base_path = os.path.abspath(AppLocation.get_directory(AppLocation.PluginsDir))
         log.debug('Base path %s ', self.base_path)

=== modified file 'openlp/core/lib/registry.py'
--- openlp/core/lib/registry.py	2013-08-31 18:17:38 +0000
+++ openlp/core/lib/registry.py	2013-11-05 22:12:59 +0000
@@ -31,11 +31,79 @@
 """
 import logging
 import sys
+import os
+
+from openlp.core.common import ErrorMixin, de_hump
 
 log = logging.getLogger(__name__)
 
 
-class Registry(object):
+def register(func):
+    """
+    Code to register the class in the registry.
+    """
+    def wrapper(*args, **kwargs):
+        Registry().register(de_hump(args[0].__class__.__name__), args[0])
+        ret = func(*args, **kwargs)
+        return ret
+    return wrapper
+
+
+class RegistryApplicationMixin(ErrorMixin):
+    """
+    This adds registry components to classes to use at run time.
+    """
+    def _get_application(self):
+        """
+        Adds the openlp to the class dynamically.
+        Windows needs to access the application in a dynamic manner.
+        """
+        if os.name == 'nt':
+            return Registry().get('application')
+        else:
+            if not hasattr(self, '_application'):
+                self._application = Registry().get('application')
+            return self._application
+
+    application = property(_get_application)
+
+
+class RegistryMixin(RegistryApplicationMixin):
+    """
+    This adds registry components to classes to use at run time.
+    """
+    def _get_main_window(self):
+        """
+        Adds the main window to the class dynamically
+        """
+        if not hasattr(self, '_main_window'):
+            self._main_window = Registry().get('main_window')
+        return self._main_window
+
+    main_window = property(_get_main_window)
+
+    def _get_service_manager(self):
+        """
+        Adds the plugin manager to the class dynamically
+        """
+        if not hasattr(self, '_service_manager'):
+            self._service_manager = Registry().get('service_manager')
+        return self._service_manager
+
+    service_manager = property(_get_service_manager)
+
+    def _get_plugin_manager(self):
+        """
+        Adds the plugin manager to the class dynamically
+        """
+        if not hasattr(self, '_plugin_manager'):
+            self._plugin_manager = Registry().get('plugin_manager')
+        return self._plugin_manager
+
+    plugin_manager = property(_get_plugin_manager)
+
+
+class Registry(ErrorMixin):
     """
     This is the Component Registry.  It is a singleton object and is used to provide a look up service for common
     objects.
@@ -76,7 +144,7 @@
         if key in self.service_list:
             return self.service_list[key]
         else:
-            log.error('Service %s not found in list' % key)
+            self.log_error('Service %s not found in list' % key)
             raise KeyError('Service %s not found in list' % key)
 
     def register(self, key, reference):
@@ -90,7 +158,7 @@
             The service address to be saved.
         """
         if key in self.service_list:
-            log.error('Duplicate service exception %s' % key)
+            self.log_error('Duplicate service exception %s' % key)
             raise KeyError('Duplicate service exception %s' % key)
         else:
             self.service_list[key] = reference
@@ -134,7 +202,7 @@
             The function to be called when the event happens.
         """
         if self.running_under_test is False:
-            log.error('Invalid Method call for key %s' % event)
+            self.log_error('Invalid Method call for key %s' % event)
             raise KeyError('Invalid Method call for key %s' % event)
         if event in self.functions_list:
             self.functions_list[event].remove(function)
@@ -161,7 +229,8 @@
                         results.append(result)
                 except TypeError:
                     # Who has called me can help in debugging
-                    import inspect
-                    log.debug(inspect.currentframe().f_back.f_locals)
+                    self.trace_error_handler()
                     log.exception('Exception for function %s', function)
+        else:
+            self.log_error("Event %s not found" % event)
         return results

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2013-10-13 20:36:42 +0000
+++ openlp/core/lib/renderer.py	2013-11-05 22:12:59 +0000
@@ -33,7 +33,7 @@
 
 from openlp.core.common import Settings
 from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, Registry, ScreenList, \
-    ServiceItem, expand_tags, build_lyrics_format_css, build_lyrics_outline_css
+    ServiceItem, expand_tags, build_lyrics_format_css, build_lyrics_outline_css, register
 from openlp.core.common import ThemeLevel
 from openlp.core.ui import MainDisplay
 
@@ -57,13 +57,13 @@
     """
     log.info('Renderer Loaded')
 
+    @register
     def __init__(self):
         """
         Initialise the renderer.
         """
         log.debug('Initialisation started')
         self.screens = ScreenList()
-        Registry().register('renderer', self)
         self.theme_level = ThemeLevel.Global
         self.global_theme_name = ''
         self.service_theme_name = ''

=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py	2013-10-18 18:10:47 +0000
+++ openlp/core/lib/theme.py	2013-11-05 22:12:59 +0000
@@ -36,7 +36,7 @@
 
 from xml.dom.minidom import Document
 from lxml import etree, objectify
-from openlp.core.common import AppLocation
+from openlp.core.common import AppLocation, de_hump
 
 from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string
 
@@ -157,9 +157,6 @@
     """
     A class to encapsulate the Theme XML.
     """
-    FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
-    SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])')
-
     def __init__(self):
         """
         Initialise the theme object.
@@ -532,7 +529,7 @@
         reject, master, element, value = self._translate_tags(master, element, value)
         if reject:
             return
-        field = self._de_hump(element)
+        field = de_hump(element)
         tag = master + '_' + field
         if field in BOOLEAN_LIST:
             setattr(self, tag, str_to_bool(value))
@@ -557,13 +554,6 @@
                 theme_strings.append('%30s: %s' % (key, getattr(self, key)))
         return '\n'.join(theme_strings)
 
-    def _de_hump(self, name):
-        """
-        Change Camel Case string to python string
-        """
-        sub_name = ThemeXML.FIRST_CAMEL_REGEX.sub(r'\1_\2', name)
-        return ThemeXML.SECOND_CAMEL_REGEX.sub(r'\1_\2', sub_name).lower()
-
     def _build_xml_from_attrs(self):
         """
         Build the XML from the varables in the object

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2013-10-13 21:07:28 +0000
+++ openlp/core/ui/mainwindow.py	2013-11-05 22:12:59 +0000
@@ -42,12 +42,12 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, Registry, \
-    ScreenList, build_icon
+    RegistryApplicationMixin, ScreenList, build_icon, register
 from openlp.core.lib.ui import UiStrings, create_action
 from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, SlideController, PluginForm, \
     MediaDockManager, ShortcutListForm, FormattingTagForm
 
-from openlp.core.common import AppLocation, Settings, check_directory_exists, translate
+from openlp.core.common import AppLocation, Settings, check_directory_exists, translate, trace
 from openlp.core.ui.media import MediaController
 from openlp.core.utils import LanguageManager, add_actions, get_application_version
 from openlp.core.utils.actions import ActionList, CategoryOrder
@@ -467,18 +467,17 @@
         self.mode_live_item.setStatusTip(translate('OpenLP.MainWindow', 'Set the view mode to Live.'))
 
 
-class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
+@trace
+class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryApplicationMixin):
     """
     The main window.
     """
-    log.info('MainWindow loaded')
-
+    @register
     def __init__(self):
         """
         This constructor sets up the interface, the various managers, and the plugins.
         """
         super(MainWindow, self).__init__()
-        Registry().register('main_window', self)
         self.clipboard = self.application.clipboard()
         self.arguments = self.application.args
         # Set up settings sections for the main application (not for use by plugins).
@@ -551,7 +550,6 @@
         """
         Called on start up to restore the last active media plugin.
         """
-        log.info('Load data from Settings')
         if Settings().value('advanced/save current plugin'):
             saved_plugin_id = Settings().value('advanced/current media plugin')
             if saved_plugin_id != -1:
@@ -581,9 +579,9 @@
         Notifies the user that a newer version of OpenLP is available.
         Triggered by delay thread and cannot display popup.
         """
-        log.debug('version_notice')
         version_text = translate('OpenLP.MainWindow', 'Version %s of OpenLP is now available for download (you are '
-            'currently running version %s). \n\nYou can download the latest version from http://openlp.org/.')
+                                 'currently running version %s). \n\nYou can download the latest version from '
+                                 'http://openlp.org/.')
         self.version_text = version_text % (version, get_application_version()['full'])
 
     def show(self):
@@ -756,6 +754,7 @@
         """
         Show the About form
         """
+        raise Exception('moo!')
         self.about_form.exec_()
 
     def on_plugin_item_clicked(self):
@@ -998,7 +997,6 @@
         """
         The screen has changed so we have to update components such as the renderer.
         """
-        log.debug('screen_changed')
         self.application.set_busy_cursor()
         self.image_manager.update_display()
         self.renderer.update_display()
@@ -1065,7 +1063,7 @@
             if Settings().value('advanced/save current plugin'):
                 Settings().setValue('advanced/current media plugin', self.media_tool_box.currentIndex())
         # Call the cleanup method to shutdown plugins.
-        log.info('cleanup plugins')
+        log.info('Cleanup plugins')
         self.plugin_manager.finalise_plugins()
         if save_settings:
             # Save settings
@@ -1108,7 +1106,7 @@
         Update the default theme indicator in the status bar
         """
         self.default_theme_label.setText(translate('OpenLP.MainWindow', 'Default Theme: %s') %
-            Settings().value('themes/global theme'))
+                                         Settings().value('themes/global theme'))
 
     def toggle_media_manager(self):
         """
@@ -1182,7 +1180,6 @@
         """
         Load the main window settings.
         """
-        log.debug('Loading QSettings')
         settings = Settings()
         # Remove obsolete entries.
         settings.remove('custom slide')
@@ -1209,7 +1206,6 @@
         # Exit if we just did a settings import.
         if self.settingsImported:
             return
-        log.debug('Saving QSettings')
         settings = Settings()
         settings.beginGroup(self.general_settings_section)
         settings.setValue('recent files', self.recent_files)
@@ -1349,7 +1345,7 @@
                 log.info('Copy sucessful')
             except (IOError, os.error, DistutilsFileError) as why:
                 self.application.set_normal_cursor()
-                log.exception('Data copy failed %s' % str(why))
+                log.error('Data copy failed %s' % str(why))
                 QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'New Data Directory Error'),
                     translate('OpenLP.MainWindow',
                         'OpenLP Data directory copy failed\n\n%s').replace('%s', str(why)),
@@ -1364,17 +1360,3 @@
         if self.new_data_path == AppLocation.get_directory(AppLocation.DataDir):
             settings.remove('advanced/data path')
         self.application.set_normal_cursor()
-
-    def _get_application(self):
-        """
-        Adds the openlp to the class dynamically.
-        Windows needs to access the application in a dynamic manner.
-        """
-        if os.name == 'nt':
-            return Registry().get('application')
-        else:
-            if not hasattr(self, '_application'):
-                self._application = Registry().get('application')
-            return self._application
-
-    application = property(_get_application)

=== modified file 'openlp/core/ui/media/mediacontroller.py'
--- openlp/core/ui/media/mediacontroller.py	2013-10-13 20:36:42 +0000
+++ openlp/core/ui/media/mediacontroller.py	2013-11-05 22:12:59 +0000
@@ -36,7 +36,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import OpenLPToolbar, Registry
+from openlp.core.lib import OpenLPToolbar, Registry, register
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players
 from openlp.core.ui.media.mediaplayer import MediaPlayer
@@ -94,11 +94,11 @@
     current_media_players is an array of player instances keyed on ControllerType.
 
     """
+    @register
     def __init__(self):
         """
         Constructor
         """
-        Registry().register('media_controller', self)
         Registry().register_function('bootstrap_initialise', self.check_available_media_players)
         self.media_players = {}
         self.display_controllers = {}

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2013-10-13 21:07:28 +0000
+++ openlp/core/ui/servicemanager.py	2013-11-05 22:12:59 +0000
@@ -43,7 +43,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.common import AppLocation, Settings, ThemeLevel, check_directory_exists, UiStrings, translate
-from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, Registry, build_icon
+from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, Registry, build_icon, register
 from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
 from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
 from openlp.core.ui.printserviceform import PrintServiceForm
@@ -289,6 +289,7 @@
     can then be zipped up with all the resources used into one OSZ or oszl file for use on any OpenLP v2 installation.
     Also handles the UI tasks of moving things up and down etc.
     """
+    @register
     def __init__(self, parent=None):
         """
         Sets up the service manager, toolbars, list view, et al.
@@ -296,7 +297,6 @@
         super(ServiceManager, self).__init__(parent)
         self.active = build_icon(':/media/auto-start_active.png')
         self.inactive = build_icon(':/media/auto-start_inactive.png')
-        Registry().register('service_manager', self)
         self.service_items = []
         self.suffixes = []
         self.drop_position = 0
@@ -730,7 +730,7 @@
                 Settings().setValue('servicemanager/last file', file_name)
             else:
                 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
-                log.exception('File contains no service data')
+                log.error('File contains no service data')
         except (IOError, NameError, zipfile.BadZipfile):
             log.exception('Problem loading service file %s' % file_name)
             critical_error_message_box(message=translate('OpenLP.ServiceManager',

=== modified file 'openlp/core/ui/settingsform.py'
--- openlp/core/ui/settingsform.py	2013-08-31 18:17:38 +0000
+++ openlp/core/ui/settingsform.py	2013-11-05 22:12:59 +0000
@@ -33,7 +33,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import PluginStatus, Registry, build_icon
+from openlp.core.lib import PluginStatus, Registry, RegistryMixin, build_icon, register
 from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
 from openlp.core.ui.media import PlayerTab
 from .settingsdialog import Ui_SettingsDialog
@@ -41,15 +41,15 @@
 log = logging.getLogger(__name__)
 
 
-class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
+class SettingsForm(QtGui.QDialog, Ui_SettingsDialog, RegistryMixin):
     """
     Provide the form to manipulate the settings for OpenLP
     """
+    @register
     def __init__(self, parent=None):
         """
         Initialise the settings form
         """
-        Registry().register('settings_form', self)
         Registry().register_function('bootstrap_post_set_up', self.post_set_up)
         super(SettingsForm, self).__init__(parent)
         self.processes = []
@@ -153,32 +153,4 @@
         if not function in self.processes:
             self.processes.append(function)
 
-    def _get_main_window(self):
-        """
-        Adds the main window to the class dynamically
-        """
-        if not hasattr(self, '_main_window'):
-            self._main_window = Registry().get('main_window')
-        return self._main_window
-
-    main_window = property(_get_main_window)
-
-    def _get_service_manager(self):
-        """
-        Adds the plugin manager to the class dynamically
-        """
-        if not hasattr(self, '_service_manager'):
-            self._service_manager = Registry().get('service_manager')
-        return self._service_manager
-
-    service_manager = property(_get_service_manager)
-
-    def _get_plugin_manager(self):
-        """
-        Adds the plugin manager to the class dynamically
-        """
-        if not hasattr(self, '_plugin_manager'):
-            self._plugin_manager = Registry().get('plugin_manager')
-        return self._plugin_manager
-
-    plugin_manager = property(_get_plugin_manager)
+

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2013-10-13 21:07:28 +0000
+++ openlp/core/ui/thememanager.py	2013-11-05 22:12:59 +0000
@@ -40,7 +40,7 @@
 
 from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate
 from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, get_text_file_string, build_icon, \
-    check_item_selected, create_thumb, validate_thumb
+    check_item_selected, create_thumb, validate_thumb, register
 from openlp.core.lib.theme import ThemeXML, BackgroundType
 from openlp.core.lib.ui import critical_error_message_box, create_widget_action
 from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper
@@ -53,12 +53,12 @@
     """
     Manages the orders of Theme.
     """
+    @register
     def __init__(self, parent=None):
         """
         Constructor
         """
         super(ThemeManager, self).__init__(parent)
-        Registry().register('theme_manager', self)
         Registry().register_function('bootstrap_initialise', self.load_first_time_themes)
         Registry().register_function('bootstrap_post_set_up', self._push_themes)
         self.settings_section = 'themes'
@@ -498,8 +498,8 @@
             theme_zip = zipfile.ZipFile(file_name)
             xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
             if len(xml_file) != 1:
-                log.exception('Theme contains "%s" XML files' % len(xml_file))
-                raise Exception('validation')
+                log.error('Theme contains "%s" XML files' % len(xml_file))
+                raise Exception('Theme validation error')
             xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
             theme_name = xml_tree.find('name').text.strip()
             theme_folder = os.path.join(directory, theme_name)
@@ -551,7 +551,7 @@
                     critical_error_message_box(
                         translate('OpenLP.ThemeManager', 'Validation Error'),
                         translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
-                    log.exception('Theme file does not contain XML data %s' % file_name)
+                    log.error('Theme file does not contain XML data %s' % file_name)
 
     def check_if_theme_exists(self, theme_name):
         """

=== modified file 'openlp/plugins/alerts/lib/alertsmanager.py'
--- openlp/plugins/alerts/lib/alertsmanager.py	2013-10-13 21:07:28 +0000
+++ openlp/plugins/alerts/lib/alertsmanager.py	2013-11-05 22:12:59 +0000
@@ -36,7 +36,7 @@
 from PyQt4 import QtCore
 
 from openlp.core.common import translate
-from openlp.core.lib import Registry
+from openlp.core.lib import Registry, register
 
 
 log = logging.getLogger(__name__)
@@ -48,9 +48,9 @@
     """
     log.info('Alert Manager loaded')
 
+    @register
     def __init__(self, parent):
         super(AlertsManager, self).__init__(parent)
-        Registry().register('alerts_manager', self)
         self.timer_id = 0
         self.alert_list = []
         Registry().register_function('live_display_active', self.generate_alert)

=== modified file 'openlp/plugins/bibles/lib/csvbible.py'
--- openlp/plugins/bibles/lib/csvbible.py	2013-10-13 21:07:28 +0000
+++ openlp/plugins/bibles/lib/csvbible.py	2013-11-05 22:12:59 +0000
@@ -93,7 +93,7 @@
         success = True
         language_id = self.get_language(bible_name)
         if not language_id:
-            log.exception('Importing books from "%s" failed' % self.filename)
+            log.error('Importing books from "%s" failed' % self.filename)
             return False
         books_file = None
         book_list = {}
@@ -112,7 +112,7 @@
                     str(line[2], details['encoding']))
                 book_ref_id = self.get_book_ref_id_by_name(str(line[2], details['encoding']), 67, language_id)
                 if not book_ref_id:
-                    log.exception('Importing books from "%s" failed' % self.booksfile)
+                    log.error('Importing books from "%s" failed' % self.booksfile)
                     return False
                 book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
                 self.create_book(str(line[2], details['encoding']), book_ref_id, book_details['testament_id'])

=== modified file 'openlp/plugins/bibles/lib/http.py'
--- openlp/plugins/bibles/lib/http.py	2013-10-13 21:07:28 +0000
+++ openlp/plugins/bibles/lib/http.py	2013-11-05 22:12:59 +0000
@@ -553,7 +553,7 @@
             handler = BSExtract(self.proxy_server)
         books = handler.get_books_from_http(self.download_name)
         if not books:
-            log.exception('Importing books from %s - download name: "%s" '\
+            log.error('Importing books from %s - download name: "%s" '\
                 'failed' % (self.download_source, self.download_name))
             return False
         self.wizard.progress_bar.setMaximum(len(books) + 2)
@@ -565,7 +565,7 @@
         else:
             language_id = self.get_language(bible_name)
         if not language_id:
-            log.exception('Importing books from %s failed' % self.filename)
+            log.error('Importing books from %s failed' % self.filename)
             return False
         for book in books:
             if self.stop_import_flag:
@@ -574,7 +574,7 @@
                 'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing <book name>...') % book)
             book_ref_id = self.get_book_ref_id_by_name(book, len(books), language_id)
             if not book_ref_id:
-                log.exception('Importing books from %s - download name: "%s" '\
+                log.error('Importing books from %s - download name: "%s" '\
                     'failed' % (self.download_source, self.download_name))
                 return False
             book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)

=== modified file 'openlp/plugins/bibles/lib/opensong.py'
--- openlp/plugins/bibles/lib/opensong.py	2013-10-13 21:07:28 +0000
+++ openlp/plugins/bibles/lib/opensong.py	2013-11-05 22:12:59 +0000
@@ -84,14 +84,14 @@
             bible = opensong.getroot()
             language_id = self.get_language(bible_name)
             if not language_id:
-                log.exception('Importing books from "%s" failed' % self.filename)
+                log.error('Importing books from "%s" failed' % self.filename)
                 return False
             for book in bible.b:
                 if self.stop_import_flag:
                     break
                 book_ref_id = self.get_book_ref_id_by_name(str(book.attrib['n']), len(bible.b), language_id)
                 if not book_ref_id:
-                    log.exception('Importing books from "%s" failed' % self.filename)
+                    log.error('Importing books from "%s" failed' % self.filename)
                     return False
                 book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
                 db_book = self.create_book(str(book.attrib['n']), book_ref_id, book_details['testament_id'])

=== modified file 'openlp/plugins/bibles/lib/osis.py'
--- openlp/plugins/bibles/lib/osis.py	2013-10-13 21:07:28 +0000
+++ openlp/plugins/bibles/lib/osis.py	2013-11-05 22:12:59 +0000
@@ -131,7 +131,7 @@
                     if not language_id:
                         language_id = self.get_language(bible_name)
                         if not language_id:
-                            log.exception('Importing books from "%s" failed' % self.filename)
+                            log.error('Importing books from "%s" failed' % self.filename)
                             return False
                     match_count += 1
                     book = str(match.group(1))
@@ -140,7 +140,7 @@
                     verse_text = match.group(4)
                     book_ref_id = self.get_book_ref_id_by_name(book, book_count, language_id)
                     if not book_ref_id:
-                        log.exception('Importing books from "%s" failed' % self.filename)
+                        log.error('Importing books from "%s" failed' % self.filename)
                         return False
                     book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
                     if not db_book or db_book.name != book_details['name']:

=== modified file 'openlp/plugins/songs/lib/importer.py'
--- openlp/plugins/songs/lib/importer.py	2013-10-13 20:36:42 +0000
+++ openlp/plugins/songs/lib/importer.py	2013-11-05 22:12:59 +0000
@@ -82,6 +82,7 @@
     except ImportError:
         log.exception('Error importing %s', 'WorshipCenterProImport')
 
+
 class SongFormatSelect(object):
     """
     This is a special enumeration class listing available file selection modes.

=== modified file 'openlp/plugins/songs/lib/oooimport.py'
--- openlp/plugins/songs/lib/oooimport.py	2013-10-13 20:36:42 +0000
+++ openlp/plugins/songs/lib/oooimport.py	2013-11-05 22:12:59 +0000
@@ -127,7 +127,7 @@
                     manager = uno_instance.ServiceManager
                     self.desktop = manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
                     return
-            raise
+            raise Exception("Unable to start LibreOffice")
 
     def startOooProcess(self):
         try:

=== modified file 'tests/functional/openlp_core_common/__init__.py'
--- tests/functional/openlp_core_common/__init__.py	2013-10-13 17:02:12 +0000
+++ tests/functional/openlp_core_common/__init__.py	2013-11-05 22:12:59 +0000
@@ -1,1 +0,0 @@
-__author__ = 'tim'

=== modified file 'tests/functional/openlp_core_common/test_applocation.py'
--- tests/functional/openlp_core_common/test_applocation.py	2013-10-14 05:01:01 +0000
+++ tests/functional/openlp_core_common/test_applocation.py	2013-11-05 22:12:59 +0000
@@ -59,7 +59,6 @@
 
             # WHEN: we call AppLocation.get_data_path()
             data_path = AppLocation.get_data_path()
-            print(data_path)
 
             # THEN: check that all the correct methods were called, and the result is correct
             mocked_settings.contains.assert_called_with('advanced/data path')

=== added file 'tests/functional/openlp_core_common/test_init.py'
--- tests/functional/openlp_core_common/test_init.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_core_common/test_init.py	2013-11-05 22:12:59 +0000
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
+# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
+# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
+# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
+# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
+# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+Functional tests to test the AppLocation class and related methods.
+"""
+
+from unittest import TestCase
+
+from openlp.core.common import de_hump
+
+class TestInitFunctions(TestCase):
+    """
+    A test suite to test out various functions in the __init__ class.
+    """
+    def de_hump_conversion_test(self):
+        """
+        Test the de_hump function with a class name
+        """
+        # GIVEN: a Class name in Camel Case
+        string = "MyClass"
+
+        # WHEN: we call de_hump
+        new_string = de_hump(string)
+
+        # THEN: the new string should be converted to python format
+        self.assertTrue(new_string == "my_class", 'The class name should have been converted')
+
+    def de_hump_static_test(self):
+        """
+        Test the de_hump function with a python string
+        """
+        # GIVEN: a Class name in Camel Case
+        string = "my_class"
+
+        # WHEN: we call de_hump
+        new_string = de_hump(string)
+
+        # THEN: the new string should be converted to python format
+        self.assertTrue(new_string == "my_class", 'The class name should have been preserved')

=== modified file 'tests/functional/openlp_core_lib/test_registry.py'
--- tests/functional/openlp_core_lib/test_registry.py	2013-10-02 21:37:00 +0000
+++ tests/functional/openlp_core_lib/test_registry.py	2013-11-05 22:12:59 +0000
@@ -99,10 +99,10 @@
 
         # WHEN: I execute the a 2nd function with the different reference and execute the function
         Registry().register_function('test2', self.dummy_function_2)
-        return_value = Registry().execute('test2')
+        return_value = Registry().execute('test2a')
 
         # THEN: I expect then function to have been called and a return given
-        self.assertEqual(return_value[0], 'function_2', 'A return value is provided and matches')
+        self.assertEqual(return_value[0], 'function_2', 'A return value should be returned and provides a matche')
 
     def dummy_function_1(self):
         return "function_1"