Merge lp:~phill-ridout/openlp/pathlib5 into lp:openlp

Proposed by Phill
Status: Superseded
Proposed branch: lp:~phill-ridout/openlp/pathlib5
Merge into: lp:openlp
Diff against target: 2562 lines (+771/-534)
34 files modified
openlp/core/__init__.py (+18/-24)
openlp/core/common/applocation.py (+0/-1)
openlp/core/common/httputils.py (+20/-22)
openlp/core/common/languagemanager.py (+1/-1)
openlp/core/common/registry.py (+1/-1)
openlp/core/common/uistrings.py (+0/-3)
openlp/core/lib/__init__.py (+14/-9)
openlp/core/lib/mediamanageritem.py (+0/-2)
openlp/core/lib/shutil.py (+112/-0)
openlp/core/ui/firsttimeform.py (+3/-3)
openlp/core/ui/lib/wizard.py (+1/-1)
openlp/core/ui/mainwindow.py (+10/-9)
openlp/core/ui/servicemanager.py (+20/-20)
openlp/core/ui/thememanager.py (+1/-1)
openlp/plugins/images/lib/mediaitem.py (+1/-1)
openlp/plugins/presentations/lib/impresscontroller.py (+19/-25)
openlp/plugins/presentations/lib/mediaitem.py (+79/-79)
openlp/plugins/presentations/lib/messagelistener.py (+16/-13)
openlp/plugins/presentations/lib/pdfcontroller.py (+54/-50)
openlp/plugins/presentations/lib/powerpointcontroller.py (+7/-7)
openlp/plugins/presentations/lib/pptviewcontroller.py (+18/-17)
openlp/plugins/presentations/lib/presentationcontroller.py (+83/-70)
openlp/plugins/presentations/lib/presentationtab.py (+5/-4)
openlp/plugins/remotes/deploy.py (+1/-1)
openlp/plugins/songs/reporting.py (+41/-47)
tests/functional/openlp_core_common/test_httputils.py (+2/-1)
tests/functional/openlp_core_lib/test_lib.py (+22/-37)
tests/functional/openlp_core_lib/test_shutil.py (+170/-0)
tests/functional/openlp_core_ui/test_exceptionform.py (+1/-1)
tests/functional/openlp_plugins/presentations/test_impresscontroller.py (+3/-4)
tests/functional/openlp_plugins/presentations/test_mediaitem.py (+10/-9)
tests/functional/openlp_plugins/presentations/test_pdfcontroller.py (+8/-7)
tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py (+7/-7)
tests/functional/openlp_plugins/presentations/test_presentationcontroller.py (+23/-57)
To merge this branch: bzr merge lp:~phill-ridout/openlp/pathlib5
Reviewer Review Type Date Requested Status
Tim Bentley Needs Fixing
Review via email: mp+330895@code.launchpad.net

This proposal has been superseded by a proposal from 2017-09-18.

To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) wrote :

Sorry but you removed too much code!

review: Needs Fixing
lp:~phill-ridout/openlp/pathlib5 updated
2774. By Phill

Revert some requested changes

2775. By Phill

Moved the patched shuilils to the path module

2776. By Phill

PEP fixes

2777. By Phill

minor fix

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/__init__.py'
2--- openlp/core/__init__.py 2017-09-03 10:18:14 +0000
3+++ openlp/core/__init__.py 2017-09-18 20:10:29 +0000
4@@ -29,8 +29,6 @@
5
6 import argparse
7 import logging
8-import os
9-import shutil
10 import sys
11 import time
12 from datetime import datetime
13@@ -43,6 +41,7 @@
14 from openlp.core.common.path import Path
15 from openlp.core.common.versionchecker import VersionThread, get_application_version
16 from openlp.core.lib import ScreenList
17+from openlp.core.lib.shutil import copytree
18 from openlp.core.resources import qInitResources
19 from openlp.core.ui import SplashScreen
20 from openlp.core.ui.exceptionform import ExceptionForm
21@@ -181,25 +180,20 @@
22 """
23 Check if the data folder path exists.
24 """
25- data_folder_path = str(AppLocation.get_data_path())
26- if not os.path.exists(data_folder_path):
27- log.critical('Database was not found in: ' + data_folder_path)
28- status = QtWidgets.QMessageBox.critical(None, translate('OpenLP', 'Data Directory Error'),
29- translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}'
30- '\n\nThe location of the data folder was '
31- 'previously changed from the OpenLP\'s '
32- 'default location. If the data was stored on '
33- 'removable device, that device needs to be '
34- 'made available.\n\nYou may reset the data '
35- 'location back to the default location, '
36- 'or you can try to make the current location '
37- 'available.\n\nDo you want to reset to the '
38- 'default data location? If not, OpenLP will be '
39- 'closed so you can try to fix the the problem.')
40- .format(path=data_folder_path),
41- QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes |
42- QtWidgets.QMessageBox.No),
43- QtWidgets.QMessageBox.No)
44+ data_folder_path = AppLocation.get_data_path()
45+ if not data_folder_path.exists():
46+ log.critical('Database was not found in: %s', data_folder_path)
47+ status = QtWidgets.QMessageBox.critical(
48+ None, translate('OpenLP', 'Data Directory Error'),
49+ translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}\n\nThe location of the data folder '
50+ 'was previously changed from the OpenLP\'s default location. If the data was '
51+ 'stored on removable device, that device needs to be made available.\n\nYou may '
52+ 'reset the data location back to the default location, or you can try to make the '
53+ 'current location available.\n\nDo you want to reset to the default data location? '
54+ 'If not, OpenLP will be closed so you can try to fix the the problem.')
55+ .format(path=data_folder_path),
56+ QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
57+ QtWidgets.QMessageBox.No)
58 if status == QtWidgets.QMessageBox.No:
59 # If answer was "No", return "True", it will shutdown OpenLP in def main
60 log.info('User requested termination')
61@@ -253,11 +247,11 @@
62 'a backup of the old data folder?'),
63 defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes:
64 # Create copy of data folder
65- data_folder_path = str(AppLocation.get_data_path())
66+ data_folder_path = AppLocation.get_data_path()
67 timestamp = time.strftime("%Y%m%d-%H%M%S")
68- data_folder_backup_path = data_folder_path + '-' + timestamp
69+ data_folder_backup_path = data_folder_path.with_name(data_folder_path.name + '-' + timestamp)
70 try:
71- shutil.copytree(data_folder_path, data_folder_backup_path)
72+ copytree(data_folder_path, data_folder_backup_path)
73 except OSError:
74 QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'),
75 translate('OpenLP', 'Backup of the data folder failed!'))
76
77=== modified file 'openlp/core/common/applocation.py'
78--- openlp/core/common/applocation.py 2017-08-26 15:06:11 +0000
79+++ openlp/core/common/applocation.py 2017-09-18 20:10:29 +0000
80@@ -29,7 +29,6 @@
81 from openlp.core.common import Settings, is_win, is_macosx
82 from openlp.core.common.path import Path
83
84-
85 if not is_win() and not is_macosx():
86 try:
87 from xdg import BaseDirectory
88
89=== modified file 'openlp/core/common/httputils.py'
90--- openlp/core/common/httputils.py 2017-08-13 05:50:44 +0000
91+++ openlp/core/common/httputils.py 2017-09-18 20:10:29 +0000
92@@ -211,7 +211,7 @@
93
94 :param callback: the class which needs to be updated
95 :param url: URL to download
96- :param f_path: Destination file
97+ :param openlp.core.common.path.Path f_path: Destination file
98 :param sha256: The check sum value to be checked against the download value
99 """
100 block_count = 0
101@@ -220,29 +220,23 @@
102 log.debug("url_get_file: " + url)
103 while True:
104 try:
105- filename = open(f_path, "wb")
106- url_file = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT)
107 if sha256:
108 hasher = hashlib.sha256()
109- # Download until finished or canceled.
110- while not callback.was_cancelled:
111- data = url_file.read(block_size)
112- if not data:
113- break
114- filename.write(data)
115- if sha256:
116- hasher.update(data)
117- block_count += 1
118- callback._download_progress(block_count, block_size)
119- filename.close()
120- if sha256 and hasher.hexdigest() != sha256:
121- log.error('sha256 sums did not match for file: {file}'.format(file=f_path))
122- os.remove(f_path)
123- return False
124- except (urllib.error.URLError, socket.timeout) as err:
125+ with f_path.open('wb') as file:
126+ url_file = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT)
127+ # Download until finished or canceled.
128+ while not callback.was_cancelled:
129+ data = url_file.read(block_size)
130+ if not data:
131+ break
132+ file.write(data)
133+ if sha256:
134+ hasher.update(data)
135+ block_count += 1
136+ callback._download_progress(block_count, block_size)
137+ except (urllib.error.URLError, socket.timeout):
138 trace_error_handler(log)
139- filename.close()
140- os.remove(f_path)
141+ f_path.unlink()
142 if retries > CONNECTION_RETRIES:
143 return False
144 else:
145@@ -251,8 +245,12 @@
146 continue
147 break
148 # Delete file if cancelled, it may be a partial file.
149+ if sha256 and hasher.hexdigest() != sha256:
150+ log.error('sha256 sums did not match for file: {file}'.format(file=f_path))
151+ f_path.unlink()
152+ return False
153 if callback.was_cancelled:
154- os.remove(f_path)
155+ f_path.unlink()
156 return True
157
158
159
160=== modified file 'openlp/core/common/languagemanager.py'
161--- openlp/core/common/languagemanager.py 2017-08-01 20:59:41 +0000
162+++ openlp/core/common/languagemanager.py 2017-09-18 20:10:29 +0000
163@@ -141,7 +141,7 @@
164 if reg_ex.exactMatch(qmf):
165 name = '{regex}'.format(regex=reg_ex.cap(1))
166 LanguageManager.__qm_list__[
167- '{count:>2i} {name}'.format(count=counter + 1, name=LanguageManager.language_name(qmf))] = name
168+ '{count:>2d} {name}'.format(count=counter + 1, name=LanguageManager.language_name(qmf))] = name
169
170 @staticmethod
171 def get_qm_list():
172
173=== modified file 'openlp/core/common/registry.py'
174--- openlp/core/common/registry.py 2017-03-03 19:27:31 +0000
175+++ openlp/core/common/registry.py 2017-09-18 20:10:29 +0000
176@@ -143,7 +143,7 @@
177 log.exception('Exception for function {function}'.format(function=function))
178 else:
179 trace_error_handler(log)
180- log.error("Event {event} called but not registered".format(event=event))
181+ log.exception('Event {event} called but not registered'.format(event=event))
182 return results
183
184 def get_flag(self, key):
185
186=== modified file 'openlp/core/common/uistrings.py'
187--- openlp/core/common/uistrings.py 2017-08-03 17:54:40 +0000
188+++ openlp/core/common/uistrings.py 2017-09-18 20:10:29 +0000
189@@ -88,9 +88,6 @@
190 self.Error = translate('OpenLP.Ui', 'Error')
191 self.Export = translate('OpenLP.Ui', 'Export')
192 self.File = translate('OpenLP.Ui', 'File')
193- self.FileNotFound = translate('OpenLP.Ui', 'File Not Found')
194- self.FileNotFoundMessage = translate('OpenLP.Ui',
195- 'File {name} not found.\nPlease try selecting it individually.')
196 self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit')
197 self.Help = translate('OpenLP.Ui', 'Help')
198 self.Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours')
199
200=== modified file 'openlp/core/lib/__init__.py'
201--- openlp/core/lib/__init__.py 2017-08-25 20:03:25 +0000
202+++ openlp/core/lib/__init__.py 2017-09-18 20:10:29 +0000
203@@ -32,6 +32,7 @@
204 from PyQt5 import QtCore, QtGui, Qt, QtWidgets
205
206 from openlp.core.common import translate
207+from openlp.core.common.path import Path
208
209 log = logging.getLogger(__name__ + '.__init__')
210
211@@ -125,10 +126,11 @@
212 Build a QIcon instance from an existing QIcon, a resource location, or a physical file location. If the icon is a
213 QIcon instance, that icon is simply returned. If not, it builds a QIcon instance from the resource or file name.
214
215- :param icon:
216- The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file
217- location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
218+ :param QtGui.QIcon | Path | QtGui.QIcon | str icon:
219+ The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file path
220+ location like ``Path(/path/to/file.png)``. However, the **recommended** way is to specify a resource string.
221 :return: The build icon.
222+ :rtype: QtGui.QIcon
223 """
224 if isinstance(icon, QtGui.QIcon):
225 return icon
226@@ -136,6 +138,8 @@
227 button_icon = QtGui.QIcon()
228 if isinstance(icon, str):
229 pix_map = QtGui.QPixmap(icon)
230+ elif isinstance(icon, Path):
231+ pix_map = QtGui.QPixmap(str(icon))
232 elif isinstance(icon, QtGui.QImage):
233 pix_map = QtGui.QPixmap.fromImage(icon)
234 if pix_map:
235@@ -217,14 +221,15 @@
236 Validates whether an file's thumb still exists and if is up to date. **Note**, you must **not** call this function,
237 before checking the existence of the file.
238
239- :param file_path: The path to the file. The file **must** exist!
240- :param thumb_path: The path to the thumb.
241- :return: True, False if the image has changed since the thumb was created.
242+ :param openlp.core.common.path.Path file_path: The path to the file. The file **must** exist!
243+ :param openlp.core.common.path.Path thumb_path: The path to the thumb.
244+ :return: Has the image changed since the thumb was created?
245+ :rtype: bool
246 """
247- if not os.path.exists(thumb_path):
248+ if not thumb_path.exists():
249 return False
250- image_date = os.stat(file_path).st_mtime
251- thumb_date = os.stat(thumb_path).st_mtime
252+ image_date = file_path.stat().st_mtime
253+ thumb_date = thumb_path.stat().st_mtime
254 return image_date <= thumb_date
255
256
257
258=== modified file 'openlp/core/lib/mediamanageritem.py'
259--- openlp/core/lib/mediamanageritem.py 2017-08-26 15:06:11 +0000
260+++ openlp/core/lib/mediamanageritem.py 2017-09-18 20:10:29 +0000
261@@ -359,10 +359,8 @@
262 :param files: The files to be loaded.
263 :param target_group: The QTreeWidgetItem of the group that will be the parent of the added files
264 """
265- names = []
266 full_list = []
267 for count in range(self.list_view.count()):
268- names.append(self.list_view.item(count).text())
269 full_list.append(self.list_view.item(count).data(QtCore.Qt.UserRole))
270 duplicates_found = False
271 files_added = False
272
273=== added file 'openlp/core/lib/shutil.py'
274--- openlp/core/lib/shutil.py 1970-01-01 00:00:00 +0000
275+++ openlp/core/lib/shutil.py 2017-09-18 20:10:29 +0000
276@@ -0,0 +1,112 @@
277+# -*- coding: utf-8 -*-
278+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
279+
280+###############################################################################
281+# OpenLP - Open Source Lyrics Projection #
282+# --------------------------------------------------------------------------- #
283+# Copyright (c) 2008-2017 OpenLP Developers #
284+# --------------------------------------------------------------------------- #
285+# This program is free software; you can redistribute it and/or modify it #
286+# under the terms of the GNU General Public License as published by the Free #
287+# Software Foundation; version 2 of the License. #
288+# #
289+# This program is distributed in the hope that it will be useful, but WITHOUT #
290+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
291+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
292+# more details. #
293+# #
294+# You should have received a copy of the GNU General Public License along #
295+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
296+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
297+###############################################################################
298+""" Patch the shutil methods we use so they accept and return Path objects"""
299+import shutil
300+
301+from openlp.core.common.path import path_to_str, str_to_path
302+from openlp.core.lib import replace_params
303+
304+
305+def copy(*args, **kwargs):
306+ """
307+ Wraps :func:`shutil.copy` so that we can accept Path objects.
308+
309+ :param src openlp.core.common.path.Path: Takes a Path object which is then converted to a str object
310+ :param dst openlp.core.common.path.Path: Takes a Path object which is then converted to a str object
311+ :return: Converts the str object received from :func:`shutil.copy` to a Path or NoneType object
312+ :rtype: openlp.core.common.path.Path | None
313+
314+ See the following link for more information on the other parameters:
315+ https://docs.python.org/3/library/shutil.html#shutil.copy
316+ """
317+
318+ args, kwargs = replace_params(args, kwargs, ((0, 'src', path_to_str), (1, 'dst', path_to_str)))
319+
320+ return str_to_path(shutil.copy(*args, **kwargs))
321+
322+
323+def copyfile(*args, **kwargs):
324+ """
325+ Wraps :func:`shutil.copyfile` so that we can accept Path objects.
326+
327+ :param openlp.core.common.path.Path src: Takes a Path object which is then converted to a str object
328+ :param openlp.core.common.path.Path dst: Takes a Path object which is then converted to a str object
329+ :return: Converts the str object received from :func:`shutil.copyfile` to a Path or NoneType object
330+ :rtype: openlp.core.common.path.Path | None
331+
332+ See the following link for more information on the other parameters:
333+ https://docs.python.org/3/library/shutil.html#shutil.copyfile
334+ """
335+
336+ args, kwargs = replace_params(args, kwargs, ((0, 'src', path_to_str), (1, 'dst', path_to_str)))
337+
338+ return str_to_path(shutil.copyfile(*args, **kwargs))
339+
340+
341+def copytree(*args, **kwargs):
342+ """
343+ Wraps :func:shutil.copytree` so that we can accept Path objects.
344+
345+ :param openlp.core.common.path.Path src : Takes a Path object which is then converted to a str object
346+ :param openlp.core.common.path.Path dst: Takes a Path object which is then converted to a str object
347+ :return: Converts the str object received from :func:`shutil.copytree` to a Path or NoneType object
348+ :rtype: openlp.core.common.path.Path | None
349+
350+ See the following link for more information on the other parameters:
351+ https://docs.python.org/3/library/shutil.html#shutil.copytree
352+ """
353+
354+ args, kwargs = replace_params(args, kwargs, ((0, 'src', path_to_str), (1, 'dst', path_to_str)))
355+
356+ return str_to_path(shutil.copytree(*args, **kwargs))
357+
358+
359+def rmtree(*args, **kwargs):
360+ """
361+ Wraps :func:shutil.rmtree` so that we can accept Path objects.
362+
363+ :param openlp.core.common.path.Path path: Takes a Path object which is then converted to a str object
364+ :return: Passes the return from :func:`shutil.rmtree` back
365+ :rtype: None
366+
367+ See the following link for more information on the other parameters:
368+ https://docs.python.org/3/library/shutil.html#shutil.rmtree
369+ """
370+
371+ args, kwargs = replace_params(args, kwargs, ((0, 'path', path_to_str),))
372+
373+ return shutil.rmtree(*args, **kwargs)
374+
375+
376+def which(*args, **kwargs):
377+ """
378+ Wraps :func:shutil.which` so that it return a Path objects.
379+
380+ :rtype: openlp.core.common.Path
381+
382+ See the following link for more information on the other parameters:
383+ https://docs.python.org/3/library/shutil.html#shutil.which
384+ """
385+ file_name = shutil.which(*args, **kwargs)
386+ if file_name:
387+ return str_to_path(file_name)
388+ return None
389
390=== modified file 'openlp/core/ui/firsttimeform.py'
391--- openlp/core/ui/firsttimeform.py 2017-08-25 20:03:25 +0000
392+++ openlp/core/ui/firsttimeform.py 2017-09-18 20:10:29 +0000
393@@ -563,7 +563,7 @@
394 filename, sha256 = item.data(QtCore.Qt.UserRole)
395 self._increment_progress_bar(self.downloading.format(name=filename), 0)
396 self.previous_size = 0
397- destination = os.path.join(songs_destination, str(filename))
398+ destination = Path(songs_destination, str(filename))
399 if not url_get_file(self, '{path}{name}'.format(path=self.songs_url, name=filename),
400 destination, sha256):
401 missed_files.append('Song: {name}'.format(name=filename))
402@@ -576,7 +576,7 @@
403 self._increment_progress_bar(self.downloading.format(name=bible), 0)
404 self.previous_size = 0
405 if not url_get_file(self, '{path}{name}'.format(path=self.bibles_url, name=bible),
406- os.path.join(bibles_destination, bible),
407+ Path(bibles_destination, bible),
408 sha256):
409 missed_files.append('Bible: {name}'.format(name=bible))
410 bibles_iterator += 1
411@@ -588,7 +588,7 @@
412 self._increment_progress_bar(self.downloading.format(name=theme), 0)
413 self.previous_size = 0
414 if not url_get_file(self, '{path}{name}'.format(path=self.themes_url, name=theme),
415- os.path.join(themes_destination, theme),
416+ Path(themes_destination, theme),
417 sha256):
418 missed_files.append('Theme: {name}'.format(name=theme))
419 if missed_files:
420
421=== modified file 'openlp/core/ui/lib/wizard.py'
422--- openlp/core/ui/lib/wizard.py 2017-08-26 15:06:11 +0000
423+++ openlp/core/ui/lib/wizard.py 2017-09-18 20:10:29 +0000
424@@ -310,7 +310,7 @@
425 """
426 folder_path = FileDialog.getExistingDirectory(
427 self, title, Settings().value(self.plugin.settings_section + '/' + setting_name),
428- QtWidgets.QFileDialog.ShowDirsOnly)
429+ FileDialog.ShowDirsOnly)
430 if folder_path:
431 editbox.setText(str(folder_path))
432 Settings().setValue(self.plugin.settings_section + '/' + setting_name, folder_path)
433
434=== modified file 'openlp/core/ui/mainwindow.py'
435--- openlp/core/ui/mainwindow.py 2017-09-03 10:18:14 +0000
436+++ openlp/core/ui/mainwindow.py 2017-09-18 20:10:29 +0000
437@@ -42,6 +42,7 @@
438 from openlp.core.common.path import Path, path_to_str, str_to_path
439 from openlp.core.common.versionchecker import get_application_version
440 from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon
441+from openlp.core.lib.shutil import copyfile
442 from openlp.core.lib.ui import create_action
443 from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \
444 ShortcutListForm, FormattingTagForm, PreviewController
445@@ -848,12 +849,12 @@
446 QtWidgets.QMessageBox.No)
447 if answer == QtWidgets.QMessageBox.No:
448 return
449- import_file_name, filter_used = QtWidgets.QFileDialog.getOpenFileName(
450+ import_file_path, filter_used = FileDialog.getOpenFileName(
451 self,
452 translate('OpenLP.MainWindow', 'Import settings'),
453- '',
454+ None,
455 translate('OpenLP.MainWindow', 'OpenLP Settings (*.conf)'))
456- if not import_file_name:
457+ if import_file_path is None:
458 return
459 setting_sections = []
460 # Add main sections.
461@@ -871,12 +872,12 @@
462 # Add plugin sections.
463 setting_sections.extend([plugin.name for plugin in self.plugin_manager.plugins])
464 # Copy the settings file to the tmp dir, because we do not want to change the original one.
465- temp_directory = os.path.join(str(gettempdir()), 'openlp')
466- check_directory_exists(Path(temp_directory))
467- temp_config = os.path.join(temp_directory, os.path.basename(import_file_name))
468- shutil.copyfile(import_file_name, temp_config)
469+ temp_dir_path = Path(gettempdir(), 'openlp')
470+ check_directory_exists(temp_dir_path)
471+ temp_config_path = temp_dir_path / import_file_path.name
472+ copyfile(import_file_path, temp_config_path)
473 settings = Settings()
474- import_settings = Settings(temp_config, Settings.IniFormat)
475+ import_settings = Settings(str(temp_config_path), Settings.IniFormat)
476
477 log.info('hook upgrade_plugin_settings')
478 self.plugin_manager.hook_upgrade_plugin_settings(import_settings)
479@@ -920,7 +921,7 @@
480 settings.setValue('{key}'.format(key=section_key), value)
481 now = datetime.now()
482 settings.beginGroup(self.header_section)
483- settings.setValue('file_imported', import_file_name)
484+ settings.setValue('file_imported', import_file_path)
485 settings.setValue('file_date_imported', now.strftime("%Y-%m-%d %H:%M"))
486 settings.endGroup()
487 settings.sync()
488
489=== modified file 'openlp/core/ui/servicemanager.py'
490--- openlp/core/ui/servicemanager.py 2017-08-26 15:06:11 +0000
491+++ openlp/core/ui/servicemanager.py 2017-09-18 20:10:29 +0000
492@@ -366,16 +366,20 @@
493 """
494 return self._modified
495
496- def set_file_name(self, file_name):
497+ def set_file_name(self, file_path):
498 """
499 Setter for service file.
500
501- :param file_name: The service file name
502+ :param openlp.core.common.path.Path file_path: The service file name
503+ :rtype: None
504 """
505- self._file_name = str(file_name)
506+ self._file_name = path_to_str(file_path)
507 self.main_window.set_service_modified(self.is_modified(), self.short_file_name())
508- Settings().setValue('servicemanager/last file', Path(file_name))
509- self._save_lite = self._file_name.endswith('.oszl')
510+ Settings().setValue('servicemanager/last file', file_path)
511+ if file_path and file_path.suffix() == '.oszl':
512+ self._save_lite = True
513+ else:
514+ self._save_lite = False
515
516 def file_name(self):
517 """
518@@ -474,7 +478,7 @@
519 """
520 self.service_manager_list.clear()
521 self.service_items = []
522- self.set_file_name('')
523+ self.set_file_name(None)
524 self.service_id += 1
525 self.set_modified(False)
526 Settings().setValue('servicemanager/last file', None)
527@@ -695,27 +699,23 @@
528 default_file_name = format_time(default_pattern, local_time)
529 else:
530 default_file_name = ''
531- directory = path_to_str(Settings().value(self.main_window.service_manager_settings_section + '/last directory'))
532- path = os.path.join(directory, default_file_name)
533+ directory_path = Settings().value(self.main_window.service_manager_settings_section + '/last directory')
534+ file_path = directory_path / default_file_name
535 # SaveAs from osz to oszl is not valid as the files will be deleted on exit which is not sensible or usable in
536 # the long term.
537 if self._file_name.endswith('oszl') or self.service_has_all_original_files:
538- file_name, filter_used = QtWidgets.QFileDialog.getSaveFileName(
539- self.main_window, UiStrings().SaveService, path,
540+ file_path, filter_used = FileDialog.getSaveFileName(
541+ self.main_window, UiStrings().SaveService, file_path,
542 translate('OpenLP.ServiceManager',
543 'OpenLP Service Files (*.osz);; OpenLP Service Files - lite (*.oszl)'))
544 else:
545- file_name, filter_used = QtWidgets.QFileDialog.getSaveFileName(
546- self.main_window, UiStrings().SaveService, path,
547+ file_path, filter_used = FileDialog.getSaveFileName(
548+ self.main_window, UiStrings().SaveService, file_path,
549 translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz);;'))
550- if not file_name:
551+ if not file_path:
552 return False
553- if os.path.splitext(file_name)[1] == '':
554- file_name += '.osz'
555- else:
556- ext = os.path.splitext(file_name)[1]
557- file_name.replace(ext, '.osz')
558- self.set_file_name(file_name)
559+ file_path.with_suffix('.osz')
560+ self.set_file_name(file_path)
561 self.decide_save_method()
562
563 def decide_save_method(self, field=None):
564@@ -772,7 +772,7 @@
565 return
566 file_to.close()
567 self.new_file()
568- self.set_file_name(file_name)
569+ self.set_file_name(str_to_path(file_name))
570 self.main_window.display_progress_bar(len(items))
571 self.process_service_items(items)
572 delete_file(Path(p_file))
573
574=== modified file 'openlp/core/ui/thememanager.py'
575--- openlp/core/ui/thememanager.py 2017-08-26 16:50:54 +0000
576+++ openlp/core/ui/thememanager.py 2017-09-18 20:10:29 +0000
577@@ -483,7 +483,7 @@
578 name = text_name
579 thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=text_name))
580 item_name = QtWidgets.QListWidgetItem(name)
581- if validate_thumb(theme, thumb):
582+ if validate_thumb(Path(theme), Path(thumb)):
583 icon = build_icon(thumb)
584 else:
585 icon = create_thumb(theme, thumb)
586
587=== modified file 'openlp/plugins/images/lib/mediaitem.py'
588--- openlp/plugins/images/lib/mediaitem.py 2017-08-26 15:06:11 +0000
589+++ openlp/plugins/images/lib/mediaitem.py 2017-09-18 20:10:29 +0000
590@@ -360,7 +360,7 @@
591 if not os.path.exists(image_file.filename):
592 icon = build_icon(':/general/general_delete.png')
593 else:
594- if validate_thumb(image_file.filename, thumb):
595+ if validate_thumb(Path(image_file.filename), Path(thumb)):
596 icon = build_icon(thumb)
597 else:
598 icon = create_thumb(image_file.filename, thumb)
599
600=== modified file 'openlp/plugins/presentations/lib/impresscontroller.py'
601--- openlp/plugins/presentations/lib/impresscontroller.py 2017-08-25 20:03:25 +0000
602+++ openlp/plugins/presentations/lib/impresscontroller.py 2017-09-18 20:10:29 +0000
603@@ -32,11 +32,14 @@
604 # http://nxsy.org/comparing-documents-with-openoffice-and-python
605
606 import logging
607-import os
608 import time
609
610-from openlp.core.common import is_win, Registry, delete_file
611-from openlp.core.common.path import Path
612+from PyQt5 import QtCore
613+
614+from openlp.core.common import Registry, delete_file, get_uno_command, get_uno_instance, is_win
615+from openlp.core.lib import ScreenList
616+from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
617+ TextType
618
619 if is_win():
620 from win32com.client import Dispatch
621@@ -55,14 +58,6 @@
622 except ImportError:
623 uno_available = False
624
625-from PyQt5 import QtCore
626-
627-from openlp.core.lib import ScreenList
628-from openlp.core.common import get_uno_command, get_uno_instance
629-from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
630- TextType
631-
632-
633 log = logging.getLogger(__name__)
634
635
636@@ -203,12 +198,15 @@
637 Class which holds information and controls a single presentation.
638 """
639
640- def __init__(self, controller, presentation):
641+ def __init__(self, controller, document_path):
642 """
643 Constructor, store information about the file and initialise.
644+
645+ :param openlp.core.common.path.Path document_path: File path for the document to load
646+ :rtype: None
647 """
648 log.debug('Init Presentation OpenOffice')
649- super(ImpressDocument, self).__init__(controller, presentation)
650+ super().__init__(controller, document_path)
651 self.document = None
652 self.presentation = None
653 self.control = None
654@@ -225,10 +223,9 @@
655 if desktop is None:
656 self.controller.start_process()
657 desktop = self.controller.get_com_desktop()
658- url = 'file:///' + self.file_path.replace('\\', '/').replace(':', '|').replace(' ', '%20')
659 else:
660 desktop = self.controller.get_uno_desktop()
661- url = uno.systemPathToFileUrl(self.file_path)
662+ url = self.file_path.as_uri()
663 if desktop is None:
664 return False
665 self.desktop = desktop
666@@ -254,11 +251,8 @@
667 log.debug('create thumbnails OpenOffice')
668 if self.check_thumbnails():
669 return
670- if is_win():
671- thumb_dir_url = 'file:///' + self.get_temp_folder().replace('\\', '/') \
672- .replace(':', '|').replace(' ', '%20')
673- else:
674- thumb_dir_url = uno.systemPathToFileUrl(self.get_temp_folder())
675+ temp_folder_path = self.get_temp_folder()
676+ thumb_dir_url = temp_folder_path.as_uri()
677 properties = []
678 properties.append(self.create_property('FilterName', 'impress_png_Export'))
679 properties = tuple(properties)
680@@ -266,17 +260,17 @@
681 pages = doc.getDrawPages()
682 if not pages:
683 return
684- if not os.path.isdir(self.get_temp_folder()):
685- os.makedirs(self.get_temp_folder())
686+ if not temp_folder_path.is_dir():
687+ temp_folder_path.mkdir(parents=True)
688 for index in range(pages.getCount()):
689 page = pages.getByIndex(index)
690 doc.getCurrentController().setCurrentPage(page)
691- url_path = '{path}/{name}.png'.format(path=thumb_dir_url, name=str(index + 1))
692- path = os.path.join(self.get_temp_folder(), str(index + 1) + '.png')
693+ url_path = '{path}/{name:d}.png'.format(path=thumb_dir_url, name=index + 1)
694+ path = temp_folder_path / '{number:d}.png'.format(number=index + 1)
695 try:
696 doc.storeToURL(url_path, properties)
697 self.convert_thumbnail(path, index + 1)
698- delete_file(Path(path))
699+ delete_file(path)
700 except ErrorCodeIOException as exception:
701 log.exception('ERROR! ErrorCodeIOException {error:d}'.format(error=exception.ErrCode))
702 except:
703
704=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
705--- openlp/plugins/presentations/lib/mediaitem.py 2017-08-26 15:06:11 +0000
706+++ openlp/plugins/presentations/lib/mediaitem.py 2017-09-18 20:10:29 +0000
707@@ -19,15 +19,13 @@
708 # with this program; if not, write to the Free Software Foundation, Inc., 59 #
709 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
710 ###############################################################################
711-
712 import logging
713-import os
714
715 from PyQt5 import QtCore, QtGui, QtWidgets
716
717 from openlp.core.common import Registry, Settings, UiStrings, translate
718 from openlp.core.common.languagemanager import get_locale_key
719-from openlp.core.common.path import path_to_str
720+from openlp.core.common.path import Path, path_to_str, str_to_path
721 from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext,\
722 build_icon, check_item_selected, create_thumb, validate_thumb
723 from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
724@@ -128,7 +126,7 @@
725 """
726 self.list_view.setIconSize(QtCore.QSize(88, 50))
727 file_paths = Settings().value(self.settings_section + '/presentations files')
728- self.load_list([path_to_str(file) for file in file_paths], initial_load=True)
729+ self.load_list([path_to_str(path) for path in file_paths], initial_load=True)
730 self.populate_display_types()
731
732 def populate_display_types(self):
733@@ -152,54 +150,57 @@
734 else:
735 self.presentation_widget.hide()
736
737- def load_list(self, files, target_group=None, initial_load=False):
738+ def load_list(self, file_paths, target_group=None, initial_load=False):
739 """
740 Add presentations into the media manager. This is called both on initial load of the plugin to populate with
741 existing files, and when the user adds new files via the media manager.
742+
743+ :param list[openlp.core.common.path.Path] file_paths: List of file paths to add to the media manager.
744 """
745- current_list = self.get_file_list()
746- titles = [file_path.name for file_path in current_list]
747+ file_paths = [str_to_path(filename) for filename in file_paths]
748+ current_paths = self.get_file_list()
749+ titles = [file_path.name for file_path in current_paths]
750 self.application.set_busy_cursor()
751 if not initial_load:
752- self.main_window.display_progress_bar(len(files))
753+ self.main_window.display_progress_bar(len(file_paths))
754 # Sort the presentations by its filename considering language specific characters.
755- files.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
756- for file in files:
757+ file_paths.sort(key=lambda file_path: get_locale_key(file_path.name))
758+ for file_path in file_paths:
759 if not initial_load:
760 self.main_window.increment_progress_bar()
761- if current_list.count(file) > 0:
762+ if current_paths.count(file_path) > 0:
763 continue
764- filename = os.path.split(file)[1]
765- if not os.path.exists(file):
766- item_name = QtWidgets.QListWidgetItem(filename)
767+ file_name = file_path.name
768+ if not file_path.exists():
769+ item_name = QtWidgets.QListWidgetItem(file_name)
770 item_name.setIcon(build_icon(ERROR_IMAGE))
771- item_name.setData(QtCore.Qt.UserRole, file)
772- item_name.setToolTip(file)
773+ item_name.setData(QtCore.Qt.UserRole, path_to_str(file_path))
774+ item_name.setToolTip(str(file_path))
775 self.list_view.addItem(item_name)
776 else:
777- if titles.count(filename) > 0:
778+ if titles.count(file_name) > 0:
779 if not initial_load:
780 critical_error_message_box(translate('PresentationPlugin.MediaItem', 'File Exists'),
781 translate('PresentationPlugin.MediaItem',
782 'A presentation with that filename already exists.'))
783 continue
784- controller_name = self.find_controller_by_type(filename)
785+ controller_name = self.find_controller_by_type(file_path)
786 if controller_name:
787 controller = self.controllers[controller_name]
788- doc = controller.add_document(file)
789- thumb = os.path.join(doc.get_thumbnail_folder(), 'icon.png')
790- preview = doc.get_thumbnail_path(1, True)
791- if not preview and not initial_load:
792+ doc = controller.add_document(file_path)
793+ thumbnail_path = doc.get_thumbnail_folder() / 'icon.png'
794+ preview_path = doc.get_thumbnail_path(1, True)
795+ if not preview_path and not initial_load:
796 doc.load_presentation()
797- preview = doc.get_thumbnail_path(1, True)
798+ preview_path = doc.get_thumbnail_path(1, True)
799 doc.close_presentation()
800- if not (preview and os.path.exists(preview)):
801+ if not (preview_path and preview_path.exists()):
802 icon = build_icon(':/general/general_delete.png')
803 else:
804- if validate_thumb(preview, thumb):
805- icon = build_icon(thumb)
806+ if validate_thumb(Path(preview_path), Path(thumbnail_path)):
807+ icon = build_icon(thumbnail_path)
808 else:
809- icon = create_thumb(preview, thumb)
810+ icon = create_thumb(str(preview_path), str(thumbnail_path))
811 else:
812 if initial_load:
813 icon = build_icon(':/general/general_delete.png')
814@@ -208,10 +209,10 @@
815 translate('PresentationPlugin.MediaItem',
816 'This type of presentation is not supported.'))
817 continue
818- item_name = QtWidgets.QListWidgetItem(filename)
819- item_name.setData(QtCore.Qt.UserRole, file)
820+ item_name = QtWidgets.QListWidgetItem(file_name)
821+ item_name.setData(QtCore.Qt.UserRole, path_to_str(file_path))
822 item_name.setIcon(icon)
823- item_name.setToolTip(file)
824+ item_name.setToolTip(str(file_path))
825 self.list_view.addItem(item_name)
826 if not initial_load:
827 self.main_window.finished_progress_bar()
828@@ -228,8 +229,8 @@
829 self.application.set_busy_cursor()
830 self.main_window.display_progress_bar(len(row_list))
831 for item in items:
832- filepath = str(item.data(QtCore.Qt.UserRole))
833- self.clean_up_thumbnails(filepath)
834+ file_path = str_to_path(item.data(QtCore.Qt.UserRole))
835+ self.clean_up_thumbnails(file_path)
836 self.main_window.increment_progress_bar()
837 self.main_window.finished_progress_bar()
838 for row in row_list:
839@@ -237,30 +238,29 @@
840 Settings().setValue(self.settings_section + '/presentations files', self.get_file_list())
841 self.application.set_normal_cursor()
842
843- def clean_up_thumbnails(self, filepath, clean_for_update=False):
844+ def clean_up_thumbnails(self, file_path, clean_for_update=False):
845 """
846 Clean up the files created such as thumbnails
847
848- :param filepath: File path of the presention to clean up after
849- :param clean_for_update: Only clean thumbnails if update is needed
850- :return: None
851+ :param openlp.core.common.path.Path file_path: File path of the presention to clean up after
852+ :param bool clean_for_update: Only clean thumbnails if update is needed
853+ :rtype: None
854 """
855 for cidx in self.controllers:
856- root, file_ext = os.path.splitext(filepath)
857- file_ext = file_ext[1:]
858+ file_ext = file_path.suffix[1:]
859 if file_ext in self.controllers[cidx].supports or file_ext in self.controllers[cidx].also_supports:
860- doc = self.controllers[cidx].add_document(filepath)
861+ doc = self.controllers[cidx].add_document(file_path)
862 if clean_for_update:
863 thumb_path = doc.get_thumbnail_path(1, True)
864- if not thumb_path or not os.path.exists(filepath) or os.path.getmtime(
865- thumb_path) < os.path.getmtime(filepath):
866+ if not thumb_path or not file_path.exists() or \
867+ thumb_path.stat().st_mtime < file_path.stat().st_mtime:
868 doc.presentation_deleted()
869 else:
870 doc.presentation_deleted()
871 doc.close_presentation()
872
873 def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
874- context=ServiceItemContext.Service, presentation_file=None):
875+ context=ServiceItemContext.Service, file_path=None):
876 """
877 Generate the slide data. Needs to be implemented by the plugin.
878
879@@ -276,10 +276,9 @@
880 items = self.list_view.selectedItems()
881 if len(items) > 1:
882 return False
883- filename = presentation_file
884- if filename is None:
885- filename = items[0].data(QtCore.Qt.UserRole)
886- file_type = os.path.splitext(filename.lower())[1][1:]
887+ if file_path is None:
888+ file_path = str_to_path(items[0].data(QtCore.Qt.UserRole))
889+ file_type = file_path.suffix.lower()[1:]
890 if not self.display_type_combo_box.currentText():
891 return False
892 service_item.add_capability(ItemCapabilities.CanEditTitle)
893@@ -292,29 +291,28 @@
894 # force a nonexistent theme
895 service_item.theme = -1
896 for bitem in items:
897- filename = presentation_file
898- if filename is None:
899- filename = bitem.data(QtCore.Qt.UserRole)
900- (path, name) = os.path.split(filename)
901- service_item.title = name
902- if os.path.exists(filename):
903- processor = self.find_controller_by_type(filename)
904+ if file_path is None:
905+ file_path = str_to_path(bitem.data(QtCore.Qt.UserRole))
906+ path, file_name = file_path.parent, file_path.name
907+ service_item.title = file_name
908+ if file_path.exists():
909+ processor = self.find_controller_by_type(file_path)
910 if not processor:
911 return False
912 controller = self.controllers[processor]
913 service_item.processor = None
914- doc = controller.add_document(filename)
915- if doc.get_thumbnail_path(1, True) is None or not os.path.isfile(
916- os.path.join(doc.get_temp_folder(), 'mainslide001.png')):
917+ doc = controller.add_document(file_path)
918+ if doc.get_thumbnail_path(1, True) is None or \
919+ not (doc.get_temp_folder() / 'mainslide001.png').is_file():
920 doc.load_presentation()
921 i = 1
922- image = os.path.join(doc.get_temp_folder(), 'mainslide{number:0>3d}.png'.format(number=i))
923- thumbnail = os.path.join(doc.get_thumbnail_folder(), 'slide%d.png' % i)
924- while os.path.isfile(image):
925- service_item.add_from_image(image, name, thumbnail=thumbnail)
926+ image_path = doc.get_temp_folder() / 'mainslide{number:0>3d}.png'.format(number=i)
927+ thumbnail_path = doc.get_thumbnail_folder() / 'slide{number:d}.png'.format(number=i)
928+ while image_path.is_file():
929+ service_item.add_from_image(str(image_path), file_name, thumbnail=str(thumbnail_path))
930 i += 1
931- image = os.path.join(doc.get_temp_folder(), 'mainslide{number:0>3d}.png'.format(number=i))
932- thumbnail = os.path.join(doc.get_thumbnail_folder(), 'slide{number:d}.png'.format(number=i))
933+ image_path = doc.get_temp_folder() / 'mainslide{number:0>3d}.png'.format(number=i)
934+ thumbnail_path = doc.get_thumbnail_folder() / 'slide{number:d}.png'.format(number=i)
935 service_item.add_capability(ItemCapabilities.HasThumbnails)
936 doc.close_presentation()
937 return True
938@@ -324,34 +322,34 @@
939 critical_error_message_box(translate('PresentationPlugin.MediaItem', 'Missing Presentation'),
940 translate('PresentationPlugin.MediaItem',
941 'The presentation {name} no longer exists.'
942- ).format(name=filename))
943+ ).format(name=file_path))
944 return False
945 else:
946 service_item.processor = self.display_type_combo_box.currentText()
947 service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
948 for bitem in items:
949- filename = bitem.data(QtCore.Qt.UserRole)
950- (path, name) = os.path.split(filename)
951- service_item.title = name
952- if os.path.exists(filename):
953+ file_path = str_to_path(bitem.data(QtCore.Qt.UserRole))
954+ path, file_name = file_path.parent, file_path.name
955+ service_item.title = file_name
956+ if file_path.exists:
957 if self.display_type_combo_box.itemData(self.display_type_combo_box.currentIndex()) == 'automatic':
958- service_item.processor = self.find_controller_by_type(filename)
959+ service_item.processor = self.find_controller_by_type(file_path)
960 if not service_item.processor:
961 return False
962 controller = self.controllers[service_item.processor]
963- doc = controller.add_document(filename)
964+ doc = controller.add_document(file_path)
965 if doc.get_thumbnail_path(1, True) is None:
966 doc.load_presentation()
967 i = 1
968- img = doc.get_thumbnail_path(i, True)
969- if img:
970+ thumbnail_path = doc.get_thumbnail_path(i, True)
971+ if thumbnail_path:
972 # Get titles and notes
973 titles, notes = doc.get_titles_and_notes()
974 service_item.add_capability(ItemCapabilities.HasDisplayTitle)
975 if notes.count('') != len(notes):
976 service_item.add_capability(ItemCapabilities.HasNotes)
977 service_item.add_capability(ItemCapabilities.HasThumbnails)
978- while img:
979+ while thumbnail_path:
980 # Use title and note if available
981 title = ''
982 if titles and len(titles) >= i:
983@@ -359,9 +357,9 @@
984 note = ''
985 if notes and len(notes) >= i:
986 note = notes[i - 1]
987- service_item.add_from_command(path, name, img, title, note)
988+ service_item.add_from_command(str(path), file_name, str(thumbnail_path), title, note)
989 i += 1
990- img = doc.get_thumbnail_path(i, True)
991+ thumbnail_path = doc.get_thumbnail_path(i, True)
992 doc.close_presentation()
993 return True
994 else:
995@@ -371,7 +369,7 @@
996 'Missing Presentation'),
997 translate('PresentationPlugin.MediaItem',
998 'The presentation {name} is incomplete, '
999- 'please reload.').format(name=filename))
1000+ 'please reload.').format(name=file_path))
1001 return False
1002 else:
1003 # File is no longer present
1004@@ -379,18 +377,20 @@
1005 critical_error_message_box(translate('PresentationPlugin.MediaItem', 'Missing Presentation'),
1006 translate('PresentationPlugin.MediaItem',
1007 'The presentation {name} no longer exists.'
1008- ).format(name=filename))
1009+ ).format(name=file_path))
1010 return False
1011
1012- def find_controller_by_type(self, filename):
1013+ def find_controller_by_type(self, file_path):
1014 """
1015 Determine the default application controller to use for the selected file type. This is used if "Automatic" is
1016 set as the preferred controller. Find the first (alphabetic) enabled controller which "supports" the extension.
1017 If none found, then look for a controller which "also supports" it instead.
1018
1019- :param filename: The file name
1020+ :param openlp.core.common.path.Path file_path: The file path
1021+ :return: The default application controller for this file type, or None if not supported
1022+ :rtype: PresentationController
1023 """
1024- file_type = os.path.splitext(filename)[1][1:]
1025+ file_type = file_path.suffix[1:]
1026 if not file_type:
1027 return None
1028 for controller in self.controllers:
1029
1030=== modified file 'openlp/plugins/presentations/lib/messagelistener.py'
1031--- openlp/plugins/presentations/lib/messagelistener.py 2016-12-31 11:01:36 +0000
1032+++ openlp/plugins/presentations/lib/messagelistener.py 2017-09-18 20:10:29 +0000
1033@@ -19,16 +19,15 @@
1034 # with this program; if not, write to the Free Software Foundation, Inc., 59 #
1035 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
1036 ###############################################################################
1037-
1038+import copy
1039 import logging
1040-import copy
1041-import os
1042
1043 from PyQt5 import QtCore
1044
1045 from openlp.core.common import Registry, Settings
1046+from openlp.core.common.path import Path
1047+from openlp.core.lib import ServiceItemContext
1048 from openlp.core.ui import HideMode
1049-from openlp.core.lib import ServiceItemContext
1050 from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
1051
1052 log = logging.getLogger(__name__)
1053@@ -325,21 +324,25 @@
1054 is_live = message[1]
1055 item = message[0]
1056 hide_mode = message[2]
1057- file = item.get_frame_path()
1058+ file_path = Path(item.get_frame_path())
1059 self.handler = item.processor
1060 # When starting presentation from the servicemanager we convert
1061 # PDF/XPS/OXPS-serviceitems into image-serviceitems. When started from the mediamanager
1062 # the conversion has already been done at this point.
1063- file_type = os.path.splitext(file.lower())[1][1:]
1064+ file_type = file_path.suffix.lower()[1:]
1065 if file_type in PDF_CONTROLLER_FILETYPES:
1066- log.debug('Converting from pdf/xps/oxps to images for serviceitem with file {name}'.format(name=file))
1067+ log.debug('Converting from pdf/xps/oxps to images for serviceitem with file {name}'.format(name=file_path))
1068 # Create a copy of the original item, and then clear the original item so it can be filled with images
1069 item_cpy = copy.copy(item)
1070 item.__init__(None)
1071 if is_live:
1072- self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Live, file)
1073+ # TODO: To Path object
1074+ self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Live,
1075+ str(file_path))
1076 else:
1077- self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Preview, file)
1078+ # TODO: To Path object
1079+ self.media_item.generate_slide_data(item, item_cpy, False, False, ServiceItemContext.Preview,
1080+ str(file_path))
1081 # Some of the original serviceitem attributes is needed in the new serviceitem
1082 item.footer = item_cpy.footer
1083 item.from_service = item_cpy.from_service
1084@@ -352,13 +355,13 @@
1085 self.handler = None
1086 else:
1087 if self.handler == self.media_item.automatic:
1088- self.handler = self.media_item.find_controller_by_type(file)
1089+ self.handler = self.media_item.find_controller_by_type(file_path)
1090 if not self.handler:
1091 return
1092 else:
1093- # the saved handler is not present so need to use one based on file suffix.
1094+ # the saved handler is not present so need to use one based on file_path suffix.
1095 if not self.controllers[self.handler].available:
1096- self.handler = self.media_item.find_controller_by_type(file)
1097+ self.handler = self.media_item.find_controller_by_type(file_path)
1098 if not self.handler:
1099 return
1100 if is_live:
1101@@ -370,7 +373,7 @@
1102 if self.handler is None:
1103 self.controller = controller
1104 else:
1105- controller.add_handler(self.controllers[self.handler], file, hide_mode, message[3])
1106+ controller.add_handler(self.controllers[self.handler], file_path, hide_mode, message[3])
1107 self.timer.start()
1108
1109 def slide(self, message):
1110
1111=== modified file 'openlp/plugins/presentations/lib/pdfcontroller.py'
1112--- openlp/plugins/presentations/lib/pdfcontroller.py 2017-08-26 15:06:11 +0000
1113+++ openlp/plugins/presentations/lib/pdfcontroller.py 2017-09-18 20:10:29 +0000
1114@@ -23,13 +23,13 @@
1115 import os
1116 import logging
1117 import re
1118-from shutil import which
1119 from subprocess import check_output, CalledProcessError
1120
1121 from openlp.core.common import AppLocation, check_binary_exists
1122 from openlp.core.common import Settings, is_win
1123 from openlp.core.common.path import Path, path_to_str
1124 from openlp.core.lib import ScreenList
1125+from openlp.core.lib.shutil import which
1126 from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
1127
1128 if is_win():
1129@@ -66,11 +66,12 @@
1130 Function that checks whether a binary is either ghostscript or mudraw or neither.
1131 Is also used from presentationtab.py
1132
1133- :param program_path:The full path to the binary to check.
1134+ :param openlp.core.common.path.Path program_path: The full path to the binary to check.
1135 :return: Type of the binary, 'gs' if ghostscript, 'mudraw' if mudraw, None if invalid.
1136+ :rtype: str | None
1137 """
1138 program_type = None
1139- runlog = check_binary_exists(Path(program_path))
1140+ runlog = check_binary_exists(program_path)
1141 # Analyse the output to see it the program is mudraw, ghostscript or neither
1142 for line in runlog.splitlines():
1143 decoded_line = line.decode()
1144@@ -107,30 +108,29 @@
1145 :return: True if program to open PDF-files was found, otherwise False.
1146 """
1147 log.debug('check_installed Pdf')
1148- self.mudrawbin = ''
1149- self.mutoolbin = ''
1150- self.gsbin = ''
1151+ self.mudrawbin = None
1152+ self.mutoolbin = None
1153+ self.gsbin = None
1154 self.also_supports = []
1155 # Use the user defined program if given
1156 if Settings().value('presentations/enable_pdf_program'):
1157- pdf_program = path_to_str(Settings().value('presentations/pdf_program'))
1158- program_type = self.process_check_binary(pdf_program)
1159+ program_path = Settings().value('presentations/pdf_program')
1160+ program_type = self.process_check_binary(program_path)
1161 if program_type == 'gs':
1162- self.gsbin = pdf_program
1163+ self.gsbin = program_path
1164 elif program_type == 'mudraw':
1165- self.mudrawbin = pdf_program
1166+ self.mudrawbin = program_path
1167 elif program_type == 'mutool':
1168- self.mutoolbin = pdf_program
1169+ self.mutoolbin = program_path
1170 else:
1171 # Fallback to autodetection
1172- application_path = str(AppLocation.get_directory(AppLocation.AppDir))
1173+ application_path = AppLocation.get_directory(AppLocation.AppDir)
1174 if is_win():
1175 # for windows we only accept mudraw.exe or mutool.exe in the base folder
1176- application_path = str(AppLocation.get_directory(AppLocation.AppDir))
1177- if os.path.isfile(os.path.join(application_path, 'mudraw.exe')):
1178- self.mudrawbin = os.path.join(application_path, 'mudraw.exe')
1179- elif os.path.isfile(os.path.join(application_path, 'mutool.exe')):
1180- self.mutoolbin = os.path.join(application_path, 'mutool.exe')
1181+ if (application_path / 'mudraw.exe').is_file():
1182+ self.mudrawbin = application_path / 'mudraw.exe'
1183+ elif (application_path / 'mutool.exe').is_file():
1184+ self.mutoolbin = application_path / 'mutool.exe'
1185 else:
1186 DEVNULL = open(os.devnull, 'wb')
1187 # First try to find mudraw
1188@@ -143,11 +143,11 @@
1189 self.gsbin = which('gs')
1190 # Last option: check if mudraw or mutool is placed in OpenLP base folder
1191 if not self.mudrawbin and not self.mutoolbin and not self.gsbin:
1192- application_path = str(AppLocation.get_directory(AppLocation.AppDir))
1193- if os.path.isfile(os.path.join(application_path, 'mudraw')):
1194- self.mudrawbin = os.path.join(application_path, 'mudraw')
1195- elif os.path.isfile(os.path.join(application_path, 'mutool')):
1196- self.mutoolbin = os.path.join(application_path, 'mutool')
1197+ application_path = AppLocation.get_directory(AppLocation.AppDir)
1198+ if (application_path / 'mudraw').is_file():
1199+ self.mudrawbin = application_path / 'mudraw'
1200+ elif (application_path / 'mutool').is_file():
1201+ self.mutoolbin = application_path / 'mutool'
1202 if self.mudrawbin or self.mutoolbin:
1203 self.also_supports = ['xps', 'oxps']
1204 return True
1205@@ -172,12 +172,15 @@
1206 image-serviceitem on the fly and present as such. Therefore some of the 'playback'
1207 functions is not implemented.
1208 """
1209- def __init__(self, controller, presentation):
1210+ def __init__(self, controller, document_path):
1211 """
1212 Constructor, store information about the file and initialise.
1213+
1214+ :param openlp.core.common.path.Path document_path: Path to the document to load
1215+ :rtype: None
1216 """
1217 log.debug('Init Presentation Pdf')
1218- PresentationDocument.__init__(self, controller, presentation)
1219+ super().__init__(controller, document_path)
1220 self.presentation = None
1221 self.blanked = False
1222 self.hidden = False
1223@@ -200,13 +203,13 @@
1224 :return: The resolution dpi to be used.
1225 """
1226 # Use a postscript script to get size of the pdf. It is assumed that all pages have same size
1227- gs_resolution_script = str(AppLocation.get_directory(
1228- AppLocation.PluginsDir)) + '/presentations/lib/ghostscript_get_resolution.ps'
1229+ gs_resolution_script = AppLocation.get_directory(
1230+ AppLocation.PluginsDir) / 'presentations' / 'lib' / 'ghostscript_get_resolution.ps'
1231 # Run the script on the pdf to get the size
1232 runlog = []
1233 try:
1234- runlog = check_output([self.controller.gsbin, '-dNOPAUSE', '-dNODISPLAY', '-dBATCH',
1235- '-sFile=' + self.file_path, gs_resolution_script],
1236+ runlog = check_output([str(self.controller.gsbin), '-dNOPAUSE', '-dNODISPLAY', '-dBATCH',
1237+ '-sFile={file_path}'.format(file_path=self.file_path), str(gs_resolution_script)],
1238 startupinfo=self.startupinfo)
1239 except CalledProcessError as e:
1240 log.debug(' '.join(e.cmd))
1241@@ -240,46 +243,47 @@
1242 :return: True is loading succeeded, otherwise False.
1243 """
1244 log.debug('load_presentation pdf')
1245+ temp_dir_path = self.get_temp_folder()
1246 # Check if the images has already been created, and if yes load them
1247- if os.path.isfile(os.path.join(self.get_temp_folder(), 'mainslide001.png')):
1248- created_files = sorted(os.listdir(self.get_temp_folder()))
1249- for fn in created_files:
1250- if os.path.isfile(os.path.join(self.get_temp_folder(), fn)):
1251- self.image_files.append(os.path.join(self.get_temp_folder(), fn))
1252+ if (temp_dir_path / 'mainslide001.png').is_file():
1253+ created_files = sorted(temp_dir_path.glob('*'))
1254+ for image_path in created_files:
1255+ if image_path.is_file():
1256+ self.image_files.append(image_path)
1257 self.num_pages = len(self.image_files)
1258 return True
1259 size = ScreenList().current['size']
1260 # Generate images from PDF that will fit the frame.
1261 runlog = ''
1262 try:
1263- if not os.path.isdir(self.get_temp_folder()):
1264- os.makedirs(self.get_temp_folder())
1265+ if not temp_dir_path.is_dir():
1266+ temp_dir_path.mkdir(parents=True)
1267 # The %03d in the file name is handled by each binary
1268 if self.controller.mudrawbin:
1269 log.debug('loading presentation using mudraw')
1270- runlog = check_output([self.controller.mudrawbin, '-w', str(size.width()), '-h', str(size.height()),
1271- '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path],
1272+ runlog = check_output([str(self.controller.mudrawbin), '-w', str(size.width()),
1273+ '-h', str(size.height()),
1274+ '-o', str(temp_dir_path / 'mainslide%03d.png'), str(self.file_path)],
1275 startupinfo=self.startupinfo)
1276 elif self.controller.mutoolbin:
1277 log.debug('loading presentation using mutool')
1278- runlog = check_output([self.controller.mutoolbin, 'draw', '-w', str(size.width()), '-h',
1279- str(size.height()),
1280- '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path],
1281+ runlog = check_output([str(self.controller.mutoolbin), 'draw', '-w', str(size.width()),
1282+ '-h', str(size.height()), '-o', str(temp_dir_path / 'mainslide%03d.png'),
1283+ str(self.file_path)],
1284 startupinfo=self.startupinfo)
1285 elif self.controller.gsbin:
1286 log.debug('loading presentation using gs')
1287 resolution = self.gs_get_resolution(size)
1288- runlog = check_output([self.controller.gsbin, '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m',
1289- '-r' + str(resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4',
1290- '-sOutputFile=' + os.path.join(self.get_temp_folder(), 'mainslide%03d.png'),
1291- self.file_path], startupinfo=self.startupinfo)
1292- created_files = sorted(os.listdir(self.get_temp_folder()))
1293- for fn in created_files:
1294- if os.path.isfile(os.path.join(self.get_temp_folder(), fn)):
1295- self.image_files.append(os.path.join(self.get_temp_folder(), fn))
1296+ runlog = check_output([str(self.controller.gsbin), '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m',
1297+ '-r{res}'.format(res=resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4',
1298+ '-sOutputFile={output}'.format(output=temp_dir_path / 'mainslide%03d.png'),
1299+ str(self.file_path)], startupinfo=self.startupinfo)
1300+ created_files = sorted(temp_dir_path.glob('*'))
1301+ for image_path in created_files:
1302+ if image_path.is_file():
1303+ self.image_files.append(image_path)
1304 except Exception as e:
1305- log.debug(e)
1306- log.debug(runlog)
1307+ log.exception(runlog)
1308 return False
1309 self.num_pages = len(self.image_files)
1310 # Create thumbnails
1311
1312=== modified file 'openlp/plugins/presentations/lib/powerpointcontroller.py'
1313--- openlp/plugins/presentations/lib/powerpointcontroller.py 2017-07-04 23:13:51 +0000
1314+++ openlp/plugins/presentations/lib/powerpointcontroller.py 2017-09-18 20:10:29 +0000
1315@@ -120,15 +120,16 @@
1316 Class which holds information and controls a single presentation.
1317 """
1318
1319- def __init__(self, controller, presentation):
1320+ def __init__(self, controller, document_path):
1321 """
1322 Constructor, store information about the file and initialise.
1323
1324 :param controller:
1325- :param presentation:
1326+ :param openlp.core.common.path.Path document_path: Path to the document to load
1327+ :rtype: None
1328 """
1329 log.debug('Init Presentation Powerpoint')
1330- super(PowerpointDocument, self).__init__(controller, presentation)
1331+ super().__init__(controller, document_path)
1332 self.presentation = None
1333 self.index_map = {}
1334 self.slide_count = 0
1335@@ -145,7 +146,7 @@
1336 try:
1337 if not self.controller.process:
1338 self.controller.start_process()
1339- self.controller.process.Presentations.Open(os.path.normpath(self.file_path), False, False, False)
1340+ self.controller.process.Presentations.Open(str(self.file_path), False, False, False)
1341 self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
1342 self.create_thumbnails()
1343 self.create_titles_and_notes()
1344@@ -177,7 +178,7 @@
1345 if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden:
1346 self.index_map[key] = num + 1
1347 self.presentation.Slides(num + 1).Export(
1348- os.path.join(self.get_thumbnail_folder(), 'slide{key:d}.png'.format(key=key)), 'png', 320, 240)
1349+ str(self.get_thumbnail_folder() / 'slide{key:d}.png'.format(key=key)), 'png', 320, 240)
1350 key += 1
1351 self.slide_count = key - 1
1352
1353@@ -363,9 +364,8 @@
1354 width=size.width(),
1355 horizontal=(right - left)))
1356 log.debug('window title: {title}'.format(title=window_title))
1357- filename_root, filename_ext = os.path.splitext(os.path.basename(self.file_path))
1358 if size.y() == top and size.height() == (bottom - top) and size.x() == left and \
1359- size.width() == (right - left) and filename_root in window_title:
1360+ size.width() == (right - left) and self.file_path.stem in window_title:
1361 log.debug('Found a match and will save the handle')
1362 self.presentation_hwnd = hwnd
1363 # Stop powerpoint from flashing in the taskbar
1364
1365=== modified file 'openlp/plugins/presentations/lib/pptviewcontroller.py'
1366--- openlp/plugins/presentations/lib/pptviewcontroller.py 2017-08-01 20:59:41 +0000
1367+++ openlp/plugins/presentations/lib/pptviewcontroller.py 2017-09-18 20:10:29 +0000
1368@@ -85,9 +85,9 @@
1369 if self.process:
1370 return
1371 log.debug('start PPTView')
1372- dll_path = os.path.join(str(AppLocation.get_directory(AppLocation.AppDir)),
1373- 'plugins', 'presentations', 'lib', 'pptviewlib', 'pptviewlib.dll')
1374- self.process = cdll.LoadLibrary(dll_path)
1375+ dll_path = AppLocation.get_directory(AppLocation.AppDir) \
1376+ / 'plugins' / 'presentations' / 'lib' / 'pptviewlib' / 'pptviewlib.dll'
1377+ self.process = cdll.LoadLibrary(str(dll_path))
1378 if log.isEnabledFor(logging.DEBUG):
1379 self.process.SetDebug(1)
1380
1381@@ -104,12 +104,15 @@
1382 """
1383 Class which holds information and controls a single presentation.
1384 """
1385- def __init__(self, controller, presentation):
1386+ def __init__(self, controller, document_path):
1387 """
1388 Constructor, store information about the file and initialise.
1389+
1390+ :param openlp.core.common.path.Path document_path: File path to the document to load
1391+ :rtype: None
1392 """
1393 log.debug('Init Presentation PowerPoint')
1394- super(PptviewDocument, self).__init__(controller, presentation)
1395+ super().__init__(controller, document_path)
1396 self.presentation = None
1397 self.ppt_id = None
1398 self.blanked = False
1399@@ -121,17 +124,16 @@
1400 the background PptView task started earlier.
1401 """
1402 log.debug('LoadPresentation')
1403- temp_folder = self.get_temp_folder()
1404+ temp_path = self.get_temp_folder()
1405 size = ScreenList().current['size']
1406 rect = RECT(size.x(), size.y(), size.right(), size.bottom())
1407- self.file_path = os.path.normpath(self.file_path)
1408- preview_path = os.path.join(temp_folder, 'slide')
1409+ preview_path = temp_path / 'slide'
1410 # Ensure that the paths are null terminated
1411- byte_file_path = self.file_path.encode('utf-16-le') + b'\0'
1412- preview_path = preview_path.encode('utf-16-le') + b'\0'
1413- if not os.path.isdir(temp_folder):
1414- os.makedirs(temp_folder)
1415- self.ppt_id = self.controller.process.OpenPPT(byte_file_path, None, rect, preview_path)
1416+ file_path_utf16 = str(self.file_path).encode('utf-16-le') + b'\0'
1417+ preview_path_utf16 = str(preview_path).encode('utf-16-le') + b'\0'
1418+ if not temp_path.is_dir():
1419+ temp_path.mkdir(parents=True)
1420+ self.ppt_id = self.controller.process.OpenPPT(file_path_utf16, None, rect, preview_path_utf16)
1421 if self.ppt_id >= 0:
1422 self.create_thumbnails()
1423 self.stop_presentation()
1424@@ -148,7 +150,7 @@
1425 return
1426 log.debug('create_thumbnails proceeding')
1427 for idx in range(self.get_slide_count()):
1428- path = '{folder}\\slide{index}.bmp'.format(folder=self.get_temp_folder(), index=str(idx + 1))
1429+ path = self.get_temp_folder() / 'slide{index:d}.bmp'.format(index=idx + 1)
1430 self.convert_thumbnail(path, idx + 1)
1431
1432 def create_titles_and_notes(self):
1433@@ -161,13 +163,12 @@
1434 """
1435 titles = None
1436 notes = None
1437- filename = os.path.normpath(self.file_path)
1438 # let's make sure we have a valid zipped presentation
1439- if os.path.exists(filename) and zipfile.is_zipfile(filename):
1440+ if self.file_path.exists() and zipfile.is_zipfile(str(self.file_path)):
1441 namespaces = {"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
1442 "a": "http://schemas.openxmlformats.org/drawingml/2006/main"}
1443 # open the file
1444- with zipfile.ZipFile(filename) as zip_file:
1445+ with zipfile.ZipFile(str(self.file_path)) as zip_file:
1446 # find the presentation.xml to get the slide count
1447 with zip_file.open('ppt/presentation.xml') as pres:
1448 tree = ElementTree.parse(pres)
1449
1450=== modified file 'openlp/plugins/presentations/lib/presentationcontroller.py'
1451--- openlp/plugins/presentations/lib/presentationcontroller.py 2017-08-25 20:03:25 +0000
1452+++ openlp/plugins/presentations/lib/presentationcontroller.py 2017-09-18 20:10:29 +0000
1453@@ -19,16 +19,14 @@
1454 # with this program; if not, write to the Free Software Foundation, Inc., 59 #
1455 # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
1456 ###############################################################################
1457-
1458 import logging
1459-import os
1460-import shutil
1461
1462 from PyQt5 import QtCore
1463
1464 from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, md5_hash
1465 from openlp.core.common.path import Path
1466 from openlp.core.lib import create_thumb, validate_thumb
1467+from openlp.core.lib.shutil import rmtree
1468
1469 log = logging.getLogger(__name__)
1470
1471@@ -86,20 +84,27 @@
1472 Returns a path to an image containing a preview for the requested slide
1473
1474 """
1475- def __init__(self, controller, name):
1476+ def __init__(self, controller, document_path):
1477 """
1478 Constructor for the PresentationController class
1479+
1480+ :param controller:
1481+ :param openlp.core.common.path.Path document_path: Path to the document to load.
1482+ :rtype: None
1483 """
1484 self.controller = controller
1485- self._setup(name)
1486+ self._setup(document_path)
1487
1488- def _setup(self, name):
1489+ def _setup(self, document_path):
1490 """
1491 Run some initial setup. This method is separate from __init__ in order to mock it out in tests.
1492+
1493+ :param openlp.core.common.path.Path document_path: Path to the document to load.
1494+ :rtype: None
1495 """
1496 self.slide_number = 0
1497- self.file_path = name
1498- check_directory_exists(Path(self.get_thumbnail_folder()))
1499+ self.file_path = document_path
1500+ check_directory_exists(self.get_thumbnail_folder())
1501
1502 def load_presentation(self):
1503 """
1504@@ -116,49 +121,54 @@
1505 a file, e.g. thumbnails
1506 """
1507 try:
1508- if os.path.exists(self.get_thumbnail_folder()):
1509- shutil.rmtree(self.get_thumbnail_folder())
1510- if os.path.exists(self.get_temp_folder()):
1511- shutil.rmtree(self.get_temp_folder())
1512+ thumbnail_folder_path = self.get_thumbnail_folder()
1513+ temp_folder_path = self.get_temp_folder()
1514+ if thumbnail_folder_path.exists():
1515+ rmtree(thumbnail_folder_path)
1516+ if temp_folder_path.exists():
1517+ rmtree(temp_folder_path)
1518 except OSError:
1519 log.exception('Failed to delete presentation controller files')
1520
1521- def get_file_name(self):
1522- """
1523- Return just the filename of the presentation, without the directory
1524- """
1525- return os.path.split(self.file_path)[1]
1526-
1527 def get_thumbnail_folder(self):
1528 """
1529 The location where thumbnail images will be stored
1530+
1531+ :return: The path to the thumbnail
1532+ :rtype: openlp.core.common.path.Path
1533 """
1534 # TODO: If statement can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
1535 if Settings().value('presentations/thumbnail_scheme') == 'md5':
1536- folder = md5_hash(self.file_path.encode('utf-8'))
1537+ folder = md5_hash(bytes(self.file_path))
1538 else:
1539- folder = self.get_file_name()
1540- return os.path.join(self.controller.thumbnail_folder, folder)
1541+ folder = self.file_path.name
1542+ return Path(self.controller.thumbnail_folder, folder)
1543
1544 def get_temp_folder(self):
1545 """
1546 The location where thumbnail images will be stored
1547+
1548+ :return: The path to the temporary file folder
1549+ :rtype: openlp.core.common.path.Path
1550 """
1551 # TODO: If statement can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
1552 if Settings().value('presentations/thumbnail_scheme') == 'md5':
1553- folder = md5_hash(self.file_path.encode('utf-8'))
1554+ folder = md5_hash(bytes(self.file_path))
1555 else:
1556- folder = folder = self.get_file_name()
1557- return os.path.join(self.controller.temp_folder, folder)
1558+ folder = self.file_path.name
1559+ return Path(self.controller.temp_folder, folder)
1560
1561 def check_thumbnails(self):
1562 """
1563- Returns ``True`` if the thumbnail images exist and are more recent than the powerpoint file.
1564+ Check that the last thumbnail image exists and is valid and are more recent than the powerpoint file.
1565+
1566+ :return: If the thumbnail is valid
1567+ :rtype: bool
1568 """
1569- last_image = self.get_thumbnail_path(self.get_slide_count(), True)
1570- if not (last_image and os.path.isfile(last_image)):
1571+ last_image_path = self.get_thumbnail_path(self.get_slide_count(), True)
1572+ if not (last_image_path and last_image_path.is_file()):
1573 return False
1574- return validate_thumb(self.file_path, last_image)
1575+ return validate_thumb(Path(self.file_path), Path(last_image_path))
1576
1577 def close_presentation(self):
1578 """
1579@@ -241,25 +251,31 @@
1580 """
1581 pass
1582
1583- def convert_thumbnail(self, file, idx):
1584+ def convert_thumbnail(self, image_path, index):
1585 """
1586 Convert the slide image the application made to a scaled 360px height .png image.
1587+
1588+ :param openlp.core.common.path.Path image_path: Path to the image to create a thumb nail of
1589+ :param int index: The index of the slide to create the thumbnail for.
1590+ :rtype: None
1591 """
1592 if self.check_thumbnails():
1593 return
1594- if os.path.isfile(file):
1595- thumb_path = self.get_thumbnail_path(idx, False)
1596- create_thumb(file, thumb_path, False, QtCore.QSize(-1, 360))
1597+ if image_path.is_file():
1598+ thumb_path = self.get_thumbnail_path(index, False)
1599+ create_thumb(str(image_path), str(thumb_path), False, QtCore.QSize(-1, 360))
1600
1601- def get_thumbnail_path(self, slide_no, check_exists):
1602+ def get_thumbnail_path(self, slide_no, check_exists=False):
1603 """
1604 Returns an image path containing a preview for the requested slide
1605
1606- :param slide_no: The slide an image is required for, starting at 1
1607- :param check_exists:
1608+ :param int slide_no: The slide an image is required for, starting at 1
1609+ :param bool check_exists: Check if the generated path exists
1610+ :return: The path, or None if the :param:`check_exists` is True and the file does not exist
1611+ :rtype: openlp.core.common.path.Path | None
1612 """
1613- path = os.path.join(self.get_thumbnail_folder(), self.controller.thumbnail_prefix + str(slide_no) + '.png')
1614- if os.path.isfile(path) or not check_exists:
1615+ path = self.get_thumbnail_folder() / (self.controller.thumbnail_prefix + str(slide_no) + '.png')
1616+ if path.is_file() or not check_exists:
1617 return path
1618 else:
1619 return None
1620@@ -302,44 +318,38 @@
1621 Reads the titles from the titles file and
1622 the notes files and returns the content in two lists
1623 """
1624- titles = []
1625 notes = []
1626- titles_file = os.path.join(self.get_thumbnail_folder(), 'titles.txt')
1627- if os.path.exists(titles_file):
1628+ titles_path = self.get_thumbnail_folder() / 'titles.txt'
1629+ try:
1630+ titles = titles_path.read_text().splitlines()
1631+ except:
1632+ log.exception('Failed to open/read existing titles file')
1633+ titles = []
1634+ for slide_no, title in enumerate(titles, 1):
1635+ notes_path = self.get_thumbnail_folder() / 'slideNotes{number:d}.txt'.format(number=slide_no)
1636 try:
1637- with open(titles_file, encoding='utf-8') as fi:
1638- titles = fi.read().splitlines()
1639+ note = notes_path.read_text()
1640 except:
1641- log.exception('Failed to open/read existing titles file')
1642- titles = []
1643- for slide_no, title in enumerate(titles, 1):
1644- notes_file = os.path.join(self.get_thumbnail_folder(), 'slideNotes{number:d}.txt'.format(number=slide_no))
1645- note = ''
1646- if os.path.exists(notes_file):
1647- try:
1648- with open(notes_file, encoding='utf-8') as fn:
1649- note = fn.read()
1650- except:
1651- log.exception('Failed to open/read notes file')
1652- note = ''
1653+ log.exception('Failed to open/read notes file')
1654+ note = ''
1655 notes.append(note)
1656 return titles, notes
1657
1658 def save_titles_and_notes(self, titles, notes):
1659 """
1660- Performs the actual persisting of titles to the titles.txt
1661- and notes to the slideNote%.txt
1662+ Performs the actual persisting of titles to the titles.txt and notes to the slideNote%.txt
1663+
1664+ :param list[str] titles: The titles to save
1665+ :param list[str] notes: The notes to save
1666+ :rtype: None
1667 """
1668 if titles:
1669- titles_file = os.path.join(self.get_thumbnail_folder(), 'titles.txt')
1670- with open(titles_file, mode='wt', encoding='utf-8') as fo:
1671- fo.writelines(titles)
1672+ titles_path = self.get_thumbnail_folder() / 'titles.txt'
1673+ titles_path.write_text('\n'.join(titles))
1674 if notes:
1675 for slide_no, note in enumerate(notes, 1):
1676- notes_file = os.path.join(self.get_thumbnail_folder(),
1677- 'slideNotes{number:d}.txt'.format(number=slide_no))
1678- with open(notes_file, mode='wt', encoding='utf-8') as fn:
1679- fn.write(note)
1680+ notes_path = self.get_thumbnail_folder() / 'slideNotes{number:d}.txt'.format(number=slide_no)
1681+ notes_path.write_text(note)
1682
1683
1684 class PresentationController(object):
1685@@ -416,12 +426,11 @@
1686 self.document_class = document_class
1687 self.settings_section = self.plugin.settings_section
1688 self.available = None
1689- self.temp_folder = os.path.join(str(AppLocation.get_section_data_path(self.settings_section)), name)
1690- self.thumbnail_folder = os.path.join(
1691- str(AppLocation.get_section_data_path(self.settings_section)), 'thumbnails')
1692+ self.temp_folder = AppLocation.get_section_data_path(self.settings_section) / name
1693+ self.thumbnail_folder = AppLocation.get_section_data_path(self.settings_section) / 'thumbnails'
1694 self.thumbnail_prefix = 'slide'
1695- check_directory_exists(Path(self.thumbnail_folder))
1696- check_directory_exists(Path(self.temp_folder))
1697+ check_directory_exists(self.thumbnail_folder)
1698+ check_directory_exists(self.temp_folder)
1699
1700 def enabled(self):
1701 """
1702@@ -456,11 +465,15 @@
1703 log.debug('Kill')
1704 self.close_presentation()
1705
1706- def add_document(self, name):
1707+ def add_document(self, document_path):
1708 """
1709 Called when a new presentation document is opened.
1710+
1711+ :param openlp.core.common.path.Path document_path: Path to the document to load
1712+ :return: The document
1713+ :rtype: PresentationDocument
1714 """
1715- document = self.document_class(self, name)
1716+ document = self.document_class(self, document_path)
1717 self.docs.append(document)
1718 return document
1719
1720
1721=== modified file 'openlp/plugins/presentations/lib/presentationtab.py'
1722--- openlp/plugins/presentations/lib/presentationtab.py 2017-08-26 15:06:11 +0000
1723+++ openlp/plugins/presentations/lib/presentationtab.py 2017-09-18 20:10:29 +0000
1724@@ -38,7 +38,6 @@
1725 """
1726 Constructor
1727 """
1728- self.parent = parent
1729 self.controllers = controllers
1730 super(PresentationTab, self).__init__(parent, title, visible_title, icon_path)
1731 self.activated = False
1732@@ -194,7 +193,7 @@
1733 pdf_program_path = self.program_path_edit.path
1734 enable_pdf_program = self.pdf_program_check_box.checkState()
1735 # If the given program is blank disable using the program
1736- if not pdf_program_path:
1737+ if pdf_program_path is None:
1738 enable_pdf_program = 0
1739 if pdf_program_path != Settings().value(self.settings_section + '/pdf_program'):
1740 Settings().setValue(self.settings_section + '/pdf_program', pdf_program_path)
1741@@ -220,9 +219,11 @@
1742
1743 def on_program_path_edit_path_changed(self, new_path):
1744 """
1745- Select the mudraw or ghostscript binary that should be used.
1746+ Handle the `pathEditChanged` signal from program_path_edit
1747+
1748+ :param openlp.core.common.path.Path new_path: File path to the new program
1749+ :rtype: None
1750 """
1751- new_path = path_to_str(new_path)
1752 if new_path:
1753 if not PdfController.process_check_binary(new_path):
1754 critical_error_message_box(UiStrings().Error,
1755
1756=== modified file 'openlp/plugins/remotes/deploy.py'
1757--- openlp/plugins/remotes/deploy.py 2017-08-12 20:58:16 +0000
1758+++ openlp/plugins/remotes/deploy.py 2017-09-18 20:10:29 +0000
1759@@ -64,6 +64,6 @@
1760 file_size = get_url_file_size('https://get.openlp.org/webclient/site.zip')
1761 callback.setRange(0, file_size)
1762 if url_get_file(callback, '{host}{name}'.format(host='https://get.openlp.org/webclient/', name='site.zip'),
1763- os.path.join(str(AppLocation.get_section_data_path('remotes')), 'site.zip'),
1764+ AppLocation.get_section_data_path('remotes') / 'site.zip',
1765 sha256=sha256):
1766 deploy_zipfile(str(AppLocation.get_section_data_path('remotes')), 'site.zip')
1767
1768=== modified file 'openlp/plugins/songs/reporting.py'
1769--- openlp/plugins/songs/reporting.py 2016-12-31 11:01:36 +0000
1770+++ openlp/plugins/songs/reporting.py 2017-09-18 20:10:29 +0000
1771@@ -25,10 +25,10 @@
1772 import csv
1773 import logging
1774
1775-from PyQt5 import QtWidgets
1776-
1777 from openlp.core.common import Registry, translate
1778+from openlp.core.common.path import Path
1779 from openlp.core.lib.ui import critical_error_message_box
1780+from openlp.core.ui.lib.filedialog import FileDialog
1781 from openlp.plugins.songs.lib.db import Song
1782
1783
1784@@ -42,58 +42,55 @@
1785 """
1786 main_window = Registry().get('main_window')
1787 plugin = Registry().get('songs').plugin
1788- report_file_name, filter_used = QtWidgets.QFileDialog.getSaveFileName(
1789+ report_file_path, filter_used = FileDialog.getSaveFileName(
1790 main_window,
1791 translate('SongPlugin.ReportSongList', 'Save File'),
1792- translate('SongPlugin.ReportSongList', 'song_extract.csv'),
1793+ Path(translate('SongPlugin.ReportSongList', 'song_extract.csv')),
1794 translate('SongPlugin.ReportSongList', 'CSV format (*.csv)'))
1795
1796- if not report_file_name:
1797+ if report_file_path is None:
1798 main_window.error_message(
1799 translate('SongPlugin.ReportSongList', 'Output Path Not Selected'),
1800- translate('SongPlugin.ReportSongList', 'You have not set a valid output location for your '
1801- 'report. \nPlease select an existing path '
1802- 'on your computer.')
1803+ translate('SongPlugin.ReportSongList', 'You have not set a valid output location for your report. \n'
1804+ 'Please select an existing path on your computer.')
1805 )
1806 return
1807- if not report_file_name.endswith('csv'):
1808- report_file_name += '.csv'
1809- file_handle = None
1810+ report_file_path.with_suffix('.csv')
1811 Registry().get('application').set_busy_cursor()
1812 try:
1813- file_handle = open(report_file_name, 'wt')
1814- fieldnames = ('Title', 'Alternative Title', 'Copyright', 'Author(s)', 'Song Book', 'Topic')
1815- writer = csv.DictWriter(file_handle, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
1816- headers = dict((n, n) for n in fieldnames)
1817- writer.writerow(headers)
1818- song_list = plugin.manager.get_all_objects(Song)
1819- for song in song_list:
1820- author_list = []
1821- for author_song in song.authors_songs:
1822- author_list.append(author_song.author.display_name)
1823- author_string = ' | '.join(author_list)
1824- book_list = []
1825- for book_song in song.songbook_entries:
1826- if hasattr(book_song, 'entry') and book_song.entry:
1827- book_list.append('{name} #{entry}'.format(name=book_song.songbook.name, entry=book_song.entry))
1828- book_string = ' | '.join(book_list)
1829- topic_list = []
1830- for topic_song in song.topics:
1831- if hasattr(topic_song, 'name'):
1832- topic_list.append(topic_song.name)
1833- topic_string = ' | '.join(topic_list)
1834- writer.writerow({'Title': song.title,
1835- 'Alternative Title': song.alternate_title,
1836- 'Copyright': song.copyright,
1837- 'Author(s)': author_string,
1838- 'Song Book': book_string,
1839- 'Topic': topic_string})
1840- Registry().get('application').set_normal_cursor()
1841- main_window.information_message(
1842- translate('SongPlugin.ReportSongList', 'Report Creation'),
1843- translate('SongPlugin.ReportSongList',
1844- 'Report \n{name} \nhas been successfully created. ').format(name=report_file_name)
1845- )
1846+ with report_file_path.open('wt') as file_handle:
1847+ fieldnames = ('Title', 'Alternative Title', 'Copyright', 'Author(s)', 'Song Book', 'Topic')
1848+ writer = csv.DictWriter(file_handle, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
1849+ headers = dict((n, n) for n in fieldnames)
1850+ writer.writerow(headers)
1851+ song_list = plugin.manager.get_all_objects(Song)
1852+ for song in song_list:
1853+ author_list = []
1854+ for author_song in song.authors_songs:
1855+ author_list.append(author_song.author.display_name)
1856+ author_string = ' | '.join(author_list)
1857+ book_list = []
1858+ for book_song in song.songbook_entries:
1859+ if hasattr(book_song, 'entry') and book_song.entry:
1860+ book_list.append('{name} #{entry}'.format(name=book_song.songbook.name, entry=book_song.entry))
1861+ book_string = ' | '.join(book_list)
1862+ topic_list = []
1863+ for topic_song in song.topics:
1864+ if hasattr(topic_song, 'name'):
1865+ topic_list.append(topic_song.name)
1866+ topic_string = ' | '.join(topic_list)
1867+ writer.writerow({'Title': song.title,
1868+ 'Alternative Title': song.alternate_title,
1869+ 'Copyright': song.copyright,
1870+ 'Author(s)': author_string,
1871+ 'Song Book': book_string,
1872+ 'Topic': topic_string})
1873+ Registry().get('application').set_normal_cursor()
1874+ main_window.information_message(
1875+ translate('SongPlugin.ReportSongList', 'Report Creation'),
1876+ translate('SongPlugin.ReportSongList',
1877+ 'Report \n{name} \nhas been successfully created. ').format(name=report_file_path)
1878+ )
1879 except OSError as ose:
1880 Registry().get('application').set_normal_cursor()
1881 log.exception('Failed to write out song usage records')
1882@@ -101,6 +98,3 @@
1883 translate('SongPlugin.ReportSongList',
1884 'An error occurred while extracting: {error}'
1885 ).format(error=ose.strerror))
1886- finally:
1887- if file_handle:
1888- file_handle.close()
1889
1890=== modified file 'tests/functional/openlp_core_common/test_httputils.py'
1891--- tests/functional/openlp_core_common/test_httputils.py 2017-06-09 20:53:13 +0000
1892+++ tests/functional/openlp_core_common/test_httputils.py 2017-09-18 20:10:29 +0000
1893@@ -29,6 +29,7 @@
1894 from unittest.mock import MagicMock, patch
1895
1896 from openlp.core.common.httputils import get_user_agent, get_web_page, get_url_file_size, url_get_file, ping
1897+from openlp.core.common.path import Path
1898
1899 from tests.helpers.testmixin import TestMixin
1900
1901@@ -267,7 +268,7 @@
1902 mocked_urlopen.side_effect = socket.timeout()
1903
1904 # WHEN: Attempt to retrieve a file
1905- url_get_file(MagicMock(), url='http://localhost/test', f_path=self.tempfile)
1906+ url_get_file(MagicMock(), url='http://localhost/test', f_path=Path(self.tempfile))
1907
1908 # THEN: socket.timeout should have been caught
1909 # NOTE: Test is if $tmpdir/tempfile is still there, then test fails since ftw deletes bad downloaded files
1910
1911=== modified file 'tests/functional/openlp_core_lib/test_lib.py'
1912--- tests/functional/openlp_core_lib/test_lib.py 2017-08-25 20:03:25 +0000
1913+++ tests/functional/openlp_core_lib/test_lib.py 2017-09-18 20:10:29 +0000
1914@@ -595,61 +595,46 @@
1915 Test the validate_thumb() function when the thumbnail does not exist
1916 """
1917 # GIVEN: A mocked out os module, with path.exists returning False, and fake paths to a file and a thumb
1918- with patch('openlp.core.lib.os') as mocked_os:
1919- file_path = 'path/to/file'
1920- thumb_path = 'path/to/thumb'
1921- mocked_os.path.exists.return_value = False
1922+ with patch.object(Path, 'exists', return_value=False) as mocked_path_exists:
1923+ file_path = Path('path', 'to', 'file')
1924+ thumb_path = Path('path', 'to', 'thumb')
1925
1926 # WHEN: we run the validate_thumb() function
1927 result = validate_thumb(file_path, thumb_path)
1928
1929 # THEN: we should have called a few functions, and the result should be False
1930- mocked_os.path.exists.assert_called_with(thumb_path)
1931- assert result is False, 'The result should be False'
1932+ thumb_path.exists.assert_called_once_with()
1933+ self.assertFalse(result, 'The result should be False')
1934
1935 def test_validate_thumb_file_exists_and_newer(self):
1936 """
1937 Test the validate_thumb() function when the thumbnail exists and has a newer timestamp than the file
1938 """
1939- # GIVEN: A mocked out os module, functions rigged to work for us, and fake paths to a file and a thumb
1940- with patch('openlp.core.lib.os') as mocked_os:
1941- file_path = 'path/to/file'
1942- thumb_path = 'path/to/thumb'
1943- file_mocked_stat = MagicMock()
1944- file_mocked_stat.st_mtime = datetime.now()
1945- thumb_mocked_stat = MagicMock()
1946- thumb_mocked_stat.st_mtime = datetime.now() + timedelta(seconds=10)
1947- mocked_os.path.exists.return_value = True
1948- mocked_os.stat.side_effect = [file_mocked_stat, thumb_mocked_stat]
1949+ with patch.object(Path, 'exists'), patch.object(Path, 'stat'):
1950+ # GIVEN: Mocked file_path and thumb_path which return different values fo the modified times
1951+ file_path = MagicMock(**{'stat.return_value': MagicMock(st_mtime=10)})
1952+ thumb_path = MagicMock(**{'exists.return_value': True, 'stat.return_value': MagicMock(st_mtime=11)})
1953
1954 # WHEN: we run the validate_thumb() function
1955+ result = validate_thumb(file_path, thumb_path)
1956
1957- # THEN: we should have called a few functions, and the result should be True
1958- # mocked_os.path.exists.assert_called_with(thumb_path)
1959+ # THEN: `validate_thumb` should return True
1960+ self.assertTrue(result)
1961
1962 def test_validate_thumb_file_exists_and_older(self):
1963 """
1964 Test the validate_thumb() function when the thumbnail exists but is older than the file
1965 """
1966- # GIVEN: A mocked out os module, functions rigged to work for us, and fake paths to a file and a thumb
1967- with patch('openlp.core.lib.os') as mocked_os:
1968- file_path = 'path/to/file'
1969- thumb_path = 'path/to/thumb'
1970- file_mocked_stat = MagicMock()
1971- file_mocked_stat.st_mtime = datetime.now()
1972- thumb_mocked_stat = MagicMock()
1973- thumb_mocked_stat.st_mtime = datetime.now() - timedelta(seconds=10)
1974- mocked_os.path.exists.return_value = True
1975- mocked_os.stat.side_effect = lambda fname: file_mocked_stat if fname == file_path else thumb_mocked_stat
1976-
1977- # WHEN: we run the validate_thumb() function
1978- result = validate_thumb(file_path, thumb_path)
1979-
1980- # THEN: we should have called a few functions, and the result should be False
1981- mocked_os.path.exists.assert_called_with(thumb_path)
1982- mocked_os.stat.assert_any_call(file_path)
1983- mocked_os.stat.assert_any_call(thumb_path)
1984- assert result is False, 'The result should be False'
1985+ # GIVEN: Mocked file_path and thumb_path which return different values fo the modified times
1986+ file_path = MagicMock(**{'stat.return_value': MagicMock(st_mtime=10)})
1987+ thumb_path = MagicMock(**{'exists.return_value': True, 'stat.return_value': MagicMock(st_mtime=9)})
1988+
1989+ # WHEN: we run the validate_thumb() function
1990+ result = validate_thumb(file_path, thumb_path)
1991+
1992+ # THEN: `validate_thumb` should return False
1993+ thumb_path.stat.assert_called_once_with()
1994+ self.assertFalse(result, 'The result should be False')
1995
1996 def test_replace_params_no_params(self):
1997 """
1998
1999=== added file 'tests/functional/openlp_core_lib/test_shutil.py'
2000--- tests/functional/openlp_core_lib/test_shutil.py 1970-01-01 00:00:00 +0000
2001+++ tests/functional/openlp_core_lib/test_shutil.py 2017-09-18 20:10:29 +0000
2002@@ -0,0 +1,170 @@
2003+import os
2004+from unittest import TestCase
2005+from unittest.mock import ANY, MagicMock, patch
2006+
2007+from openlp.core.common.path import Path
2008+from openlp.core.lib.shutil import copy, copyfile, copytree, rmtree, which
2009+
2010+
2011+class TestShutil(TestCase):
2012+ """
2013+ Tests for the :mod:`openlp.core.lib.shutil` module
2014+ """
2015+
2016+ def test_copy(self):
2017+ """
2018+ Test :func:`copy`
2019+ """
2020+ # GIVEN: A mocked `shutil.copy` which returns a test path as a string
2021+ with patch('openlp.core.lib.shutil.shutil.copy', return_value=os.path.join('destination', 'test', 'path')) \
2022+ as mocked_shutil_copy:
2023+
2024+ # WHEN: Calling :func:`copy` with the src and dst parameters as Path object types
2025+ result = copy(Path('source', 'test', 'path'), Path('destination', 'test', 'path'))
2026+
2027+ # THEN: :func:`shutil.copy` should have been called with the str equivalents of the Path objects.
2028+ # :func:`copy` should return the str type result of calling :func:`shutil.copy` as a Path object.
2029+ mocked_shutil_copy.assert_called_once_with(os.path.join('source', 'test', 'path'),
2030+ os.path.join('destination', 'test', 'path'))
2031+ self.assertEqual(result, Path('destination', 'test', 'path'))
2032+
2033+ def test_copy_follow_optional_params(self):
2034+ """
2035+ Test :func:`copy` when follow_symlinks is set to false
2036+ """
2037+ # GIVEN: A mocked `shutil.copy`
2038+ with patch('openlp.core.lib.shutil.shutil.copy', return_value='') as mocked_shutil_copy:
2039+
2040+ # WHEN: Calling :func:`copy` with :param:`follow_symlinks` set to False
2041+ copy(Path('source', 'test', 'path'), Path('destination', 'test', 'path'), follow_symlinks=False)
2042+
2043+ # THEN: :func:`shutil.copy` should have been called with :param:`follow_symlinks` set to false
2044+ mocked_shutil_copy.assert_called_once_with(ANY, ANY, follow_symlinks=False)
2045+
2046+ def test_copyfile(self):
2047+ """
2048+ Test :func:`copyfile`
2049+ """
2050+ # GIVEN: A mocked :func:`shutil.copyfile` which returns a test path as a string
2051+ with patch('openlp.core.lib.shutil.shutil.copyfile',
2052+ return_value=os.path.join('destination', 'test', 'path')) as mocked_shutil_copyfile:
2053+
2054+ # WHEN: Calling :func:`copyfile` with the src and dst parameters as Path object types
2055+ result = copyfile(Path('source', 'test', 'path'), Path('destination', 'test', 'path'))
2056+
2057+ # THEN: :func:`shutil.copyfile` should have been called with the str equivalents of the Path objects.
2058+ # :func:`copyfile` should return the str type result of calling :func:`shutil.copyfile` as a Path
2059+ # object.
2060+ mocked_shutil_copyfile.assert_called_once_with(os.path.join('source', 'test', 'path'),
2061+ os.path.join('destination', 'test', 'path'))
2062+ self.assertEqual(result, Path('destination', 'test', 'path'))
2063+
2064+ def test_copyfile_optional_params(self):
2065+ """
2066+ Test :func:`copyfile` when follow_symlinks is set to false
2067+ """
2068+ # GIVEN: A mocked :func:`shutil.copyfile`
2069+ with patch('openlp.core.lib.shutil.shutil.copyfile', return_value='') as mocked_shutil_copyfile:
2070+
2071+ # WHEN: Calling :func:`copyfile` with :param:`follow_symlinks` set to False
2072+ copyfile(Path('source', 'test', 'path'), Path('destination', 'test', 'path'), follow_symlinks=False)
2073+
2074+ # THEN: :func:`shutil.copyfile` should have been called with the optional parameters, with out any of the
2075+ # values being modified
2076+ mocked_shutil_copyfile.assert_called_once_with(ANY, ANY, follow_symlinks=False)
2077+
2078+ def test_copytree(self):
2079+ """
2080+ Test :func:`copytree`
2081+ """
2082+ # GIVEN: A mocked :func:`shutil.copytree` which returns a test path as a string
2083+ with patch('openlp.core.lib.shutil.shutil.copytree',
2084+ return_value=os.path.join('destination', 'test', 'path')) as mocked_shutil_copytree:
2085+
2086+ # WHEN: Calling :func:`copytree` with the src and dst parameters as Path object types
2087+ result = copytree(Path('source', 'test', 'path'), Path('destination', 'test', 'path'))
2088+
2089+ # THEN: :func:`shutil.copytree` should have been called with the str equivalents of the Path objects.
2090+ # :func:`patches.copytree` should return the str type result of calling :func:`shutil.copytree` as a
2091+ # Path object.
2092+ mocked_shutil_copytree.assert_called_once_with(os.path.join('source', 'test', 'path'),
2093+ os.path.join('destination', 'test', 'path'))
2094+ self.assertEqual(result, Path('destination', 'test', 'path'))
2095+
2096+ def test_copytree_optional_params(self):
2097+ """
2098+ Test :func:`copytree` when optional parameters are passed
2099+ """
2100+ # GIVEN: A mocked :func:`shutil.copytree`
2101+ with patch('openlp.core.lib.shutil.shutil.copytree', return_value='') as mocked_shutil_copytree:
2102+ mocked_ignore = MagicMock()
2103+ mocked_copy_function = MagicMock()
2104+
2105+ # WHEN: Calling :func:`copytree` with the optional parameters set
2106+ copytree(Path('source', 'test', 'path'), Path('destination', 'test', 'path'), symlinks=True,
2107+ ignore=mocked_ignore, copy_function=mocked_copy_function, ignore_dangling_symlinks=True)
2108+
2109+ # THEN: :func:`shutil.copytree` should have been called with the optional parameters, with out any of the
2110+ # values being modified
2111+ mocked_shutil_copytree.assert_called_once_with(ANY, ANY, symlinks=True, ignore=mocked_ignore,
2112+ copy_function=mocked_copy_function,
2113+ ignore_dangling_symlinks=True)
2114+
2115+ def test_rmtree(self):
2116+ """
2117+ Test :func:`rmtree`
2118+ """
2119+ # GIVEN: A mocked :func:`shutil.rmtree`
2120+ with patch('openlp.core.lib.shutil.shutil.rmtree', return_value=None) as mocked_shutil_rmtree:
2121+
2122+ # WHEN: Calling :func:`rmtree` with the path parameter as Path object type
2123+ result = rmtree(Path('test', 'path'))
2124+
2125+ # THEN: :func:`shutil.rmtree` should have been called with the str equivalents of the Path object.
2126+ mocked_shutil_rmtree.assert_called_once_with(os.path.join('test', 'path'))
2127+ self.assertIsNone(result)
2128+
2129+ def test_rmtree_optional_params(self):
2130+ """
2131+ Test :func:`rmtree` when optional parameters are passed
2132+ """
2133+ # GIVEN: A mocked :func:`shutil.rmtree`
2134+ with patch('openlp.core.lib.shutil.shutil.rmtree', return_value='') as mocked_shutil_rmtree:
2135+ mocked_on_error = MagicMock()
2136+
2137+ # WHEN: Calling :func:`rmtree` with :param:`ignore_errors` set to True and `onerror` set to a mocked object
2138+ rmtree(Path('test', 'path'), ignore_errors=True, onerror=mocked_on_error)
2139+
2140+ # THEN: :func:`shutil.rmtree` should have been called with the optional parameters, with out any of the
2141+ # values being modified
2142+ mocked_shutil_rmtree.assert_called_once_with(ANY, ignore_errors=True, onerror=mocked_on_error)
2143+
2144+ def test_which_no_command(self):
2145+ """
2146+ Test :func:`which` when the command is not found.
2147+ """
2148+ # GIVEN: A mocked :func:``shutil.which` when the command is not found.
2149+ with patch('openlp.core.lib.shutil.shutil.which', return_value=None) as mocked_shutil_which:
2150+
2151+ # WHEN: Calling :func:`which` with a command that does not exist.
2152+ result = which('no_command')
2153+
2154+ # THEN: :func:`shutil.which` should have been called with the command, and :func:`which` should return None.
2155+ mocked_shutil_which.assert_called_once_with('no_command')
2156+ self.assertIsNone(result)
2157+
2158+ def test_which_command(self):
2159+ """
2160+ Test :func:`which` when a command has been found.
2161+ """
2162+ # GIVEN: A mocked :func:`shutil.which` when the command is found.
2163+ with patch('openlp.core.lib.shutil.shutil.which',
2164+ return_value=os.path.join('path', 'to', 'command')) as mocked_shutil_which:
2165+
2166+ # WHEN: Calling :func:`which` with a command that exists.
2167+ result = which('command')
2168+
2169+ # THEN: :func:`shutil.which` should have been called with the command, and :func:`which` should return a
2170+ # Path object equivalent of the command path.
2171+ mocked_shutil_which.assert_called_once_with('command')
2172+ self.assertEqual(result, Path('path', 'to', 'command'))
2173
2174=== modified file 'tests/functional/openlp_core_ui/test_exceptionform.py'
2175--- tests/functional/openlp_core_ui/test_exceptionform.py 2017-08-26 15:06:11 +0000
2176+++ tests/functional/openlp_core_ui/test_exceptionform.py 2017-09-18 20:10:29 +0000
2177@@ -103,7 +103,7 @@
2178 os.remove(self.tempfile)
2179
2180 @patch("openlp.core.ui.exceptionform.Ui_ExceptionDialog")
2181- @patch("openlp.core.ui.exceptionform.QtWidgets.QFileDialog")
2182+ @patch("openlp.core.ui.exceptionform.FileDialog")
2183 @patch("openlp.core.ui.exceptionform.QtCore.QUrl")
2184 @patch("openlp.core.ui.exceptionform.QtCore.QUrlQuery.addQueryItem")
2185 @patch("openlp.core.ui.exceptionform.Qt")
2186
2187=== modified file 'tests/functional/openlp_plugins/presentations/test_impresscontroller.py'
2188--- tests/functional/openlp_plugins/presentations/test_impresscontroller.py 2017-06-08 21:36:17 +0000
2189+++ tests/functional/openlp_plugins/presentations/test_impresscontroller.py 2017-09-18 20:10:29 +0000
2190@@ -24,13 +24,12 @@
2191 """
2192 from unittest import TestCase
2193 from unittest.mock import MagicMock
2194-import os
2195 import shutil
2196 from tempfile import mkdtemp
2197
2198 from openlp.core.common import Settings
2199-from openlp.plugins.presentations.lib.impresscontroller import \
2200- ImpressController, ImpressDocument, TextType
2201+from openlp.core.common.path import Path
2202+from openlp.plugins.presentations.lib.impresscontroller import ImpressController, ImpressDocument, TextType
2203 from openlp.plugins.presentations.presentationplugin import __default_settings__
2204
2205 from tests.utils.constants import TEST_RESOURCES_PATH
2206@@ -82,7 +81,7 @@
2207 mocked_plugin = MagicMock()
2208 mocked_plugin.settings_section = 'presentations'
2209 Settings().extend_default_settings(__default_settings__)
2210- self.file_name = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.pptx')
2211+ self.file_name = Path(TEST_RESOURCES_PATH, 'presentations', 'test.pptx')
2212 self.ppc = ImpressController(mocked_plugin)
2213 self.doc = ImpressDocument(self.ppc, self.file_name)
2214
2215
2216=== modified file 'tests/functional/openlp_plugins/presentations/test_mediaitem.py'
2217--- tests/functional/openlp_plugins/presentations/test_mediaitem.py 2017-04-24 05:17:55 +0000
2218+++ tests/functional/openlp_plugins/presentations/test_mediaitem.py 2017-09-18 20:10:29 +0000
2219@@ -26,6 +26,7 @@
2220 from unittest.mock import patch, MagicMock, call
2221
2222 from openlp.core.common import Registry
2223+from openlp.core.common.path import Path
2224 from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem
2225
2226 from tests.helpers.testmixin import TestMixin
2227@@ -92,17 +93,18 @@
2228 """
2229 # GIVEN: A mocked controller, and mocked os.path.getmtime
2230 mocked_controller = MagicMock()
2231- mocked_doc = MagicMock()
2232+ mocked_doc = MagicMock(**{'get_thumbnail_path.return_value': Path()})
2233 mocked_controller.add_document.return_value = mocked_doc
2234 mocked_controller.supports = ['tmp']
2235 self.media_item.controllers = {
2236 'Mocked': mocked_controller
2237 }
2238- presentation_file = 'file.tmp'
2239- with patch('openlp.plugins.presentations.lib.mediaitem.os.path.getmtime') as mocked_getmtime, \
2240- patch('openlp.plugins.presentations.lib.mediaitem.os.path.exists') as mocked_exists:
2241- mocked_getmtime.side_effect = [100, 200]
2242- mocked_exists.return_value = True
2243+
2244+ thmub_path = MagicMock(st_mtime=100)
2245+ file_path = MagicMock(st_mtime=400)
2246+ with patch.object(Path, 'stat', side_effect=[thmub_path, file_path]), \
2247+ patch.object(Path, 'exists', return_value=True):
2248+ presentation_file = Path('file.tmp')
2249
2250 # WHEN: calling clean_up_thumbnails
2251 self.media_item.clean_up_thumbnails(presentation_file, True)
2252@@ -123,9 +125,8 @@
2253 self.media_item.controllers = {
2254 'Mocked': mocked_controller
2255 }
2256- presentation_file = 'file.tmp'
2257- with patch('openlp.plugins.presentations.lib.mediaitem.os.path.exists') as mocked_exists:
2258- mocked_exists.return_value = False
2259+ presentation_file = Path('file.tmp')
2260+ with patch.object(Path, 'exists', return_value=False):
2261
2262 # WHEN: calling clean_up_thumbnails
2263 self.media_item.clean_up_thumbnails(presentation_file, True)
2264
2265=== modified file 'tests/functional/openlp_plugins/presentations/test_pdfcontroller.py'
2266--- tests/functional/openlp_plugins/presentations/test_pdfcontroller.py 2017-04-24 05:17:55 +0000
2267+++ tests/functional/openlp_plugins/presentations/test_pdfcontroller.py 2017-09-18 20:10:29 +0000
2268@@ -32,6 +32,7 @@
2269
2270 from openlp.plugins.presentations.lib.pdfcontroller import PdfController, PdfDocument
2271 from openlp.core.common import Settings
2272+from openlp.core.common.path import Path
2273 from openlp.core.lib import ScreenList
2274
2275 from tests.utils.constants import TEST_RESOURCES_PATH
2276@@ -66,8 +67,8 @@
2277 self.desktop.screenGeometry.return_value = SCREEN['size']
2278 self.screens = ScreenList.create(self.desktop)
2279 Settings().extend_default_settings(__default_settings__)
2280- self.temp_folder = mkdtemp()
2281- self.thumbnail_folder = mkdtemp()
2282+ self.temp_folder = Path(mkdtemp())
2283+ self.thumbnail_folder = Path(mkdtemp())
2284 self.mock_plugin = MagicMock()
2285 self.mock_plugin.settings_section = self.temp_folder
2286
2287@@ -77,8 +78,8 @@
2288 """
2289 del self.screens
2290 self.destroy_settings()
2291- shutil.rmtree(self.thumbnail_folder)
2292- shutil.rmtree(self.temp_folder)
2293+ shutil.rmtree(str(self.thumbnail_folder))
2294+ shutil.rmtree(str(self.temp_folder))
2295
2296 def test_constructor(self):
2297 """
2298@@ -98,7 +99,7 @@
2299 Test loading of a Pdf using the PdfController
2300 """
2301 # GIVEN: A Pdf-file
2302- test_file = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'pdf_test1.pdf')
2303+ test_file = Path(TEST_RESOURCES_PATH, 'presentations', 'pdf_test1.pdf')
2304
2305 # WHEN: The Pdf is loaded
2306 controller = PdfController(plugin=self.mock_plugin)
2307@@ -118,7 +119,7 @@
2308 Test loading of a Pdf and check size of generate pictures
2309 """
2310 # GIVEN: A Pdf-file
2311- test_file = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'pdf_test1.pdf')
2312+ test_file = Path(TEST_RESOURCES_PATH, 'presentations', 'pdf_test1.pdf')
2313
2314 # WHEN: The Pdf is loaded
2315 controller = PdfController(plugin=self.mock_plugin)
2316@@ -131,7 +132,7 @@
2317
2318 # THEN: The load should succeed and pictures should be created and have been scales to fit the screen
2319 self.assertTrue(loaded, 'The loading of the PDF should succeed.')
2320- image = QtGui.QImage(os.path.join(self.temp_folder, 'pdf_test1.pdf', 'mainslide001.png'))
2321+ image = QtGui.QImage(os.path.join(str(self.temp_folder), 'pdf_test1.pdf', 'mainslide001.png'))
2322 # Based on the converter used the resolution will differ a bit
2323 if controller.gsbin:
2324 self.assertEqual(760, image.height(), 'The height should be 760')
2325
2326=== modified file 'tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py'
2327--- tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py 2017-05-30 18:42:35 +0000
2328+++ tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py 2017-09-18 20:10:29 +0000
2329@@ -22,7 +22,6 @@
2330 """
2331 This module contains tests for the pptviewcontroller module of the Presentations plugin.
2332 """
2333-import os
2334 import shutil
2335 from tempfile import mkdtemp
2336 from unittest import TestCase
2337@@ -30,6 +29,7 @@
2338
2339 from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument, PptviewController
2340 from openlp.core.common import is_win
2341+from openlp.core.common.path import Path
2342
2343 from tests.helpers.testmixin import TestMixin
2344 from tests.utils.constants import TEST_RESOURCES_PATH
2345@@ -184,7 +184,7 @@
2346 """
2347 # GIVEN: mocked PresentationController.save_titles_and_notes and a pptx file
2348 doc = PptviewDocument(self.mock_controller, self.mock_presentation)
2349- doc.file_path = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.pptx')
2350+ doc.file_path = Path(TEST_RESOURCES_PATH, 'presentations', 'test.pptx')
2351 doc.save_titles_and_notes = MagicMock()
2352
2353 # WHEN reading the titles and notes
2354@@ -201,13 +201,13 @@
2355 """
2356 # GIVEN: mocked PresentationController.save_titles_and_notes and an nonexistent file
2357 with patch('builtins.open') as mocked_open, \
2358- patch('openlp.plugins.presentations.lib.pptviewcontroller.os.path.exists') as mocked_exists, \
2359+ patch.object(Path, 'exists') as mocked_path_exists, \
2360 patch('openlp.plugins.presentations.lib.presentationcontroller.check_directory_exists') as \
2361 mocked_dir_exists:
2362- mocked_exists.return_value = False
2363+ mocked_path_exists.return_value = False
2364 mocked_dir_exists.return_value = False
2365 doc = PptviewDocument(self.mock_controller, self.mock_presentation)
2366- doc.file_path = 'Idontexist.pptx'
2367+ doc.file_path = Path('Idontexist.pptx')
2368 doc.save_titles_and_notes = MagicMock()
2369
2370 # WHEN: Reading the titles and notes
2371@@ -215,7 +215,7 @@
2372
2373 # THEN: File existens should have been checked, and not have been opened.
2374 doc.save_titles_and_notes.assert_called_once_with(None, None)
2375- mocked_exists.assert_any_call('Idontexist.pptx')
2376+ mocked_path_exists.assert_called_with()
2377 self.assertEqual(mocked_open.call_count, 0, 'There should be no calls to open a file.')
2378
2379 def test_create_titles_and_notes_invalid_file(self):
2380@@ -228,7 +228,7 @@
2381 mocked_is_zf.return_value = False
2382 mocked_open.filesize = 10
2383 doc = PptviewDocument(self.mock_controller, self.mock_presentation)
2384- doc.file_path = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.ppt')
2385+ doc.file_path = Path(TEST_RESOURCES_PATH, 'presentations', 'test.ppt')
2386 doc.save_titles_and_notes = MagicMock()
2387
2388 # WHEN: reading the titles and notes
2389
2390=== modified file 'tests/functional/openlp_plugins/presentations/test_presentationcontroller.py'
2391--- tests/functional/openlp_plugins/presentations/test_presentationcontroller.py 2017-08-25 20:03:25 +0000
2392+++ tests/functional/openlp_plugins/presentations/test_presentationcontroller.py 2017-09-18 20:10:29 +0000
2393@@ -23,9 +23,8 @@
2394 Functional tests to test the PresentationController and PresentationDocument
2395 classes and related methods.
2396 """
2397-import os
2398 from unittest import TestCase
2399-from unittest.mock import MagicMock, mock_open, patch
2400+from unittest.mock import MagicMock, call, patch
2401
2402 from openlp.core.common.path import Path
2403 from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
2404@@ -67,23 +66,18 @@
2405 Test PresentationDocument.save_titles_and_notes method with two valid lists
2406 """
2407 # GIVEN: two lists of length==2 and a mocked open and get_thumbnail_folder
2408- mocked_open = mock_open()
2409- with patch('builtins.open', mocked_open), patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder:
2410+ with patch('openlp.plugins.presentations.lib.presentationcontroller.Path.write_text') as mocked_write_text, \
2411+ patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder:
2412 titles = ['uno', 'dos']
2413 notes = ['one', 'two']
2414
2415 # WHEN: calling save_titles_and_notes
2416- mocked_get_thumbnail_folder.return_value = 'test'
2417+ mocked_get_thumbnail_folder.return_value = Path('test')
2418 self.document.save_titles_and_notes(titles, notes)
2419
2420 # THEN: the last call to open should have been for slideNotes2.txt
2421- mocked_open.assert_any_call(os.path.join('test', 'titles.txt'), mode='wt', encoding='utf-8')
2422- mocked_open.assert_any_call(os.path.join('test', 'slideNotes1.txt'), mode='wt', encoding='utf-8')
2423- mocked_open.assert_any_call(os.path.join('test', 'slideNotes2.txt'), mode='wt', encoding='utf-8')
2424- self.assertEqual(mocked_open.call_count, 3, 'There should be exactly three files opened')
2425- mocked_open().writelines.assert_called_once_with(['uno', 'dos'])
2426- mocked_open().write.assert_any_call('one')
2427- mocked_open().write.assert_any_call('two')
2428+ self.assertEqual(mocked_write_text.call_count, 3, 'There should be exactly three files written')
2429+ mocked_write_text.assert_has_calls([call('uno\ndos'), call('one'), call('two')])
2430
2431 def test_save_titles_and_notes_with_None(self):
2432 """
2433@@ -107,10 +101,11 @@
2434 """
2435 # GIVEN: A mocked open, get_thumbnail_folder and exists
2436
2437- with patch('builtins.open', mock_open(read_data='uno\ndos\n')) as mocked_open, \
2438+ with patch('openlp.plugins.presentations.lib.presentationcontroller.Path.read_text',
2439+ return_value='uno\ndos\n') as mocked_read_text, \
2440 patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder, \
2441- patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists:
2442- mocked_get_thumbnail_folder.return_value = 'test'
2443+ patch('openlp.plugins.presentations.lib.presentationcontroller.Path.exists') as mocked_exists:
2444+ mocked_get_thumbnail_folder.return_value = Path('test')
2445 mocked_exists.return_value = True
2446
2447 # WHEN: calling get_titles_and_notes
2448@@ -121,45 +116,36 @@
2449 self.assertEqual(len(result_titles), 2, 'There should be two items in the titles')
2450 self.assertIs(type(result_notes), list, 'result_notes should be of type list')
2451 self.assertEqual(len(result_notes), 2, 'There should be two items in the notes')
2452- self.assertEqual(mocked_open.call_count, 3, 'Three files should be opened')
2453- mocked_open.assert_any_call(os.path.join('test', 'titles.txt'), encoding='utf-8')
2454- mocked_open.assert_any_call(os.path.join('test', 'slideNotes1.txt'), encoding='utf-8')
2455- mocked_open.assert_any_call(os.path.join('test', 'slideNotes2.txt'), encoding='utf-8')
2456- self.assertEqual(mocked_exists.call_count, 3, 'Three files should have been checked')
2457+ self.assertEqual(mocked_read_text.call_count, 3, 'Three files should be read')
2458
2459 def test_get_titles_and_notes_with_file_not_found(self):
2460 """
2461 Test PresentationDocument.get_titles_and_notes method with file not found
2462 """
2463 # GIVEN: A mocked open, get_thumbnail_folder and exists
2464- with patch('builtins.open') as mocked_open, \
2465- patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder, \
2466- patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists:
2467- mocked_get_thumbnail_folder.return_value = 'test'
2468- mocked_exists.return_value = False
2469+ with patch('openlp.plugins.presentations.lib.presentationcontroller.Path.read_text') as mocked_read_text, \
2470+ patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder:
2471+ mocked_read_text.side_effect = FileNotFoundError()
2472+ mocked_get_thumbnail_folder.return_value = Path('test')
2473
2474 # WHEN: calling get_titles_and_notes
2475 result_titles, result_notes = self.document.get_titles_and_notes()
2476
2477 # THEN: it should return two empty lists
2478- self.assertIs(type(result_titles), list, 'result_titles should be of type list')
2479+ self.assertIsInstance(result_titles, list, 'result_titles should be of type list')
2480 self.assertEqual(len(result_titles), 0, 'there be no titles')
2481- self.assertIs(type(result_notes), list, 'result_notes should be a list')
2482+ self.assertIsInstance(result_notes, list, 'result_notes should be a list')
2483 self.assertEqual(len(result_notes), 0, 'but the list should be empty')
2484- self.assertEqual(mocked_open.call_count, 0, 'No calls to open files')
2485- self.assertEqual(mocked_exists.call_count, 1, 'There should be one call to file exists')
2486
2487 def test_get_titles_and_notes_with_file_error(self):
2488 """
2489 Test PresentationDocument.get_titles_and_notes method with file errors
2490 """
2491 # GIVEN: A mocked open, get_thumbnail_folder and exists
2492- with patch('builtins.open') as mocked_open, \
2493- patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder, \
2494- patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists:
2495- mocked_get_thumbnail_folder.return_value = 'test'
2496- mocked_exists.return_value = True
2497- mocked_open.side_effect = IOError()
2498+ with patch('openlp.plugins.presentations.lib.presentationcontroller.Path.read_text') as mocked_read_text, \
2499+ patch(FOLDER_TO_PATCH) as mocked_get_thumbnail_folder:
2500+ mocked_read_text.side_effect = IOError()
2501+ mocked_get_thumbnail_folder.return_value = Path('test')
2502
2503 # WHEN: calling get_titles_and_notes
2504 result_titles, result_notes = self.document.get_titles_and_notes()
2505@@ -180,18 +166,16 @@
2506 patch('openlp.plugins.presentations.lib.presentationcontroller.check_directory_exists')
2507 self.get_thumbnail_folder_patcher = \
2508 patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder')
2509- self.os_patcher = patch('openlp.plugins.presentations.lib.presentationcontroller.os')
2510 self._setup_patcher = \
2511 patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument._setup')
2512
2513 self.mock_check_directory_exists = self.check_directory_exists_patcher.start()
2514 self.mock_get_thumbnail_folder = self.get_thumbnail_folder_patcher.start()
2515- self.mock_os = self.os_patcher.start()
2516 self.mock_setup = self._setup_patcher.start()
2517
2518 self.mock_controller = MagicMock()
2519
2520- self.mock_get_thumbnail_folder.return_value = 'returned/path/'
2521+ self.mock_get_thumbnail_folder.return_value = Path('returned/path/')
2522
2523 def tearDown(self):
2524 """
2525@@ -199,7 +183,6 @@
2526 """
2527 self.check_directory_exists_patcher.stop()
2528 self.get_thumbnail_folder_patcher.stop()
2529- self.os_patcher.stop()
2530 self._setup_patcher.stop()
2531
2532 def test_initialise_presentation_document(self):
2533@@ -227,7 +210,7 @@
2534 PresentationDocument(self.mock_controller, 'Name')
2535
2536 # THEN: check_directory_exists should have been called with 'returned/path/'
2537- self.mock_check_directory_exists.assert_called_once_with(Path('returned', 'path'))
2538+ self.mock_check_directory_exists.assert_called_once_with(Path('returned', 'path/'))
2539
2540 self._setup_patcher.start()
2541
2542@@ -244,20 +227,3 @@
2543
2544 # THEN: load_presentation should return false
2545 self.assertFalse(result, "PresentationDocument.load_presentation should return false.")
2546-
2547- def test_get_file_name(self):
2548- """
2549- Test the PresentationDocument.get_file_name method.
2550- """
2551-
2552- # GIVEN: A mocked os.path.split which returns a list, an instance of PresentationDocument and
2553- # arbitary file_path.
2554- self.mock_os.path.split.return_value = ['directory', 'file.ext']
2555- instance = PresentationDocument(self.mock_controller, 'Name')
2556- instance.file_path = 'filepath'
2557-
2558- # WHEN: Calling get_file_name
2559- result = instance.get_file_name()
2560-
2561- # THEN: get_file_name should return 'file.ext'
2562- self.assertEqual(result, 'file.ext')