1# -*- coding: utf-8 -*-
2#
3# Copyright © Spyder Project Contributors
4# Licensed under the terms of the MIT License
5# (see spyder/__init__.py for details)
6
7# Standard library imports
8import json
9import ssl
10
11# Third party imports
12from qtpy.QtCore import QObject, Signal
13
14# Local imports
15from spyder import __version__
16from spyder.config.base import _
17from spyder.py3compat import PY3
18from spyder.utils.programs import check_version, is_stable_version
19
20
21if PY3:
22    from urllib.request import urlopen
23    from urllib.error import URLError, HTTPError
24else:
25    from urllib2 import urlopen, URLError, HTTPError
26
27
28class WorkerUpdates(QObject):
29    """
30    Worker that checks for releases using the Github API without blocking the
31    Spyder user interface, in case of connections issues.
32    """
33    sig_ready = Signal()
34
35    def __init__(self, parent, startup):
36        QObject.__init__(self)
37        self._parent = parent
38        self.error = None
39        self.latest_release = None
40        self.startup = startup
41
42    def check_update_available(self, version, releases):
43        """Checks if there is an update available.
44
45        It takes as parameters the current version of Spyder and a list of
46        valid cleaned releases in chronological order (what github api returns
47        by default). Example: ['2.3.4', '2.3.3' ...]
48        """
49        if is_stable_version(version):
50            # Remove non stable versions from the list
51            releases = [r for r in releases if is_stable_version(r)]
52
53        latest_release = releases[0]
54
55        if version.endswith('dev'):
56            return (False, latest_release)
57
58        return (check_version(version, latest_release, '<'), latest_release)
59
60    def start(self):
61        """Main method of the WorkerUpdates worker"""
62        self.url = 'https://api.github.com/repos/spyder-ide/spyder/releases'
63        self.update_available = False
64        self.latest_release = __version__
65
66        error_msg = None
67
68        try:
69            if hasattr(ssl, '_create_unverified_context'):
70                # Fix for issue # 2685 [Works only with Python >=2.7.9]
71                # More info: https://www.python.org/dev/peps/pep-0476/#opting-out
72                context = ssl._create_unverified_context()
73                page = urlopen(self.url, context=context)
74            else:
75                page = urlopen(self.url)
76            try:
77                data = page.read()
78
79                # Needed step for python3 compatibility
80                if not isinstance(data, str):
81                    data = data.decode()
82
83                data = json.loads(data)
84                releases = [item['tag_name'].replace('v', '') for item in data]
85                version = __version__
86
87                result = self.check_update_available(version, releases)
88                self.update_available, self.latest_release = result
89            except Exception:
90                error_msg = _('Unable to retrieve information.')
91        except HTTPError:
92            error_msg = _('Unable to retrieve information.')
93        except URLError:
94            error_msg = _('Unable to connect to the internet. <br><br>Make '
95                          'sure the connection is working properly.')
96        except Exception:
97            error_msg = _('Unable to check for updates.')
98
99        # Don't show dialog when starting up spyder and an error occur
100        if not (self.startup and error_msg is not None):
101            self.error = error_msg
102            self.sig_ready.emit()
103