← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~phill-ridout/openlp/fixes-mkII into lp:openlp

 

Phill has proposed merging lp:~phill-ridout/openlp/fixes-mkII into lp:openlp.

Requested reviews:
  Tim Bentley (trb143)
Related bugs:
  Bug #1400415 in OpenLP: "Multiple exceptions merged into OSError"
  https://bugs.launchpad.net/openlp/+bug/1400415
  Bug #1532193 in OpenLP: "Typos in songusageplugin.py"
  https://bugs.launchpad.net/openlp/+bug/1532193
  Bug #1660473 in OpenLP: "OSZL is ignored on save (inconsistent gui)"
  https://bugs.launchpad.net/openlp/+bug/1660473
  Bug #1660478 in OpenLP: "Opening recent file does not prompt to save changes"
  https://bugs.launchpad.net/openlp/+bug/1660478
  Bug #1660486 in OpenLP: "Dragging item in service manager without changes triggeres "unsaved""
  https://bugs.launchpad.net/openlp/+bug/1660486
  Bug #1661416 in OpenLP: "Initial "extract song usage data" produces a traceback"
  https://bugs.launchpad.net/openlp/+bug/1661416
  Bug #1672229 in OpenLP: "Media Library duplicates on boot and when a new item is added"
  https://bugs.launchpad.net/openlp/+bug/1672229
  Bug #1698021 in OpenLP: "Copying and Pasting from Word inserts invalid characters"
  https://bugs.launchpad.net/openlp/+bug/1698021
  Bug #1715125 in OpenLP: "Missing .osz file extension on save service"
  https://bugs.launchpad.net/openlp/+bug/1715125
  Bug #1727517 in OpenLP: "Unicode control chars causes song importer to crash"
  https://bugs.launchpad.net/openlp/+bug/1727517

For more details, see:
https://code.launchpad.net/~phill-ridout/openlp/fixes-mkII/+merge/333570

Fixed a number of bugs, and tests.

Failing on Code Analysis2, but this looks like fallout from the refactors (it hasn't passed since the beginning of october)

Also contains superflys branch lp:~raoul-snyman/openlp/fix-linting

lp:~phill-ridout/openlp/fixes-mkII (revision 2793)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/2275/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/2177/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/2058/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1389/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1214/
[SUCCESS] https://ci.openlp.io/job/Branch-04c-Code_Analysis2/344/
[FAILURE] https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/178/
Stopping after failure
-- 
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/common/__init__.py'
--- openlp/core/common/__init__.py	2017-10-07 07:05:07 +0000
+++ openlp/core/common/__init__.py	2017-11-10 21:03:15 +0000
@@ -43,9 +43,13 @@
 
 FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
 SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])')
-CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE)
-INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE)
+CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]')
+INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]')
 IMAGES_FILTER = None
+REPLACMENT_CHARS_MAP = str.maketrans({'\u2018': '\'', '\u2019': '\'', '\u201c': '"', '\u201d': '"', '\u2026': '...',
+                                      '\u2013': '-', '\u2014': '-', '\v': '\n\n', '\f': '\n\n'})
+NEW_LINE_REGEX = re.compile(r' ?(\r\n?|\n) ?')
+WHITESPACE_REGEX = re.compile(r'[ \t]+')
 
 
 def trace_error_handler(logger):
@@ -339,7 +343,7 @@
         if file_path.exists():
             file_path.unlink()
         return True
-    except (IOError, OSError):
+    except OSError:
         log.exception('Unable to delete file {file_path}'.format(file_path=file_path))
         return False
 
@@ -436,3 +440,17 @@
         return detector.result
     except OSError:
         log.exception('Error detecting file encoding')
+
+
+def normalize_str(irreg_str):
+    """
+    Normalize the supplied string. Remove unicode control chars and tidy up white space.
+
+    :param str irreg_str: The string to normalize.
+    :return: The normalized string
+    :rtype: str
+    """
+    irreg_str = irreg_str.translate(REPLACMENT_CHARS_MAP)
+    irreg_str = CONTROL_CHARS.sub('', irreg_str)
+    irreg_str = NEW_LINE_REGEX.sub('\n', irreg_str)
+    return WHITESPACE_REGEX.sub(' ', irreg_str)

=== modified file 'openlp/core/common/applocation.py'
--- openlp/core/common/applocation.py	2017-10-07 07:05:07 +0000
+++ openlp/core/common/applocation.py	2017-11-10 21:03:15 +0000
@@ -83,7 +83,7 @@
         """
         # Check if we have a different data location.
         if Settings().contains('advanced/data path'):
-            path = Settings().value('advanced/data path')
+            path = Path(Settings().value('advanced/data path'))
         else:
             path = AppLocation.get_directory(AppLocation.DataDir)
             create_paths(path)

=== modified file 'openlp/core/common/httputils.py'
--- openlp/core/common/httputils.py	2017-10-07 07:05:07 +0000
+++ openlp/core/common/httputils.py	2017-11-10 21:03:15 +0000
@@ -97,8 +97,8 @@
             response = requests.get(url, headers=headers, proxies=proxies, timeout=float(CONNECTION_TIMEOUT))
             log.debug('Downloaded page {url}'.format(url=response.url))
             break
-        except IOError:
-            # For now, catch IOError. All requests errors inherit from IOError
+        except OSError:
+            # For now, catch OSError. All requests errors inherit from OSError
             log.exception('Unable to connect to {url}'.format(url=url))
             response = None
             if retries >= CONNECTION_RETRIES:
@@ -127,7 +127,7 @@
         try:
             response = requests.head(url, timeout=float(CONNECTION_TIMEOUT), allow_redirects=True)
             return int(response.headers['Content-Length'])
-        except IOError:
+        except OSError:
             if retries > CONNECTION_RETRIES:
                 raise ConnectionError('Unable to download {url}'.format(url=url))
             else:
@@ -173,7 +173,7 @@
                     file_path.unlink()
                 return False
             break
-        except IOError:
+        except OSError:
             trace_error_handler(log)
             if retries > CONNECTION_RETRIES:
                 if file_path.exists():

=== modified file 'openlp/core/common/i18n.py'
--- openlp/core/common/i18n.py	2017-10-07 07:05:07 +0000
+++ openlp/core/common/i18n.py	2017-11-10 21:03:15 +0000
@@ -53,7 +53,7 @@
 
 Language = namedtuple('Language', ['id', 'name', 'code'])
 ICU_COLLATOR = None
-DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+', re.UNICODE)
+DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+')
 LANGUAGES = sorted([
     Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),
     Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),

=== modified file 'openlp/core/common/mixins.py'
--- openlp/core/common/mixins.py	2017-10-23 22:09:57 +0000
+++ openlp/core/common/mixins.py	2017-11-10 21:03:15 +0000
@@ -101,6 +101,20 @@
     """
     This adds registry components to classes to use at run time.
     """
+    _application = None
+    _plugin_manager = None
+    _image_manager = None
+    _media_controller = None
+    _service_manager = None
+    _preview_controller = None
+    _live_controller = None
+    _main_window = None
+    _renderer = None
+    _theme_manager = None
+    _settings_form = None
+    _alerts_manager = None
+    _projector_manager = None
+
     @property
     def application(self):
         """

=== modified file 'openlp/core/common/path.py'
--- openlp/core/common/path.py	2017-10-07 07:05:07 +0000
+++ openlp/core/common/path.py	2017-11-10 21:03:15 +0000
@@ -233,7 +233,7 @@
         try:
             if not path.exists():
                 path.mkdir(parents=True)
-        except IOError:
+        except OSError:
             if not kwargs.get('do_not_log', False):
                 log.exception('failed to check if directory exists or create directory')
 

=== modified file 'openlp/core/common/settings.py'
--- openlp/core/common/settings.py	2017-10-07 07:05:07 +0000
+++ openlp/core/common/settings.py	2017-11-10 21:03:15 +0000
@@ -258,6 +258,12 @@
         ('media/last directory', 'media/last directory', [(str_to_path, None)])
     ]
 
+    __setting_upgrade_3__ = [
+        ('songuasge/db password', 'songusage/db password', []),
+        ('songuasge/db hostname', 'songusage/db hostname', []),
+        ('songuasge/db database', 'songusage/db database', [])
+    ]
+
     @staticmethod
     def extend_default_settings(default_values):
         """

=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2017-10-10 07:08:44 +0000
+++ openlp/core/lib/__init__.py	2017-11-10 21:03:15 +0000
@@ -104,7 +104,7 @@
                 # no BOM was found
                 file_handle.seek(0)
             content = file_handle.read()
-    except (IOError, UnicodeError):
+    except (OSError, UnicodeError):
         log.exception('Failed to open text file {text}'.format(text=text_file_path))
     return content
 

=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py	2017-10-23 22:09:57 +0000
+++ openlp/core/lib/mediamanageritem.py	2017-11-10 21:03:15 +0000
@@ -92,7 +92,7 @@
         Run some initial setup. This method is separate from __init__ in order to mock it out in tests.
         """
         self.hide()
-        self.whitespace = re.compile(r'[\W_]+', re.UNICODE)
+        self.whitespace = re.compile(r'[\W_]+')
         visible_title = self.plugin.get_string(StringContent.VisibleName)
         self.title = str(visible_title['title'])
         Registry().register(self.plugin.name, self)
@@ -344,7 +344,9 @@
             else:
                 new_files.append(file_name)
         if new_files:
-            self.validate_and_load(new_files, data['target'])
+            if 'target' in data:
+                self.validate_and_load(new_files, data['target'])
+            self.validate_and_load(new_files)
 
     def dnd_move_internal(self, target):
         """

=== modified file 'openlp/core/ui/exceptionform.py'
--- openlp/core/ui/exceptionform.py	2017-10-23 22:09:57 +0000
+++ openlp/core/ui/exceptionform.py	2017-11-10 21:03:15 +0000
@@ -155,7 +155,7 @@
             try:
                 with file_path.open('w') as report_file:
                     report_file.write(report_text)
-            except IOError:
+            except OSError:
                 log.exception('Failed to write crash report')
 
     def on_send_report_button_clicked(self):

=== modified file 'openlp/core/ui/formattingtagcontroller.py'
--- openlp/core/ui/formattingtagcontroller.py	2017-10-07 07:05:07 +0000
+++ openlp/core/ui/formattingtagcontroller.py	2017-11-10 21:03:15 +0000
@@ -43,7 +43,7 @@
             r'(?P<tag>[^\s/!\?>]+)(?:\s+[^\s=]+="[^"]*")*\s*(?P<empty>/)?'
             r'|(?P<cdata>!\[CDATA\[(?:(?!\]\]>).)*\]\])'
             r'|(?P<procinst>\?(?:(?!\?>).)*\?)'
-            r'|(?P<comment>!--(?:(?!-->).)*--))>', re.UNICODE)
+            r'|(?P<comment>!--(?:(?!-->).)*--))>')
         self.html_regex = re.compile(r'^(?:[^<>]*%s)*[^<>]*$' % self.html_tag_regex.pattern)
 
     def pre_save(self):

=== added directory 'openlp/core/ui/lib'
=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2017-10-23 22:09:57 +0000
+++ openlp/core/ui/mainwindow.py	2017-11-10 21:03:15 +0000
@@ -180,7 +180,7 @@
                                             triggers=self.service_manager_contents.on_load_service_clicked)
         self.file_save_item = create_action(main_window, 'fileSaveItem', icon=':/general/general_save.png',
                                             can_shortcuts=True, category=UiStrings().File,
-                                            triggers=self.service_manager_contents.save_file)
+                                            triggers=self.service_manager_contents.decide_save_method)
         self.file_save_as_item = create_action(main_window, 'fileSaveAsItem', can_shortcuts=True,
                                                category=UiStrings().File,
                                                triggers=self.service_manager_contents.save_file_as)
@@ -1367,7 +1367,7 @@
                               '- Please wait for copy to finish').format(path=self.new_data_path))
                 dir_util.copy_tree(str(old_data_path), str(self.new_data_path))
                 log.info('Copy successful')
-            except (IOError, os.error, DistutilsFileError) as why:
+            except (OSError, DistutilsFileError) as why:
                 self.application.set_normal_cursor()
                 log.exception('Data copy failed {err}'.format(err=str(why)))
                 err_text = translate('OpenLP.MainWindow',

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2017-10-23 22:09:57 +0000
+++ openlp/core/ui/servicemanager.py	2017-11-10 21:03:15 +0000
@@ -193,18 +193,6 @@
             text=translate('OpenLP.ServiceManager', 'Move to &bottom'), icon=':/services/service_bottom.png',
             tooltip=translate('OpenLP.ServiceManager', 'Move item to the end of the service.'),
             can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_end)
-        self.down_action = self.order_toolbar.add_toolbar_action(
-            'down',
-            text=translate('OpenLP.ServiceManager', 'Move &down'), can_shortcuts=True,
-            tooltip=translate('OpenLP.ServiceManager', 'Moves the selection down the window.'), visible=False,
-            triggers=self.on_move_selection_down)
-        action_list.add_action(self.down_action)
-        self.up_action = self.order_toolbar.add_toolbar_action(
-            'up',
-            text=translate('OpenLP.ServiceManager', 'Move up'), can_shortcuts=True,
-            tooltip=translate('OpenLP.ServiceManager', 'Moves the selection up the window.'), visible=False,
-            triggers=self.on_move_selection_up)
-        action_list.add_action(self.up_action)
         self.order_toolbar.addSeparator()
         self.delete_action = self.order_toolbar.add_toolbar_action(
             'delete', can_shortcuts=True,
@@ -300,8 +288,8 @@
         self.theme_menu = QtWidgets.QMenu(translate('OpenLP.ServiceManager', '&Change Item Theme'))
         self.menu.addMenu(self.theme_menu)
         self.service_manager_list.addActions([self.move_down_action, self.move_up_action, self.make_live_action,
-                                              self.move_top_action, self.move_bottom_action, self.up_action,
-                                              self.down_action, self.expand_action, self.collapse_action])
+                                              self.move_top_action, self.move_bottom_action, self.expand_action,
+                                              self.collapse_action])
         Registry().register_function('theme_update_list', self.update_theme_list)
         Registry().register_function('config_screen_changed', self.regenerate_service_items)
         Registry().register_function('theme_update_global', self.theme_change)
@@ -474,6 +462,12 @@
         Load a recent file as the service triggered by mainwindow recent service list.
         :param field:
         """
+        if self.is_modified():
+            result = self.save_modified_service()
+            if result == QtWidgets.QMessageBox.Cancel:
+                return False
+            elif result == QtWidgets.QMessageBox.Save:
+                self.decide_save_method()
         sender = self.sender()
         self.load_file(sender.data())
 
@@ -603,7 +597,7 @@
                 if not os.path.exists(save_file):
                     shutil.copy(audio_from, save_file)
                 zip_file.write(audio_from, audio_to)
-        except IOError:
+        except OSError:
             self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
             self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
                                            translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
@@ -664,7 +658,7 @@
             zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, True)
             # First we add service contents.
             zip_file.writestr(service_file_name, service_content)
-        except IOError:
+        except OSError:
             self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
             self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
                                            translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
@@ -712,18 +706,23 @@
             default_file_path = directory_path / default_file_path
         # SaveAs from osz to oszl is not valid as the files will be deleted on exit which is not sensible or usable in
         # the long term.
+        lite_filter = translate('OpenLP.ServiceManager', 'OpenLP Service Files - lite (*.oszl)')
+        packaged_filter = translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)')
+
         if self._file_name.endswith('oszl') or self.service_has_all_original_files:
             file_path, filter_used = FileDialog.getSaveFileName(
                 self.main_window, UiStrings().SaveService, default_file_path,
-                translate('OpenLP.ServiceManager',
-                          'OpenLP Service Files (*.osz);; OpenLP Service Files - lite (*.oszl)'))
+                '{packaged};; {lite}'.format(packaged=packaged_filter, lite=lite_filter))
         else:
             file_path, filter_used = FileDialog.getSaveFileName(
-                self.main_window, UiStrings().SaveService, file_path,
-                translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz);;'))
+                self.main_window, UiStrings().SaveService, default_file_path,
+                '{packaged};;'.format(packaged=packaged_filter))
         if not file_path:
             return False
-        file_path.with_suffix('.osz')
+        if filter_used == lite_filter:
+            file_path = file_path.with_suffix('.oszl')
+        else:
+            file_path = file_path.with_suffix('.osz')
         self.set_file_name(file_path)
         self.decide_save_method()
 
@@ -791,11 +790,11 @@
             else:
                 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
                 self.log_error('File contains no service data')
-        except (IOError, NameError):
+        except (OSError, NameError):
             self.log_exception('Problem loading service file {name}'.format(name=file_name))
             critical_error_message_box(message=translate('OpenLP.ServiceManager',
                                        'File could not be opened because it is corrupt.'))
-        except zipfile.BadZipfile:
+        except zipfile.BadZipFile:
             if os.path.getsize(file_name) == 0:
                 self.log_exception('Service file is zero sized: {name}'.format(name=file_name))
                 QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
@@ -1657,14 +1656,15 @@
                 if start_pos == -1:
                     return
                 if item is None:
-                    end_pos = len(self.service_items)
+                    end_pos = len(self.service_items) - 1
                 else:
                     end_pos = get_parent_item_data(item) - 1
                 service_item = self.service_items[start_pos]
-                self.service_items.remove(service_item)
-                self.service_items.insert(end_pos, service_item)
-                self.repaint_service_list(end_pos, child)
-                self.set_modified()
+                if start_pos != end_pos:
+                    self.service_items.remove(service_item)
+                    self.service_items.insert(end_pos, service_item)
+                    self.repaint_service_list(end_pos, child)
+                    self.set_modified()
             else:
                 # we are not over anything so drop
                 replace = False

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2017-10-23 22:09:57 +0000
+++ openlp/core/ui/thememanager.py	2017-11-10 21:03:15 +0000
@@ -604,7 +604,7 @@
                     else:
                         with full_name.open('wb') as out_file:
                             out_file.write(theme_zip.read(zipped_file))
-        except (IOError, zipfile.BadZipfile):
+        except (OSError, zipfile.BadZipFile):
             self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))
             raise ValidationError
         except ValidationError:
@@ -667,7 +667,7 @@
         theme_path = theme_dir / '{file_name}.json'.format(file_name=name)
         try:
                 theme_path.write_text(theme_pretty)
-        except IOError:
+        except OSError:
             self.log_exception('Saving theme to file failed')
         if image_source_path and image_destination_path:
             if self.old_background_image_path and image_destination_path != self.old_background_image_path:
@@ -675,7 +675,7 @@
             if image_source_path != image_destination_path:
                 try:
                     copyfile(image_source_path, image_destination_path)
-                except IOError:
+                except OSError:
                     self.log_exception('Failed to save theme image')
         self.generate_and_save_image(name, theme)
 

=== modified file 'openlp/core/version.py'
--- openlp/core/version.py	2017-10-07 07:05:07 +0000
+++ openlp/core/version.py	2017-11-10 21:03:15 +0000
@@ -96,7 +96,7 @@
                 remote_version = response.text
                 log.debug('New version found: %s', remote_version)
                 break
-            except IOError:
+            except OSError:
                 log.exception('Unable to connect to OpenLP server to download version file')
                 retries += 1
         else:
@@ -182,7 +182,7 @@
         try:
             version_file = open(file_path, 'r')
             full_version = str(version_file.read()).rstrip()
-        except IOError:
+        except OSError:
             log.exception('Error in version file.')
             full_version = '0.0.0-bzr000'
         finally:

=== modified file 'openlp/core/widgets/edits.py'
--- openlp/core/widgets/edits.py	2017-10-23 22:09:57 +0000
+++ openlp/core/widgets/edits.py	2017-11-10 21:03:15 +0000
@@ -27,6 +27,7 @@
 
 from PyQt5 import QtCore, QtGui, QtWidgets
 
+from openlp.core.common import CONTROL_CHARS
 from openlp.core.common.i18n import UiStrings, translate
 from openlp.core.common.path import Path, path_to_str, str_to_path
 from openlp.core.common.settings import Settings
@@ -241,7 +242,7 @@
         self.line_edit.editingFinished.connect(self.on_line_edit_editing_finished)
         self.update_button_tool_tips()
 
-    @property
+    @QtCore.pyqtProperty('QVariant')
     def path(self):
         """
         A property getter method to return the selected path.
@@ -349,7 +350,7 @@
         :rtype: None
         """
         if self._path != path:
-            self.path = path
+            self._path = path
             self.pathChanged.emit(path)
 
 
@@ -470,12 +471,21 @@
                     cursor.insertText(html['start tag'])
                     cursor.insertText(html['end tag'])
 
+    def insertFromMimeData(self, source):
+        """
+        Reimplement `insertFromMimeData` so that we can remove any control characters
+
+        :param QtCore.QMimeData source: The mime data to insert
+        :rtype: None
+        """
+        self.insertPlainText(CONTROL_CHARS.sub('', source.text()))
+
 
 class Highlighter(QtGui.QSyntaxHighlighter):
     """
     Provides a text highlighter for pointing out spelling errors in text.
     """
-    WORDS = r'(?iu)[\w\']+'
+    WORDS = r'(?i)[\w\']+'
 
     def __init__(self, *args):
         """

=== modified file 'openlp/core/widgets/views.py'
--- openlp/core/widgets/views.py	2017-10-23 22:09:57 +0000
+++ openlp/core/widgets/views.py	2017-11-10 21:03:15 +0000
@@ -336,7 +336,7 @@
                     for file in listing:
                         files.append(os.path.join(local_file, file))
             Registry().execute('{mime_data}_dnd'.format(mime_data=self.mime_data_text),
-                               {'files': files, 'target': self.itemAt(event.pos())})
+                               {'files': files})
         else:
             event.ignore()
 

=== modified file 'openlp/plugins/bibles/forms/booknameform.py'
--- openlp/plugins/bibles/forms/booknameform.py	2017-10-07 07:05:07 +0000
+++ openlp/plugins/bibles/forms/booknameform.py	2017-11-10 21:03:15 +0000
@@ -113,8 +113,7 @@
             cor_book = self.corresponding_combo_box.currentText()
             for character in '\\.^$*+?{}[]()':
                 cor_book = cor_book.replace(character, '\\' + character)
-            books = [key for key in list(self.book_names.keys()) if re.match(cor_book, str(self.book_names[key]),
-                                                                             re.UNICODE)]
+            books = [key for key in list(self.book_names.keys()) if re.match(cor_book, str(self.book_names[key]))]
             books = [_f for _f in map(BiblesResourcesDB.get_book, books) if _f]
             if books:
                 self.book_id = books[0]['id']

=== modified file 'openlp/plugins/bibles/lib/__init__.py'
--- openlp/plugins/bibles/lib/__init__.py	2017-10-07 07:05:07 +0000
+++ openlp/plugins/bibles/lib/__init__.py	2017-11-10 21:03:15 +0000
@@ -224,13 +224,13 @@
     range_regex = '(?:(?P<from_chapter>[0-9]+){sep_v})?' \
         '(?P<from_verse>[0-9]+)(?P<range_to>{sep_r}(?:(?:(?P<to_chapter>' \
         '[0-9]+){sep_v})?(?P<to_verse>[0-9]+)|{sep_e})?)?'.format_map(REFERENCE_SEPARATORS)
-    REFERENCE_MATCHES['range'] = re.compile(r'^\s*{range}\s*$'.format(range=range_regex), re.UNICODE)
-    REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'], re.UNICODE)
+    REFERENCE_MATCHES['range'] = re.compile(r'^\s*{range}\s*$'.format(range=range_regex))
+    REFERENCE_MATCHES['range_separator'] = re.compile(REFERENCE_SEPARATORS['sep_l'])
     # full reference match: <book>(<range>(,(?!$)|(?=$)))+
     REFERENCE_MATCHES['full'] = \
         re.compile(r'^\s*(?!\s)(?P<book>[\d]*[.]?[^\d\.]+)\.*(?<!\s)\s*'
                    r'(?P<ranges>(?:{range_regex}(?:{sep_l}(?!\s*$)|(?=\s*$)))+)\s*$'.format(
-                       range_regex=range_regex, sep_l=REFERENCE_SEPARATORS['sep_l']), re.UNICODE)
+                       range_regex=range_regex, sep_l=REFERENCE_SEPARATORS['sep_l']))
 
 
 def get_reference_separator(separator_type):

=== modified file 'openlp/plugins/bibles/lib/db.py'
--- openlp/plugins/bibles/lib/db.py	2017-10-23 22:09:57 +0000
+++ openlp/plugins/bibles/lib/db.py	2017-11-10 21:03:15 +0000
@@ -307,8 +307,7 @@
         book_escaped = book
         for character in RESERVED_CHARACTERS:
             book_escaped = book_escaped.replace(character, '\\' + character)
-        regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())),
-                                re.UNICODE | re.IGNORECASE)
+        regex_book = re.compile('\\s*{book}\\s*'.format(book='\\s*'.join(book_escaped.split())), re.IGNORECASE)
         if language_selection == LanguageSelection.Bible:
             db_book = self.get_book(book)
             if db_book:

=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py	2017-10-23 22:09:57 +0000
+++ openlp/plugins/images/lib/mediaitem.py	2017-11-10 21:03:15 +0000
@@ -366,7 +366,7 @@
                 if validate_thumb(image.file_path, thumbnail_path):
                     icon = build_icon(thumbnail_path)
                 else:
-                    icon = create_thumb(image.file_path, thumbnail_path)
+                    icon = create_thumb(str(image.file_path), str(thumbnail_path))
             item_name = QtWidgets.QTreeWidgetItem([file_name])
             item_name.setText(0, file_name)
             item_name.setIcon(0, icon)
@@ -390,6 +390,7 @@
         :param files: A List of strings containing the filenames of the files to be loaded
         :param target_group: The QTreeWidgetItem of the group that will be the parent of the added files
         """
+        file_paths = [Path(file) for file in file_paths]
         self.application.set_normal_cursor()
         self.load_list(file_paths, target_group)
         last_dir = file_paths[0].parent

=== modified file 'openlp/plugins/presentations/lib/pptviewcontroller.py'
--- openlp/plugins/presentations/lib/pptviewcontroller.py	2017-10-10 07:08:44 +0000
+++ openlp/plugins/presentations/lib/pptviewcontroller.py	2017-11-10 21:03:15 +0000
@@ -70,7 +70,7 @@
             try:
                 self.start_process()
                 return self.process.CheckInstalled()
-            except WindowsError:
+            except OSError:
                 return False
 
         def start_process(self):

=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py	2017-10-23 22:09:57 +0000
+++ openlp/plugins/songs/forms/editsongform.py	2017-11-10 21:03:15 +0000
@@ -105,9 +105,9 @@
         self.topics_list_view.setSortingEnabled(False)
         self.topics_list_view.setAlternatingRowColors(True)
         self.audio_list_widget.setAlternatingRowColors(True)
-        self.find_verse_split = re.compile('---\[\]---\n', re.UNICODE)
-        self.whitespace = re.compile(r'\W+', re.UNICODE)
-        self.find_tags = re.compile(u'\{/?\w+\}', re.UNICODE)
+        self.find_verse_split = re.compile('---\[\]---\n')
+        self.whitespace = re.compile(r'\W+')
+        self.find_tags = re.compile(r'\{/?\w+\}')
 
     def _load_objects(self, cls, combo, cache):
         """

=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py	2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/__init__.py	2017-11-10 21:03:15 +0000
@@ -24,7 +24,6 @@
 """
 
 import logging
-import os
 import re
 
 from PyQt5 import QtWidgets
@@ -39,8 +38,8 @@
 
 log = logging.getLogger(__name__)
 
-WHITESPACE = re.compile(r'[\W_]+', re.UNICODE)
-APOSTROPHE = re.compile('[\'`’ʻ′]', re.UNICODE)
+WHITESPACE = re.compile(r'[\W_]+')
+APOSTROPHE = re.compile(r'[\'`’ʻ′]')
 # PATTERN will look for the next occurence of one of these symbols:
 #   \controlword - optionally preceded by \*, optionally followed by a number
 #   \'## - where ## is a pair of hex digits, representing a single character

=== modified file 'openlp/plugins/songs/lib/importers/easyslides.py'
--- openlp/plugins/songs/lib/importers/easyslides.py	2017-09-30 20:16:30 +0000
+++ openlp/plugins/songs/lib/importers/easyslides.py	2017-11-10 21:03:15 +0000
@@ -25,6 +25,7 @@
 
 from lxml import etree, objectify
 
+from openlp.core.common import normalize_str
 from openlp.plugins.songs.lib import VerseType
 from openlp.plugins.songs.lib.importers.songimport import SongImport
 
@@ -225,7 +226,7 @@
                 verses[reg].setdefault(vt, {})
                 verses[reg][vt].setdefault(vn, {})
                 verses[reg][vt][vn].setdefault(inst, [])
-                verses[reg][vt][vn][inst].append(self.tidy_text(line))
+                verses[reg][vt][vn][inst].append(normalize_str(line))
         # done parsing
         versetags = []
         # we use our_verse_order to ensure, we insert lyrics in the same order

=== modified file 'openlp/plugins/songs/lib/importers/mediashout.py'
--- openlp/plugins/songs/lib/importers/mediashout.py	2017-10-07 07:05:07 +0000
+++ openlp/plugins/songs/lib/importers/mediashout.py	2017-11-10 21:03:15 +0000
@@ -101,7 +101,7 @@
             self.song_book_name = song.SongID
         for verse in verses:
             tag = VERSE_TAGS[verse.Type] + str(verse.Number) if verse.Type < len(VERSE_TAGS) else 'O'
-            self.add_verse(self.tidy_text(verse.Text), tag)
+            self.add_verse(verse.Text, tag)
         for order in verse_order:
             if order.Type < len(VERSE_TAGS):
                 self.verse_order_list.append(VERSE_TAGS[order.Type] + str(order.Number))

=== modified file 'openlp/plugins/songs/lib/importers/openoffice.py'
--- openlp/plugins/songs/lib/importers/openoffice.py	2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/importers/openoffice.py	2017-11-10 21:03:15 +0000
@@ -24,7 +24,7 @@
 
 from PyQt5 import QtCore
 
-from openlp.core.common import is_win, get_uno_command, get_uno_instance
+from openlp.core.common import get_uno_command, get_uno_instance, is_win, normalize_str
 from openlp.core.common.i18n import translate
 from .songimport import SongImport
 
@@ -241,7 +241,7 @@
 
         :param text: The text.
         """
-        song_texts = self.tidy_text(text).split('\f')
+        song_texts = normalize_str(text).split('\f')
         self.set_defaults()
         for song_text in song_texts:
             if song_text.strip():

=== modified file 'openlp/plugins/songs/lib/importers/opensong.py'
--- openlp/plugins/songs/lib/importers/opensong.py	2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/importers/opensong.py	2017-11-10 21:03:15 +0000
@@ -25,6 +25,7 @@
 from lxml import objectify
 from lxml.etree import Error, LxmlError
 
+from openlp.core.common import normalize_str
 from openlp.core.common.i18n import translate
 from openlp.core.common.settings import Settings
 from openlp.plugins.songs.lib import VerseType
@@ -262,7 +263,7 @@
                                                               post=this_line[offset + column:])
                     offset += len(chord) + 2
             # Tidy text and remove the ____s from extended words
-            this_line = self.tidy_text(this_line)
+            this_line = normalize_str(this_line)
             this_line = this_line.replace('_', '')
             this_line = this_line.replace('||', '\n[---]\n')
             this_line = this_line.strip()

=== modified file 'openlp/plugins/songs/lib/importers/songimport.py'
--- openlp/plugins/songs/lib/importers/songimport.py	2017-10-23 22:09:57 +0000
+++ openlp/plugins/songs/lib/importers/songimport.py	2017-11-10 21:03:15 +0000
@@ -25,6 +25,7 @@
 
 from PyQt5 import QtCore
 
+from openlp.core.common import normalize_str
 from openlp.core.common.applocation import AppLocation
 from openlp.core.common.i18n import translate
 from openlp.core.common.path import copyfile, create_paths
@@ -130,26 +131,6 @@
     def register(self, import_wizard):
         self.import_wizard = import_wizard
 
-    def tidy_text(self, text):
-        """
-        Get rid of some dodgy unicode and formatting characters we're not interested in. Some can be converted to ascii.
-        """
-        text = text.replace('\u2018', '\'')
-        text = text.replace('\u2019', '\'')
-        text = text.replace('\u201c', '"')
-        text = text.replace('\u201d', '"')
-        text = text.replace('\u2026', '...')
-        text = text.replace('\u2013', '-')
-        text = text.replace('\u2014', '-')
-        # Replace vertical tab with 2 linebreaks
-        text = text.replace('\v', '\n\n')
-        # Replace form feed (page break) with 2 linebreaks
-        text = text.replace('\f', '\n\n')
-        # Remove surplus blank lines, spaces, trailing/leading spaces
-        text = re.sub(r'[ \t]+', ' ', text)
-        text = re.sub(r' ?(\r\n?|\n) ?', '\n', text)
-        return text
-
     def process_song_text(self, text):
         """
         Process the song text from import
@@ -368,7 +349,7 @@
                 verse_tag = VerseType.tags[VerseType.Other]
                 log.info('Versetype {old} changing to {new}'.format(old=verse_def, new=new_verse_def))
                 verse_def = new_verse_def
-            sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
+            sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], normalize_str(verse_text), lang)
         song.lyrics = str(sxml.extract_xml(), 'utf-8')
         if not self.verse_order_list and self.verse_order_list_generated_useful:
             self.verse_order_list = self.verse_order_list_generated

=== modified file 'openlp/plugins/songs/lib/importers/songsoffellowship.py'
--- openlp/plugins/songs/lib/importers/songsoffellowship.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/songsoffellowship.py	2017-11-10 21:03:15 +0000
@@ -194,7 +194,6 @@
         :param text_portion: A Piece of text
         """
         text = text_portion.getString()
-        text = self.tidy_text(text)
         if text.strip() == '':
             return text
         if text_portion.CharWeight == BOLD:

=== modified file 'openlp/plugins/songs/lib/importers/zionworx.py'
--- openlp/plugins/songs/lib/importers/zionworx.py	2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/importers/zionworx.py	2017-11-10 21:03:15 +0000
@@ -30,9 +30,6 @@
 
 log = logging.getLogger(__name__)
 
-# Used to strip control chars (except 10=LF, 13=CR)
-CONTROL_CHARS_MAP = dict.fromkeys(list(range(10)) + [11, 12] + list(range(14, 32)) + [127])
-
 
 class ZionWorxImport(SongImport):
     """
@@ -95,12 +92,12 @@
                     return
                 self.set_defaults()
                 try:
-                    self.title = self._decode(record['Title1'])
+                    self.title = record['Title1']
                     if record['Title2']:
-                        self.alternate_title = self._decode(record['Title2'])
-                    self.parse_author(self._decode(record['Writer']))
-                    self.add_copyright(self._decode(record['Copyright']))
-                    lyrics = self._decode(record['Lyrics'])
+                        self.alternate_title = record['Title2']
+                    self.parse_author(record['Writer'])
+                    self.add_copyright(record['Copyright'])
+                    lyrics = record['Lyrics']
                 except UnicodeDecodeError as e:
                     self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record {index}').format(index=index),
                                    translate('SongsPlugin.ZionWorxImport', 'Decoding error: {error}').format(error=e))
@@ -122,10 +119,3 @@
                 if not self.finish():
                     self.log_error(translate('SongsPlugin.ZionWorxImport', 'Record %d') % index +
                                    (': "' + title + '"' if title else ''))
-
-    def _decode(self, str):
-        """
-        Strips all control characters (except new lines).
-        """
-        # ZionWorx has no option for setting the encoding for its songs, so we assume encoding is always the same.
-        return str.translate(CONTROL_CHARS_MAP)

=== modified file 'openlp/plugins/songs/lib/openlyricsxml.py'
--- openlp/plugins/songs/lib/openlyricsxml.py	2017-10-10 02:29:56 +0000
+++ openlp/plugins/songs/lib/openlyricsxml.py	2017-11-10 21:03:15 +0000
@@ -281,7 +281,7 @@
         # Process the formatting tags.
         # Have we any tags in song lyrics?
         tags_element = None
-        match = re.search('\{/?\w+\}', song.lyrics, re.UNICODE)
+        match = re.search(r'\{/?\w+\}', song.lyrics)
         if match:
             # Named 'format_' - 'format' is built-in function in Python.
             format_ = etree.SubElement(song_xml, 'format')

=== modified file 'openlp/plugins/songusage/forms/songusagedetailform.py'
--- openlp/plugins/songusage/forms/songusagedetailform.py	2017-10-23 22:09:57 +0000
+++ openlp/plugins/songusage/forms/songusagedetailform.py	2017-11-10 21:03:15 +0000
@@ -54,8 +54,14 @@
         """
         We need to set up the screen
         """
-        self.from_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/from date'))
-        self.to_date_calendar.setSelectedDate(Settings().value(self.plugin.settings_section + '/to date'))
+        to_date = Settings().value(self.plugin.settings_section + '/to date')
+        if not (isinstance(to_date, QtCore.QDate) and to_date.isValid()):
+            to_date = QtCore.QDate.currentDate()
+        from_date = Settings().value(self.plugin.settings_section + '/from date')
+        if not (isinstance(from_date, QtCore.QDate) and from_date.isValid()):
+            from_date = to_date.addYears(-1)
+        self.from_date_calendar.setSelectedDate(from_date)
+        self.to_date_calendar.setSelectedDate(to_date)
         self.report_path_edit.path = Settings().value(self.plugin.settings_section + '/last directory export')
 
     def on_report_path_edit_path_changed(self, file_path):

=== modified file 'openlp/plugins/songusage/songusageplugin.py'
--- openlp/plugins/songusage/songusageplugin.py	2017-10-07 07:05:07 +0000
+++ openlp/plugins/songusage/songusageplugin.py	2017-11-10 21:03:15 +0000
@@ -38,20 +38,17 @@
 
 log = logging.getLogger(__name__)
 
-YEAR = QtCore.QDate().currentDate().year()
-if QtCore.QDate().currentDate().month() < 9:
-    YEAR -= 1
-
+TODAY = QtCore.QDate.currentDate()
 
 __default_settings__ = {
     'songusage/db type': 'sqlite',
     'songusage/db username': '',
-    'songuasge/db password': '',
-    'songuasge/db hostname': '',
-    'songuasge/db database': '',
+    'songusage/db password': '',
+    'songusage/db hostname': '',
+    'songusage/db database': '',
     'songusage/active': False,
-    'songusage/to date': QtCore.QDate(YEAR, 8, 31),
-    'songusage/from date': QtCore.QDate(YEAR - 1, 9, 1),
+    'songusage/to date': TODAY,
+    'songusage/from date': TODAY.addYears(-1),
     'songusage/last directory export': None
 }
 

=== modified file 'tests/functional/openlp_core/common/test_actions.py'
--- tests/functional/openlp_core/common/test_actions.py	2017-10-07 07:05:07 +0000
+++ tests/functional/openlp_core/common/test_actions.py	2017-11-10 21:03:15 +0000
@@ -153,6 +153,7 @@
         """
         Prepare the tests
         """
+        self.setup_application()
         self.action_list = ActionList.get_instance()
         self.build_settings()
         self.settings = Settings()

=== modified file 'tests/functional/openlp_core/common/test_httputils.py'
--- tests/functional/openlp_core/common/test_httputils.py	2017-09-25 20:34:05 +0000
+++ tests/functional/openlp_core/common/test_httputils.py	2017-11-10 21:03:15 +0000
@@ -233,7 +233,7 @@
         Test socket timeout gets caught
         """
         # GIVEN: Mocked urlopen to fake a network disconnect in the middle of a download
-        mocked_requests.get.side_effect = IOError
+        mocked_requests.get.side_effect = OSError
 
         # WHEN: Attempt to retrieve a file
         url_get_file(MagicMock(), url='http://localhost/test', file_path=Path(self.tempfile))

=== modified file 'tests/functional/openlp_core/common/test_i18n.py'
--- tests/functional/openlp_core/common/test_i18n.py	2017-10-07 07:05:07 +0000
+++ tests/functional/openlp_core/common/test_i18n.py	2017-11-10 21:03:15 +0000
@@ -155,7 +155,7 @@
     assert first_instance is second_instance, 'Two UiStrings objects should be the same instance'
 
 
-def test_translate(self):
+def test_translate():
     """
     Test the translate() function
     """

=== modified file 'tests/functional/openlp_core/common/test_path.py'
--- tests/functional/openlp_core/common/test_path.py	2017-10-07 07:05:07 +0000
+++ tests/functional/openlp_core/common/test_path.py	2017-11-10 21:03:15 +0000
@@ -371,13 +371,13 @@
     @patch('openlp.core.common.path.log')
     def test_create_paths_dir_io_error(self, mocked_logger):
         """
-        Test the create_paths() when an IOError is raised
+        Test the create_paths() when an OSError is raised
         """
         # GIVEN: A `Path` to check with patched out mkdir and exists methods
         mocked_path = MagicMock()
-        mocked_path.exists.side_effect = IOError('Cannot make directory')
+        mocked_path.exists.side_effect = OSError('Cannot make directory')
 
-        # WHEN: An IOError is raised when checking the if the path exists.
+        # WHEN: An OSError is raised when checking the if the path exists.
         create_paths(mocked_path)
 
         # THEN: The Error should have been logged
@@ -385,7 +385,7 @@
 
     def test_create_paths_dir_value_error(self):
         """
-        Test the create_paths() when an error other than IOError is raised
+        Test the create_paths() when an error other than OSError is raised
         """
         # GIVEN: A `Path` to check with patched out mkdir and exists methods
         mocked_path = MagicMock()

=== modified file 'tests/functional/openlp_core/lib/test_lib.py'
--- tests/functional/openlp_core/lib/test_lib.py	2017-10-10 07:08:44 +0000
+++ tests/functional/openlp_core/lib/test_lib.py	2017-11-10 21:03:15 +0000
@@ -168,7 +168,7 @@
                 patch.object(Path, 'open'):
             file_path = Path('testfile.txt')
             file_path.is_file.return_value = True
-            file_path.open.side_effect = IOError()
+            file_path.open.side_effect = OSError()
 
             # WHEN: get_text_file_string is called
             result = get_text_file_string(file_path)

=== modified file 'tests/functional/openlp_core/ui/test_first_time.py'
--- tests/functional/openlp_core/ui/test_first_time.py	2017-09-20 16:55:21 +0000
+++ tests/functional/openlp_core/ui/test_first_time.py	2017-11-10 21:03:15 +0000
@@ -40,7 +40,7 @@
         Test get_web_page will attempt CONNECTION_RETRIES+1 connections - bug 1409031
         """
         # GIVEN: Initial settings and mocks
-        mocked_requests.get.side_effect = IOError('Unable to connect')
+        mocked_requests.get.side_effect = OSError('Unable to connect')
 
         # WHEN: A webpage is requested
         try:

=== modified file 'tests/functional/openlp_core/widgets/test_views.py'
--- tests/functional/openlp_core/widgets/test_views.py	2017-10-23 22:09:57 +0000
+++ tests/functional/openlp_core/widgets/test_views.py	2017-11-10 21:03:15 +0000
@@ -627,4 +627,3 @@
         assert widget.allow_internal_dnd is False
         assert widget.indentation() == 0
         assert widget.isAnimated() is True
-

=== modified file 'tests/functional/openlp_plugins/presentations/test_presentationcontroller.py'
--- tests/functional/openlp_plugins/presentations/test_presentationcontroller.py	2017-10-07 07:05:07 +0000
+++ tests/functional/openlp_plugins/presentations/test_presentationcontroller.py	2017-11-10 21:03:15 +0000
@@ -144,7 +144,7 @@
         # GIVEN: A mocked open, get_thumbnail_folder and exists
         with patch('openlp.plugins.presentations.lib.presentationcontroller.Path.read_text') as mocked_read_text, \
                 patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder:
-            mocked_read_text.side_effect = IOError()
+            mocked_read_text.side_effect = OSError()
             mocked_get_thumbnail_folder.return_value = Path('test')
 
             # WHEN: calling get_titles_and_notes

=== modified file 'tests/interfaces/openlp_core/ui/test_projectormanager.py'
--- tests/interfaces/openlp_core/ui/test_projectormanager.py	2017-10-07 07:05:07 +0000
+++ tests/interfaces/openlp_core/ui/test_projectormanager.py	2017-11-10 21:03:15 +0000
@@ -42,8 +42,8 @@
         """
         Create the UI and setup necessary options
         """
+        self.setup_application()
         self.build_settings()
-        self.setup_application()
         Registry.create()
         with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:
             if os.path.exists(TEST_DB):

=== modified file 'tests/interfaces/openlp_core/ui/test_projectorsourceform.py'
--- tests/interfaces/openlp_core/ui/test_projectorsourceform.py	2017-10-07 07:05:07 +0000
+++ tests/interfaces/openlp_core/ui/test_projectorsourceform.py	2017-11-10 21:03:15 +0000
@@ -64,8 +64,8 @@
         Set up anything necessary for all tests
         """
         mocked_init_url.return_value = 'sqlite:///{}'.format(TEST_DB)
+        self.setup_application()
         self.build_settings()
-        self.setup_application()
         Registry.create()
         # Do not try to recreate if we've already been created from a previous test
         if not hasattr(self, 'projectordb'):

=== modified file 'tests/interfaces/openlp_core/ui/test_thememanager.py'
--- tests/interfaces/openlp_core/ui/test_thememanager.py	2017-10-10 01:08:09 +0000
+++ tests/interfaces/openlp_core/ui/test_thememanager.py	2017-11-10 21:03:15 +0000
@@ -41,8 +41,8 @@
         """
         Create the UI
         """
+        self.setup_application()
         self.build_settings()
-        self.setup_application()
         Registry.create()
         self.theme_manager = ThemeManager()
 

=== modified file 'tests/utils/__init__.py'
--- tests/utils/__init__.py	2016-12-31 11:01:36 +0000
+++ tests/utils/__init__.py	2017-11-10 21:03:15 +0000
@@ -36,7 +36,7 @@
     try:
         items = json.load(open_file)
         first_line = items[row]
-    except IOError:
+    except OSError:
         first_line = ''
     finally:
         open_file.close()

=== modified file 'tests/utils/test_pylint.py'
--- tests/utils/test_pylint.py	2016-12-31 11:01:36 +0000
+++ tests/utils/test_pylint.py	2017-11-10 21:03:15 +0000
@@ -58,17 +58,21 @@
         # GIVEN: Some checks to disable and enable, and the pylint script
         disabled_checks = 'import-error,no-member'
         enabled_checks = 'missing-format-argument-key,unused-format-string-argument,bad-format-string'
-        if is_win() or 'arch' in platform.dist()[0].lower():
-            pylint_script = 'pylint'
-        else:
-            pylint_script = 'pylint3'
+        pylint_kwargs = {
+            'return_std': True
+        }
+        if version < '1.7.0':
+            if is_win() or 'arch' in platform.dist()[0].lower():
+                pylint_kwargs.update({'script': 'pylint'})
+            else:
+                pylint_kwargs.update({'script': 'pylint3'})
 
         # WHEN: Running pylint
         (pylint_stdout, pylint_stderr) = \
             lint.py_run('openlp --errors-only --disable={disabled} --enable={enabled} '
                         '--reports=no --output-format=parseable'.format(disabled=disabled_checks,
                                                                         enabled=enabled_checks),
-                        return_std=True, script=pylint_script)
+                        **pylint_kwargs)
         stdout = pylint_stdout.read()
         stderr = pylint_stderr.read()
         filtered_stdout = self._filter_tolerated_errors(stdout)


Follow ups