Repository: spyder-ide/spyder

Issue:
4.0beta1 is offered as an update when using a development version and a virtualenv
We need to avoid showing beta versions when checking for updates


Discussion:

Resolving patch:
diff --git a/spyder/workers/updates.py b/spyder/workers/updates.py
--- a/spyder/workers/updates.py
+++ b/spyder/workers/updates.py
@@ -7,6 +7,7 @@
 # Standard library imports
 import json
 import os
+import re
 import ssl
 import sys
 
@@ -37,34 +38,41 @@ class WorkerUpdates(QObject):
     """
     sig_ready = Signal()
 
-    def __init__(self, parent, startup, version=""):
+    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.version = version
+        self.releases = releases
 
-    def check_update_available(self, version, releases, github=False):
+        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' ...]
         """
-        if is_stable_version(version):
-            # Remove non stable versions from the list
-            releases = [r for r in releases if is_stable_version(r)]
+        # Don't perform any check for development versions
+        if 'dev' in self.version:
+            return (False, latest_release)
 
-        if github:
-            latest_release = releases[0]
+        # Filter releases
+        if is_stable_version(self.version):
+            releases = [r for r in self.releases if is_stable_version(r)]
         else:
-            latest_release = releases[-1]
+            releases = [r for r in self.releases
+                        if not is_stable_version(r) or r in self.version]
 
-        if version.endswith('dev'):
-            return (False, latest_release)
+        latest_release = releases[-1]
 
-        return (check_version(version, latest_release, '<'), latest_release)
+        return (check_version(self.version, latest_release, '<'),
+                latest_release)
 
     def start(self):
         """Main method of the WorkerUpdates worker"""
@@ -98,24 +106,23 @@ def start(self):
                 # Needed step for python3 compatibility
                 if not isinstance(data, str):
                     data = data.decode()
-
                 data = json.loads(data)
-                if not self.version:
-                    self.version = __version__
 
                 if is_anaconda():
-                    releases = []
-                    for item in data['packages']:
-                        if 'spyder' in item and 'spyder-kernels' not in item:
-                            releases.append(item.split('-')[1])
-                    result = self.check_update_available(self.version,
-                                                         releases)
+                    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:
-                    releases = [item['tag_name'].replace('v', '')
-                                for item in data]
-                    result = self.check_update_available(self.version,
-                                                         releases,
-                                                         github=True)
+                    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.')


Test patch:
diff --git a/spyder/utils/tests/test_programs.py b/spyder/utils/tests/test_programs.py
--- a/spyder/utils/tests/test_programs.py
+++ b/spyder/utils/tests/test_programs.py
@@ -15,7 +15,8 @@
                                    is_python_interpreter,
                                    is_python_interpreter_valid_name,
                                    find_program, shell_split, check_version,
-                                   is_module_installed, get_temp_dir)
+                                   is_module_installed, get_temp_dir,
+                                   is_stable_version)
 
 
 if os.name == 'nt':
@@ -132,5 +133,12 @@ def test_get_temp_dir_ensure_dir_exists():
     assert another_call == temp_dir
 
 
+def test_is_stable_version():
+    """Test for is_stable_version."""
+    assert is_stable_version('3.3.0')
+    assert not is_stable_version('4.0.0b1')
+    assert not is_stable_version('3.3.2.dev0')
+
+
 if __name__ == '__main__':
     pytest.main()
diff --git a/spyder/workers/tests/test_update.py b/spyder/workers/tests/test_update.py
--- a/spyder/workers/tests/test_update.py
+++ b/spyder/workers/tests/test_update.py
@@ -6,28 +6,55 @@
 
 import pytest
 
-from spyder.utils.programs import check_version
+from spyder.config.utils import is_anaconda
 from spyder.workers.updates import WorkerUpdates
 
-# Max time to wait for the update check
-# (in miliseconds)
-UPDATE_TIMEOUT = 20000
-
 
 def test_update(qtbot):
-    """Test the update checking for a version that needs an update."""
+    """Test we offer updates for lower versions."""
     worker = WorkerUpdates(None, False, version="1.0.0")
     worker.start()
     assert worker.update_available
-    assert check_version("1.0.0", worker.latest_release, '<')
 
 
-def test_not_update(qtbot):
-    """Test the update checking for a version that don't needs an update."""
+def test_no_update(qtbot):
+    """Test we don't offer updates for very high versions."""
     worker = WorkerUpdates(None, False, version="1000.0.0")
     worker.start()
     assert not worker.update_available
-    assert not check_version("1000.0.0", worker.latest_release, '<')
+
+
+def test_no_update_development(qtbot):
+    """Test we don't offer updates for development versions."""
+    worker = WorkerUpdates(None, False, version="3.3.2.dev0",
+                           releases=['3.3.1'])
+    worker.start()
+    assert not worker.update_available
+
+
+def test_update_pre_to_pre(qtbot):
+    """Test we offer updates between prereleases."""
+    worker = WorkerUpdates(None, False, version="4.0.0a1",
+                           releases=['4.0.0b5'])
+    worker.start()
+    assert worker.update_available
+
+
+def test_update_pre_to_final(qtbot):
+    """Test we offer updates from prereleases to the final versions."""
+    worker = WorkerUpdates(None, False, version="4.0.0b3",
+                           releases=['4.0.0'])
+    worker.start()
+    assert worker.update_available
+
+
+@pytest.mark.skipif(not is_anaconda(),
+                    reason='It only makes sense for Anaconda.')
+def test_releases_anaconda(qtbot):
+    """Test we don't include spyder-kernels releases in detected releases."""
+    worker = WorkerUpdates(None, False, version="3.3.1")
+    worker.start()
+    assert '0.2.4' not in worker.releases
 
 
 if __name__ == "__main__":