forked from openlp/openlp
Fix bug 1422683: Added exception checks to get_web_page() to help with changed description. Rearrange calls so thumbnail downloads don't hang other event threads. Reraise exception when retries > CONNECTION_RETRIES.
bzr-revno: 2512 Fixes: https://launchpad.net/bugs/1422683
This commit is contained in:
commit
85b8cb2c50
@ -25,6 +25,7 @@ This module contains the first time wizard.
|
|||||||
import hashlib
|
import hashlib
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
import time
|
import time
|
||||||
import urllib.request
|
import urllib.request
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
@ -61,6 +62,7 @@ class ThemeScreenshotWorker(QtCore.QObject):
|
|||||||
self.filename = filename
|
self.filename = filename
|
||||||
self.sha256 = sha256
|
self.sha256 = sha256
|
||||||
self.screenshot = screenshot
|
self.screenshot = screenshot
|
||||||
|
socket.setdefaulttimeout(CONNECTION_TIMEOUT)
|
||||||
super(ThemeScreenshotWorker, self).__init__()
|
super(ThemeScreenshotWorker, self).__init__()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -250,7 +252,6 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
# Download the theme screenshots
|
# Download the theme screenshots
|
||||||
themes = self.config.get('themes', 'files').split(',')
|
themes = self.config.get('themes', 'files').split(',')
|
||||||
for theme in themes:
|
for theme in themes:
|
||||||
self.application.process_events()
|
|
||||||
title = self.config.get('theme_%s' % theme, 'title')
|
title = self.config.get('theme_%s' % theme, 'title')
|
||||||
filename = self.config.get('theme_%s' % theme, 'filename')
|
filename = self.config.get('theme_%s' % theme, 'filename')
|
||||||
sha256 = self.config.get('theme_%s' % theme, 'sha256', fallback='')
|
sha256 = self.config.get('theme_%s' % theme, 'sha256', fallback='')
|
||||||
@ -264,6 +265,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
worker.finished.connect(thread.quit)
|
worker.finished.connect(thread.quit)
|
||||||
worker.moveToThread(thread)
|
worker.moveToThread(thread)
|
||||||
thread.start()
|
thread.start()
|
||||||
|
self.application.process_events()
|
||||||
|
|
||||||
def set_defaults(self):
|
def set_defaults(self):
|
||||||
"""
|
"""
|
||||||
@ -403,8 +405,8 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
retries = 0
|
retries = 0
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
url_file = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT)
|
|
||||||
filename = open(f_path, "wb")
|
filename = open(f_path, "wb")
|
||||||
|
url_file = urllib.request.urlopen(url, timeout=CONNECTION_TIMEOUT)
|
||||||
if sha256:
|
if sha256:
|
||||||
hasher = hashlib.sha256()
|
hasher = hashlib.sha256()
|
||||||
# Download until finished or canceled.
|
# Download until finished or canceled.
|
||||||
@ -422,7 +424,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
log.error('sha256 sums did not match for file: {}'.format(f_path))
|
log.error('sha256 sums did not match for file: {}'.format(f_path))
|
||||||
os.remove(f_path)
|
os.remove(f_path)
|
||||||
return False
|
return False
|
||||||
except urllib.error.URLError:
|
except (urllib.error.URLError, socket.timeout) as err:
|
||||||
trace_error_handler(log)
|
trace_error_handler(log)
|
||||||
filename.close()
|
filename.close()
|
||||||
os.remove(f_path)
|
os.remove(f_path)
|
||||||
@ -447,7 +449,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
for index, theme in enumerate(themes):
|
for index, theme in enumerate(themes):
|
||||||
screenshot = self.config.get('theme_%s' % theme, 'screenshot')
|
screenshot = self.config.get('theme_%s' % theme, 'screenshot')
|
||||||
item = self.themes_list_widget.item(index)
|
item = self.themes_list_widget.item(index)
|
||||||
# if item:
|
if item:
|
||||||
item.setIcon(build_icon(os.path.join(gettempdir(), 'openlp', screenshot)))
|
item.setIcon(build_icon(os.path.join(gettempdir(), 'openlp', screenshot)))
|
||||||
|
|
||||||
def _get_file_size(self, url):
|
def _get_file_size(self, url):
|
||||||
@ -617,6 +619,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
songs_destination = os.path.join(gettempdir(), 'openlp')
|
songs_destination = os.path.join(gettempdir(), 'openlp')
|
||||||
bibles_destination = AppLocation.get_section_data_path('bibles')
|
bibles_destination = AppLocation.get_section_data_path('bibles')
|
||||||
themes_destination = AppLocation.get_section_data_path('themes')
|
themes_destination = AppLocation.get_section_data_path('themes')
|
||||||
|
missed_files = []
|
||||||
# Download songs
|
# Download songs
|
||||||
for i in range(self.songs_list_widget.count()):
|
for i in range(self.songs_list_widget.count()):
|
||||||
item = self.songs_list_widget.item(i)
|
item = self.songs_list_widget.item(i)
|
||||||
@ -626,7 +629,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
self.previous_size = 0
|
self.previous_size = 0
|
||||||
destination = os.path.join(songs_destination, str(filename))
|
destination = os.path.join(songs_destination, str(filename))
|
||||||
if not self.url_get_file('%s%s' % (self.songs_url, filename), destination, sha256):
|
if not self.url_get_file('%s%s' % (self.songs_url, filename), destination, sha256):
|
||||||
return False
|
missed_files.append('Song: {}'.format(filename))
|
||||||
# Download Bibles
|
# Download Bibles
|
||||||
bibles_iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget)
|
bibles_iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget)
|
||||||
while bibles_iterator.value():
|
while bibles_iterator.value():
|
||||||
@ -637,7 +640,7 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
self.previous_size = 0
|
self.previous_size = 0
|
||||||
if not self.url_get_file('%s%s' % (self.bibles_url, bible), os.path.join(bibles_destination, bible),
|
if not self.url_get_file('%s%s' % (self.bibles_url, bible), os.path.join(bibles_destination, bible),
|
||||||
sha256):
|
sha256):
|
||||||
return False
|
missed_files.append('Bible: {}'.format(bible))
|
||||||
bibles_iterator += 1
|
bibles_iterator += 1
|
||||||
# Download themes
|
# Download themes
|
||||||
for i in range(self.themes_list_widget.count()):
|
for i in range(self.themes_list_widget.count()):
|
||||||
@ -648,7 +651,20 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
self.previous_size = 0
|
self.previous_size = 0
|
||||||
if not self.url_get_file('%s%s' % (self.themes_url, theme), os.path.join(themes_destination, theme),
|
if not self.url_get_file('%s%s' % (self.themes_url, theme), os.path.join(themes_destination, theme),
|
||||||
sha256):
|
sha256):
|
||||||
return False
|
missed_files.append('Theme: {}'.format(theme))
|
||||||
|
if missed_files:
|
||||||
|
file_list = ''
|
||||||
|
for entry in missed_files:
|
||||||
|
file_list += '{}<br \>'.format(entry)
|
||||||
|
msg = QtGui.QMessageBox()
|
||||||
|
msg.setIcon(QtGui.QMessageBox.Warning)
|
||||||
|
msg.setWindowTitle(translate('OpenLP.FirstTimeWizard', 'Network Error'))
|
||||||
|
msg.setText(translate('OpenLP.FirstTimeWizard', 'Unable to download some files'))
|
||||||
|
msg.setInformativeText(translate('OpenLP.FirstTimeWizard',
|
||||||
|
'The following files were not able to be '
|
||||||
|
'downloaded:<br \>{}'.format(file_list)))
|
||||||
|
msg.setStandardButtons(msg.Ok)
|
||||||
|
ans = msg.exec_()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _set_plugin_status(self, field, tag):
|
def _set_plugin_status(self, field, tag):
|
||||||
|
@ -29,6 +29,7 @@ import locale
|
|||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
|
import socket
|
||||||
import time
|
import time
|
||||||
from shutil import which
|
from shutil import which
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
@ -394,26 +395,44 @@ def get_web_page(url, header=None, update_openlp=False):
|
|||||||
req.add_header('User-Agent', user_agent)
|
req.add_header('User-Agent', user_agent)
|
||||||
if header:
|
if header:
|
||||||
req.add_header(header[0], header[1])
|
req.add_header(header[0], header[1])
|
||||||
page = None
|
|
||||||
log.debug('Downloading URL = %s' % url)
|
log.debug('Downloading URL = %s' % url)
|
||||||
retries = 1
|
retries = 0
|
||||||
while True:
|
while retries <= CONNECTION_RETRIES:
|
||||||
try:
|
|
||||||
page = urllib.request.urlopen(req, timeout=CONNECTION_TIMEOUT)
|
|
||||||
log.debug('Downloaded URL = %s' % page.geturl())
|
|
||||||
except (urllib.error.URLError, ConnectionError):
|
|
||||||
if retries > CONNECTION_RETRIES:
|
|
||||||
log.exception('The web page could not be downloaded')
|
|
||||||
raise
|
|
||||||
else:
|
|
||||||
retries += 1
|
retries += 1
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
continue
|
try:
|
||||||
|
page = urllib.request.urlopen(req, timeout=CONNECTION_TIMEOUT)
|
||||||
|
log.debug('Downloaded page {}'.format(page.geturl()))
|
||||||
|
except urllib.error.URLError as err:
|
||||||
|
log.exception('URLError on {}'.format(url))
|
||||||
|
log.exception('URLError: {}'.format(err.reason))
|
||||||
|
page = None
|
||||||
|
if retries > CONNECTION_RETRIES:
|
||||||
|
raise
|
||||||
|
except socket.timeout:
|
||||||
|
log.exception('Socket timeout: {}'.format(url))
|
||||||
|
page = None
|
||||||
|
if retries > CONNECTION_RETRIES:
|
||||||
|
raise
|
||||||
|
except ConnectionRefusedError:
|
||||||
|
log.exception('ConnectionRefused: {}'.format(url))
|
||||||
|
page = None
|
||||||
|
if retries > CONNECTION_RETRIES:
|
||||||
|
raise
|
||||||
break
|
break
|
||||||
if not page:
|
except ConnectionError:
|
||||||
return None
|
log.exception('Connection error: {}'.format(url))
|
||||||
|
page = None
|
||||||
|
if retries > CONNECTION_RETRIES:
|
||||||
|
raise
|
||||||
|
except:
|
||||||
|
# Don't know what's happening, so reraise the original
|
||||||
|
raise
|
||||||
if update_openlp:
|
if update_openlp:
|
||||||
Registry().get('application').process_events()
|
Registry().get('application').process_events()
|
||||||
|
if not page:
|
||||||
|
log.exception('{} could not be downloaded'.format(url))
|
||||||
|
return None
|
||||||
log.debug(page)
|
log.debug(page)
|
||||||
return page
|
return page
|
||||||
|
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
Package to test the openlp.core.ui.firsttimeform package.
|
Package to test the openlp.core.ui.firsttimeform package.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
|
import tempfile
|
||||||
import urllib
|
import urllib
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
@ -70,6 +72,11 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
|||||||
self.app.process_events = lambda: None
|
self.app.process_events = lambda: None
|
||||||
Registry.create()
|
Registry.create()
|
||||||
Registry().register('application', self.app)
|
Registry().register('application', self.app)
|
||||||
|
self.tempfile = os.path.join(tempfile.gettempdir(), 'testfile')
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if os.path.isfile(self.tempfile):
|
||||||
|
os.remove(self.tempfile)
|
||||||
|
|
||||||
def initialise_test(self):
|
def initialise_test(self):
|
||||||
"""
|
"""
|
||||||
@ -229,3 +236,20 @@ class TestFirstTimeForm(TestCase, TestMixin):
|
|||||||
# THEN: the critical_error_message_box should have been called
|
# THEN: the critical_error_message_box should have been called
|
||||||
self.assertEquals(mocked_message_box.mock_calls[1][1][0], 'Network Error 407',
|
self.assertEquals(mocked_message_box.mock_calls[1][1][0], 'Network Error 407',
|
||||||
'first_time_form should have caught Network Error')
|
'first_time_form should have caught Network Error')
|
||||||
|
|
||||||
|
@patch('openlp.core.ui.firsttimeform.urllib.request.urlopen')
|
||||||
|
def socket_timeout_test(self, mocked_urlopen):
|
||||||
|
"""
|
||||||
|
Test socket timeout gets caught
|
||||||
|
"""
|
||||||
|
# GIVEN: Mocked urlopen to fake a network disconnect in the middle of a download
|
||||||
|
first_time_form = FirstTimeForm(None)
|
||||||
|
first_time_form.initialize(MagicMock())
|
||||||
|
mocked_urlopen.side_effect = socket.timeout()
|
||||||
|
|
||||||
|
# WHEN: Attempt to retrieve a file
|
||||||
|
first_time_form.url_get_file(url='http://localhost/test', f_path=self.tempfile)
|
||||||
|
|
||||||
|
# THEN: socket.timeout should have been caught
|
||||||
|
# NOTE: Test is if $tmpdir/tempfile is still there, then test fails since ftw deletes bad downloaded files
|
||||||
|
self.assertFalse(os.path.exists(self.tempfile), 'FTW url_get_file should have caught socket.timeout')
|
||||||
|
Loading…
Reference in New Issue
Block a user