# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
# Standard library imports
import json
import os
import re
import ssl
import sys
# Third party imports
from qtpy.QtCore import QObject, Signal
# Local imports
from spyder import __version__
from spyder.config.base import _, is_stable_version
from spyder.py3compat import PY3, is_text_string
from spyder.config.utils import is_anaconda
from spyder.utils.programs import check_version
if PY3:
from urllib.request import urlopen
from urllib.error import URLError, HTTPError
else:
from urllib2 import urlopen, URLError, HTTPError
class WorkerUpdates(QObject):
"""
Worker that checks for releases using either the Anaconda
default channels or the Github Releases page without
blocking the Spyder user interface, in case of connection
issues.
"""
sig_ready = Signal()
def __init__(self, parent, startup, version="", releases=None):
QObject.__init__(self)
self._parent = parent
self.error = None
self.latest_release = None
self.startup = startup
self.releases = releases
if not version:
self.version = __version__
else:
self.version = version
def check_update_available(self):
"""Checks if there is an update available.
It takes as parameters the current version of Spyder and a list of
valid cleaned releases in chronological order.
Example: ['2.3.2', '2.3.3' ...] or with github ['2.3.4', '2.3.3' ...]
"""
# Don't perform any check for development versions
if 'dev' in self.version:
return (False, latest_release)
# Filter releases
if is_stable_version(self.version):
releases = [r for r in self.releases if is_stable_version(r)]
else:
releases = [r for r in self.releases
if not is_stable_version(r) or r in self.version]
latest_release = releases[-1]
return (check_version(self.version, latest_release, '<'),
latest_release)
def start(self):
"""Main method of the WorkerUpdates worker"""
if is_anaconda():
self.url = 'https://repo.anaconda.com/pkgs/main'
if os.name == 'nt':
self.url += '/win-64/repodata.json'
elif sys.platform == 'darwin':
self.url += '/osx-64/repodata.json'
else:
self.url += '/linux-64/repodata.json'
else:
self.url = ('https://api.github.com/repos/'
'spyder-ide/spyder/releases')
self.update_available = False
self.latest_release = __version__
error_msg = None
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)
try:
data = page.read()
# Needed step for python3 compatibility
if not is_text_string(data):
data = data.decode()
data = json.loads(data)
if is_anaconda():
if self.releases is None:
self.releases = []
for item in data['packages']:
if ('spyder' in item and
not re.search(r'spyder-[a-zA-Z]', item)):
self.releases.append(item.split('-')[1])
result = self.check_update_available()
else:
if self.releases is None:
self.releases = [item['tag_name'].replace('v', '')
for item in data]
self.releases = list(reversed(self.releases))
result = self.check_update_available()
self.update_available, self.latest_release = result
except Exception:
error_msg = _('Unable to retrieve information.')
except HTTPError:
error_msg = _('Unable to retrieve information.')
except URLError:
error_msg = _('Unable to connect to the internet.
Make '
'sure the connection is working properly.')
except Exception:
error_msg = _('Unable to check for updates.')
# Don't show dialog when starting up spyder and an error occur
if not (self.startup and error_msg is not None):
self.error = error_msg
self.sig_ready.emit()