Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR: Add plugin teardown operations to the plugin registry #16012

Merged
merged 68 commits into from
Oct 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
70434f6
Add plugin teardown operations to the plugin registry
andfoy Oct 5, 2021
9081b6c
Use the teardown mechanism when closing Spyder
andfoy Oct 5, 2021
3ddada2
Remove direct ipythonconsole reference from the editor
andfoy Oct 5, 2021
55b8a43
Prevent hard reference to the editor on the mainwindow
andfoy Oct 5, 2021
5d595c7
Check another reference to the editor in the mainwindow
andfoy Oct 5, 2021
f47e146
Restore toolbar plugin on_close
andfoy Oct 5, 2021
cee776e
Prevent AttributeError during tests related to layouts
andfoy Oct 5, 2021
700ae02
Apply review comments
andfoy Oct 5, 2021
1559bc2
Add deprecation warning for unregister
andfoy Oct 5, 2021
1d4fb82
Remove call to unregister
andfoy Oct 5, 2021
679350b
Remove unregister from preferences
andfoy Oct 5, 2021
9941b36
Start application migration to use the teardown mechanism
andfoy Oct 5, 2021
8bd3f9b
Enable tests in forks
andfoy Oct 5, 2021
ef2741f
Disconnect console and shortcuts
andfoy Oct 5, 2021
5df20c5
Add remove_item_from_application_menu method in main menu
andfoy Oct 5, 2021
2984c6d
Disconnect from main menu
andfoy Oct 5, 2021
8175a43
Prevent non-existing action removal
andfoy Oct 5, 2021
06e8ab6
Apply review comments
andfoy Oct 5, 2021
1603da3
Remove shortcuts teardown method
andfoy Oct 5, 2021
34ae1da
Migrate breakpoints to use the new teardown mechanism
andfoy Oct 5, 2021
521400b
Update remove_item_from_application_menu call
andfoy Oct 5, 2021
be435d9
Migrate completions plugin to use the teardown mechanism
andfoy Oct 5, 2021
cdae372
Remove kite-defined actions
andfoy Oct 5, 2021
88f0a27
Fix minor issue in Kite
andfoy Oct 5, 2021
35900d4
Reimplement can_close
andfoy Oct 5, 2021
d716e8a
Update remove_item_from_application_menu calls
andfoy Oct 5, 2021
6896462
Add remove_application_menu to main menu
andfoy Oct 5, 2021
e0fbc17
Remove unregister method
andfoy Oct 5, 2021
def4e71
Migrate Console, Explorer and Find in files to use the new teardown m…
andfoy Oct 5, 2021
f55385d
Minor typo correction
andfoy Oct 5, 2021
5927dcb
Prevent non-existinng search_menu_actions list
andfoy Oct 5, 2021
0f1bead
Update calls to remove_item_from_application_menu
andfoy Oct 5, 2021
306d6db
Migrate help to use the new teardown mechanism
andfoy Oct 5, 2021
6bdcb1c
Update call to remove_item_from_application_menu
andfoy Oct 5, 2021
8f1e97c
Apply review comments
andfoy Oct 5, 2021
b377a97
Migrate history and layouts to use the new teardown mmechanism
andfoy Oct 5, 2021
efd40ac
Update calls to main menu and toolbar to use ids
andfoy Oct 5, 2021
0a3305b
Address review comments
andfoy Oct 5, 2021
cd99948
Migrate Outline explorer, plots, preferences and profiler to use the …
andfoy Oct 5, 2021
6dcaa6b
Update profiler calls to remove_item_from_application_menu
andfoy Oct 5, 2021
dd784a8
Address review comments
andfoy Oct 5, 2021
a426c07
Migrate projects to use the new teardown mechanism
andfoy Oct 5, 2021
34c0933
Update calls to remove_item_from_application_menu
andfoy Oct 5, 2021
5292949
Address review comments
andfoy Oct 5, 2021
ea4824f
Address further review comments
andfoy Oct 5, 2021
bab393e
Minor error correction
andfoy Oct 5, 2021
b968e57
Restore sig_stop_completions connection
andfoy Oct 5, 2021
ce5931c
Migrate pylint, run and shortcuts to use the new teardown mechanism
andfoy Oct 5, 2021
05baf63
Remove hard reference to editor in pylint
andfoy Oct 5, 2021
1b007b2
Update calls to remove_item_from_application_menu
andfoy Oct 5, 2021
b7831d8
Address review comments
andfoy Oct 5, 2021
cd8dcac
Final review comments
andfoy Oct 5, 2021
739b5e4
Migrate statusbar, toolbar and tours to use the new teardown mechanism
andfoy Oct 5, 2021
66ea40b
Update calls to remove_item_from_application_menu
andfoy Oct 5, 2021
65833aa
Address review comments
andfoy Oct 5, 2021
bbc8d53
Add proper signature to statusbar on_close method
andfoy Oct 5, 2021
a8dbe84
Address review comments
andfoy Oct 5, 2021
b93c040
Migrate variable explorer and working directory to use the new teardo…
andfoy Oct 5, 2021
8647928
Update calls to remove_item_from_application_menu
andfoy Oct 5, 2021
80b47d5
Apply review comments
andfoy Oct 5, 2021
1a743ed
Remove duplicate on__close
andfoy Oct 5, 2021
e14b5ac
Add preference page to enable and disable plugins
andfoy Oct 5, 2021
bff2395
Restart Spyder after a plugin is enabled/disabled
andfoy Oct 5, 2021
d01d43b
Disable also Spyder 4 plugins
andfoy Oct 5, 2021
ca21fff
Fix test_preferences_checkboxes_not_checked_regression
andfoy Oct 5, 2021
8361e14
Address review comments
andfoy Oct 5, 2021
84747b2
Always add external plugin metadata
andfoy Oct 6, 2021
0f34e0c
Revert Github Actions changes
andfoy Oct 6, 2021
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
117 changes: 117 additions & 0 deletions spyder/api/plugin_registration/_confpage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
#
# Copyright © Spyder Project Contributors
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)

"""Plugin registry configuration page."""

# Third party imports
from qtpy.QtWidgets import (QGroupBox, QVBoxLayout, QCheckBox,
QGridLayout, QLabel)

# Local imports
from spyder.api.plugins import SpyderPlugin
from spyder.api.preferences import PluginConfigPage
from spyder.config.base import _
from spyder.config.manager import CONF


class PluginsConfigPage(PluginConfigPage):
def setup_page(self):
newcb = self.create_checkbox
self.plugins_checkboxes = {}

header_label = QLabel(
_("Here you can turn on/off any internal or external Spyder plugin "
"to disable functionality that is not desired or to have a lighter "
"experience. Unchecked plugins in this page will be unloaded "
"immediately and will not be loaded the next time Spyder starts."))
header_label.setWordWrap(True)

# ------------------ Internal plugin status group ---------------------
internal_layout = QGridLayout()
self.internal_plugins_group = QGroupBox(_("Internal plugins"))

i = 0
for plugin_name in self.plugin.all_internal_plugins:
(conf_section_name,
PluginClass) = self.plugin.all_internal_plugins[plugin_name]

if not getattr(PluginClass, 'CAN_BE_DISABLED', True):
# Do not list core plugins that can not be disabled
continue

plugin_loc_name = None
if hasattr(PluginClass, 'get_name'):
plugin_loc_name = PluginClass.get_name()
elif hasattr(PluginClass, 'get_plugin_title'):
plugin_loc_name = PluginClass.get_plugin_title()

plugin_state = CONF.get(conf_section_name, 'enable', True)
cb = newcb(plugin_loc_name, 'enable', default=True,
section=conf_section_name, restart=True)
internal_layout.addWidget(cb, i // 2, i % 2)
self.plugins_checkboxes[plugin_name] = (cb, plugin_state)
i += 1

self.internal_plugins_group.setLayout(internal_layout)

# ------------------ External plugin status group ---------------------
external_layout = QGridLayout()
self.external_plugins_group = QGroupBox(_("External plugins"))

i = 0
for i, plugin_name in enumerate(self.plugin.all_external_plugins):
(conf_section_name,
PluginClass) = self.plugin.all_external_plugins[plugin_name]

plugin_loc_name = None
if hasattr(PluginClass, 'get_name'):
plugin_loc_name = PluginClass.get_name()
elif hasattr(PluginClass, 'get_plugin_title'):
plugin_loc_name = PluginClass.get_plugin_title()

cb = newcb(plugin_loc_name, 'enable', default=True,
section=conf_section_name, restart=True)
external_layout.addWidget(cb, i // 2, i % 2)
self.plugins_checkboxes[plugin_name] = cb
i += 1

self.external_plugins_group.setLayout(external_layout)

layout = QVBoxLayout()
layout.addWidget(header_label)
layout.addWidget(self.internal_plugins_group)
if self.plugin.all_external_plugins:
layout.addWidget(self.external_plugins_group)
layout.addStretch(1)
self.setLayout(layout)

def apply_settings(self):
for plugin_name in self.plugins_checkboxes:
cb, previous_state = self.plugins_checkboxes[plugin_name]
if cb.isChecked() and not previous_state:
self.plugin.set_plugin_enabled(plugin_name)
PluginClass = None
external = False
if plugin_name in self.plugin.all_internal_plugins:
(__,
PluginClass) = self.plugin.all_internal_plugins[plugin_name]
elif plugin_name in self.plugin.all_external_plugins:
(__,
PluginClass) = self.plugin.all_external_plugins[plugin_name]
external = True

# TODO: Once we can test that all plugins can be restarted
# without problems during runtime, we can enable the
# autorestart feature provided by the plugin registry:
# self.plugin.register_plugin(self.main, PluginClass,
# external=external)
elif not cb.isChecked() and previous_state:
# TODO: Once we can test that all plugins can be restarted
# without problems during runtime, we can enable the
# autorestart feature provided by the plugin registry:
# self.plugin.delete_plugin(plugin_name)
pass
return set({})
35 changes: 35 additions & 0 deletions spyder/api/plugin_registration/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,38 @@ def on_plugin_available(func: Callable = None,

func._plugin_listen = plugin
return func


def on_plugin_teardown(func: Callable = None,
plugin: Optional[str] = None):
"""
Method decorator used to handle plugin teardown on Spyder.
This decorator will be called **before** the specified plugin is deleted
and also **before** the plugin that uses the decorator is destroyed.
The methods that use this decorator must have the following signature:
`def method(self)`.
Parameters
----------
func: Callable
Method to decorate. Given by default when applying the decorator.
plugin: str
Name of the requested plugin whose teardown triggers the method.
Returns
-------
func: Callable
The same method that was given as input.
"""
if func is None:
return functools.partial(on_plugin_teardown, plugin=plugin)

if plugin is None:
raise ValueError('on_plugin_teardown must have a well defined '
'plugin keyword argument value, '
'e.g., plugin=Plugins.Editor')

func._plugin_teardown = plugin
return func
24 changes: 24 additions & 0 deletions spyder/api/plugin_registration/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,20 @@ class SpyderPluginObserver:

def __init__(self):
self._plugin_listeners = {}
self._plugin_teardown_listeners = {}
for method_name in dir(self):
method = getattr(self, method_name, None)
if hasattr(method, '_plugin_listen'):
info = method._plugin_listen
logger.debug(f'Method {method_name} is watching plugin {info}')
self._plugin_listeners[info] = method_name

if hasattr(method, '_plugin_teardown'):
info = method._plugin_teardown
logger.debug(f'Method {method_name} will handle plugin '
f'teardown for {info}')
self._plugin_teardown_listeners[info] = method_name

def _on_plugin_available(self, plugin: str):
"""
Handle plugin availability and redirect it to plugin-specific
Expand All @@ -63,3 +70,20 @@ def _on_plugin_available(self, plugin: str):
method_name = self._plugin_listeners['__all']
method = getattr(self, method_name)
method(plugin)

def _on_plugin_teardown(self, plugin: str):
"""
Handle plugin teardown and redirect it to plugin-specific teardown
handlers.
Parameters
----------
plugin: str
Name of the plugin that is going through its teardown process.
"""
# Call plugin specific handler
if plugin in self._plugin_teardown_listeners:
method_name = self._plugin_teardown_listeners[plugin]
method = getattr(self, method_name)
logger.debug(f'Calling {method}')
method()
Loading