Skip to content

Commit

Permalink
Merge pull request #22598 from mrclary/update-manager
Browse files Browse the repository at this point in the history
PR: Check for asset availability when checking for updates
  • Loading branch information
ccordoba12 authored Oct 16, 2024
2 parents a857397 + 24e8366 commit f9dfab7
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 80 deletions.
63 changes: 52 additions & 11 deletions spyder/plugins/updatemanager/tests/test_update_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@

import os
import logging
from packaging.version import parse

import pytest

from spyder.config.base import running_in_ci
from spyder.plugins.updatemanager import workers
from spyder.plugins.updatemanager.workers import WorkerUpdate, HTTP_ERROR_MSG
from spyder.plugins.updatemanager.workers import (
UpdateType, get_asset_info, WorkerUpdate, HTTP_ERROR_MSG
)
from spyder.plugins.updatemanager.widgets import update
from spyder.plugins.updatemanager.widgets.update import UpdateManagerWidget

Expand Down Expand Up @@ -49,7 +52,7 @@ def test_updates(qtbot, mocker, caplog, version, channel):
mocker.patch.object(
UpdateManagerWidget, "start_update", new=lambda x: None
)
mocker.patch.object(workers, "__version__", new=version)
mocker.patch.object(workers, "CURRENT_VERSION", new=parse(version))
mocker.patch.object(
workers, "get_spyder_conda_channel", return_value=channel
)
Expand Down Expand Up @@ -77,25 +80,63 @@ def test_updates(qtbot, mocker, caplog, version, channel):
assert update_available
else:
assert not update_available
assert len(um.update_worker.releases) >= 1


@pytest.mark.parametrize("release", ["4.0.1", "4.0.1a1"])
@pytest.mark.parametrize("release", ["6.0.0", "6.0.0b3"])
@pytest.mark.parametrize("version", ["4.0.0a1", "4.0.0"])
@pytest.mark.parametrize("stable_only", [True, False])
def test_update_non_stable(qtbot, mocker, version, release, stable_only):
"""Test we offer unstable updates."""
mocker.patch.object(workers, "__version__", new=version)
mocker.patch.object(workers, "CURRENT_VERSION", new=parse(version))

release = parse(release)
worker = WorkerUpdate(stable_only)
worker.releases = [release]
worker._check_update_available()
worker._check_update_available([release])

update_available = worker.update_available
if "a" in release and stable_only:
assert not update_available
if release.is_prerelease and stable_only:
assert not worker.update_available
else:
assert update_available
assert worker.update_available


@pytest.mark.parametrize("version", ["4.0.0", "6.0.0"])
def test_update_no_asset(qtbot, mocker, version):
"""Test update availability when asset is not available"""
mocker.patch.object(workers, "CURRENT_VERSION", new=parse(version))

releases = [parse("6.0.1"), parse("6.100.0")]
worker = WorkerUpdate(True)
worker._check_update_available(releases)

# For both values of version, there should be an update available
# However, the available version should be 6.0.1, since there is
# no asset for 6.100.0
assert worker.update_available
assert worker.latest_release == releases[0]


@pytest.mark.parametrize(
"release,update_type",
[
("6.0.1", UpdateType.Micro),
("6.1.0", UpdateType.Minor),
("7.0.0", UpdateType.Major)
]
)
@pytest.mark.parametrize("app", [True, False])
def test_get_asset_info(qtbot, mocker, release, update_type, app):
mocker.patch.object(workers, "CURRENT_VERSION", new=parse("6.0.0"))
mocker.patch.object(workers, "is_conda_based_app", return_value=app)

info = get_asset_info(release)
assert info['update_type'] == update_type

if update_type == "major" or not app:
assert info['url'].endswith(('.exe', '.pkg', '.sh'))
assert info['filename'].endswith(('.exe', '.pkg', '.sh'))
else:
assert info['url'].endswith(".zip")
assert info['filename'].endswith(".zip")


# ---- Test WorkerDownloadInstaller
Expand Down
49 changes: 16 additions & 33 deletions spyder/plugins/updatemanager/widgets/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@
import logging
import os
import os.path as osp
import platform
import shutil
import subprocess
import sys
from sysconfig import get_path

# Third-party imports
from packaging.version import parse
from qtpy.QtCore import Qt, QThread, QTimer, Signal
from qtpy.QtWidgets import QMessageBox, QWidget, QProgressBar, QPushButton
from spyder_kernels.utils.pythonenv import is_conda_env
Expand All @@ -28,6 +26,7 @@
from spyder.api.translations import _
from spyder.config.base import is_conda_based_app
from spyder.plugins.updatemanager.workers import (
get_asset_info,
WorkerUpdate,
WorkerDownloadInstaller
)
Expand Down Expand Up @@ -149,7 +148,7 @@ def __init__(self, parent):

def set_status(self, status=NO_STATUS):
"""Set the update manager status."""
self.sig_set_status.emit(status, self.latest_release)
self.sig_set_status.emit(status, str(self.latest_release))

def cleanup_threads(self):
"""Clean up QThreads"""
Expand Down Expand Up @@ -251,28 +250,11 @@ def _process_check_update(self):

def _set_installer_path(self):
"""Set the temp file path for the downloaded installer."""
if parse(__version__).major < parse(self.latest_release).major:
self.update_type = 'major'
elif parse(__version__).minor < parse(self.latest_release).minor:
self.update_type = 'minor'
else:
self.update_type = 'micro'

mach = platform.machine().lower().replace("amd64", "x86_64")

if self.update_type == 'major' or not is_conda_based_app():
if os.name == 'nt':
plat, ext = 'Windows', 'exe'
if sys.platform == 'darwin':
plat, ext = 'macOS', 'pkg'
if sys.platform.startswith('linux'):
plat, ext = 'Linux', 'sh'
fname = f'Spyder-{plat}-{mach}.{ext}'
else:
fname = 'spyder-conda-lock.zip'
asset_info = get_asset_info(self.latest_release)
self.update_type = asset_info['update_type']

dirname = osp.join(get_temp_dir(), 'updates', self.latest_release)
self.installer_path = osp.join(dirname, fname)
dirname = osp.join(get_temp_dir(), 'updates', str(self.latest_release))
self.installer_path = osp.join(dirname, asset_info['filename'])
self.installer_size_path = osp.join(dirname, "size")

logger.info(f"Update type: {self.update_type}")
Expand Down Expand Up @@ -472,14 +454,15 @@ def start_install(self):
if os.name == 'nt':
cmd = ['start', '"Update Spyder"'] + sub_cmd
elif sys.platform == 'darwin':
# Terminal cannot accept a command with arguments therefore
# create a temporary script
tmpscript = osp.join(get_temp_dir(), 'tmp_install.sh')
with open(tmpscript, 'w') as f:
f.write(' '.join(sub_cmd))
os.chmod(tmpscript, 0o711) # set executable permissions

cmd = ['open', '-b', 'com.apple.terminal', tmpscript]
# Terminal cannot accept a command with arguments. Creating a
# wrapper script pollutes the shell history. Best option is to
# use osascript
sub_cmd_str = ' '.join(sub_cmd)
cmd = [
"osascript", "-e",
("""'tell application "Terminal" to do script"""
f""" "set +o history; {sub_cmd_str}; exit;"'"""),
]
else:
programs = [
{'cmd': 'gnome-terminal', 'exe-opt': '--window --'},
Expand Down Expand Up @@ -629,7 +612,7 @@ def manual_update_messagebox(parent, latest_release, channel):
).format(dont_mix_pip_conda_video)
else:
if channel == 'pkgs/main':
channel = ''
channel = '-c defaults'
else:
channel = f'-c {channel}'

Expand Down
Loading

0 comments on commit f9dfab7

Please sign in to comment.