Skip to content

Commit

Permalink
Initial commit for Update Manager Plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
mrclary committed Nov 1, 2023
1 parent 06d0daf commit e5bcfb7
Show file tree
Hide file tree
Showing 17 changed files with 1,616 additions and 2 deletions.
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ def run(self):
'switcher = spyder.plugins.switcher.plugin:Switcher',
'toolbar = spyder.plugins.toolbar.plugin:Toolbar',
'tours = spyder.plugins.tours.plugin:Tours',
'update_manager = spyder.plugins.updatemanager.plugin:UpdateManager',
'variable_explorer = spyder.plugins.variableexplorer.plugin:VariableExplorer',
'workingdir = spyder.plugins.workingdirectory.plugin:WorkingDirectory',
]
Expand Down
1 change: 1 addition & 0 deletions spyder/api/plugins/enum.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Plugins:
Switcher = 'switcher'
Toolbar = "toolbar"
Tours = 'tours'
UpdateManager = 'update_manager'
VariableExplorer = 'variable_explorer'
WorkingDirectory = 'workingdir'

Expand Down
5 changes: 5 additions & 0 deletions spyder/config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@
'report_error/remember_token': False,
'show_dpi_message': True,
}),
('update_manager',
{
'check_updates_on_startup': True,
'check_stable_only': True,
}),
('toolbar',
{
'enable': True,
Expand Down
4 changes: 2 additions & 2 deletions spyder/plugins/statusbar/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class StatusBar(SpyderPluginV2):
'clock_status', 'cpu_status', 'memory_status', 'read_write_status',
'eol_status', 'encoding_status', 'cursor_position_status',
'vcs_status', 'lsp_status', 'completion_status',
'interpreter_status'}
'interpreter_status', 'update_manager_status'}

# ---- SpyderPluginV2 API
@staticmethod
Expand Down Expand Up @@ -216,7 +216,7 @@ def _organize_status_widgets(self):
'clock_status', 'cpu_status', 'memory_status', 'read_write_status',
'eol_status', 'encoding_status', 'cursor_position_status',
'vcs_status', 'lsp_status', 'completion_status',
'interpreter_status']
'interpreter_status', 'update_manager_status']
external_left = list(self.EXTERNAL_LEFT_WIDGETS.keys())

# Remove all widgets from the statusbar, except the external right
Expand Down
12 changes: 12 additions & 0 deletions spyder/plugins/updatemanager/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""
spyder.plugins.updatemanager
============================
Application Update Manager Plugin.
"""
11 changes: 11 additions & 0 deletions spyder/plugins/updatemanager/api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""
Spyder update manager API.
"""

from spyder.plugins.updatemanager.container import UpdateManagerActions
42 changes: 42 additions & 0 deletions spyder/plugins/updatemanager/confpage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""
General entry in Preferences.
For historical reasons (dating back to Spyder 2) the main class here is called
`MainConfigPage` and its associated entry in our config system is called
`main`.
"""

from qtpy.QtWidgets import QGroupBox, QVBoxLayout

from spyder.config.base import _
from spyder.api.preferences import PluginConfigPage


class UpdateManagerConfigPage(PluginConfigPage):
def setup_page(self):
"""Setup config page widgets and options."""
updates_group = QGroupBox(_("Updates"))
check_updates = self.create_checkbox(
_("Check for updates on startup"),
'check_updates_on_startup'
)
stable_only = self.create_checkbox(
_("Check for stable releases only"),
'check_stable_only'
)

updates_layout = QVBoxLayout()
updates_layout.addWidget(check_updates)
updates_layout.addWidget(stable_only)
updates_group.setLayout(updates_layout)

vlayout = QVBoxLayout()
vlayout.addWidget(updates_group)
vlayout.addStretch(1)
self.setLayout(vlayout)
105 changes: 105 additions & 0 deletions spyder/plugins/updatemanager/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""
Container Widget.
Holds references for base actions in the Application of Spyder.
"""

# Standard library imports
import logging

# Third party imports
from qtpy.QtCore import Slot

# Local imports
from spyder.api.translations import _
from spyder.api.widgets.main_container import PluginMainContainer
from spyder.plugins.updatemanager.widgets.status import UpdateManagerStatus
from spyder.plugins.updatemanager.widgets.update import (
UpdateManagerWidget, NO_STATUS
)
from spyder.utils.qthelpers import DialogManager

# Logger setup
logger = logging.getLogger(__name__)


# Actions
class UpdateManagerActions:
SpyderCheckUpdateAction = "spyder_check_update_action"


class UpdateManagerContainer(PluginMainContainer):

def __init__(self, name, plugin, parent=None):
super().__init__(name, plugin, parent)

self.install_on_close = False

def setup(self):
self.dialog_manager = DialogManager()
self.update_manager = UpdateManagerWidget(parent=self)
self.update_manager_status = UpdateManagerStatus(parent=self)

# Actions
self.check_update_action = self.create_action(
UpdateManagerActions.SpyderCheckUpdateAction,
_("Check for updates..."),
triggered=self.start_check_update
)

# Signals
self.update_manager.sig_set_status.connect(self.set_status)
self.update_manager.sig_disable_actions.connect(
self.check_update_action.setDisabled)
self.update_manager.sig_block_status_signals.connect(
self.update_manager_status.blockSignals)
self.update_manager.sig_download_progress.connect(
self.update_manager_status.set_download_progress)
self.update_manager.sig_install_on_close.connect(
self.set_install_on_close)
self.update_manager.sig_quit_requested.connect(self.sig_quit_requested)

self.update_manager_status.sig_check_update.connect(
self.start_check_update)
self.update_manager_status.sig_start_update.connect(self.start_update)
self.update_manager_status.sig_show_progress_dialog.connect(
self.update_manager.show_progress_dialog)

self.set_status(NO_STATUS)

def update_actions(self):
pass

def set_status(self, status, latest_version=None):
"""Set Update Manager status"""
self.update_manager_status.set_value(status)

@Slot()
def start_check_update(self, startup=False):
"""Check for spyder updates."""
self.update_manager.start_check_update(startup=startup)

@Slot()
def start_update(self):
"""Start the update process"""
self.update_manager.start_update()

def set_install_on_close(self, install_on_close):
"""Set whether start install on close."""
self.install_on_close = install_on_close

def on_close(self):
"""To call from Spyder when the plugin is closed."""
self.update_manager.cleanup_threads()

# Run installer after Spyder is closed
if self.install_on_close:
self.update_manager.start_install()

self.dialog_manager.close_all()
131 changes: 131 additions & 0 deletions spyder/plugins/updatemanager/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""
Update Manager Plugin.
"""

# Local imports
from spyder.api.plugins import Plugins, SpyderPluginV2
from spyder.api.translations import _
from spyder.api.plugin_registration.decorators import (
on_plugin_available, on_plugin_teardown)
from spyder.config.base import DEV
from spyder.plugins.updatemanager.confpage import UpdateManagerConfigPage
from spyder.plugins.updatemanager.container import (UpdateManagerActions,
UpdateManagerContainer)
from spyder.plugins.mainmenu.api import ApplicationMenus, HelpMenuSections


class UpdateManager(SpyderPluginV2):
NAME = 'update_manager'
REQUIRES = [Plugins.Preferences]
OPTIONAL = [Plugins.Help, Plugins.MainMenu, Plugins.Shortcuts,
Plugins.StatusBar]
CONTAINER_CLASS = UpdateManagerContainer
CONF_SECTION = 'update_manager'
CONF_FILE = False
CONF_WIDGET_CLASS = UpdateManagerConfigPage
CAN_BE_DISABLED = False

@staticmethod
def get_name():
return _('Update Manager')

@classmethod
def get_icon(cls):
return cls.create_icon('genprefs')

@staticmethod
def get_description():
return _('Manage application updates.')

# --------------------- PLUGIN INITIALIZATION -----------------------------

def on_initialize(self):
pass

@on_plugin_available(plugin=Plugins.Preferences)
def on_preferences_available(self):
# Register conf page
preferences = self.get_plugin(Plugins.Preferences)
preferences.register_plugin_preferences(self)

@on_plugin_available(plugin=Plugins.MainMenu)
def on_main_menu_available(self):
if self.is_plugin_enabled(Plugins.Shortcuts):
if self.is_plugin_available(Plugins.Shortcuts):
self._populate_help_menu()
else:
self._populate_help_menu()

@on_plugin_available(plugin=Plugins.StatusBar)
def on_statusbar_available(self):
# Add status widget
statusbar = self.get_plugin(Plugins.StatusBar)
statusbar.add_status_widget(self.update_manager_status)

# -------------------------- PLUGIN TEARDOWN ------------------------------

@on_plugin_teardown(plugin=Plugins.StatusBar)
def on_statusbar_teardown(self):
# Remove status widget if created
statusbar = self.get_plugin(Plugins.StatusBar)
statusbar.remove_status_widget(self.update_manager_status.ID)

@on_plugin_teardown(plugin=Plugins.Preferences)
def on_preferences_teardown(self):
preferences = self.get_plugin(Plugins.Preferences)
preferences.deregister_plugin_preferences(self)

@on_plugin_teardown(plugin=Plugins.MainMenu)
def on_main_menu_teardown(self):
self._depopulate_help_menu()

def on_close(self, _unused=True):
# The container is closed directly in the plugin registry
pass

def on_mainwindow_visible(self):
"""Actions after the mainwindow in visible."""
container = self.get_container()

# Check for updates on startup
if DEV is None and self.get_conf('check_updates_on_startup'):
container.start_check_updates(startup=True)

# ---- Private API
# ------------------------------------------------------------------------
def _populate_help_menu(self):
"""Add update action and menu to the Help menu."""
mainmenu = self.get_plugin(Plugins.MainMenu)
mainmenu.add_item_to_application_menu(
self.check_update_action,
menu_id=ApplicationMenus.Help,
section=HelpMenuSections.Support,
before_section=HelpMenuSections.ExternalDocumentation)

@property
def _window(self):
return self.main.window()

def _depopulate_help_menu(self):
"""Remove update action from the Help main menu."""
mainmenu = self.get_plugin(Plugins.MainMenu)
mainmenu.remove_item_from_application_menu(
UpdateManagerActions.SpyderCheckUpdateAction,
menu_id=ApplicationMenus.Help)

# ---- Public API
# ------------------------------------------------------------------------
@property
def check_update_action(self):
"""Check if a new version of Spyder is available."""
return self.get_container().check_update_action

@property
def update_manager_status(self):
return self.get_container().update_manager_status
Loading

0 comments on commit e5bcfb7

Please sign in to comment.