← Back to team overview

openlp-core team mailing list archive

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

 

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

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~phill-ridout/openlp/pathlib8/+merge/331621

Pathlib changes for the song plugin

lp:~phill-ridout/openlp/pathlib8 (revision 2777)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/2230/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/2133/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/2015/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Code_Analysis/1380/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Test_Coverage/1207/
[SUCCESS] https://ci.openlp.io/job/Branch-04c-Code_Analysis2/337/
[FAILURE] https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/174/
Stopping after failure
-- 
Your team OpenLP Core is requested to review the proposed merge of lp:~phill-ridout/openlp/pathlib8 into lp:openlp.
=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2017-08-03 17:54:40 +0000
+++ openlp/core/lib/serviceitem.py	2017-09-30 23:24:21 +0000
@@ -426,10 +426,10 @@
             self.background_audio = []
             for filename in header['background_audio']:
                 # Give them real file paths.
-                filepath = filename
+                filepath = str(filename)
                 if path:
                     # Windows can handle both forward and backward slashes, so we use ntpath to get the basename
-                    filepath = os.path.join(path, ntpath.basename(filename))
+                    filepath = os.path.join(path, ntpath.basename(str(filename)))
                 self.background_audio.append(filepath)
         self.theme_overwritten = header.get('theme_overwritten', False)
         if self.service_item_type == ServiceItemType.Text:

=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py	2017-09-26 16:39:13 +0000
+++ openlp/core/ui/maindisplay.py	2017-09-30 23:24:21 +0000
@@ -684,7 +684,7 @@
         if not isinstance(file_names, list):
             file_names = [file_names]
         for file_name in file_names:
-            self.playlist.addMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(file_name)))
+            self.playlist.addMedia(QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile(str(file_name))))
 
     def next(self):
         """

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2017-09-23 13:06:42 +0000
+++ openlp/core/ui/servicemanager.py	2017-09-30 23:24:21 +0000
@@ -561,7 +561,7 @@
                 service_item = item['service_item'].get_service_repr(self._save_lite)
                 if service_item['header']['background_audio']:
                     for i, file_name in enumerate(service_item['header']['background_audio']):
-                        new_file = os.path.join('audio', item['service_item'].unique_identifier, file_name)
+                        new_file = os.path.join('audio', item['service_item'].unique_identifier, str(file_name))
                         audio_files.append((file_name, new_file))
                         service_item['header']['background_audio'][i] = new_file
                 # Add the service item to the service.
@@ -586,6 +586,8 @@
             for write_from in write_list:
                 zip_file.write(write_from, write_from)
             for audio_from, audio_to in audio_files:
+                audio_from = str(audio_from)
+                audio_to = str(audio_to)
                 if audio_from.startswith('audio'):
                     # When items are saved, they get new unique_identifier. Let's copy the file to the new location.
                     # Unused files can be ignored, OpenLP automatically cleans up the service manager dir on exit.

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2017-06-06 20:58:12 +0000
+++ openlp/core/ui/slidecontroller.py	2017-09-30 23:24:21 +0000
@@ -868,7 +868,7 @@
                     self.track_menu.clear()
                     for counter in range(len(self.service_item.background_audio)):
                         action = self.track_menu.addAction(
-                            os.path.basename(self.service_item.background_audio[counter]))
+                            os.path.basename(str(self.service_item.background_audio[counter])))
                         action.setData(counter)
                         action.triggered.connect(self.on_track_triggered)
                     self.display.audio_player.repeat = \

=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py	2017-08-25 20:03:25 +0000
+++ openlp/plugins/songs/forms/editsongform.py	2017-09-30 23:24:21 +0000
@@ -23,27 +23,25 @@
 The :mod:`~openlp.plugins.songs.forms.editsongform` module contains the form
 used to edit songs.
 """
-
 import logging
+import os
 import re
-import os
-import shutil
 
 from PyQt5 import QtCore, QtWidgets
 
 from openlp.core.common import Registry, RegistryProperties, AppLocation, UiStrings, check_directory_exists, translate
-from openlp.core.common.path import Path, path_to_str
+from openlp.core.common.languagemanager import get_natural_key
+from openlp.core.common.path import copyfile
 from openlp.core.lib import PluginStatus, MediaType, create_separated_list
 from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
 from openlp.core.ui.lib.filedialog import FileDialog
-from openlp.core.common.languagemanager import get_natural_key
+from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
+from openlp.plugins.songs.forms.editverseform import EditVerseForm
+from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm
 from openlp.plugins.songs.lib import VerseType, clean_song
 from openlp.plugins.songs.lib.db import Book, Song, Author, AuthorType, Topic, MediaFile, SongBookEntry
+from openlp.plugins.songs.lib.openlyricsxml import SongXML
 from openlp.plugins.songs.lib.ui import SongStrings
-from openlp.plugins.songs.lib.openlyricsxml import SongXML
-from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
-from openlp.plugins.songs.forms.editverseform import EditVerseForm
-from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm
 
 log = logging.getLogger(__name__)
 
@@ -545,9 +543,9 @@
                                             songbook_entry.entry)
         self.audio_list_widget.clear()
         for media in self.song.media_files:
-            media_file = QtWidgets.QListWidgetItem(os.path.split(media.file_name)[1])
-            media_file.setData(QtCore.Qt.UserRole, media.file_name)
-            self.audio_list_widget.addItem(media_file)
+            item = QtWidgets.QListWidgetItem(media.file_path.name)
+            item.setData(QtCore.Qt.UserRole, media.file_path)
+            self.audio_list_widget.addItem(item)
         self.title_edit.setFocus()
         # Hide or show the preview button.
         self.preview_button.setVisible(preview)
@@ -927,12 +925,11 @@
         Loads file(s) from the filesystem.
         """
         filters = '{text} (*)'.format(text=UiStrings().AllFiles)
-        file_paths, selected_filter = FileDialog.getOpenFileNames(
-            self, translate('SongsPlugin.EditSongForm', 'Open File(s)'), Path(), filters)
+        file_paths, filter_used = FileDialog.getOpenFileNames(
+            parent=self, caption=translate('SongsPlugin.EditSongForm', 'Open File(s)'), filter=filters)
         for file_path in file_paths:
-            filename = path_to_str(file_path)
-            item = QtWidgets.QListWidgetItem(os.path.split(str(filename))[1])
-            item.setData(QtCore.Qt.UserRole, filename)
+            item = QtWidgets.QListWidgetItem(file_path.name)
+            item.setData(QtCore.Qt.UserRole, file_path)
             self.audio_list_widget.addItem(item)
 
     def on_audio_add_from_media_button_clicked(self):
@@ -940,9 +937,9 @@
         Loads file(s) from the media plugin.
         """
         if self.media_form.exec():
-            for filename in self.media_form.get_selected_files():
-                item = QtWidgets.QListWidgetItem(os.path.split(str(filename))[1])
-                item.setData(QtCore.Qt.UserRole, filename)
+            for file_path in self.media_form.get_selected_files():
+                item = QtWidgets.QListWidgetItem(file_path.name)
+                item.setData(QtCore.Qt.UserRole, file_path)
                 self.audio_list_widget.addItem(item)
 
     def on_audio_remove_button_clicked(self):
@@ -1066,34 +1063,33 @@
         # Save the song here because we need a valid id for the audio files.
         clean_song(self.manager, self.song)
         self.manager.save_object(self.song)
-        audio_files = [a.file_name for a in self.song.media_files]
-        log.debug(audio_files)
-        save_path = os.path.join(str(AppLocation.get_section_data_path(self.media_item.plugin.name)), 'audio',
-                                 str(self.song.id))
-        check_directory_exists(Path(save_path))
+        audio_paths = [a.file_path for a in self.song.media_files]
+        log.debug(audio_paths)
+        save_path = AppLocation.get_section_data_path(self.media_item.plugin.name) / 'audio' / str(self.song.id)
+        check_directory_exists(save_path)
         self.song.media_files = []
-        files = []
+        file_paths = []
         for row in range(self.audio_list_widget.count()):
             item = self.audio_list_widget.item(row)
-            filename = item.data(QtCore.Qt.UserRole)
-            if not filename.startswith(save_path):
-                old_file, filename = filename, os.path.join(save_path, os.path.split(filename)[1])
-                shutil.copyfile(old_file, filename)
-            files.append(filename)
+            file_path = item.data(QtCore.Qt.UserRole)
+            if save_path not in file_path.parents:
+                old_file_path, file_path = file_path, save_path / file_path.name
+                copyfile(old_file_path, file_path)
+            file_paths.append(file_path)
             media_file = MediaFile()
-            media_file.file_name = filename
+            media_file.file_path = file_path
             media_file.type = 'audio'
             media_file.weight = row
             self.song.media_files.append(media_file)
-        for audio in audio_files:
-            if audio not in files:
+        for audio_path in audio_paths:
+            if audio_path not in file_paths:
                 try:
-                    os.remove(audio)
+                    audio_path.unlink()
                 except:
-                    log.exception('Could not remove file: {audio}'.format(audio=audio))
-        if not files:
+                    log.exception('Could not remove file: {audio}'.format(audio=audio_path))
+        if not file_paths:
             try:
-                os.rmdir(save_path)
+                save_path.rmdir()
             except OSError:
                 log.exception('Could not remove directory: {path}'.format(path=save_path))
         clean_song(self.manager, self.song)

=== modified file 'openlp/plugins/songs/forms/mediafilesform.py'
--- openlp/plugins/songs/forms/mediafilesform.py	2017-06-04 12:14:23 +0000
+++ openlp/plugins/songs/forms/mediafilesform.py	2017-09-30 23:24:21 +0000
@@ -41,12 +41,19 @@
                                              QtCore.Qt.WindowCloseButtonHint)
         self.setupUi(self)
 
-    def populate_files(self, files):
+    def populate_files(self, file_paths):
+        """
+        :param list[openlp.core.common.path.Path] file_paths:
+        :return:
+        """
         self.file_list_widget.clear()
-        for file in files:
-            item = QtWidgets.QListWidgetItem(os.path.split(file)[1])
-            item.setData(QtCore.Qt.UserRole, file)
+        for file_path in file_paths:
+            item = QtWidgets.QListWidgetItem(file_path.name)
+            item.setData(QtCore.Qt.UserRole, file_path)
             self.file_list_widget.addItem(item)
 
     def get_selected_files(self):
+        """
+        :rtype: list[openlp.core.common.path.Path]
+        """
         return [item.data(QtCore.Qt.UserRole) for item in self.file_list_widget.selectedItems()]

=== modified file 'openlp/plugins/songs/forms/songexportform.py'
--- openlp/plugins/songs/forms/songexportform.py	2017-05-30 18:42:35 +0000
+++ openlp/plugins/songs/forms/songexportform.py	2017-09-30 23:24:21 +0000
@@ -27,9 +27,10 @@
 
 from PyQt5 import QtCore, QtWidgets
 
-from openlp.core.common import Registry, UiStrings, translate
-from openlp.core.lib import create_separated_list, build_icon
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import create_separated_list
 from openlp.core.lib.ui import critical_error_message_box
+from openlp.core.ui.lib import PathEdit, PathType
 from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
 from openlp.plugins.songs.lib.db import Song
 from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
@@ -76,7 +77,6 @@
         self.search_line_edit.textEdited.connect(self.on_search_line_edit_changed)
         self.uncheck_button.clicked.connect(self.on_uncheck_button_clicked)
         self.check_button.clicked.connect(self.on_check_button_clicked)
-        self.directory_button.clicked.connect(self.on_directory_button_clicked)
 
     def add_custom_pages(self):
         """
@@ -120,21 +120,15 @@
         self.grid_layout.setObjectName('range_layout')
         self.selected_list_widget = QtWidgets.QListWidget(self.export_song_page)
         self.selected_list_widget.setObjectName('selected_list_widget')
-        self.grid_layout.addWidget(self.selected_list_widget, 1, 0, 1, 1)
-        # FIXME: self.horizontal_layout is already defined above?!?!? Replace with Path Eidt!
-        self.horizontal_layout = QtWidgets.QHBoxLayout()
-        self.horizontal_layout.setObjectName('horizontal_layout')
+        self.grid_layout.addWidget(self.selected_list_widget, 1, 0, 1, 2)
+        self.output_directory_path_edit = PathEdit(
+            self.export_song_page, PathType.Directories,
+            dialog_caption=translate('SongsPlugin.ExportWizardForm', 'Select Destination Folder'), show_revert=False)
+        self.output_directory_path_edit.path = Settings().value('songs/last directory export')
         self.directory_label = QtWidgets.QLabel(self.export_song_page)
         self.directory_label.setObjectName('directory_label')
-        self.horizontal_layout.addWidget(self.directory_label)
-        self.directory_line_edit = QtWidgets.QLineEdit(self.export_song_page)
-        self.directory_line_edit.setObjectName('directory_line_edit')
-        self.horizontal_layout.addWidget(self.directory_line_edit)
-        self.directory_button = QtWidgets.QToolButton(self.export_song_page)
-        self.directory_button.setIcon(build_icon(':/exports/export_load.png'))
-        self.directory_button.setObjectName('directory_button')
-        self.horizontal_layout.addWidget(self.directory_button)
-        self.grid_layout.addLayout(self.horizontal_layout, 0, 0, 1, 1)
+        self.grid_layout.addWidget(self.directory_label, 0, 0)
+        self.grid_layout.addWidget(self.output_directory_path_edit, 0, 1)
         self.export_song_layout.addLayout(self.grid_layout)
         self.addPage(self.export_song_page)
 
@@ -188,11 +182,12 @@
                 self.selected_list_widget.addItem(song)
             return True
         elif self.currentPage() == self.export_song_page:
-            if not self.directory_line_edit.text():
+            if not self.output_directory_path_edit.path:
                 critical_error_message_box(
                     translate('SongsPlugin.ExportWizardForm', 'No Save Location specified'),
                     translate('SongsPlugin.ExportWizardForm', 'You need to specify a directory.'))
                 return False
+            Settings().setValue('songs/last directory export', self.output_directory_path_edit.path)
             return True
         elif self.currentPage() == self.progress_page:
             self.available_list_widget.clear()
@@ -211,8 +206,6 @@
         self.finish_button.setVisible(False)
         self.cancel_button.setVisible(True)
         self.available_list_widget.clear()
-        self.selected_list_widget.clear()
-        self.directory_line_edit.clear()
         self.search_line_edit.clear()
         # Load the list of songs.
         self.application.set_busy_cursor()
@@ -247,7 +240,7 @@
             song.data(QtCore.Qt.UserRole)
             for song in find_list_widget_items(self.selected_list_widget)
         ]
-        exporter = OpenLyricsExport(self, songs, self.directory_line_edit.text())
+        exporter = OpenLyricsExport(self, songs, self.output_directory_path_edit.path)
         try:
             if exporter.do_export():
                 self.progress_label.setText(
@@ -291,15 +284,6 @@
             if not item.isHidden():
                 item.setCheckState(QtCore.Qt.Checked)
 
-    def on_directory_button_clicked(self):
-        """
-        Called when the *directory_button* was clicked. Opens a dialog and writes
-        the path to *directory_line_edit*.
-        """
-        self.get_folder(
-            translate('SongsPlugin.ExportWizardForm', 'Select Destination Folder'),
-            self.directory_line_edit, 'last directory export')
-
 
 def find_list_widget_items(list_widget, text=''):
     """

=== modified file 'openlp/plugins/songs/forms/songimportform.py'
--- openlp/plugins/songs/forms/songimportform.py	2017-08-26 15:06:11 +0000
+++ openlp/plugins/songs/forms/songimportform.py	2017-09-30 23:24:21 +0000
@@ -22,15 +22,13 @@
 """
 The song import functions for OpenLP.
 """
-import codecs
 import logging
-import os
 
 from PyQt5 import QtCore, QtWidgets
 
 from openlp.core.common import RegistryProperties, Settings, UiStrings, translate
-from openlp.core.common.path import path_to_str, str_to_path
 from openlp.core.lib.ui import critical_error_message_box
+from openlp.core.ui.lib import PathEdit, PathType
 from openlp.core.ui.lib.filedialog import FileDialog
 from openlp.core.ui.lib.wizard import OpenLPWizard, WizardStrings
 from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect
@@ -93,9 +91,7 @@
                 self.format_widgets[song_format]['addButton'].clicked.connect(self.on_add_button_clicked)
                 self.format_widgets[song_format]['removeButton'].clicked.connect(self.on_remove_button_clicked)
             else:
-                self.format_widgets[song_format]['browseButton'].clicked.connect(self.on_browse_button_clicked)
-                self.format_widgets[song_format]['file_path_edit'].textChanged.\
-                    connect(self.on_filepath_edit_text_changed)
+                self.format_widgets[song_format]['path_edit'].pathChanged.connect(self.on_path_edit_path_changed)
 
     def add_custom_pages(self):
         """
@@ -155,7 +151,6 @@
                 self.format_widgets[format_list]['removeButton'].setText(
                     translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
             else:
-                self.format_widgets[format_list]['browseButton'].setText(UiStrings().Browse)
                 f_label = 'Filename:'
                 if select_mode == SongFormatSelect.SingleFolder:
                     f_label = 'Folder:'
@@ -172,16 +167,11 @@
         self.error_save_to_button.setText(translate('SongsPlugin.ImportWizardForm', 'Save to File'))
         # Align all QFormLayouts towards each other.
         formats = [f for f in SongFormat.get_format_list() if 'filepathLabel' in self.format_widgets[f]]
-        labels = [self.format_widgets[f]['filepathLabel'] for f in formats]
+        labels = [self.format_widgets[f]['filepathLabel'] for f in formats] + [self.format_label]
         # Get max width of all labels
-        max_label_width = max(self.format_label.minimumSizeHint().width(),
-                              max([label.minimumSizeHint().width() for label in labels]))
-        self.format_spacer.changeSize(max_label_width, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
-        spacers = [self.format_widgets[f]['filepathSpacer'] for f in formats]
-        for index, spacer in enumerate(spacers):
-            spacer.changeSize(
-                max_label_width - labels[index].minimumSizeHint().width(), 0,
-                QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
+        max_label_width = max(labels, key=lambda label: label.minimumSizeHint().width()).minimumSizeHint().width()
+        for label in labels:
+            label.setFixedWidth(max_label_width)
         # Align descriptionLabels with rest of layout
         for format_list in SongFormat.get_format_list():
             if SongFormat.get(format_list, 'descriptionText') is not None:
@@ -209,13 +199,13 @@
             Settings().setValue('songs/last import type', this_format)
             select_mode, class_, error_msg = SongFormat.get(this_format, 'selectMode', 'class', 'invalidSourceMsg')
             if select_mode == SongFormatSelect.MultipleFiles:
-                import_source = self.get_list_of_files(self.format_widgets[this_format]['file_list_widget'])
+                import_source = self.get_list_of_paths(self.format_widgets[this_format]['file_list_widget'])
                 error_title = UiStrings().IFSp
                 focus_button = self.format_widgets[this_format]['addButton']
             else:
-                import_source = self.format_widgets[this_format]['file_path_edit'].text()
+                import_source = self.format_widgets[this_format]['path_edit'].path
                 error_title = (UiStrings().IFSs if select_mode == SongFormatSelect.SingleFile else UiStrings().IFdSs)
-                focus_button = self.format_widgets[this_format]['browseButton']
+                focus_button = self.format_widgets[this_format]['path_edit']
             if not class_.is_valid_source(import_source):
                 critical_error_message_box(error_title, error_msg)
                 focus_button.setFocus()
@@ -238,20 +228,23 @@
         if filters:
             filters += ';;'
         filters += '{text} (*)'.format(text=UiStrings().AllFiles)
-        file_paths, selected_filter = FileDialog.getOpenFileNames(
-            self, title, Settings().value(self.plugin.settings_section + '/last directory import'), filters)
+        file_paths, filter_used = FileDialog.getOpenFileNames(
+            self, title,
+            Settings().value(self.plugin.settings_section + '/last directory import'), filters)
+        for file_path in file_paths:
+            list_item = QtWidgets.QListWidgetItem(str(file_path))
+            list_item.setData(QtCore.Qt.UserRole, file_path)
+            listbox.addItem(list_item)
         if file_paths:
-            file_names = [path_to_str(file_path) for file_path in file_paths]
-            listbox.addItems(file_names)
             Settings().setValue(self.plugin.settings_section + '/last directory import', file_paths[0].parent)
 
-    def get_list_of_files(self, list_box):
+    def get_list_of_paths(self, list_box):
         """
         Return a list of file from the list_box
 
         :param list_box: The source list box
         """
-        return [list_box.item(i).text() for i in range(list_box.count())]
+        return [list_box.item(i).data(QtCore.Qt.UserRole) for i in range(list_box.count())]
 
     def remove_selected_items(self, list_box):
         """
@@ -263,20 +256,6 @@
             item = list_box.takeItem(list_box.row(item))
             del item
 
-    def on_browse_button_clicked(self):
-        """
-        Browse for files or a directory.
-        """
-        this_format = self.current_format
-        select_mode, format_name, ext_filter = SongFormat.get(this_format, 'selectMode', 'name', 'filter')
-        file_path_edit = self.format_widgets[this_format]['file_path_edit']
-        if select_mode == SongFormatSelect.SingleFile:
-            self.get_file_name(WizardStrings.OpenTypeFile.format(file_type=format_name),
-                               file_path_edit, 'last directory import', ext_filter)
-        elif select_mode == SongFormatSelect.SingleFolder:
-            self.get_folder(
-                WizardStrings.OpenTypeFolder.format(folder_name=format_name), file_path_edit, 'last directory import')
-
     def on_add_button_clicked(self):
         """
         Add a file or directory.
@@ -296,7 +275,7 @@
         self.remove_selected_items(self.format_widgets[self.current_format]['file_list_widget'])
         self.source_page.completeChanged.emit()
 
-    def on_filepath_edit_text_changed(self):
+    def on_path_edit_path_changed(self):
         """
         Called when the content of the Filename/Folder edit box changes.
         """
@@ -317,8 +296,6 @@
             select_mode = SongFormat.get(format_list, 'selectMode')
             if select_mode == SongFormatSelect.MultipleFiles:
                 self.format_widgets[format_list]['file_list_widget'].clear()
-            else:
-                self.format_widgets[format_list]['file_path_edit'].setText('')
         self.error_report_text_edit.clear()
         self.error_report_text_edit.setHidden(True)
         self.error_copy_to_button.setHidden(True)
@@ -341,14 +318,14 @@
         select_mode = SongFormat.get(source_format, 'selectMode')
         if select_mode == SongFormatSelect.SingleFile:
             importer = self.plugin.import_songs(source_format,
-                                                filename=self.format_widgets[source_format]['file_path_edit'].text())
+                                                file_path=self.format_widgets[source_format]['path_edit'].path)
         elif select_mode == SongFormatSelect.SingleFolder:
             importer = self.plugin.import_songs(source_format,
-                                                folder=self.format_widgets[source_format]['file_path_edit'].text())
+                                                folder_path=self.format_widgets[source_format]['path_edit'].path)
         else:
             importer = self.plugin.import_songs(
                 source_format,
-                filenames=self.get_list_of_files(self.format_widgets[source_format]['file_list_widget']))
+                file_paths=self.get_list_of_paths(self.format_widgets[source_format]['file_list_widget']))
         importer.do_import()
         self.progress_label.setText(WizardStrings.FinishedImport)
 
@@ -366,18 +343,17 @@
         """
         file_path, filter_used = FileDialog.getSaveFileName(
             self, Settings().value(self.plugin.settings_section + '/last directory import'))
-        if not file_path:
+        if file_path is None:
             return
-        with file_path.open('w', encoding='utf-8') as report_file:
-            report_file.write(self.error_report_text_edit.toPlainText())
+        file_path.write_text(self.error_report_text_edit.toPlainText(), encoding='utf-8')
 
     def add_file_select_item(self):
         """
         Add a file selection page.
         """
         this_format = self.current_format
-        prefix, can_disable, description_text, select_mode = \
-            SongFormat.get(this_format, 'prefix', 'canDisable', 'descriptionText', 'selectMode')
+        format_name, prefix, can_disable, description_text, select_mode, filters = \
+            SongFormat.get(this_format, 'name', 'prefix', 'canDisable', 'descriptionText', 'selectMode', 'filter')
         page = QtWidgets.QWidget()
         page.setObjectName(prefix + 'Page')
         if can_disable:
@@ -403,26 +379,23 @@
         if select_mode == SongFormatSelect.SingleFile or select_mode == SongFormatSelect.SingleFolder:
             file_path_layout = QtWidgets.QHBoxLayout()
             file_path_layout.setObjectName(prefix + '_file_path_layout')
-            file_path_layout.setContentsMargins(0, self.format_v_spacing, 0, 0)
             file_path_label = QtWidgets.QLabel(import_widget)
-            file_path_label.setObjectName(prefix + 'FilepathLabel')
             file_path_layout.addWidget(file_path_label)
-            file_path_spacer = QtWidgets.QSpacerItem(0, 0, QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
-            file_path_layout.addSpacerItem(file_path_spacer)
-            file_path_edit = QtWidgets.QLineEdit(import_widget)
-            file_path_edit.setObjectName(prefix + '_file_path_edit')
-            file_path_layout.addWidget(file_path_edit)
-            browse_button = QtWidgets.QToolButton(import_widget)
-            browse_button.setIcon(self.open_icon)
-            browse_button.setObjectName(prefix + 'BrowseButton')
-            file_path_layout.addWidget(browse_button)
+            if select_mode == SongFormatSelect.SingleFile:
+                path_type = PathType.Files
+                dialog_caption = WizardStrings.OpenTypeFile.format(file_type=format_name)
+            else:
+                path_type = PathType.Directories
+                dialog_caption = WizardStrings.OpenTypeFolder.format(folder_name=format_name)
+            path_edit = PathEdit(
+                parent=import_widget, path_type=path_type, dialog_caption=dialog_caption, show_revert=False)
+            path_edit.filters = path_edit.filters + filters
+            path_edit.path = Settings().value(self.plugin.settings_section + '/last directory import')
+            file_path_layout.addWidget(path_edit)
             import_layout.addLayout(file_path_layout)
             import_layout.addSpacerItem(self.stack_spacer)
             self.format_widgets[this_format]['filepathLabel'] = file_path_label
-            self.format_widgets[this_format]['filepathSpacer'] = file_path_spacer
-            self.format_widgets[this_format]['file_path_layout'] = file_path_layout
-            self.format_widgets[this_format]['file_path_edit'] = file_path_edit
-            self.format_widgets[this_format]['browseButton'] = browse_button
+            self.format_widgets[this_format]['path_edit'] = path_edit
         elif select_mode == SongFormatSelect.MultipleFiles:
             file_list_widget = QtWidgets.QListWidget(import_widget)
             file_list_widget.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
@@ -496,6 +469,8 @@
         * or if SingleFolder mode, the specified folder exists
 
         When this method returns True, the wizard's Next button is enabled.
+
+        :rtype: bool
         """
         wizard = self.wizard()
         this_format = wizard.current_format
@@ -505,10 +480,10 @@
                 if wizard.format_widgets[this_format]['file_list_widget'].count() > 0:
                     return True
             else:
-                file_path = str(wizard.format_widgets[this_format]['file_path_edit'].text())
+                file_path = wizard.format_widgets[this_format]['path_edit'].path
                 if file_path:
-                    if select_mode == SongFormatSelect.SingleFile and os.path.isfile(file_path):
+                    if select_mode == SongFormatSelect.SingleFile and file_path.is_file():
                         return True
-                    elif select_mode == SongFormatSelect.SingleFolder and os.path.isdir(file_path):
+                    elif select_mode == SongFormatSelect.SingleFolder and file_path.is_dir():
                         return True
         return False

=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py	2017-08-03 17:54:40 +0000
+++ openlp/plugins/songs/lib/__init__.py	2017-09-30 23:24:21 +0000
@@ -534,13 +534,13 @@
     media_files = song_plugin.manager.get_all_objects(MediaFile, MediaFile.song_id == song_id)
     for media_file in media_files:
         try:
-            os.remove(media_file.file_name)
+            media_file.file_path.unlink()
         except OSError:
-            log.exception('Could not remove file: {name}'.format(name=media_file.file_name))
+            log.exception('Could not remove file: {name}'.format(name=media_file.file_path))
     try:
-        save_path = os.path.join(str(AppLocation.get_section_data_path(song_plugin.name)), 'audio', str(song_id))
-        if os.path.exists(save_path):
-            os.rmdir(save_path)
+        save_path = AppLocation.get_section_data_path(song_plugin.name) / 'audio' / str(song_id)
+        if save_path.exists():
+            save_path.rmdir()
     except OSError:
         log.exception('Could not remove directory: {path}'.format(path=save_path))
     song_plugin.manager.delete_object(Song, song_id)

=== modified file 'openlp/plugins/songs/lib/db.py'
--- openlp/plugins/songs/lib/db.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/db.py	2017-09-30 23:24:21 +0000
@@ -23,14 +23,15 @@
 The :mod:`db` module provides the database and schema that is the backend for
 the Songs plugin
 """
-
+from contextlib import suppress
 from sqlalchemy import Column, ForeignKey, Table, types
 from sqlalchemy.orm import mapper, relation, reconstructor
 from sqlalchemy.sql.expression import func, text
 
-from openlp.core.lib.db import BaseModel, init_db
+from openlp.core.common.applocation import AppLocation
 from openlp.core.common.languagemanager import get_natural_key
 from openlp.core.lib import translate
+from openlp.core.lib.db import BaseModel, PathType, init_db
 
 
 class Author(BaseModel):
@@ -238,7 +239,7 @@
 
     **media_files Table**
         * id
-        * file_name
+        * _file_path
         * type
 
     **song_books Table**
@@ -305,7 +306,7 @@
         'media_files', metadata,
         Column('id', types.Integer(), primary_key=True),
         Column('song_id', types.Integer(), ForeignKey('songs.id'), default=None),
-        Column('file_name', types.Unicode(255), nullable=False),
+        Column('file_path', PathType, nullable=False),
         Column('type', types.Unicode(64), nullable=False, default='audio'),
         Column('weight', types.Integer(), default=0)
     )

=== modified file 'openlp/plugins/songs/lib/importers/cclifile.py'
--- openlp/plugins/songs/lib/importers/cclifile.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/cclifile.py	2017-09-30 23:24:21 +0000
@@ -19,11 +19,9 @@
 # with this program; if not, write to the Free Software Foundation, Inc., 59  #
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
-
-import logging
-import os
 import chardet
 import codecs
+import logging
 
 from openlp.core.lib import translate
 from openlp.plugins.songs.lib import VerseType
@@ -48,7 +46,7 @@
         :param manager: The song manager for the running OpenLP installation.
         :param kwargs:  The files to be imported.
         """
-        super(CCLIFileImport, self).__init__(manager, **kwargs)
+        super().__init__(manager, **kwargs)
 
     def do_import(self):
         """
@@ -56,37 +54,35 @@
         """
         log.debug('Starting CCLI File Import')
         self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-        for filename in self.import_source:
-            filename = str(filename)
-            log.debug('Importing CCLI File: {name}'.format(name=filename))
-            if os.path.isfile(filename):
-                detect_file = open(filename, 'rb')
-                detect_content = detect_file.read(2048)
-                try:
-                    str(detect_content, 'utf-8')
-                    details = {'confidence': 1, 'encoding': 'utf-8'}
-                except UnicodeDecodeError:
-                    details = chardet.detect(detect_content)
-                detect_file.close()
-                infile = codecs.open(filename, 'r', details['encoding'])
-                if not infile.read(1) == '\ufeff':
+        for file_path in self.import_source:
+            log.debug('Importing CCLI File: {name}'.format(name=file_path))
+            if file_path.is_file():
+                with file_path.open('rb') as detect_file:
+                    detect_content = detect_file.read(2048)
+                    try:
+                        str(detect_content, 'utf-8')
+                        details = {'confidence': 1, 'encoding': 'utf-8'}
+                    except UnicodeDecodeError:
+                        details = chardet.detect(detect_content)
+                in_file = codecs.open(str(file_path), 'r', details['encoding'])
+                if not in_file.read(1) == '\ufeff':
                     # not UTF or no BOM was found
-                    infile.seek(0)
-                lines = infile.readlines()
-                infile.close()
-                ext = os.path.splitext(filename)[1]
-                if ext.lower() == '.usr' or ext.lower() == '.bin':
-                    log.info('SongSelect USR format file found: {name}'.format(name=filename))
+                    in_file.seek(0)
+                lines = in_file.readlines()
+                in_file.close()
+                ext = file_path.suffix.lower()
+                if ext == '.usr' or ext == '.bin':
+                    log.info('SongSelect USR format file found: {name}'.format(name=file_path))
                     if not self.do_import_usr_file(lines):
-                        self.log_error(filename)
-                elif ext.lower() == '.txt':
-                    log.info('SongSelect TEXT format file found: {name}'.format(name=filename))
+                        self.log_error(file_path)
+                elif ext == '.txt':
+                    log.info('SongSelect TEXT format file found: {name}'.format(name=file_path))
                     if not self.do_import_txt_file(lines):
-                        self.log_error(filename)
+                        self.log_error(file_path)
                 else:
-                    self.log_error(filename, translate('SongsPlugin.CCLIFileImport', 'The file does not have a valid '
-                                                                                     'extension.'))
-                    log.info('Extension {name} is not valid'.format(name=filename))
+                    self.log_error(file_path, translate('SongsPlugin.CCLIFileImport',
+                                                        'The file does not have a valid extension.'))
+                    log.info('Extension {name} is not valid'.format(name=file_path))
             if self.stop_import_flag:
                 return
 

=== modified file 'openlp/plugins/songs/lib/importers/chordpro.py'
--- openlp/plugins/songs/lib/importers/chordpro.py	2017-02-26 21:14:49 +0000
+++ openlp/plugins/songs/lib/importers/chordpro.py	2017-09-30 23:24:21 +0000
@@ -47,12 +47,11 @@
     """
     def do_import(self):
         self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-        for filename in self.import_source:
+        for file_path in self.import_source:
             if self.stop_import_flag:
                 return
-            song_file = open(filename, 'rt')
-            self.do_import_file(song_file)
-            song_file.close()
+            with file_path.open('rt') as song_file:
+                self.do_import_file(song_file)
 
     def do_import_file(self, song_file):
         """

=== modified file 'openlp/plugins/songs/lib/importers/dreambeam.py'
--- openlp/plugins/songs/lib/importers/dreambeam.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/dreambeam.py	2017-09-30 23:24:21 +0000
@@ -78,27 +78,29 @@
 
     def do_import(self):
         """
-        Receive a single file or a list of files to import.
+        Receive a single file_path or a list of files to import.
         """
         if isinstance(self.import_source, list):
             self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-            for file in self.import_source:
+            for file_path in self.import_source:
                 if self.stop_import_flag:
                     return
                 self.set_defaults()
                 parser = etree.XMLParser(remove_blank_text=True)
                 try:
-                    parsed_file = etree.parse(open(file, 'r'), parser)
+                    with file_path.open('r') as xml_file:
+                        parsed_file = etree.parse(xml_file, parser)
                 except etree.XMLSyntaxError:
-                    log.exception('XML syntax error in file {name}'.format(name=file))
-                    self.log_error(file, SongStrings.XMLSyntaxError)
+                    log.exception('XML syntax error in file_path {name}'.format(name=file_path))
+                    self.log_error(file_path, SongStrings.XMLSyntaxError)
                     continue
                 xml = etree.tostring(parsed_file).decode()
                 song_xml = objectify.fromstring(xml)
                 if song_xml.tag != 'DreamSong':
                     self.log_error(
-                        file,
-                        translate('SongsPlugin.DreamBeamImport', 'Invalid DreamBeam song file. Missing DreamSong tag.'))
+                        file_path,
+                        translate('SongsPlugin.DreamBeamImport',
+                                  'Invalid DreamBeam song file_path. Missing DreamSong tag.'))
                     continue
                 if hasattr(song_xml, 'Version'):
                     self.version = float(song_xml.Version.text)
@@ -144,4 +146,4 @@
                     else:
                         self.parse_author(author_copyright)
                 if not self.finish():
-                    self.log_error(file)
+                    self.log_error(file_path)

=== modified file 'openlp/plugins/songs/lib/importers/easyslides.py'
--- openlp/plugins/songs/lib/importers/easyslides.py	2017-01-12 21:31:01 +0000
+++ openlp/plugins/songs/lib/importers/easyslides.py	2017-09-30 23:24:21 +0000
@@ -47,7 +47,7 @@
     def do_import(self):
         log.info('Importing EasySlides XML file {source}'.format(source=self.import_source))
         parser = etree.XMLParser(remove_blank_text=True, recover=True)
-        parsed_file = etree.parse(self.import_source, parser)
+        parsed_file = etree.parse(str(self.import_source), parser)
         xml = etree.tostring(parsed_file).decode()
         song_xml = objectify.fromstring(xml)
         self.import_wizard.progress_bar.setMaximum(len(song_xml.Item))

=== modified file 'openlp/plugins/songs/lib/importers/easyworship.py'
--- openlp/plugins/songs/lib/importers/easyworship.py	2017-04-01 04:45:12 +0000
+++ openlp/plugins/songs/lib/importers/easyworship.py	2017-09-30 23:24:21 +0000
@@ -22,14 +22,15 @@
 """
 The :mod:`easyworship` module provides the functionality for importing EasyWorship song databases into OpenLP.
 """
-
+import logging
 import os
+import re
 import struct
-import re
 import zlib
-import logging
+
 import sqlite3
 
+from openlp.core.common.path import Path
 from openlp.core.lib import translate
 from openlp.plugins.songs.lib import VerseType
 from openlp.plugins.songs.lib import retrieve_windows_encoding, strip_rtf
@@ -76,9 +77,11 @@
         """
         Determines the type of file to import and calls the appropiate method
         """
-        if self.import_source.lower().endswith('ews'):
+        self.import_source = Path(self.import_source)
+        ext = self.import_source.suffix.lower()
+        if ext == '.ews':
             self.import_ews()
-        elif self.import_source.endswith('DB'):
+        elif ext == '.db':
             self.import_db()
         else:
             self.import_sqlite_db()
@@ -91,11 +94,11 @@
         or here: http://wiki.openlp.org/Development:EasyWorship_EWS_Format
         """
         # Open ews file if it exists
-        if not os.path.isfile(self.import_source):
+        if not self.import_source.is_file():
             log.debug('Given ews file does not exists.')
             return
         # Make sure there is room for at least a header and one entry
-        if os.path.getsize(self.import_source) < 892:
+        if self.import_source.stat().st_size < 892:
             log.debug('Given ews file is to small to contain valid data.')
             return
         # Take a stab at how text is encoded
@@ -104,7 +107,7 @@
         if not self.encoding:
             log.debug('No encoding set.')
             return
-        self.ews_file = open(self.import_source, 'rb')
+        self.ews_file = self.import_source.open('rb')
         # EWS header, version '1.6'/'  3'/'  5':
         # Offset   Field             Data type    Length    Details
         # --------------------------------------------------------------------------------------------------
@@ -199,23 +202,22 @@
         Import the songs from the database
         """
         # Open the DB and MB files if they exist
-        import_source_mb = self.import_source.replace('.DB', '.MB')
-        if not os.path.isfile(self.import_source):
+        import_source_mb = self.import_source.with_suffix('.MB')
+        if not self.import_source.is_file():
             self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
                                                          'This file does not exist.'))
             return
-        if not os.path.isfile(import_source_mb):
+        if not import_source_mb.is_file():
             self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
                                                          'Could not find the "Songs.MB" file. It must be in the same '
                                                          'folder as the "Songs.DB" file.'))
             return
-        db_size = os.path.getsize(self.import_source)
-        if db_size < 0x800:
+        if self.import_source.stat().st_size < 0x800:
             self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
                                                          'This file is not a valid EasyWorship database.'))
             return
-        db_file = open(self.import_source, 'rb')
-        self.memo_file = open(import_source_mb, 'rb')
+        db_file = self.import_source.open('rb')
+        self.memo_file = import_source_mb.open('rb')
         # Don't accept files that are clearly not paradox files
         record_size, header_size, block_size, first_block, num_fields = struct.unpack('<hhxb8xh17xh', db_file.read(35))
         if header_size != 0x800 or block_size < 1 or block_size > 4:
@@ -340,52 +342,34 @@
         db_file.close()
         self.memo_file.close()
 
-    def _find_file(self, base_path, path_list):
-        """
-        Find the specified file, with the option of the file being at any level in the specified directory structure.
-
-        :param base_path: the location search in
-        :param path_list: the targeted file, preceded by directories that may be their parents relative to the base_path
-        :return: path for targeted file
-        """
-        target_file = ''
-        while len(path_list) > 0:
-            target_file = os.path.join(path_list[-1], target_file)
-            path_list = path_list[:len(path_list) - 1]
-            full_path = os.path.join(base_path, target_file)
-            full_path = full_path[:len(full_path) - 1]
-            if os.path.isfile(full_path):
-                return full_path
-        return ''
-
     def import_sqlite_db(self):
         """
         Import the songs from an EasyWorship 6 SQLite database
         """
-        songs_db_path = self._find_file(self.import_source, ["Databases", "Data", "Songs.db"])
-        song_words_db_path = self._find_file(self.import_source, ["Databases", "Data", "SongWords.db"])
-        invalid_dir_msg = 'This does not appear to be a valid Easy Worship 6 database directory.'
+        songs_db_path = next(self.import_source.rglob('Songs.db'), None)
+        song_words_db_path = next(self.import_source.rglob('SongWords.db'), None)
+        invalid_dir_msg = translate('SongsPlugin.EasyWorshipSongImport',
+                                    'This does not appear to be a valid Easy Worship 6 database directory.')
+        invalid_db_msg = translate('SongsPlugin.EasyWorshipSongImport', 'This is not a valid Easy Worship 6 database.')
         # check to see if needed files are there
-        if not os.path.isfile(songs_db_path):
-            self.log_error(songs_db_path, translate('SongsPlugin.EasyWorshipSongImport', invalid_dir_msg))
+        if not (songs_db_path and songs_db_path.is_file()):
+            self.log_error(self.import_source, invalid_dir_msg)
             return
-        if not os.path.isfile(song_words_db_path):
-            self.log_error(song_words_db_path, translate('SongsPlugin.EasyWorshipSongImport', invalid_dir_msg))
+        if not (song_words_db_path and song_words_db_path.is_file()):
+            self.log_error(self.import_source, invalid_dir_msg)
             return
         # get database handles
-        songs_conn = sqlite3.connect(songs_db_path)
-        words_conn = sqlite3.connect(song_words_db_path)
+        songs_conn = sqlite3.connect(str(songs_db_path))
+        words_conn = sqlite3.connect(str(song_words_db_path))
         if songs_conn is None or words_conn is None:
-            self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
-                                                         'This is not a valid Easy Worship 6 database.'))
+            self.log_error(self.import_source, invalid_db_msg)
             songs_conn.close()
             words_conn.close()
             return
         songs_db = songs_conn.cursor()
         words_db = words_conn.cursor()
         if songs_conn is None or words_conn is None:
-            self.log_error(self.import_source, translate('SongsPlugin.EasyWorshipSongImport',
-                                                         'This is not a valid Easy Worship 6 database.'))
+            self.log_error(self.import_source, invalid_db_msg)
             songs_conn.close()
             words_conn.close()
             return

=== modified file 'openlp/plugins/songs/lib/importers/foilpresenter.py'
--- openlp/plugins/songs/lib/importers/foilpresenter.py	2017-05-30 18:42:35 +0000
+++ openlp/plugins/songs/lib/importers/foilpresenter.py	2017-09-30 23:24:21 +0000
@@ -82,10 +82,8 @@
     </kategorien>
     </foilpresenterfolie>
 """
-
 import logging
 import re
-import os
 
 from lxml import etree, objectify
 
@@ -121,10 +119,9 @@
         for file_path in self.import_source:
             if self.stop_import_flag:
                 return
-            self.import_wizard.increment_progress_bar(
-                WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
+            self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name))
             try:
-                parsed_file = etree.parse(file_path, parser)
+                parsed_file = etree.parse(str(file_path), parser)
                 xml = etree.tostring(parsed_file).decode()
                 self.foil_presenter.xml_to_song(xml)
             except etree.XMLSyntaxError:

=== modified file 'openlp/plugins/songs/lib/importers/lyrix.py'
--- openlp/plugins/songs/lib/importers/lyrix.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/lyrix.py	2017-09-30 23:24:21 +0000
@@ -50,12 +50,11 @@
         if not isinstance(self.import_source, list):
             return
         self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-        for filename in self.import_source:
+        for file_path in self.import_source:
             if self.stop_import_flag:
                 return
-            song_file = open(filename, 'rt', encoding='cp1251')
-            self.do_import_file(song_file)
-            song_file.close()
+            with file_path.open('rt', encoding='cp1251') as song_file:
+                self.do_import_file(song_file)
 
     def do_import_file(self, file):
         """

=== modified file 'openlp/plugins/songs/lib/importers/openlp.py'
--- openlp/plugins/songs/lib/importers/openlp.py	2017-05-30 18:42:35 +0000
+++ openlp/plugins/songs/lib/importers/openlp.py	2017-09-30 23:24:21 +0000
@@ -266,7 +266,7 @@
             if has_media_files and song.media_files:
                 for media_file in song.media_files:
                     existing_media_file = self.manager.get_object_filtered(
-                        MediaFile, MediaFile.file_name == media_file.file_name)
+                        MediaFile, MediaFile.file_path == media_file.file_path)
                     if existing_media_file:
                         new_song.media_files.append(existing_media_file)
                     else:

=== modified file 'openlp/plugins/songs/lib/importers/openlyrics.py'
--- openlp/plugins/songs/lib/importers/openlyrics.py	2017-05-30 18:42:35 +0000
+++ openlp/plugins/songs/lib/importers/openlyrics.py	2017-09-30 23:24:21 +0000
@@ -23,9 +23,7 @@
 The :mod:`openlyrics` module provides the functionality for importing
 songs which are saved as OpenLyrics files.
 """
-
 import logging
-import os
 
 from lxml import etree
 
@@ -58,12 +56,11 @@
         for file_path in self.import_source:
             if self.stop_import_flag:
                 return
-            self.import_wizard.increment_progress_bar(
-                WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
+            self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name))
             try:
                 # Pass a file object, because lxml does not cope with some
                 # special characters in the path (see lp:757673 and lp:744337).
-                parsed_file = etree.parse(open(file_path, 'rb'), parser)
+                parsed_file = etree.parse(file_path.open('rb'), parser)
                 xml = etree.tostring(parsed_file).decode()
                 self.open_lyrics.xml_to_song(xml)
             except etree.XMLSyntaxError:

=== modified file 'openlp/plugins/songs/lib/importers/openoffice.py'
--- openlp/plugins/songs/lib/importers/openoffice.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/openoffice.py	2017-09-30 23:24:21 +0000
@@ -20,7 +20,6 @@
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
 import logging
-import os
 import time
 
 from PyQt5 import QtCore
@@ -70,12 +69,11 @@
             log.error(exc)
             return
         self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-        for filename in self.import_source:
+        for file_path in self.import_source:
             if self.stop_import_flag:
                 break
-            filename = str(filename)
-            if os.path.isfile(filename):
-                self.open_ooo_file(filename)
+            if file_path.is_file():
+                self.open_ooo_file(file_path)
                 if self.document:
                     self.process_ooo_document()
                     self.close_ooo_file()
@@ -144,12 +142,7 @@
         Open the passed file in OpenOffice.org Impress
         """
         self.file_path = file_path
-        if is_win():
-            url = file_path.replace('\\', '/')
-            url = url.replace(':', '|').replace(' ', '%20')
-            url = 'file:///' + url
-        else:
-            url = uno.systemPathToFileUrl(file_path)
+        url = file_path.as_uri()
         properties = []
         properties.append(self.create_property('Hidden', True))
         properties = tuple(properties)
@@ -159,7 +152,7 @@
                     self.document.supportsService("com.sun.star.text.TextDocument"):
                 self.close_ooo_file()
             else:
-                self.import_wizard.increment_progress_bar('Processing file ' + file_path, 0)
+                self.import_wizard.increment_progress_bar('Processing file {file_path}'.format(file_path=file_path), 0)
         except AttributeError:
             log.exception("open_ooo_file failed: {url}".format(url=url))
         return

=== modified file 'openlp/plugins/songs/lib/importers/opensong.py'
--- openlp/plugins/songs/lib/importers/opensong.py	2017-02-26 21:14:49 +0000
+++ openlp/plugins/songs/lib/importers/opensong.py	2017-09-30 23:24:21 +0000
@@ -19,7 +19,6 @@
 # with this program; if not, write to the Free Software Foundation, Inc., 59  #
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
-
 import logging
 import re
 
@@ -116,12 +115,11 @@
         if not isinstance(self.import_source, list):
             return
         self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-        for filename in self.import_source:
+        for file_path in self.import_source:
             if self.stop_import_flag:
                 return
-            song_file = open(filename, 'rb')
-            self.do_import_file(song_file)
-            song_file.close()
+            with file_path.open('rb') as song_file:
+                self.do_import_file(song_file)
 
     def do_import_file(self, file):
         """

=== modified file 'openlp/plugins/songs/lib/importers/opspro.py'
--- openlp/plugins/songs/lib/importers/opspro.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/opspro.py	2017-09-30 23:24:21 +0000
@@ -231,16 +231,15 @@
         xor_pattern_2k = (0xa1, 0xec, 0x7a, 0x9c, 0xe1, 0x28, 0x34, 0x8a, 0x73, 0x7b, 0xd2, 0xdf, 0x50)
         # Access97 XOR of the source
         xor_pattern_97 = (0x86, 0xfb, 0xec, 0x37, 0x5d, 0x44, 0x9c, 0xfa, 0xc6, 0x5e, 0x28, 0xe6, 0x13)
-        mdb = open(self.import_source, 'rb')
-        mdb.seek(0x14)
-        version = struct.unpack('B', mdb.read(1))[0]
-        # Get encrypted logo
-        mdb.seek(0x62)
-        EncrypFlag = struct.unpack('B', mdb.read(1))[0]
-        # Get encrypted password
-        mdb.seek(0x42)
-        encrypted_password = mdb.read(26)
-        mdb.close()
+        with self.import_source.open('rb') as mdb_file:
+            mdb_file.seek(0x14)
+            version = struct.unpack('B', mdb_file.read(1))[0]
+            # Get encrypted logo
+            mdb_file.seek(0x62)
+            EncrypFlag = struct.unpack('B', mdb_file.read(1))[0]
+            # Get encrypted password
+            mdb_file.seek(0x42)
+            encrypted_password = mdb_file.read(26)
         # "Decrypt" the password based on the version
         decrypted_password = ''
         if version < 0x01:

=== modified file 'openlp/plugins/songs/lib/importers/powerpraise.py'
--- openlp/plugins/songs/lib/importers/powerpraise.py	2017-05-30 18:42:35 +0000
+++ openlp/plugins/songs/lib/importers/powerpraise.py	2017-09-30 23:24:21 +0000
@@ -23,8 +23,6 @@
 The :mod:`powerpraiseimport` module provides the functionality for importing
 Powerpraise song files into the current database.
 """
-
-import os
 from lxml import objectify
 
 from openlp.core.ui.lib.wizard import WizardStrings
@@ -41,10 +39,10 @@
         for file_path in self.import_source:
             if self.stop_import_flag:
                 return
-            self.import_wizard.increment_progress_bar(
-                WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
-            root = objectify.parse(open(file_path, 'rb')).getroot()
-            self.process_song(root)
+            self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name))
+            with file_path.open('rb') as xml_file:
+                root = objectify.parse(xml_file).getroot()
+                self.process_song(root)
 
     def process_song(self, root):
         self.set_defaults()

=== modified file 'openlp/plugins/songs/lib/importers/powersong.py'
--- openlp/plugins/songs/lib/importers/powersong.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/powersong.py	2017-09-30 23:24:21 +0000
@@ -72,10 +72,14 @@
         Checks if source is a PowerSong 1.0 folder:
             * is a directory
             * contains at least one \*.song file
+
+        :param openlp.core.common.path.Path import_source: Should be a Path object that fulfills the above criteria
+        :return: If the source is valid
+        :rtype: bool
         """
-        if os.path.isdir(import_source):
-            for file in os.listdir(import_source):
-                if fnmatch.fnmatch(file, '*.song'):
+        if import_source.is_dir():
+            for file_path in import_source.iterdir():
+                if file_path.suffix == '.song':
                     return True
         return False
 

=== modified file 'openlp/plugins/songs/lib/importers/presentationmanager.py'
--- openlp/plugins/songs/lib/importers/presentationmanager.py	2017-05-30 18:42:35 +0000
+++ openlp/plugins/songs/lib/importers/presentationmanager.py	2017-09-30 23:24:21 +0000
@@ -23,13 +23,11 @@
 The :mod:`presentationmanager` module provides the functionality for importing
 Presentationmanager song files into the current database.
 """
-import os
 import re
 
-import chardet
 from lxml import objectify, etree
 
-from openlp.core.common import translate
+from openlp.core.common import get_file_encoding, translate
 from openlp.core.ui.lib.wizard import WizardStrings
 from .songimport import SongImport
 
@@ -44,17 +42,14 @@
         for file_path in self.import_source:
             if self.stop_import_flag:
                 return
-            self.import_wizard.increment_progress_bar(
-                WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
+            self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name))
             try:
-                tree = etree.parse(file_path, parser=etree.XMLParser(recover=True))
+                tree = etree.parse(str(file_path), parser=etree.XMLParser(recover=True))
             except etree.XMLSyntaxError:
                 # Try to detect encoding and use it
-                file = open(file_path, mode='rb')
-                encoding = chardet.detect(file.read())['encoding']
-                file.close()
+                encoding = get_file_encoding(file_path)['encoding']
                 # Open file with detected encoding and remove encoding declaration
-                text = open(file_path, mode='r', encoding=encoding).read()
+                text = file_path.read_text(encoding=encoding)
                 text = re.sub('.+\?>\n', '', text)
                 try:
                     tree = etree.fromstring(text, parser=etree.XMLParser(recover=True))
@@ -80,6 +75,11 @@
             return ''
 
     def process_song(self, root, file_path):
+        """
+        :param root:
+        :param openlp.core.common.path.Path file_path: Path to the file to process
+        :rtype: None
+        """
         self.set_defaults()
         attrs = None
         if hasattr(root, 'attributes'):
@@ -123,4 +123,4 @@
 
         self.verse_order_list = verse_order_list
         if not self.finish():
-            self.log_error(os.path.basename(file_path))
+            self.log_error(file_path.name)

=== modified file 'openlp/plugins/songs/lib/importers/propresenter.py'
--- openlp/plugins/songs/lib/importers/propresenter.py	2017-05-30 18:42:35 +0000
+++ openlp/plugins/songs/lib/importers/propresenter.py	2017-09-30 23:24:21 +0000
@@ -23,8 +23,6 @@
 The :mod:`propresenter` module provides the functionality for importing
 ProPresenter song files into the current installation database.
 """
-
-import os
 import base64
 import logging
 from lxml import objectify
@@ -47,11 +45,17 @@
             if self.stop_import_flag:
                 return
             self.import_wizard.increment_progress_bar(
-                WizardStrings.ImportingType.format(source=os.path.basename(file_path)))
-            root = objectify.parse(open(file_path, 'rb')).getroot()
-            self.process_song(root, file_path)
+                WizardStrings.ImportingType.format(source=file_path.name))
+            with file_path.open('rb') as xml_file:
+                root = objectify.parse(xml_file).getroot()
+                self.process_song(root, file_path)
 
-    def process_song(self, root, filename):
+    def process_song(self, root, file_path):
+        """
+        :param root:
+        :param openlp.core.common.path.Path file_path: Path to the file thats being imported
+        :rtype: None
+        """
         self.set_defaults()
 
         # Extract ProPresenter versionNumber
@@ -64,9 +68,7 @@
         # Title
         self.title = root.get('CCLISongTitle')
         if not self.title or self.title == '':
-            self.title = os.path.basename(filename)
-            if self.title[-5:-1] == '.pro':
-                self.title = self.title[:-5]
+            self.title = file_path.stem
         # Notes
         self.comments = root.get('notes')
         # Author

=== modified file 'openlp/plugins/songs/lib/importers/songbeamer.py'
--- openlp/plugins/songs/lib/importers/songbeamer.py	2017-08-25 20:03:25 +0000
+++ openlp/plugins/songs/lib/importers/songbeamer.py	2017-09-30 23:24:21 +0000
@@ -112,7 +112,7 @@
         if not isinstance(self.import_source, list):
             return
         self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-        for import_file in self.import_source:
+        for file_path in self.import_source:
             # TODO: check that it is a valid SongBeamer file
             if self.stop_import_flag:
                 return
@@ -120,20 +120,19 @@
             self.current_verse = ''
             self.current_verse_type = VerseType.tags[VerseType.Verse]
             self.chord_table = None
-            file_name = os.path.split(import_file)[1]
-            if os.path.isfile(import_file):
+            if file_path.is_file():
                 # Detect the encoding
-                self.input_file_encoding = get_file_encoding(Path(import_file))['encoding']
+                self.input_file_encoding = get_file_encoding(file_path)['encoding']
                 # The encoding should only be ANSI (cp1252), UTF-8, Unicode, Big-Endian-Unicode.
                 # So if it doesn't start with 'u' we default to cp1252. See:
                 # https://forum.songbeamer.com/viewtopic.php?p=419&sid=ca4814924e37c11e4438b7272a98b6f2
                 if not self.input_file_encoding.lower().startswith('u'):
                     self.input_file_encoding = 'cp1252'
-                infile = open(import_file, 'rt', encoding=self.input_file_encoding)
-                song_data = infile.readlines()
+                with file_path.open(encoding=self.input_file_encoding) as song_file:
+                    song_data = song_file.readlines()
             else:
                 continue
-            self.title = file_name.split('.sng')[0]
+            self.title = file_path.stem
             read_verses = False
             # The first verse separator doesn't count, but the others does, so line count starts at -1
             line_number = -1
@@ -185,7 +184,7 @@
                                 # inserted by songbeamer, but are manually added headings. So restart the loop, and
                                 # count tags as lines.
                                 self.set_defaults()
-                                self.title = file_name.split('.sng')[0]
+                                self.title = file_path.stem
                                 verse_tags_mode = VerseTagMode.ContainsNoTagsRestart
                                 read_verses = False
                                 # The first verseseparator doesn't count, but the others does, so linecount starts at -1
@@ -207,7 +206,7 @@
                 self.replace_html_tags()
                 self.add_verse(self.current_verse, self.current_verse_type)
             if not self.finish():
-                self.log_error(import_file)
+                self.log_error(file_path)
 
     def insert_chords(self, line_number, line):
         """
@@ -414,14 +413,15 @@
         """
         # The path is relative to SongBeamers Song folder
         if is_win():
-            user_doc_folder = os.path.expandvars('$DOCUMENTS')
+            user_doc_path = Path(os.path.expandvars('$DOCUMENTS'))
         elif is_macosx():
-            user_doc_folder = os.path.join(os.path.expanduser('~'), 'Documents')
+            user_doc_path = Path.home() / 'Documents'
         else:
             # SongBeamer only runs on mac and win...
             return
-        audio_file_path = os.path.normpath(os.path.join(user_doc_folder, 'SongBeamer', 'Songs', audio_file_path))
-        if os.path.isfile(audio_file_path):
+        audio_file_path = user_doc_path / 'SongBeamer' / 'Songs' / audio_file_path
+        if audio_file_path.is_file():
             self.add_media_file(audio_file_path)
         else:
-            log.debug('Could not import mediafile "%s" since it does not exists!' % audio_file_path)
+            log.debug('Could not import mediafile "{audio_file_path}" since it does not exists!'
+                      .format(audio_file_path=audio_file_path))

=== modified file 'openlp/plugins/songs/lib/importers/songimport.py'
--- openlp/plugins/songs/lib/importers/songimport.py	2017-08-25 20:03:25 +0000
+++ openlp/plugins/songs/lib/importers/songimport.py	2017-09-30 23:24:21 +0000
@@ -22,13 +22,11 @@
 
 import logging
 import re
-import shutil
-import os
 
 from PyQt5 import QtCore
 
 from openlp.core.common import Registry, AppLocation, check_directory_exists, translate
-from openlp.core.common.path import Path
+from openlp.core.common.path import copyfile
 from openlp.core.ui.lib.wizard import WizardStrings
 from openlp.plugins.songs.lib import clean_song, VerseType
 from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
@@ -62,14 +60,14 @@
         """
         self.manager = manager
         QtCore.QObject.__init__(self)
-        if 'filename' in kwargs:
-            self.import_source = kwargs['filename']
-        elif 'filenames' in kwargs:
-            self.import_source = kwargs['filenames']
-        elif 'folder' in kwargs:
-            self.import_source = kwargs['folder']
+        if 'file_path' in kwargs:
+            self.import_source = kwargs['file_path']
+        elif 'file_paths' in kwargs:
+            self.import_source = kwargs['file_paths']
+        elif 'folder_path' in kwargs:
+            self.import_source = kwargs['folder_path']
         else:
-            raise KeyError('Keyword arguments "filename[s]" or "folder" not supplied.')
+            raise KeyError('Keyword arguments "file_path[s]" or "folder_path" not supplied.')
         log.debug(self.import_source)
         self.import_wizard = None
         self.song = None
@@ -270,13 +268,13 @@
             return
         self.authors.append((author, type))
 
-    def add_media_file(self, filename, weight=0):
+    def add_media_file(self, file_path, weight=0):
         """
         Add a media file to the list
         """
-        if filename in [x[0] for x in self.media_files]:
+        if file_path in [x[0] for x in self.media_files]:
             return
-        self.media_files.append((filename, weight))
+        self.media_files.append((file_path, weight))
 
     def add_verse(self, verse_text, verse_def='v', lang=None):
         """
@@ -403,29 +401,30 @@
         self.manager.save_object(song)
         # Now loop through the media files, copy them to the correct location,
         # and save the song again.
-        for filename, weight in self.media_files:
-            media_file = self.manager.get_object_filtered(MediaFile, MediaFile.file_name == filename)
+        for file_path, weight in self.media_files:
+            media_file = self.manager.get_object_filtered(MediaFile, MediaFile.file_path == file_path)
             if not media_file:
-                if os.path.dirname(filename):
-                    filename = self.copy_media_file(song.id, filename)
-                song.media_files.append(MediaFile.populate(file_name=filename, weight=weight))
+                if file_path.parent:
+                    file_path = self.copy_media_file(song.id, file_path)
+                song.media_files.append(MediaFile.populate(file_path=file_path, weight=weight))
         self.manager.save_object(song)
         self.set_defaults()
         return True
 
-    def copy_media_file(self, song_id, filename):
+    def copy_media_file(self, song_id, file_path):
         """
         This method copies the media file to the correct location and returns
         the new file location.
 
         :param song_id:
-        :param filename: The file to copy.
+        :param openlp.core.common.path.Path file_path: The file to copy.
+        :return: The new location of the file
+        :rtype: openlp.core.common.path.Path
         """
         if not hasattr(self, 'save_path'):
-            self.save_path = os.path.join(str(AppLocation.get_section_data_path(self.import_wizard.plugin.name)),
-                                          'audio', str(song_id))
-        check_directory_exists(Path(self.save_path))
-        if not filename.startswith(self.save_path):
-            old_file, filename = filename, os.path.join(self.save_path, os.path.split(filename)[1])
-            shutil.copyfile(old_file, filename)
-        return filename
+            self.save_path = AppLocation.get_section_data_path(self.import_wizard.plugin.name) / 'audio' / str(song_id)
+        check_directory_exists(self.save_path)
+        if self.save_path not in file_path.parents:
+            old_path, file_path = file_path, self.save_path / file_path.name
+            copyfile(old_path, file_path)
+        return file_path

=== modified file 'openlp/plugins/songs/lib/importers/songpro.py'
--- openlp/plugins/songs/lib/importers/songpro.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/songpro.py	2017-09-30 23:24:21 +0000
@@ -25,6 +25,7 @@
 """
 import re
 
+from openlp.core.common.path import Path
 from openlp.plugins.songs.lib import strip_rtf
 from openlp.plugins.songs.lib.importers.songimport import SongImport
 
@@ -72,7 +73,8 @@
         Receive a single file or a list of files to import.
         """
         self.encoding = None
-        with open(self.import_source, 'rt', errors='ignore') as songs_file:
+        self.import_source = Path(self.import_source)
+        with self.import_source.open('rt', errors='ignore') as songs_file:
             self.import_wizard.progress_bar.setMaximum(0)
             tag = ''
             text = ''

=== modified file 'openlp/plugins/songs/lib/importers/songshowplus.py'
--- openlp/plugins/songs/lib/importers/songshowplus.py	2017-06-01 06:18:47 +0000
+++ openlp/plugins/songs/lib/importers/songshowplus.py	2017-09-30 23:24:21 +0000
@@ -23,7 +23,6 @@
 The :mod:`songshowplus` module provides the functionality for importing SongShow Plus songs into the OpenLP
 database.
 """
-import os
 import logging
 import re
 import struct
@@ -93,97 +92,95 @@
         if not isinstance(self.import_source, list):
             return
         self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-        for file in self.import_source:
+        for file_path in self.import_source:
             if self.stop_import_flag:
                 return
             self.ssp_verse_order_list = []
             self.other_count = 0
             self.other_list = {}
-            file_name = os.path.split(file)[1]
-            self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_name), 0)
-            song_data = open(file, 'rb')
-            while True:
-                block_key, = struct.unpack("I", song_data.read(4))
-                log.debug('block_key: %d' % block_key)
-                # The file ends with 4 NULL's
-                if block_key == 0:
-                    break
-                next_block_starts, = struct.unpack("I", song_data.read(4))
-                next_block_starts += song_data.tell()
-                if block_key in (VERSE, CHORUS, BRIDGE):
-                    null, verse_no, = struct.unpack("BB", song_data.read(2))
-                elif block_key == CUSTOM_VERSE:
-                    null, verse_name_length, = struct.unpack("BB", song_data.read(2))
-                    verse_name = self.decode(song_data.read(verse_name_length))
-                length_descriptor_size, = struct.unpack("B", song_data.read(1))
-                log.debug('length_descriptor_size: %d' % length_descriptor_size)
-                # In the case of song_numbers the number is in the data from the
-                # current position to the next block starts
-                if block_key == SONG_NUMBER:
-                    sn_bytes = song_data.read(length_descriptor_size - 1)
-                    self.song_number = int.from_bytes(sn_bytes, byteorder='little')
-                    continue
-                # Detect if/how long the length descriptor is
-                if length_descriptor_size == 12 or length_descriptor_size == 20:
-                    length_descriptor, = struct.unpack("I", song_data.read(4))
-                elif length_descriptor_size == 2:
-                    length_descriptor = 1
-                elif length_descriptor_size == 9:
-                    length_descriptor = 0
-                else:
-                    length_descriptor, = struct.unpack("B", song_data.read(1))
-                log.debug('length_descriptor: %d' % length_descriptor)
-                data = song_data.read(length_descriptor)
-                log.debug(data)
-                if block_key == TITLE:
-                    self.title = self.decode(data)
-                elif block_key == AUTHOR:
-                    authors = self.decode(data).split(" / ")
-                    for author in authors:
-                        if author.find(",") != -1:
-                            author_parts = author.split(", ")
-                            author = author_parts[1] + " " + author_parts[0]
-                        self.parse_author(author)
-                elif block_key == COPYRIGHT:
-                    self.add_copyright(self.decode(data))
-                elif block_key == CCLI_NO:
-                    # Try to get the CCLI number even if the field contains additional text
-                    match = re.search(r'\d+', self.decode(data))
-                    if match:
-                        self.ccli_number = int(match.group())
-                    else:
-                        log.warning("Can't parse CCLI Number from string: {text}".format(text=self.decode(data)))
-                elif block_key == VERSE:
-                    self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Verse],
-                                                                             number=verse_no))
-                elif block_key == CHORUS:
-                    self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Chorus],
-                                                                             number=verse_no))
-                elif block_key == BRIDGE:
-                    self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Bridge],
-                                                                             number=verse_no))
-                elif block_key == TOPIC:
-                    self.topics.append(self.decode(data))
-                elif block_key == COMMENTS:
-                    self.comments = self.decode(data)
-                elif block_key == VERSE_ORDER:
-                    verse_tag = self.to_openlp_verse_tag(self.decode(data), True)
-                    if verse_tag:
-                        if not isinstance(verse_tag, str):
-                            verse_tag = self.decode(verse_tag)
-                        self.ssp_verse_order_list.append(verse_tag)
-                elif block_key == SONG_BOOK:
-                    self.song_book_name = self.decode(data)
-                elif block_key == CUSTOM_VERSE:
-                    verse_tag = self.to_openlp_verse_tag(verse_name)
-                    self.add_verse(self.decode(data), verse_tag)
-                else:
-                    log.debug("Unrecognised blockKey: {key}, data: {data}".format(key=block_key, data=data))
-                    song_data.seek(next_block_starts)
-            self.verse_order_list = self.ssp_verse_order_list
-            song_data.close()
-            if not self.finish():
-                self.log_error(file)
+            self.import_wizard.increment_progress_bar(WizardStrings.ImportingType.format(source=file_path.name), 0)
+            with file_path.open('rb') as song_file:
+                while True:
+                    block_key, = struct.unpack("I", song_file.read(4))
+                    log.debug('block_key: %d' % block_key)
+                    # The file ends with 4 NULL's
+                    if block_key == 0:
+                        break
+                    next_block_starts, = struct.unpack("I", song_file.read(4))
+                    next_block_starts += song_file.tell()
+                    if block_key in (VERSE, CHORUS, BRIDGE):
+                        null, verse_no, = struct.unpack("BB", song_file.read(2))
+                    elif block_key == CUSTOM_VERSE:
+                        null, verse_name_length, = struct.unpack("BB", song_file.read(2))
+                        verse_name = self.decode(song_file.read(verse_name_length))
+                    length_descriptor_size, = struct.unpack("B", song_file.read(1))
+                    log.debug('length_descriptor_size: %d' % length_descriptor_size)
+                    # In the case of song_numbers the number is in the data from the
+                    # current position to the next block starts
+                    if block_key == SONG_NUMBER:
+                        sn_bytes = song_file.read(length_descriptor_size - 1)
+                        self.song_number = int.from_bytes(sn_bytes, byteorder='little')
+                        continue
+                    # Detect if/how long the length descriptor is
+                    if length_descriptor_size == 12 or length_descriptor_size == 20:
+                        length_descriptor, = struct.unpack("I", song_file.read(4))
+                    elif length_descriptor_size == 2:
+                        length_descriptor = 1
+                    elif length_descriptor_size == 9:
+                        length_descriptor = 0
+                    else:
+                        length_descriptor, = struct.unpack("B", song_file.read(1))
+                    log.debug('length_descriptor: %d' % length_descriptor)
+                    data = song_file.read(length_descriptor)
+                    log.debug(data)
+                    if block_key == TITLE:
+                        self.title = self.decode(data)
+                    elif block_key == AUTHOR:
+                        authors = self.decode(data).split(" / ")
+                        for author in authors:
+                            if author.find(",") != -1:
+                                author_parts = author.split(", ")
+                                author = author_parts[1] + " " + author_parts[0]
+                            self.parse_author(author)
+                    elif block_key == COPYRIGHT:
+                        self.add_copyright(self.decode(data))
+                    elif block_key == CCLI_NO:
+                        # Try to get the CCLI number even if the field contains additional text
+                        match = re.search(r'\d+', self.decode(data))
+                        if match:
+                            self.ccli_number = int(match.group())
+                        else:
+                            log.warning("Can't parse CCLI Number from string: {text}".format(text=self.decode(data)))
+                    elif block_key == VERSE:
+                        self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Verse],
+                                                                                 number=verse_no))
+                    elif block_key == CHORUS:
+                        self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Chorus],
+                                                                                 number=verse_no))
+                    elif block_key == BRIDGE:
+                        self.add_verse(self.decode(data), "{tag}{number}".format(tag=VerseType.tags[VerseType.Bridge],
+                                                                                 number=verse_no))
+                    elif block_key == TOPIC:
+                        self.topics.append(self.decode(data))
+                    elif block_key == COMMENTS:
+                        self.comments = self.decode(data)
+                    elif block_key == VERSE_ORDER:
+                        verse_tag = self.to_openlp_verse_tag(self.decode(data), True)
+                        if verse_tag:
+                            if not isinstance(verse_tag, str):
+                                verse_tag = self.decode(verse_tag)
+                            self.ssp_verse_order_list.append(verse_tag)
+                    elif block_key == SONG_BOOK:
+                        self.song_book_name = self.decode(data)
+                    elif block_key == CUSTOM_VERSE:
+                        verse_tag = self.to_openlp_verse_tag(verse_name)
+                        self.add_verse(self.decode(data), verse_tag)
+                    else:
+                        log.debug("Unrecognised blockKey: {key}, data: {data}".format(key=block_key, data=data))
+                        song_file.seek(next_block_starts)
+                self.verse_order_list = self.ssp_verse_order_list
+                if not self.finish():
+                    self.log_error(file_path)
 
     def to_openlp_verse_tag(self, verse_name, ignore_unique=False):
         """

=== modified file 'openlp/plugins/songs/lib/importers/sundayplus.py'
--- openlp/plugins/songs/lib/importers/sundayplus.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/sundayplus.py	2017-09-30 23:24:21 +0000
@@ -19,11 +19,8 @@
 # with this program; if not, write to the Free Software Foundation, Inc., 59  #
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
-
 import os
 import re
-import logging
-
 
 from openlp.plugins.songs.lib import VerseType, retrieve_windows_encoding
 from openlp.plugins.songs.lib import strip_rtf
@@ -60,12 +57,11 @@
 
     def do_import(self):
         self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-        for filename in self.import_source:
+        for file_path in self.import_source:
             if self.stop_import_flag:
                 return
-            song_file = open(filename, 'rb')
-            self.do_import_file(song_file)
-            song_file.close()
+            with file_path.open('rb') as song_file:
+                self.do_import_file(song_file)
 
     def do_import_file(self, file):
         """

=== modified file 'openlp/plugins/songs/lib/importers/videopsalm.py'
--- openlp/plugins/songs/lib/importers/videopsalm.py	2017-02-26 21:14:49 +0000
+++ openlp/plugins/songs/lib/importers/videopsalm.py	2017-09-30 23:24:21 +0000
@@ -22,13 +22,12 @@
 """
 The :mod:`lyrix` module provides the functionality for importing songs which are
 exproted from Lyrix."""
-
+import json
 import logging
-import json
-import os
 import re
 
 from openlp.core.common import translate, Settings
+from openlp.core.common.path import Path
 from openlp.plugins.songs.lib.importers.songimport import SongImport
 from openlp.plugins.songs.lib.db import AuthorType
 
@@ -50,11 +49,10 @@
         """
         Process the VideoPsalm file - pass in a file-like object, not a file path.
         """
+        self.import_source = Path(self.import_source)
         self.set_defaults()
-        # Open SongBook file
-        song_file = open(self.import_source, 'rt', encoding='utf-8-sig')
         try:
-            file_content = song_file.read()
+            file_content = self.import_source.read_text(encoding='utf-8-sig')
             processed_content = ''
             inside_quotes = False
             # The VideoPsalm format is not valid json, it uses illegal line breaks and unquoted keys, this must be fixed
@@ -89,7 +87,7 @@
             songs = songbook['Songs']
             self.import_wizard.progress_bar.setMaximum(len(songs))
             songbook_name = songbook['Text']
-            media_folder = os.path.normpath(os.path.join(os.path.dirname(song_file.name), '..', 'Audio'))
+            media_path = Path('..', 'Audio')
             for song in songs:
                 self.song_book_name = songbook_name
                 if 'Text' in song:
@@ -114,7 +112,7 @@
                 if 'Theme' in song:
                     self.topics = song['Theme'].splitlines()
                 if 'AudioFile' in song:
-                    self.add_media_file(os.path.join(media_folder, song['AudioFile']))
+                    self.add_media_file(media_path / song['AudioFile'])
                 if 'Memo1' in song:
                     self.add_comment(song['Memo1'])
                 if 'Memo2' in song:
@@ -132,4 +130,5 @@
                 if not self.finish():
                     self.log_error('Could not import {title}'.format(title=self.title))
         except Exception as e:
-            self.log_error(song_file.name, translate('SongsPlugin.VideoPsalmImport', 'Error: {error}').format(error=e))
+            self.log_error(self.import_source.name,
+                           translate('SongsPlugin.VideoPsalmImport', 'Error: {error}').format(error=e))

=== modified file 'openlp/plugins/songs/lib/importers/wordsofworship.py'
--- openlp/plugins/songs/lib/importers/wordsofworship.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/wordsofworship.py	2017-09-30 23:24:21 +0000
@@ -25,6 +25,7 @@
 """
 import os
 import logging
+from openlp.core.common.path import Path
 
 from openlp.core.common import translate
 from openlp.plugins.songs.lib.importers.songimport import SongImport
@@ -100,62 +101,60 @@
         """
         if isinstance(self.import_source, list):
             self.import_wizard.progress_bar.setMaximum(len(self.import_source))
-            for source in self.import_source:
+            for file_path in self.import_source:
                 if self.stop_import_flag:
                     return
                 self.set_defaults()
-                song_data = open(source, 'rb')
-                if song_data.read(19).decode() != 'WoW File\nSong Words':
-                    self.log_error(source,
-                                   translate('SongsPlugin.WordsofWorshipSongImport',
-                                             'Invalid Words of Worship song file. Missing "{text}" '
-                                             'header.').format(text='WoW File\\nSong Words'))
-                    continue
-                # Seek to byte which stores number of blocks in the song
-                song_data.seek(56)
-                no_of_blocks = ord(song_data.read(1))
-                song_data.seek(66)
-                if song_data.read(16).decode() != 'CSongDoc::CBlock':
-                    self.log_error(source,
-                                   translate('SongsPlugin.WordsofWorshipSongImport',
-                                             'Invalid Words of Worship song file. Missing "{text}" '
-                                             'string.').format(text='CSongDoc::CBlock'))
-                    continue
-                # Seek to the beginning of the first block
-                song_data.seek(82)
-                for block in range(no_of_blocks):
-                    skip_char_at_end = True
-                    self.lines_to_read = ord(song_data.read(4)[:1])
-                    block_text = ''
-                    while self.lines_to_read:
-                        self.line_text = str(song_data.read(ord(song_data.read(1))), 'cp1252')
-                        if skip_char_at_end:
-                            skip_char = ord(song_data.read(1))
-                            # Check if we really should skip a char. In some wsg files we shouldn't
-                            if skip_char != 0:
-                                song_data.seek(-1, os.SEEK_CUR)
-                                skip_char_at_end = False
-                        if block_text:
-                            block_text += '\n'
-                        block_text += self.line_text
-                        self.lines_to_read -= 1
-                    block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])]
-                    # Blocks are separated by 2 bytes, skip them, but not if
-                    # this is the last block!
-                    if block + 1 < no_of_blocks:
-                        song_data.seek(2, os.SEEK_CUR)
-                    self.add_verse(block_text, block_type)
-                # Now to extract the author
-                author_length = ord(song_data.read(1))
-                if author_length:
-                    self.parse_author(str(song_data.read(author_length), 'cp1252'))
-                # Finally the copyright
-                copyright_length = ord(song_data.read(1))
-                if copyright_length:
-                    self.add_copyright(str(song_data.read(copyright_length), 'cp1252'))
-                file_name = os.path.split(source)[1]
-                # Get the song title
-                self.title = file_name.rpartition('.')[0]
-                song_data.close()
-                if not self.finish():
-                    self.log_error(source)
+                with file_path.open('rb') as song_data:
+                    if song_data.read(19).decode() != 'WoW File\nSong Words':
+                        self.log_error(file_path,
+                                       translate('SongsPlugin.WordsofWorshipSongImport',
+                                                 'Invalid Words of Worship song file. Missing "{text}" '
+                                                 'header.').format(text='WoW File\\nSong Words'))
+                        continue
+                    # Seek to byte which stores number of blocks in the song
+                    song_data.seek(56)
+                    no_of_blocks = ord(song_data.read(1))
+                    song_data.seek(66)
+                    if song_data.read(16).decode() != 'CSongDoc::CBlock':
+                        self.log_error(file_path,
+                                       translate('SongsPlugin.WordsofWorshipSongImport',
+                                                 'Invalid Words of Worship song file. Missing "{text}" '
+                                                 'string.').format(text='CSongDoc::CBlock'))
+                        continue
+                    # Seek to the beginning of the first block
+                    song_data.seek(82)
+                    for block in range(no_of_blocks):
+                        skip_char_at_end = True
+                        self.lines_to_read = ord(song_data.read(4)[:1])
+                        block_text = ''
+                        while self.lines_to_read:
+                            self.line_text = str(song_data.read(ord(song_data.read(1))), 'cp1252')
+                            if skip_char_at_end:
+                                skip_char = ord(song_data.read(1))
+                                # Check if we really should skip a char. In some wsg files we shouldn't
+                                if skip_char != 0:
+                                    song_data.seek(-1, os.SEEK_CUR)
+                                    skip_char_at_end = False
+                            if block_text:
+                                block_text += '\n'
+                            block_text += self.line_text
+                            self.lines_to_read -= 1
+                        block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])]
+                        # Blocks are separated by 2 bytes, skip them, but not if
+                        # this is the last block!
+                        if block + 1 < no_of_blocks:
+                            song_data.seek(2, os.SEEK_CUR)
+                        self.add_verse(block_text, block_type)
+                    # Now to extract the author
+                    author_length = ord(song_data.read(1))
+                    if author_length:
+                        self.parse_author(str(song_data.read(author_length), 'cp1252'))
+                    # Finally the copyright
+                    copyright_length = ord(song_data.read(1))
+                    if copyright_length:
+                        self.add_copyright(str(song_data.read(copyright_length), 'cp1252'))
+                    # Get the song title
+                    self.title = file_path.stem
+                    if not self.finish():
+                        self.log_error(file_path)

=== modified file 'openlp/plugins/songs/lib/importers/worshipassistant.py'
--- openlp/plugins/songs/lib/importers/worshipassistant.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/worshipassistant.py	2017-09-30 23:24:21 +0000
@@ -28,7 +28,7 @@
 import logging
 import re
 
-from openlp.core.common import translate
+from openlp.core.common import get_file_encoding, translate
 from openlp.plugins.songs.lib import VerseType
 from openlp.plugins.songs.lib.importers.songimport import SongImport
 
@@ -81,19 +81,16 @@
         Receive a CSV file to import.
         """
         # Get encoding
-        detect_file = open(self.import_source, 'rb')
-        detect_content = detect_file.read()
-        details = chardet.detect(detect_content)
-        detect_file.close()
-        songs_file = open(self.import_source, 'r', encoding=details['encoding'])
-        songs_reader = csv.DictReader(songs_file, escapechar='\\')
-        try:
-            records = list(songs_reader)
-        except csv.Error as e:
-            self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'),
-                           translate('SongsPlugin.WorshipAssistantImport',
-                                     'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e))
-            return
+        encoding = get_file_encoding(self.import_source)['encoding']
+        with self.import_source.open('r', encoding=encoding) as songs_file:
+            songs_reader = csv.DictReader(songs_file, escapechar='\\')
+            try:
+                records = list(songs_reader)
+            except csv.Error as e:
+                self.log_error(translate('SongsPlugin.WorshipAssistantImport', 'Error reading CSV file.'),
+                               translate('SongsPlugin.WorshipAssistantImport',
+                                         'Line {number:d}: {error}').format(number=songs_reader.line_num, error=e))
+                return
         num_records = len(records)
         log.info('{count} records found in CSV file'.format(count=num_records))
         self.import_wizard.progress_bar.setMaximum(num_records)
@@ -185,4 +182,3 @@
                 self.log_error(translate('SongsPlugin.WorshipAssistantImport',
                                          'Record {count:d}').format(count=index) +
                                (': "' + self.title + '"' if self.title else ''))
-            songs_file.close()

=== modified file 'openlp/plugins/songs/lib/importers/zionworx.py'
--- openlp/plugins/songs/lib/importers/zionworx.py	2016-12-31 11:01:36 +0000
+++ openlp/plugins/songs/lib/importers/zionworx.py	2017-09-30 23:24:21 +0000
@@ -76,7 +76,7 @@
         Receive a CSV file (from a ZionWorx database dump) to import.
         """
         # Encoding should always be ISO-8859-1
-        with open(self.import_source, 'rt', encoding='ISO-8859-1') as songs_file:
+        with self.import_source.open('rt', encoding='ISO-8859-1') as songs_file:
             field_names = ['SongNum', 'Title1', 'Title2', 'Lyrics', 'Writer', 'Copyright', 'Keywords',
                            'DefaultStyle']
             songs_reader = csv.DictReader(songs_file, field_names)

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2017-08-25 20:03:25 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2017-09-30 23:24:21 +0000
@@ -22,25 +22,24 @@
 
 import logging
 import os
-import shutil
 
 from PyQt5 import QtCore, QtWidgets
 from sqlalchemy.sql import and_, or_
 
 from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
-from openlp.core.common.path import Path
+from openlp.core.common.languagemanager import get_natural_key
+from openlp.core.common.path import copyfile
 from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
     check_item_selected, create_separated_list
 from openlp.core.lib.ui import create_widget_action
-from openlp.core.common.languagemanager import get_natural_key
 from openlp.plugins.songs.forms.editsongform import EditSongForm
+from openlp.plugins.songs.forms.songexportform import SongExportForm
+from openlp.plugins.songs.forms.songimportform import SongImportForm
 from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
-from openlp.plugins.songs.forms.songimportform import SongImportForm
-from openlp.plugins.songs.forms.songexportform import SongExportForm
 from openlp.plugins.songs.lib import VerseType, clean_string, delete_song
 from openlp.plugins.songs.lib.db import Author, AuthorType, Song, Book, MediaFile, SongBookEntry, Topic
+from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML
 from openlp.plugins.songs.lib.ui import SongStrings
-from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML
 
 log = logging.getLogger(__name__)
 
@@ -88,11 +87,11 @@
     def _update_background_audio(self, song, item):
         song.media_files = []
         for i, bga in enumerate(item.background_audio):
-            dest_file = os.path.join(
-                str(AppLocation.get_section_data_path(self.plugin.name)), 'audio', str(song.id), os.path.split(bga)[1])
-            check_directory_exists(Path(os.path.split(dest_file)[0]))
-            shutil.copyfile(os.path.join(str(AppLocation.get_section_data_path('servicemanager')), bga), dest_file)
-            song.media_files.append(MediaFile.populate(weight=i, file_name=dest_file))
+            dest_path =\
+                AppLocation.get_section_data_path(self.plugin.name) / 'audio' / str(song.id) / os.path.split(bga)[1]
+            check_directory_exists(dest_path.parent)
+            copyfile(AppLocation.get_section_data_path('servicemanager') / bga, dest_path)
+            song.media_files.append(MediaFile.populate(weight=i, file_path=dest_path))
         self.plugin.manager.save_object(song, True)
 
     def add_end_header_bar(self):
@@ -534,14 +533,13 @@
                                                                       'copy', 'For song cloning'))
             # Copy audio files from the old to the new song
             if len(old_song.media_files) > 0:
-                save_path = os.path.join(
-                    str(AppLocation.get_section_data_path(self.plugin.name)), 'audio', str(new_song.id))
-                check_directory_exists(Path(save_path))
+                save_path = AppLocation.get_section_data_path(self.plugin.name) / 'audio' / str(new_song.id)
+                check_directory_exists(save_path)
                 for media_file in old_song.media_files:
-                    new_media_file_name = os.path.join(save_path, os.path.basename(media_file.file_name))
-                    shutil.copyfile(media_file.file_name, new_media_file_name)
+                    new_media_file_path = save_path / media_file.file_path.name
+                    copyfile(media_file.file_path, new_media_file_path)
                     new_media_file = MediaFile()
-                    new_media_file.file_name = new_media_file_name
+                    new_media_file.file_path = new_media_file_path
                     new_media_file.type = media_file.type
                     new_media_file.weight = media_file.weight
                     new_song.media_files.append(new_media_file)
@@ -613,7 +611,7 @@
         # Add the audio file to the service item.
         if song.media_files:
             service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
-            service_item.background_audio = [m.file_name for m in song.media_files]
+            service_item.background_audio = [m.file_path for m in song.media_files]
         return True
 
     def generate_footer(self, item, song):

=== modified file 'openlp/plugins/songs/lib/openlyricsexport.py'
--- openlp/plugins/songs/lib/openlyricsexport.py	2017-08-25 20:03:25 +0000
+++ openlp/plugins/songs/lib/openlyricsexport.py	2017-09-30 23:24:21 +0000
@@ -24,12 +24,10 @@
 format.
 """
 import logging
-import os
 
 from lxml import etree
 
 from openlp.core.common import RegistryProperties, check_directory_exists, translate, clean_filename
-from openlp.core.common.path import Path
 from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
 
 log = logging.getLogger(__name__)
@@ -42,13 +40,16 @@
     def __init__(self, parent, songs, save_path):
         """
         Initialise the export.
+
+        :param openlp.core.common.path.Path save_path: The directory to save the exported songs in
+        :rtype: None
         """
         log.debug('initialise OpenLyricsExport')
         self.parent = parent
         self.manager = parent.plugin.manager
         self.songs = songs
         self.save_path = save_path
-        check_directory_exists(Path(self.save_path))
+        check_directory_exists(self.save_path)
 
     def do_export(self):
         """
@@ -69,15 +70,15 @@
                                                    author=', '.join([author.display_name for author in song.authors]))
             filename = clean_filename(filename)
             # Ensure the filename isn't too long for some filesystems
-            filename_with_ext = '{name}.xml'.format(name=filename[0:250 - len(self.save_path)])
+            path_length = len(str(self.save_path))
+            filename_with_ext = '{name}.xml'.format(name=filename[0:250 - path_length])
             # Make sure we're not overwriting an existing file
             conflicts = 0
-            while os.path.exists(os.path.join(self.save_path, filename_with_ext)):
+            while (self.save_path / filename_with_ext).exists():
                 conflicts += 1
-                filename_with_ext = '{name}-{extra}.xml'.format(name=filename[0:247 - len(self.save_path)],
-                                                                extra=conflicts)
+                filename_with_ext = '{name}-{extra}.xml'.format(name=filename[0:247 - path_length], extra=conflicts)
             # Pass a file object, because lxml does not cope with some special
             # characters in the path (see lp:757673 and lp:744337).
-            tree.write(open(os.path.join(self.save_path, filename_with_ext), 'wb'), encoding='utf-8',
-                       xml_declaration=True, pretty_print=True)
+            with (self.save_path / filename_with_ext).open('wb') as out_file:
+                tree.write(out_file, encoding='utf-8', xml_declaration=True, pretty_print=True)
         return True

=== modified file 'openlp/plugins/songs/lib/upgrade.py'
--- openlp/plugins/songs/lib/upgrade.py	2017-06-10 05:57:00 +0000
+++ openlp/plugins/songs/lib/upgrade.py	2017-09-30 23:24:21 +0000
@@ -23,16 +23,20 @@
 The :mod:`upgrade` module provides a way for the database and schema that is the
 backend for the Songs plugin
 """
+import json
 import logging
 
 from sqlalchemy import Table, Column, ForeignKey, types
 from sqlalchemy.sql.expression import func, false, null, text
 
+from openlp.core.common import AppLocation
 from openlp.core.common.db import drop_columns
-from openlp.core.lib.db import get_upgrade_op
+from openlp.core.common.json import OpenLPJsonEncoder
+from openlp.core.common.path import Path
+from openlp.core.lib.db import PathType, get_upgrade_op
 
 log = logging.getLogger(__name__)
-__version__ = 6
+__version__ = 7
 
 
 # TODO: When removing an upgrade path the ftw-data needs updating to the minimum supported version
@@ -162,3 +166,28 @@
             op.drop_column('songs', 'song_number')
     # Finally, clean up our mess in people's databases
     op.execute('DELETE FROM songs_songbooks WHERE songbook_id = 0')
+
+
+def upgrade_7(session, metadata):
+    """
+    Version 7 upgrade - Move file path from old db to JSON encoded path to new db. Upgrade added in 2.5 dev
+    """
+    log.debug('Starting upgrade_7 for file_path to JSON')
+    old_table = Table('media_files', metadata, autoload=True)
+    if 'file_path' not in [col.name for col in old_table.c.values()]:
+        op = get_upgrade_op(session)
+        op.add_column('media_files', Column('file_path', PathType()))
+        conn = op.get_bind()
+        results = conn.execute('SELECT * FROM media_files')
+        data_path = AppLocation.get_data_path()
+        for row in results.fetchall():
+            file_path_json = json.dumps(Path(row.file_name), cls=OpenLPJsonEncoder, base_path=data_path)
+            sql = 'UPDATE media_files SET file_path = \'{file_path_json}\' WHERE id = {id}'.format(
+                file_path_json=file_path_json, id=row.id)
+            conn.execute(sql)
+        # Drop old columns
+        if metadata.bind.url.get_dialect().name == 'sqlite':
+            drop_columns(op, 'media_files', ['file_name', ])
+        else:
+            op.drop_constraint('media_files', 'foreignkey')
+            op.drop_column('media_files', 'filenames')

=== modified file 'openlp/plugins/songs/reporting.py'
--- openlp/plugins/songs/reporting.py	2017-09-07 21:52:39 +0000
+++ openlp/plugins/songs/reporting.py	2017-09-30 23:24:21 +0000
@@ -31,7 +31,6 @@
 from openlp.core.ui.lib.filedialog import FileDialog
 from openlp.plugins.songs.lib.db import Song
 
-
 log = logging.getLogger(__name__)
 
 
@@ -58,9 +57,9 @@
     report_file_path.with_suffix('.csv')
     Registry().get('application').set_busy_cursor()
     try:
-        with report_file_path.open('wt') as file_handle:
+        with report_file_path.open('wt') as export_file:
             fieldnames = ('Title', 'Alternative Title', 'Copyright', 'Author(s)', 'Song Book', 'Topic')
-            writer = csv.DictWriter(file_handle, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
+            writer = csv.DictWriter(export_file, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
             headers = dict((n, n) for n in fieldnames)
             writer.writerow(headers)
             song_list = plugin.manager.get_all_objects(Song)

=== modified file 'openlp/plugins/songs/songsplugin.py'
--- openlp/plugins/songs/songsplugin.py	2017-08-26 15:06:11 +0000
+++ openlp/plugins/songs/songsplugin.py	2017-09-30 23:24:21 +0000
@@ -37,7 +37,6 @@
 from openlp.core.lib import Plugin, StringContent, build_icon
 from openlp.core.lib.db import Manager
 from openlp.core.lib.ui import create_action
-
 from openlp.plugins.songs import reporting
 from openlp.plugins.songs.endpoint import api_songs_endpoint, songs_endpoint
 from openlp.plugins.songs.forms.duplicatesongremovalform import DuplicateSongRemovalForm
@@ -50,7 +49,6 @@
 from openlp.plugins.songs.lib.mediaitem import SongSearch
 from openlp.plugins.songs.lib.songstab import SongsTab
 
-
 log = logging.getLogger(__name__)
 __default_settings__ = {
     'songs/db type': 'sqlite',
@@ -340,7 +338,7 @@
         progress.forceShow()
         self.application.process_events()
         for db in song_dbs:
-            importer = OpenLPSongImport(self.manager, filename=db)
+            importer = OpenLPSongImport(self.manager, file_path=db)
             importer.do_import(progress)
             self.application.process_events()
         progress.setValue(song_count)

=== modified file 'tests/functional/openlp_plugins/images/test_upgrade.py'
--- tests/functional/openlp_plugins/images/test_upgrade.py	2017-09-24 19:33:07 +0000
+++ tests/functional/openlp_plugins/images/test_upgrade.py	2017-09-30 23:24:21 +0000
@@ -79,5 +79,6 @@
                                     2: Path('/', 'test', 'dir', 'image2.jpg'),
                                     3: Path('/', 'test', 'dir', 'subdir', 'image3.jpg')}
 
+            self.assertEqual(len(upgraded_results), 3)
             for result in upgraded_results:
                 self.assertEqual(expected_result_data[result.id], result.file_path)

=== modified file 'tests/functional/openlp_plugins/songs/test_chordproimport.py'
--- tests/functional/openlp_plugins/songs/test_chordproimport.py	2017-05-11 20:24:20 +0000
+++ tests/functional/openlp_plugins/songs/test_chordproimport.py	2017-09-30 23:24:21 +0000
@@ -24,6 +24,8 @@
 """
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 from unittest.mock import patch, MagicMock
 
@@ -48,5 +50,5 @@
         mocked_returned_settings.value.side_effect = lambda value: True if value == 'songs/enable chords' else False
         mocked_settings.return_value = mocked_returned_settings
         # Do the test import
-        self.file_import([os.path.join(TEST_PATH, 'swing-low.chordpro')],
+        self.file_import([Path(TEST_PATH, 'swing-low.chordpro')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'swing-low.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_easyslidesimport.py'
--- tests/functional/openlp_plugins/songs/test_easyslidesimport.py	2017-01-12 21:31:01 +0000
+++ tests/functional/openlp_plugins/songs/test_easyslidesimport.py	2017-09-30 23:24:21 +0000
@@ -21,9 +21,10 @@
 """
 This module contains tests for the EasySlides song importer.
 """
-
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 
 TEST_PATH = os.path.abspath(
@@ -41,7 +42,7 @@
         """
         Test that loading an EasySlides file works correctly on various files
         """
-        self.file_import(os.path.join(TEST_PATH, 'amazing-grace.xml'),
+        self.file_import(Path(TEST_PATH, 'amazing-grace.xml'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
-        self.file_import(os.path.join(TEST_PATH, 'Export_2017-01-12_BB.xml'),
+        self.file_import(Path(TEST_PATH, 'Export_2017-01-12_BB.xml'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Export_2017-01-12_BB.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_ewimport.py'
--- tests/functional/openlp_plugins/songs/test_ewimport.py	2017-05-22 19:07:07 +0000
+++ tests/functional/openlp_plugins/songs/test_ewimport.py	2017-09-30 23:24:21 +0000
@@ -97,7 +97,7 @@
     _title_assignment_list = []
 
     def __init__(self, manager):
-        EasyWorshipSongImport.__init__(self, manager, filenames=[])
+        EasyWorshipSongImport.__init__(self, manager, file_paths=[])
 
     @property
     def title(self):
@@ -180,7 +180,7 @@
             mocked_manager = MagicMock()
 
             # WHEN: An importer object is created
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
 
             # THEN: The importer object should not be None
             self.assertIsNotNone(importer, 'Import should not be none')
@@ -192,7 +192,7 @@
         # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions.
         with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
             mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
             importer.field_descriptions = TEST_FIELD_DESCS
 
             # WHEN: Called with a field name that exists
@@ -210,7 +210,7 @@
         # GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions
         with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
             mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
             importer.field_descriptions = TEST_FIELD_DESCS
 
             # WHEN: Called with a field name that does not exist
@@ -229,7 +229,7 @@
         with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
                 patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct:
             mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
 
             # WHEN: db_set_record_struct is called with a list of field descriptions
             return_value = importer.db_set_record_struct(TEST_FIELD_DESCS)
@@ -246,7 +246,7 @@
         # GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results
         with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
             mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
             importer.encoding = TEST_DATA_ENCODING
             importer.fields = TEST_FIELDS
             importer.field_descriptions = TEST_FIELD_DESCS
@@ -270,7 +270,7 @@
             with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'):
                 mocked_manager = MagicMock()
                 mocked_memo_file = MagicMock()
-                importer = EasyWorshipSongImport(mocked_manager, filenames=[])
+                importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
                 importer.memo_file = mocked_memo_file
                 importer.encoding = TEST_DATA_ENCODING
 
@@ -294,44 +294,25 @@
                     else:
                         mocked_memo_file.seek.assert_any_call(call[0], call[1])
 
-    def test_do_import_source(self):
-        """
-        Test the :mod:`do_import` module opens the correct files
-        """
-        # GIVEN: A mocked out SongImport class, a mocked out "manager"
-        with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
-                patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path:
-            mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
-            mocked_os_path.isfile.side_effect = [True, False]
-
-            # WHEN: Supplied with an import source
-            importer.import_source = 'Songs.DB'
-
-            # THEN: do_import should return None having called os.path.isfile
-            self.assertIsNone(importer.do_import(), 'do_import should return None')
-            mocked_os_path.isfile.assert_any_call('Songs.DB')
-            mocked_os_path.isfile.assert_any_call('Songs.MB')
-
     def test_do_import_source_invalid(self):
         """
         Test the :mod:`do_import` module produces an error when Songs.MB not found.
         """
         # GIVEN: A mocked out SongImport class, a mocked out "manager"
         with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
-                patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path:
+                patch('openlp.plugins.songs.lib.importers.easyworship.Path.is_file', side_effect=[True, False]):
             mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
-            importer.log_error = MagicMock()
-            mocked_os_path.isfile.side_effect = [True, False]
-
-            # WHEN: do_import is supplied with an import source (Songs.MB missing)
-            importer.import_source = 'Songs.DB'
-            importer.do_import()
-
-            # THEN: do_import should have logged an error that the Songs.MB file could not be found.
-            importer.log_error.assert_any_call(importer.import_source, 'Could not find the "Songs.MB" file. It must be '
-                                                                       'in the same folder as the "Songs.DB" file.')
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
+            with patch.object(importer, 'log_error') as mocked_log_error:
+
+                # WHEN: do_import is supplied with an import source (Songs.MB missing)
+                importer.import_source = 'Songs.DB'
+                importer.do_import()
+
+                # THEN: do_import should have logged an error that the Songs.MB file could not be found.
+                mocked_log_error.assert_any_call(importer.import_source,
+                                                 'Could not find the "Songs.MB" file. It must be in the same folder as '
+                                                 'the "Songs.DB" file.')
 
     def test_do_import_database_validity(self):
         """
@@ -339,18 +320,19 @@
         """
         # GIVEN: A mocked out SongImport class, os.path and a mocked out "manager"
         with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
-                patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path:
+                patch('openlp.plugins.songs.lib.importers.easyworship.Path.is_file', return_value=True), \
+                patch('openlp.plugins.songs.lib.importers.easyworship.Path.stat') as mocked_stat:
+
             mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
-            mocked_os_path.isfile.return_value = True
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
             importer.import_source = 'Songs.DB'
 
             # WHEN: DB file size is less than 0x800
-            mocked_os_path.getsize.return_value = 0x7FF
+            mocked_stat.return_value.st_size = 0x7FF
 
-            # THEN: do_import should return None having called os.path.isfile
+            # THEN: do_import should return None having called Path.stat()
             self.assertIsNone(importer.do_import(), 'do_import should return None when db_size is less than 0x800')
-            mocked_os_path.getsize.assert_any_call('Songs.DB')
+            mocked_stat.assert_called_once_with()
 
     def test_do_import_memo_validty(self):
         """
@@ -358,13 +340,12 @@
         """
         # GIVEN: A mocked out SongImport class, a mocked out "manager"
         with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
-            patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path, \
-            patch('builtins.open') as mocked_open, \
+                patch('openlp.plugins.songs.lib.importers.easyworship.Path.is_file', return_value=True), \
+                patch('openlp.plugins.songs.lib.importers.easyworship.Path.stat', **{'return_value.st_size': 0x800}), \
+                patch('openlp.plugins.songs.lib.importers.easyworship.Path.open') as mocked_open, \
                 patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct:
             mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
-            mocked_os_path.isfile.return_value = True
-            mocked_os_path.getsize.return_value = 0x800
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
             importer.import_source = 'Songs.DB'
 
             # WHEN: Unpacking first 35 bytes of Memo file
@@ -385,14 +366,14 @@
         """
         # GIVEN: A mocked out SongImport class, a mocked out "manager"
         with patch('openlp.plugins.songs.lib.importers.easyworship.SongImport'), \
-            patch('openlp.plugins.songs.lib.importers.easyworship.os.path') as mocked_os_path, \
+            patch('openlp.plugins.songs.lib.importers.easyworship.Path.is_file', return_value=True), \
+            patch('openlp.plugins.songs.lib.importers.easyworship.Path.stat', **{'return_value.st_size': 0x800}), \
+            patch('openlp.plugins.songs.lib.importers.easyworship.Path.open'), \
             patch('builtins.open'), patch('openlp.plugins.songs.lib.importers.easyworship.struct') as mocked_struct, \
-                patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') as \
+            patch('openlp.plugins.songs.lib.importers.easyworship.retrieve_windows_encoding') as \
                 mocked_retrieve_windows_encoding:
             mocked_manager = MagicMock()
-            importer = EasyWorshipSongImport(mocked_manager, filenames=[])
-            mocked_os_path.isfile.return_value = True
-            mocked_os_path.getsize.return_value = 0x800
+            importer = EasyWorshipSongImport(mocked_manager, file_paths=[])
             importer.import_source = 'Songs.DB'
 
             # WHEN: Unpacking the code page

=== modified file 'tests/functional/openlp_plugins/songs/test_lyriximport.py'
--- tests/functional/openlp_plugins/songs/test_lyriximport.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_lyriximport.py	2017-09-30 23:24:21 +0000
@@ -22,7 +22,8 @@
 This module contains tests for the LyriX song importer.
 """
 import os
-from unittest.mock import patch
+
+from openlp.core.common.path import Path
 
 from tests.helpers.songfileimport import SongImportTestHelper
 
@@ -41,9 +42,9 @@
         """
         Test that loading an LyriX file works correctly on various files
         """
-        self.file_import([os.path.join(TEST_PATH, 'A06.TXT')],
+        self.file_import([Path(TEST_PATH, 'A06.TXT')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
-        self.file_import([os.path.join(TEST_PATH, 'A002.TXT')],
+        self.file_import([Path(TEST_PATH, 'A002.TXT')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace2.json')))
-        self.file_import([os.path.join(TEST_PATH, 'AO05.TXT')],
+        self.file_import([Path(TEST_PATH, 'AO05.TXT')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'in die regterhand.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_mediashout.py'
--- tests/functional/openlp_plugins/songs/test_mediashout.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_mediashout.py	2017-09-30 23:24:21 +0000
@@ -51,7 +51,7 @@
         """
         # GIVEN: A MediaShoutImport class
         # WHEN: It is created
-        importer = MediaShoutImport(MagicMock(), filename='mediashout.db')
+        importer = MediaShoutImport(MagicMock(), file_path='mediashout.db')
 
         # THEN: It should not be None
         self.assertIsNotNone(importer)
@@ -62,7 +62,7 @@
         Test that do_import exits early when unable to connect to the database
         """
         # GIVEN: A MediaShoutImport instance
-        importer = MediaShoutImport(MagicMock(), filename='mediashout.db')
+        importer = MediaShoutImport(MagicMock(), file_path='mediashout.db')
         mocked_pyodbc.connect.side_effect = Exception('Unable to connect')
 
         # WHEN: do_import is called
@@ -89,7 +89,7 @@
         group = GroupRecord('Hymns')
 
         # GIVEN: A MediaShoutImport instance and a bunch of stuff mocked out
-        importer = MediaShoutImport(MagicMock(), filename='mediashout.db')
+        importer = MediaShoutImport(MagicMock(), file_path='mediashout.db')
         mocked_cursor = MagicMock()
         mocked_cursor.fetchall.side_effect = [[song], [verse], [play_order], [theme], [group]]
         mocked_cursor.tables.fetchone.return_value = True
@@ -124,7 +124,7 @@
         song = SongRecord(1, 'Amazing Grace', 'William Wilberforce', 'Public Domain', 1, '654321', '')
 
         # GIVEN: A MediaShoutImport instance and a bunch of stuff mocked out
-        importer = MediaShoutImport(MagicMock(), filename='mediashout.db')
+        importer = MediaShoutImport(MagicMock(), file_path='mediashout.db')
         mocked_cursor = MagicMock()
         mocked_cursor.fetchall.return_value = [song]
         mocked_connection = MagicMock()
@@ -158,7 +158,7 @@
         play_order = PlayOrderRecord(0, 1, 1)
         theme = ThemeRecord('Grace')
         group = GroupRecord('Hymns')
-        importer = MediaShoutImport(MagicMock(), filename='mediashout.db')
+        importer = MediaShoutImport(MagicMock(), file_path='mediashout.db')
 
         # WHEN: A song is processed
         with patch.object(importer, 'set_defaults') as mocked_set_defaults, \
@@ -200,7 +200,7 @@
         play_order = PlayOrderRecord(0, 1, 1)
         theme = ThemeRecord('Grace')
         group = GroupRecord('Hymns')
-        importer = MediaShoutImport(MagicMock(), filename='mediashout.db')
+        importer = MediaShoutImport(MagicMock(), file_path='mediashout.db')
 
         # WHEN: A song is processed
         with patch.object(importer, 'set_defaults') as mocked_set_defaults, \

=== modified file 'tests/functional/openlp_plugins/songs/test_openlpimporter.py'
--- tests/functional/openlp_plugins/songs/test_openlpimporter.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_openlpimporter.py	2017-09-30 23:24:21 +0000
@@ -48,7 +48,7 @@
             mocked_manager = MagicMock()
 
             # WHEN: An importer object is created
-            importer = OpenLPSongImport(mocked_manager, filenames=[])
+            importer = OpenLPSongImport(mocked_manager, file_paths=[])
 
             # THEN: The importer object should not be None
             self.assertIsNotNone(importer, 'Import should not be none')
@@ -61,7 +61,7 @@
         with patch('openlp.plugins.songs.lib.importers.openlp.SongImport'):
             mocked_manager = MagicMock()
             mocked_import_wizard = MagicMock()
-            importer = OpenLPSongImport(mocked_manager, filenames=[])
+            importer = OpenLPSongImport(mocked_manager, file_paths=[])
             importer.import_wizard = mocked_import_wizard
             importer.stop_import_flag = True
 

=== modified file 'tests/functional/openlp_plugins/songs/test_openlyricsexport.py'
--- tests/functional/openlp_plugins/songs/test_openlyricsexport.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_openlyricsexport.py	2017-09-30 23:24:21 +0000
@@ -22,14 +22,14 @@
 """
 This module contains tests for the OpenLyrics song importer.
 """
-import os
 import shutil
 from tempfile import mkdtemp
 from unittest import TestCase
 from unittest.mock import MagicMock, patch
 
+from openlp.core.common import Registry
+from openlp.core.common.path import Path, rmtree
 from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
-from openlp.core.common import Registry
 
 from tests.helpers.testmixin import TestMixin
 
@@ -43,13 +43,13 @@
         Create the registry
         """
         Registry.create()
-        self.temp_folder = mkdtemp()
+        self.temp_folder = Path(mkdtemp())
 
     def tearDown(self):
         """
         Cleanup
         """
-        shutil.rmtree(self.temp_folder)
+        rmtree(self.temp_folder)
 
     def test_export_same_filename(self):
         """
@@ -73,7 +73,9 @@
             ol_export.do_export()
 
             # THEN: The exporter should have created 2 files
-            self.assertTrue(os.path.exists(os.path.join(self.temp_folder,
-                                                        '%s (%s).xml' % (song.title, author.display_name))))
-            self.assertTrue(os.path.exists(os.path.join(self.temp_folder,
-                                                        '%s (%s)-1.xml' % (song.title, author.display_name))))
+            self.assertTrue((self.temp_folder /
+                             '{title} ({display_name}).xml'.format(
+                                 title=song.title, display_name=author.display_name)).exists())
+            self.assertTrue((self.temp_folder /
+                             '{title} ({display_name})-1.xml'.format(
+                                 title=song.title, display_name=author.display_name)).exists())

=== modified file 'tests/functional/openlp_plugins/songs/test_openlyricsimport.py'
--- tests/functional/openlp_plugins/songs/test_openlyricsimport.py	2017-06-06 20:58:12 +0000
+++ tests/functional/openlp_plugins/songs/test_openlyricsimport.py	2017-09-30 23:24:21 +0000
@@ -29,10 +29,11 @@
 
 from lxml import etree, objectify
 
+from openlp.core.common import Registry, Settings
+from openlp.core.common.path import Path
 from openlp.plugins.songs.lib.importers.openlyrics import OpenLyricsImport
 from openlp.plugins.songs.lib.importers.songimport import SongImport
 from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
-from openlp.core.common import Registry, Settings
 
 from tests.helpers.testmixin import TestMixin
 
@@ -109,7 +110,7 @@
             mocked_manager = MagicMock()
 
             # WHEN: An importer object is created
-            importer = OpenLyricsImport(mocked_manager, filenames=[])
+            importer = OpenLyricsImport(mocked_manager, file_paths=[])
 
             # THEN: The importer should be an instance of SongImport
             self.assertIsInstance(importer, SongImport)
@@ -122,13 +123,13 @@
         for song_file in SONG_TEST_DATA:
             mocked_manager = MagicMock()
             mocked_import_wizard = MagicMock()
-            importer = OpenLyricsImport(mocked_manager, filenames=[])
+            importer = OpenLyricsImport(mocked_manager, file_paths=[])
             importer.import_wizard = mocked_import_wizard
             importer.open_lyrics = MagicMock()
             importer.open_lyrics.xml_to_song = MagicMock()
 
             # WHEN: Importing each file
-            importer.import_source = [os.path.join(TEST_PATH, song_file)]
+            importer.import_source = [Path(TEST_PATH, song_file)]
             importer.do_import()
 
             # THEN: The xml_to_song() method should have been called

=== modified file 'tests/functional/openlp_plugins/songs/test_openoffice.py'
--- tests/functional/openlp_plugins/songs/test_openoffice.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_openoffice.py	2017-09-30 23:24:21 +0000
@@ -54,7 +54,7 @@
         mocked_manager = MagicMock()
 
         # WHEN: An importer object is created
-        importer = OpenOfficeImport(mocked_manager, filenames=[])
+        importer = OpenOfficeImport(mocked_manager, file_paths=[])
 
         # THEN: The importer object should not be None
         self.assertIsNotNone(importer, 'Import should not be none')
@@ -66,7 +66,7 @@
         """
         # GIVEN: A mocked out SongImport class, a mocked out "manager" and a document that raises an exception
         mocked_manager = MagicMock()
-        importer = OpenOfficeImport(mocked_manager, filenames=[])
+        importer = OpenOfficeImport(mocked_manager, file_paths=[])
         importer.document = MagicMock()
         importer.document.close = MagicMock(side_effect=Exception())
 

=== modified file 'tests/functional/openlp_plugins/songs/test_opensongimport.py'
--- tests/functional/openlp_plugins/songs/test_opensongimport.py	2017-05-11 20:24:20 +0000
+++ tests/functional/openlp_plugins/songs/test_opensongimport.py	2017-09-30 23:24:21 +0000
@@ -27,6 +27,7 @@
 from unittest.mock import patch, MagicMock
 
 from openlp.core.common import Registry
+from openlp.core.common.path import Path
 from openlp.plugins.songs.lib.importers.opensong import OpenSongImport
 
 from tests.helpers.songfileimport import SongImportTestHelper
@@ -52,15 +53,15 @@
         mocked_returned_settings.value.side_effect = lambda value: True if value == 'songs/enable chords' else False
         mocked_settings.return_value = mocked_returned_settings
         # Do the test import
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
-        self.file_import([os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer')],
+        self.file_import([Path(TEST_PATH, 'Beautiful Garden Of Prayer')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
-        self.file_import([os.path.join(TEST_PATH, 'One, Two, Three, Four, Five')],
+        self.file_import([Path(TEST_PATH, 'One, Two, Three, Four, Five')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'One, Two, Three, Four, Five.json')))
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace2')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace2')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace with bad CCLI')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace with bad CCLI')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace without CCLI.json')))
 
 
@@ -83,7 +84,7 @@
             mocked_manager = MagicMock()
 
             # WHEN: An importer object is created
-            importer = OpenSongImport(mocked_manager, filenames=[])
+            importer = OpenSongImport(mocked_manager, file_paths=[])
 
             # THEN: The importer object should not be None
             self.assertIsNotNone(importer, 'Import should not be none')
@@ -96,7 +97,7 @@
         with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'):
             mocked_manager = MagicMock()
             mocked_import_wizard = MagicMock()
-            importer = OpenSongImport(mocked_manager, filenames=[])
+            importer = OpenSongImport(mocked_manager, file_paths=[])
             importer.import_wizard = mocked_import_wizard
             importer.stop_import_flag = True
 
@@ -117,7 +118,7 @@
         with patch('openlp.plugins.songs.lib.importers.opensong.SongImport'):
             mocked_manager = MagicMock()
             mocked_import_wizard = MagicMock()
-            importer = OpenSongImport(mocked_manager, filenames=[])
+            importer = OpenSongImport(mocked_manager, file_paths=[])
             importer.import_wizard = mocked_import_wizard
             importer.stop_import_flag = True
 

=== modified file 'tests/functional/openlp_plugins/songs/test_opsproimport.py'
--- tests/functional/openlp_plugins/songs/test_opsproimport.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_opsproimport.py	2017-09-30 23:24:21 +0000
@@ -86,7 +86,7 @@
         mocked_manager = MagicMock()
 
         # WHEN: An importer object is created
-        importer = OPSProImport(mocked_manager, filenames=[])
+        importer = OPSProImport(mocked_manager, file_paths=[])
 
         # THEN: The importer object should not be None
         self.assertIsNotNone(importer, 'Import should not be none')
@@ -98,7 +98,7 @@
         """
         # GIVEN: A mocked out SongImport class, a mocked out "manager" and a mocked song and lyrics entry
         mocked_manager = MagicMock()
-        importer = OPSProImport(mocked_manager, filenames=[])
+        importer = OPSProImport(mocked_manager, file_paths=[])
         importer.finish = MagicMock()
         song, lyrics = _build_data('you are so faithfull.txt', False)
 
@@ -118,7 +118,7 @@
         """
         # GIVEN: A mocked out SongImport class, a mocked out "manager" and a mocked song and lyrics entry
         mocked_manager = MagicMock()
-        importer = OPSProImport(mocked_manager, filenames=[])
+        importer = OPSProImport(mocked_manager, file_paths=[])
         importer.finish = MagicMock()
         song, lyrics = _build_data('amazing grace.txt', False)
 
@@ -138,7 +138,7 @@
         """
         # GIVEN: A mocked out SongImport class, a mocked out "manager" and a mocked song and lyrics entry
         mocked_manager = MagicMock()
-        importer = OPSProImport(mocked_manager, filenames=[])
+        importer = OPSProImport(mocked_manager, file_paths=[])
         importer.finish = MagicMock()
         song, lyrics = _build_data('amazing grace2.txt', True)
 
@@ -158,7 +158,7 @@
         """
         # GIVEN: A mocked out SongImport class, a mocked out "manager" and a mocked song and lyrics entry
         mocked_manager = MagicMock()
-        importer = OPSProImport(mocked_manager, filenames=[])
+        importer = OPSProImport(mocked_manager, file_paths=[])
         importer.finish = MagicMock()
         song, lyrics = _build_data('amazing grace3.txt', True)
 

=== modified file 'tests/functional/openlp_plugins/songs/test_powerpraiseimport.py'
--- tests/functional/openlp_plugins/songs/test_powerpraiseimport.py	2016-12-31 11:01:36 +0000
+++ tests/functional/openlp_plugins/songs/test_powerpraiseimport.py	2017-09-30 23:24:21 +0000
@@ -26,8 +26,9 @@
 
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
-from openlp.core.common import Registry
 
 TEST_PATH = os.path.abspath(
     os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'powerpraisesongs'))
@@ -44,7 +45,7 @@
         """
         Test that loading a PowerPraise file works correctly
         """
-        self.file_import([os.path.join(TEST_PATH, 'Naher, mein Gott zu Dir.ppl')],
+        self.file_import([Path(TEST_PATH, 'Naher, mein Gott zu Dir.ppl')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Naher, mein Gott zu Dir.json')))
-        self.file_import([os.path.join(TEST_PATH, 'You are so faithful.ppl')],
+        self.file_import([Path(TEST_PATH, 'You are so faithful.ppl')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'You are so faithful.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py'
--- tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py	2016-12-31 11:01:36 +0000
+++ tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py	2017-09-30 23:24:21 +0000
@@ -22,9 +22,10 @@
 """
 This module contains tests for the PresentationManager song importer.
 """
-
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 
 TEST_PATH = os.path.abspath(
@@ -42,7 +43,7 @@
         """
         Test that loading a PresentationManager file works correctly
         """
-        self.file_import([os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.sng')],
+        self.file_import([Path(TEST_PATH, 'Great Is Thy Faithfulness.sng')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json')))
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sng')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace.sng')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_propresenterimport.py'
--- tests/functional/openlp_plugins/songs/test_propresenterimport.py	2016-12-31 11:01:36 +0000
+++ tests/functional/openlp_plugins/songs/test_propresenterimport.py	2017-09-30 23:24:21 +0000
@@ -23,9 +23,10 @@
 The :mod:`propresenterimport` module provides the functionality for importing
 ProPresenter song files into the current installation database.
 """
-
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 
 TEST_PATH = os.path.abspath(
@@ -43,19 +44,19 @@
         """
         Test that loading a ProPresenter 4 file works correctly
         """
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro4')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace.pro4')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
 
     def test_pro5_song_import(self):
         """
         Test that loading a ProPresenter 5 file works correctly
         """
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro5')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace.pro5')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
 
     def test_pro6_song_import(self):
         """
         Test that loading a ProPresenter 6 file works correctly
         """
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro6')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace.pro6')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_songbeamerimport.py'
--- tests/functional/openlp_plugins/songs/test_songbeamerimport.py	2017-05-11 20:24:20 +0000
+++ tests/functional/openlp_plugins/songs/test_songbeamerimport.py	2017-09-30 23:24:21 +0000
@@ -26,8 +26,9 @@
 from unittest import TestCase
 from unittest.mock import MagicMock, patch
 
+from openlp.core.common import Registry
+from openlp.core.common.path import Path
 from openlp.plugins.songs.lib.importers.songbeamer import SongBeamerImport, SongBeamerTypes
-from openlp.core.common import Registry
 
 from tests.helpers.songfileimport import SongImportTestHelper
 
@@ -51,18 +52,18 @@
         mocked_returned_settings = MagicMock()
         mocked_returned_settings.value.side_effect = lambda value: True if value == 'songs/enable chords' else False
         mocked_settings.return_value = mocked_returned_settings
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sng')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace.sng')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
-        self.file_import([os.path.join(TEST_PATH, 'Lobsinget dem Herrn.sng')],
+        self.file_import([Path(TEST_PATH, 'Lobsinget dem Herrn.sng')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Lobsinget dem Herrn.json')))
-        self.file_import([os.path.join(TEST_PATH, 'When I Call On You.sng')],
+        self.file_import([Path(TEST_PATH, 'When I Call On You.sng')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'When I Call On You.json')))
 
     def test_cp1252_encoded_file(self):
         """
         Test that a CP1252 encoded file get's decoded properly.
         """
-        self.file_import([os.path.join(TEST_PATH, 'cp1252song.sng')],
+        self.file_import([Path(TEST_PATH, 'cp1252song.sng')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'cp1252song.json')))
 
 
@@ -78,7 +79,7 @@
         self.song_import_patcher = patch('openlp.plugins.songs.lib.importers.songbeamer.SongImport')
         self.song_import_patcher.start()
         mocked_manager = MagicMock()
-        self.importer = SongBeamerImport(mocked_manager, filenames=[])
+        self.importer = SongBeamerImport(mocked_manager, file_paths=[])
 
     def tearDown(self):
         """
@@ -95,7 +96,7 @@
             mocked_manager = MagicMock()
 
             # WHEN: An importer object is created
-            importer = SongBeamerImport(mocked_manager, filenames=[])
+            importer = SongBeamerImport(mocked_manager, file_paths=[])
 
             # THEN: The importer object should not be None
             self.assertIsNotNone(importer, 'Import should not be none')

=== modified file 'tests/functional/openlp_plugins/songs/test_songproimport.py'
--- tests/functional/openlp_plugins/songs/test_songproimport.py	2016-12-31 11:01:36 +0000
+++ tests/functional/openlp_plugins/songs/test_songproimport.py	2017-09-30 23:24:21 +0000
@@ -23,9 +23,10 @@
 The :mod:`songproimport` module provides the functionality for importing
 SongPro song files into the current installation database.
 """
-
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 
 TEST_PATH = os.path.abspath(
@@ -43,5 +44,5 @@
         """
         Test that loading an SongPro file works correctly
         """
-        self.file_import(os.path.join(TEST_PATH, 'amazing-grace.txt'),
+        self.file_import(Path(TEST_PATH, 'amazing-grace.txt'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_songselect.py'
--- tests/functional/openlp_plugins/songs/test_songselect.py	2017-05-30 19:40:01 +0000
+++ tests/functional/openlp_plugins/songs/test_songselect.py	2017-09-30 23:24:21 +0000
@@ -31,6 +31,7 @@
 from PyQt5 import QtWidgets
 
 from openlp.core import Registry
+from openlp.core.common.path import Path
 from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker
 from openlp.plugins.songs.lib import Song
 from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGIN_PAGE, LOGOUT_URL, BASE_URL
@@ -810,15 +811,15 @@
     def __init__(self, *args, **kwargs):
         self.importer_class_name = 'CCLIFileImport'
         self.importer_module_name = 'cclifile'
-        super(TestSongSelectFileImport, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
 
     def test_song_import(self):
         """
         Test that loading an OpenSong file works correctly on various files
         """
-        self.file_import([os.path.join(TEST_PATH, 'TestSong.bin')],
+        self.file_import([Path(TEST_PATH, 'TestSong.bin')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'TestSong-bin.json')))
-        self.file_import([os.path.join(TEST_PATH, 'TestSong.txt')],
+        self.file_import([Path(TEST_PATH, 'TestSong.txt')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'TestSong-txt.json')))
 
 

=== modified file 'tests/functional/openlp_plugins/songs/test_songshowplusimport.py'
--- tests/functional/openlp_plugins/songs/test_songshowplusimport.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_songshowplusimport.py	2017-09-30 23:24:21 +0000
@@ -26,6 +26,7 @@
 from unittest import TestCase
 from unittest.mock import patch, MagicMock
 
+from openlp.core.common.path import Path
 from openlp.plugins.songs.lib import VerseType
 from openlp.plugins.songs.lib.importers.songshowplus import SongShowPlusImport
 
@@ -46,13 +47,13 @@
         """
         Test that loading a SongShow Plus file works correctly on various files
         """
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sbsong')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace.sbsong')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
-        self.file_import([os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong')],
+        self.file_import([Path(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
-        self.file_import([os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong')],
+        self.file_import([Path(TEST_PATH, 'a mighty fortress is our god.sbsong')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json')))
-        self.file_import([os.path.join(TEST_PATH, 'cleanse-me.sbsong')],
+        self.file_import([Path(TEST_PATH, 'cleanse-me.sbsong')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'cleanse-me.json')))
 
 
@@ -69,7 +70,7 @@
             mocked_manager = MagicMock()
 
             # WHEN: An importer object is created
-            importer = SongShowPlusImport(mocked_manager, filenames=[])
+            importer = SongShowPlusImport(mocked_manager, file_paths=[])
 
             # THEN: The importer object should not be None
             self.assertIsNotNone(importer, 'Import should not be none')
@@ -82,7 +83,7 @@
         with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
             mocked_manager = MagicMock()
             mocked_import_wizard = MagicMock()
-            importer = SongShowPlusImport(mocked_manager, filenames=[])
+            importer = SongShowPlusImport(mocked_manager, file_paths=[])
             importer.import_wizard = mocked_import_wizard
             importer.stop_import_flag = True
 
@@ -103,7 +104,7 @@
         with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
             mocked_manager = MagicMock()
             mocked_import_wizard = MagicMock()
-            importer = SongShowPlusImport(mocked_manager, filenames=[])
+            importer = SongShowPlusImport(mocked_manager, file_paths=[])
             importer.import_wizard = mocked_import_wizard
             importer.stop_import_flag = True
 
@@ -123,7 +124,7 @@
         # GIVEN: A mocked out SongImport class, and a mocked out "manager"
         with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
             mocked_manager = MagicMock()
-            importer = SongShowPlusImport(mocked_manager, filenames=[])
+            importer = SongShowPlusImport(mocked_manager, file_paths=[])
 
             # WHEN: Supplied with the following arguments replicating verses being added
             test_values = [
@@ -151,7 +152,7 @@
         # GIVEN: A mocked out SongImport class, and a mocked out "manager"
         with patch('openlp.plugins.songs.lib.importers.songshowplus.SongImport'):
             mocked_manager = MagicMock()
-            importer = SongShowPlusImport(mocked_manager, filenames=[])
+            importer = SongShowPlusImport(mocked_manager, file_paths=[])
 
             # WHEN: Supplied with the following arguments replicating a verse order being added
             test_values = [

=== modified file 'tests/functional/openlp_plugins/songs/test_sundayplusimport.py'
--- tests/functional/openlp_plugins/songs/test_sundayplusimport.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_sundayplusimport.py	2017-09-30 23:24:21 +0000
@@ -24,6 +24,8 @@
 import os
 from unittest.mock import patch
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 
 TEST_PATH = os.path.abspath(
@@ -44,5 +46,5 @@
         with patch('openlp.plugins.songs.lib.importers.sundayplus.retrieve_windows_encoding') as \
                 mocked_retrieve_windows_encoding:
             mocked_retrieve_windows_encoding.return_value = 'cp1252'
-            self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.ptf')],
+            self.file_import([Path(TEST_PATH, 'Amazing Grace.ptf')],
                              self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_videopsalm.py'
--- tests/functional/openlp_plugins/songs/test_videopsalm.py	2017-05-11 20:24:20 +0000
+++ tests/functional/openlp_plugins/songs/test_videopsalm.py	2017-09-30 23:24:21 +0000
@@ -21,9 +21,10 @@
 """
 This module contains tests for the VideoPsalm song importer.
 """
-
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 from unittest.mock import patch, MagicMock
 
@@ -48,7 +49,7 @@
         mocked_returned_settings.value.side_effect = lambda value: True if value == 'songs/enable chords' else False
         mocked_settings.return_value = mocked_returned_settings
         # Do the test import
-        self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'),
+        self.file_import(Path(TEST_PATH, 'videopsalm-as-safe-a-stronghold.json'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold.json')))
-        self.file_import(os.path.join(TEST_PATH, 'videopsalm-as-safe-a-stronghold2.json'),
+        self.file_import(Path(TEST_PATH, 'videopsalm-as-safe-a-stronghold2.json'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'as-safe-a-stronghold2.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py'
--- tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py	2016-12-31 11:01:36 +0000
+++ tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py	2017-09-30 23:24:21 +0000
@@ -22,9 +22,10 @@
 """
 This module contains tests for the Words of Worship song importer.
 """
-
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 from openlp.plugins.songs.lib.importers.wordsofworship import WordsOfWorshipImport
 
@@ -43,10 +44,10 @@
         """
         Test that loading a Words of Worship file works correctly
         """
-        self.file_import([os.path.join(TEST_PATH, 'Amazing Grace (6 Verses).wow-song')],
+        self.file_import([Path(TEST_PATH, 'Amazing Grace (6 Verses).wow-song')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace (6 Verses).json')))
-        self.file_import([os.path.join(TEST_PATH, 'When morning gilds the skies.wsg')],
+        self.file_import([Path(TEST_PATH, 'When morning gilds the skies.wsg')],
                          self.load_external_result_data(os.path.join(TEST_PATH, 'When morning gilds the skies.json')))
-        self.file_import([os.path.join(TEST_PATH, 'Holy Holy Holy Lord God Almighty.wow-song')],
+        self.file_import([Path(TEST_PATH, 'Holy Holy Holy Lord God Almighty.wow-song')],
                          self.load_external_result_data(os.path.join(TEST_PATH,
                                                                      'Holy Holy Holy Lord God Almighty.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_worshipassistantimport.py'
--- tests/functional/openlp_plugins/songs/test_worshipassistantimport.py	2016-12-31 11:01:36 +0000
+++ tests/functional/openlp_plugins/songs/test_worshipassistantimport.py	2017-09-30 23:24:21 +0000
@@ -23,9 +23,10 @@
 The :mod:`worshipassistantimport` module provides the functionality for importing
 WorshipAssistant song files into the current installation database.
 """
-
 import os
 
+from openlp.core.common.path import Path
+
 from tests.helpers.songfileimport import SongImportTestHelper
 
 TEST_PATH = os.path.abspath(
@@ -43,9 +44,9 @@
         """
         Test that loading an Worship Assistant file works correctly
         """
-        self.file_import(os.path.join(TEST_PATH, 'du_herr.csv'),
+        self.file_import(Path(TEST_PATH, 'du_herr.csv'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'du_herr.json')))
-        self.file_import(os.path.join(TEST_PATH, 'would_you_be_free.csv'),
+        self.file_import(Path(TEST_PATH, 'would_you_be_free.csv'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json')))
-        self.file_import(os.path.join(TEST_PATH, 'would_you_be_free2.csv'),
+        self.file_import(Path(TEST_PATH, 'would_you_be_free2.csv'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json')))

=== modified file 'tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py'
--- tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py	2017-09-30 23:24:21 +0000
@@ -55,7 +55,7 @@
         _title_assignment_list = []
 
         def __init__(self, manager):
-            WorshipCenterProImport.__init__(self, manager, filenames=[])
+            WorshipCenterProImport.__init__(self, manager, file_paths=[])
 
         @property
         def title(self):
@@ -153,7 +153,7 @@
             mocked_manager = MagicMock()
 
             # WHEN: An importer object is created
-            importer = WorshipCenterProImport(mocked_manager, filenames=[])
+            importer = WorshipCenterProImport(mocked_manager, file_paths=[])
 
             # THEN: The importer object should not be None
             self.assertIsNotNone(importer, 'Import should not be none')
@@ -170,7 +170,7 @@
             mocked_manager = MagicMock()
             mocked_log_error = MagicMock()
             mocked_translate.return_value = 'Translated Text'
-            importer = WorshipCenterProImport(mocked_manager, filenames=[])
+            importer = WorshipCenterProImport(mocked_manager, file_paths=[])
             importer.log_error = mocked_log_error
             importer.import_source = 'import_source'
             pyodbc_errors = [pyodbc.DatabaseError, pyodbc.IntegrityError, pyodbc.InternalError, pyodbc.OperationalError]

=== modified file 'tests/functional/openlp_plugins/songs/test_zionworximport.py'
--- tests/functional/openlp_plugins/songs/test_zionworximport.py	2017-04-24 05:17:55 +0000
+++ tests/functional/openlp_plugins/songs/test_zionworximport.py	2017-09-30 23:24:21 +0000
@@ -26,9 +26,10 @@
 from unittest import TestCase
 from unittest.mock import MagicMock, patch
 
+from openlp.core.common import Registry
+from openlp.core.common.path import Path
 from openlp.plugins.songs.lib.importers.zionworx import ZionWorxImport
 from openlp.plugins.songs.lib.importers.songimport import SongImport
-from openlp.core.common import Registry
 
 from tests.helpers.songfileimport import SongImportTestHelper
 
@@ -55,7 +56,7 @@
             mocked_manager = MagicMock()
 
             # WHEN: An importer object is created
-            importer = ZionWorxImport(mocked_manager, filenames=[])
+            importer = ZionWorxImport(mocked_manager, file_paths=[])
 
             # THEN: The importer should be an instance of SongImport
             self.assertIsInstance(importer, SongImport)
@@ -72,5 +73,5 @@
         """
         Test that loading an ZionWorx file works correctly on various files
         """
-        self.file_import(os.path.join(TEST_PATH, 'zionworx.csv'),
+        self.file_import(Path(TEST_PATH, 'zionworx.csv'),
                          self.load_external_result_data(os.path.join(TEST_PATH, 'zionworx.json')))

=== modified file 'tests/helpers/songfileimport.py'
--- tests/helpers/songfileimport.py	2017-04-24 05:17:55 +0000
+++ tests/helpers/songfileimport.py	2017-09-30 23:24:21 +0000
@@ -89,7 +89,7 @@
         """
         Import the given file and check that it has imported correctly
         """
-        importer = self.importer_class(self.mocked_manager, filenames=[source_file_name])
+        importer = self.importer_class(self.mocked_manager, file_paths=[source_file_name])
         importer.import_wizard = self.mocked_import_wizard
         importer.stop_import_flag = False
         importer.topics = []


Follow ups