Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions spyder/plugins/updatemanager/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# -*- 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 os
from pathlib import Path
import subprocess as sp
import sys

# Local imports
from spyder.config.base import is_conda_based_app


def get_updater_info() -> (str, str):
"""
Get spyder-updater info

Returns
-------
updater_exe : str
Full path to the spyder-updater executable. This may not exist.
version : str
Version of spyder-updater. If not installed, defaults to "0.0.0"
"""
real_pyexec = Path(sys.executable).resolve()
env_dir = real_pyexec.parent.parent
if os.name == 'nt':
updater_exe = (
env_dir / "spyder-updater" / "Scripts" / "spyder-updater.exe"
)
else:
env_dir = env_dir.parent
updater_exe = env_dir / "spyder-updater" / "bin" / "spyder-updater"

version = "0.0.0" # Not installed
if is_conda_based_app() and updater_exe.exists():
cmd = " ".join([str(updater_exe), "--version"])
proc = sp.run(cmd, shell=True, text=True, capture_output=True)
if proc.returncode == 0:
version = proc.stdout

return str(updater_exe), version
5 changes: 5 additions & 0 deletions spyder/plugins/updatemanager/widgets/status.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
CHECKING,
DOWNLOAD_FINISHED,
DOWNLOADING_INSTALLER,
UPDATING_UPDATER,
INSTALL_ON_CLOSE,
NO_STATUS,
PENDING
Expand Down Expand Up @@ -88,6 +89,10 @@ def set_value(self, value):
self.tooltip = value
self.custom_widget.hide()
self.show()
elif value == UPDATING_UPDATER:
self.tooltip = value
self.custom_widget.hide()
self.show()
else:
self.tooltip = ""
if self.custom_widget:
Expand Down
102 changes: 101 additions & 1 deletion spyder/plugins/updatemanager/widgets/update.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"""Update Manager widgets."""

# Standard library imports
import json
import logging
import os
import os.path as osp
Expand All @@ -25,12 +26,17 @@
from spyder.api.config.mixins import SpyderConfigurationAccessor
from spyder.api.translations import _
from spyder.config.base import is_conda_based_app
from spyder.config.gui import is_dark_interface
from spyder.plugins.updatemanager.workers import (
UPDATER_PATH,
UpdateType,
validate_download,
WorkerUpdate,
WorkerUpdateUpdater,
WorkerDownloadInstaller
)
from spyder.utils.conda import find_conda, is_anaconda_pkg
from spyder.utils.palette import SpyderPalette
from spyder.utils.programs import get_temp_dir, is_program_installed
from spyder.widgets.helperwidgets import MessageCheckBox

Expand All @@ -39,6 +45,7 @@

# Update manager process statuses
NO_STATUS = __version__
UPDATING_UPDATER = _("Updating Spyder-updater")
DOWNLOADING_INSTALLER = _("Downloading update")
DOWNLOAD_FINISHED = _("Download finished")
PENDING = _("Update available")
Expand Down Expand Up @@ -135,6 +142,9 @@ def __init__(self, parent):
self.update_timer = None
self.asset_info = None

self.update_updater_thread = None
self.update_updater_worker = None

self.cancelled = False
self.download_thread = None
self.download_worker = None
Expand Down Expand Up @@ -313,7 +323,38 @@ def start_update(self):
self, msg, _('Spyder update'), version=version, checkbox=True
)
if box.result() == QMessageBox.Yes:
self._start_download()
if self.asset_info["update_type"] == UpdateType.Major:
self._start_download()
else:
self._start_update_updater()

def _start_update_updater(self):
"""Check for and install updates for Spyder-updater."""
self.sig_disable_actions.emit(True)
self.set_status(UPDATING_UPDATER)

self.update_updater_thread = QThread(None)
self.update_updater_worker = WorkerUpdateUpdater(
self.get_conf('check_stable_only')
)
self.update_updater_worker.sig_exception_occurred.connect(
self.sig_exception_occurred
)

self.update_updater_worker.sig_ready.connect(
lambda x: self._start_download() if x else None
)
self.update_updater_worker.sig_ready.connect(
self.update_updater_thread.quit
)
self.update_updater_worker.sig_ready.connect(
lambda: self.sig_disable_actions.emit(False)
)
self.update_updater_worker.moveToThread(self.update_updater_thread)
self.update_updater_thread.started.connect(
self.update_updater_worker.start
)
self.update_updater_thread.start()

def _start_download(self):
"""
Expand Down Expand Up @@ -427,7 +468,13 @@ def _confirm_install(self):

def start_install(self):
"""Install from downloaded installer or update through conda."""
if self.asset_info["update_type"] == UpdateType.Major:
self._start_major_install()
else:
self._start_updater()

def _start_major_install(self):
"""Install major update from downloaded installer"""
# Install script
# Copy to temp location to be safe
script_name = 'install.' + ('bat' if os.name == 'nt' else 'sh')
Expand Down Expand Up @@ -474,6 +521,59 @@ def start_install(self):

subprocess.Popen(' '.join(cmd), shell=True)

def _start_updater(self):
"""Start updater application."""
if self.get_conf('high_dpi_custom_scale_factor', section='main'):
scale_factors = self.get_conf(
'high_dpi_custom_scale_factors',
section='main'
)
scale_factor = float(scale_factors.split(";")[0])
else:
scale_factor = 1

info = {
"install_file": self.installer_path,
"conda_exec": find_conda(),
"env_path": sys.prefix,
"update_type": self.asset_info["update_type"],
"window_title": _("Spyder update"),
"scale_factor": scale_factor,
"initial_message": _(
"Updating Spyder, this will take a few minutes ..."
),
"success_message": _(
"The update was succesful!<br>Spyder will be launched shortly"
),
"failure_message": _("Unfortunately the update failed"),
"error_message": _("There was an error in the update process"),
"details_title": _("Show details"),
"font_family": self.get_conf(
"app_font/family", section="appearance"
),
"font_size": int(
self.get_conf("app_font/size", section="appearance")
),
"monospace_font_family": self.get_conf(
"monospace_app_font/family", section="appearance"
),
"monospace_font_size": int(
self.get_conf("monospace_app_font/size", section="appearance")
),
"interface_theme": "dark" if is_dark_interface() else "light",
"icon_color": SpyderPalette.ICON_1,
}

info_file = osp.join(
osp.dirname(self.installer_path), "update-info.json"
)
with open(info_file, "w") as f:
json.dump(info, f, indent=4)

# Launch updater
cmd = [UPDATER_PATH, "--update-info-file", info_file]
subprocess.Popen(" ".join(cmd), shell=True)


class UpdateMessageBox(QMessageBox):
def __init__(self, icon=None, text=None, parent=None):
Expand Down
Loading
Loading