Skip to content

Commit

Permalink
Merge pull request #21423 from mrclary/requests
Browse files Browse the repository at this point in the history
PR: Use `requests` instead of `urllib` for updates
  • Loading branch information
ccordoba12 authored Oct 18, 2023
2 parents 27978d6 + 314c3ea commit 2562a84
Showing 1 changed file with 77 additions and 44 deletions.
121 changes: 77 additions & 44 deletions spyder/workers/updates.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,37 +5,49 @@
# (see spyder/__init__.py for details)

# Standard library imports
import json
import logging
import os
import os.path as osp
import ssl
import tempfile
import traceback
from urllib.request import urlopen, urlretrieve
from urllib.error import URLError, HTTPError

# Third party imports
from qtpy.QtCore import QObject, Signal
import requests
from requests.adapters import ConnectionError, SSLError

# Local imports
from spyder import __version__
from spyder.config.base import (
_, is_stable_version, is_pynsist, running_in_mac_app)
from spyder.py3compat import is_text_string
from spyder.config.utils import is_anaconda
from spyder.utils.conda import get_spyder_conda_channel
from spyder.utils.programs import check_version, is_module_installed

# Logger setup
logger = logging.getLogger(__name__)

SSL_ERROR_MSG = _(
'SSL certificate verification failed while checking for Spyder updates.'
'<br><br>Please contact your network administrator for assistance.'
)

CONNECT_ERROR_MSG = _(
'Unable to connect to the internet while checking for Spyder updates.'
'<br><br>Make sure the connection is working properly.'
)


class UpdateDownloadCancelledException(Exception):
"""Download for installer to update was cancelled."""
pass


class UpdateDownloadIncompleteError(Exception):
"""Error occured while downloading file"""
pass


class WorkerUpdates(QObject):
"""
Worker that checks for releases using either the Anaconda
Expand Down Expand Up @@ -107,21 +119,9 @@ def start(self):
self.url = pypi_url

try:
if hasattr(ssl, '_create_unverified_context'):
# Fix for spyder-ide/spyder#2685.
# [Works only with Python >=2.7.9]
# More info: https://www.python.org/dev/peps/pep-0476/#opting-out
context = ssl._create_unverified_context()
page = urlopen(self.url, context=context)
else:
page = urlopen(self.url)

data = page.read()

# Needed step for python3 compatibility
if not is_text_string(data):
data = data.decode()
data = json.loads(data)
logger.debug(f"Checking for updates from {self.url}")
page = requests.get(self.url)
data = page.json()

if is_pynsist() or running_in_mac_app():
if self.releases is None:
Expand All @@ -140,14 +140,13 @@ def start(self):

result = self.check_update_available()
self.update_available, self.latest_release = result
except URLError:
error_msg = _(
'It was not possible to connect to the internet to check for '
'Spyder updates.'
'.<br><br>'
'Make sure the connection is working properly.'
)
except Exception:
except SSLError as err:
error_msg = SSL_ERROR_MSG
logger.debug(err, stack_info=True)
except ConnectionError as err:
error_msg = CONNECT_ERROR_MSG
logger.debug(err, stack_info=True)
except Exception as err:
error = traceback.format_exc()
formatted_error = error.replace('\n', '<br>').replace(' ', '&nbsp;')

Expand All @@ -157,6 +156,7 @@ def start(self):
'<br><br>'
'<tt>{}</tt>'
).format(formatted_error)
logger.debug(err, stack_info=True)

# Don't show dialog when starting up spyder and an error occur
if not (self.startup and error_msg is not None):
Expand Down Expand Up @@ -214,7 +214,6 @@ def _progress_reporter(self, block_number, read_size, total_size):

def _download_installer(self):
"""Donwload latest Spyder standalone installer executable."""
logger.debug("Downloading installer executable")
tmpdir = tempfile.gettempdir()
is_full_installer = (is_module_installed('numpy') or
is_module_installed('pandas'))
Expand All @@ -238,13 +237,37 @@ def _download_installer(self):

installer_path = osp.join(installer_dir_path, name)
self.installer_path = installer_path
if not osp.isfile(installer_path):
logger.debug(
f"Downloading installer from {url} to {installer_path}")
urlretrieve(
url, installer_path, reporthook=self._progress_reporter)
else:
self._progress_reporter(1, 1)

if osp.isfile(installer_path):
# Installer already downloaded
logger.info(f"{installer_path} already downloaded")
self._progress_reporter(1, 1, 1)
return

logger.debug(f"Downloading installer from {url} to {installer_path}")
with requests.get(url, stream=True) as r:
with open(installer_path, 'wb') as f:
chunk_size = 8 * 1024
size = -1
size_read = 0
chunk_num = 0

if "content-length" in r.headers:
size = int(r.headers["content-length"])

self._progress_reporter(chunk_num, chunk_size, size)

for chunk in r.iter_content(chunk_size=chunk_size):
size_read += len(chunk)
f.write(chunk)
chunk_num += 1
self._progress_reporter(chunk_num, chunk_size, size)

if size >= 0 and size_read < size:
raise UpdateDownloadIncompleteError(
"Download incomplete: retrieved only "
f"{size_read} out of {size} bytes."
)

def start(self):
"""Main method of the WorkerDownloadInstaller worker."""
Expand All @@ -253,15 +276,25 @@ def start(self):
self._download_installer()
except UpdateDownloadCancelledException:
if self.installer_path:
os.remove(self.installer_path)
os.remove(self.installer_path)
return
except HTTPError:
error_msg = _('Unable to retrieve installer information.')
except URLError:
error_msg = _('Unable to connect to the internet. <br><br>'
'Make sure the connection is working properly.')
except Exception:
error_msg = _('Unable to download the installer.')
except SSLError as err:
error_msg = SSL_ERROR_MSG
logger.debug(err, stack_info=True)
except ConnectionError as err:
error_msg = CONNECT_ERROR_MSG
logger.debug(err, stack_info=True)
except Exception as err:
error = traceback.format_exc()
formatted_error = error.replace('\n', '<br>').replace(' ', '&nbsp;')

error_msg = _(
'It was not possible to download the installer due to the '
'following error:'
'<br><br>'
'<tt>{}</tt>'
).format(formatted_error)
logger.debug(err, stack_info=True)
self.error = error_msg
try:
self.sig_ready.emit(self.installer_path)
Expand Down

0 comments on commit 2562a84

Please sign in to comment.