Skip to content

Commit

Permalink
PR: Initial Editor migration to the new API (#21353)
Browse files Browse the repository at this point in the history
  • Loading branch information
dalthviz authored Mar 21, 2024
1 parent f64ff79 commit 00986c1
Show file tree
Hide file tree
Showing 51 changed files with 5,606 additions and 4,224 deletions.
3 changes: 2 additions & 1 deletion runtests.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def run_pytest(run_slow=False, extra_args=None):
"""Run pytest tests for Spyder."""
# Be sure to ignore subrepos
pytest_args = ['-vv', '-rw', '--durations=10', '--ignore=./external-deps',
'-W ignore::UserWarning', '--timeout=120']
'-W ignore::UserWarning', '--timeout=120',
'--timeout_method=thread']

if CI:
# Show coverage
Expand Down
26 changes: 10 additions & 16 deletions spyder/api/plugin_registration/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
from spyder.utils.icon_manager import ima


# TODO: Remove SpyderPlugin and SpyderPluginWidget once the migration
# is complete.
# TODO: Remove SpyderPlugin and SpyderPluginWidget now that the migration
# is complete
Spyder5PluginClass = Union[SpyderPluginV2, SpyderDockablePlugin]
Spyder4PluginClass = Union[SpyderPlugin, SpyderPluginWidget]
SpyderPluginClass = Union[Spyder4PluginClass, Spyder5PluginClass]
Expand Down Expand Up @@ -403,18 +403,12 @@ def can_delete_plugin(self, plugin_name: str) -> bool:
Returns
-------
plugin_deleted: bool
True if the plugin can be deleted. False otherwise.
can_close: bool
True if the plugin can be closed. False otherwise.
"""
plugin_instance = self.plugin_registry[plugin_name]
# Determine if plugin can be closed
can_delete = True
if isinstance(plugin_instance, SpyderPluginV2):
can_delete = plugin_instance.can_close()
elif isinstance(plugin_instance, SpyderPlugin):
can_delete = plugin_instance.closing_plugin(True)

return can_delete
return plugin_instance.can_close()

def dock_undocked_plugin(
self, plugin_name: str, save_undocked: bool = False):
Expand Down Expand Up @@ -598,11 +592,9 @@ def can_delete_all_plugins(self,
for plugin_name in (
set(self.external_plugins) | set(self.internal_plugins)):
if plugin_name not in excluding:
plugin_instance = self.plugin_registry[plugin_name]
if isinstance(plugin_instance, SpyderPlugin):
can_close &= self.can_delete_plugin(plugin_name)
if not can_close:
break
can_close &= self.can_delete_plugin(plugin_name)
if not can_close:
break

return can_close

Expand Down Expand Up @@ -794,6 +786,8 @@ def reset(self):
# Omit failures if there are no slots connected
pass

dependencies.DEPENDENCIES = []

def set_all_internal_plugins(
self, all_plugins: Dict[str, Type[SpyderPluginClass]]):
self.all_internal_plugins = all_plugins
Expand Down
6 changes: 3 additions & 3 deletions spyder/api/plugins/new_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,9 @@ def __init__(self, parent, configuration=None):
parent=parent
)

if hasattr(container, '_setup'):
container._setup()

if isinstance(container, SpyderWidgetMixin):
container.setup()
container.update_actions()
Expand All @@ -344,9 +347,6 @@ def __init__(self, parent, configuration=None):

self.after_container_creation()

if hasattr(container, '_setup'):
container._setup()

# Load the custom images of the plugin
if self.IMG_PATH:
plugin_path = osp.join(self.get_path(), self.IMG_PATH)
Expand Down
5 changes: 5 additions & 0 deletions spyder/api/widgets/menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ def add_action(self: T,
item_id = None
if isinstance(action, SpyderAction) or hasattr(action, 'action_id'):
item_id = action.action_id

# This is necessary when we set a menu for `action`, e.g. for
# todo_list_action in EditorMainWidget.
if action.menu() and isinstance(action.menu(), SpyderMenu):
action.menu()._is_submenu = True
elif isinstance(action, SpyderMenu) or hasattr(action, 'menu_id'):
item_id = action.menu_id
action._is_submenu = True
Expand Down
6 changes: 5 additions & 1 deletion spyder/api/widgets/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,8 @@ def _update_action_state(self, action_name, value):
# other it refers to a section of the configuration (or the widget
# name where it is applied).
def create_action(self, name, text, icon=None, icon_text='', tip=None,
toggled=None, triggered=None, shortcut_context=None,
toggled=None, triggered=None, data=None,
shortcut_context=None,
context=Qt.WidgetWithChildrenShortcut, initial=None,
register_shortcut=False, section=None, option=None,
parent=None, register_action=True, overwrite=False,
Expand All @@ -461,6 +462,8 @@ def create_action(self, name, text, icon=None, icon_text='', tip=None,
behave like a checkbox.
triggered: callable
The callable to use when triggering this action.
data: Any
Data to be set on the action.
shortcut_context: str
Set the `str` context of the shortcut.
context: Qt.ShortcutContext
Expand Down Expand Up @@ -528,6 +531,7 @@ def create_action(self, name, text, icon=None, icon_text='', tip=None,
tip=tip,
toggled=toggled,
triggered=triggered,
data=data,
context=context,
section=section,
option=option,
Expand Down
22 changes: 12 additions & 10 deletions spyder/api/widgets/toolbars.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ def add_item(self, action_or_widget: ToolbarItem,
item_id = action_or_widget.action_id
elif hasattr(action_or_widget, 'ID'):
item_id = action_or_widget.ID

if not omit_id and item_id is None and action_or_widget is not None:
raise SpyderAPIError(
f'Item {action_or_widget} must declare an ID attribute.')
Expand Down Expand Up @@ -219,15 +218,18 @@ def add_item(self, action_or_widget: ToolbarItem,

def remove_item(self, item_id: str):
"""Remove action or widget from toolbar by id."""
item = self._item_map.pop(item_id)
for section in list(self._section_items.keys()):
section_items = self._section_items[section]
if item in section_items:
section_items.remove(item)
if len(section_items) == 0:
self._section_items.pop(section)
self.clear()
self._render()
try:
item = self._item_map.pop(item_id)
for section in list(self._section_items.keys()):
section_items = self._section_items[section]
if item in section_items:
section_items.remove(item)
if len(section_items) == 0:
self._section_items.pop(section)
self.clear()
self._render()
except KeyError:
pass

def _render(self):
"""
Expand Down
28 changes: 15 additions & 13 deletions spyder/app/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,7 @@ def signal_handler(signum, frame=None):
self.paste_action = None
self.selectall_action = None

# TODO: Move to corresponding Plugins
self.file_toolbar = None
self.file_toolbar_actions = []

# TODO: Is this being used somewhere?
self.menus = []

if running_under_pytest():
Expand Down Expand Up @@ -801,14 +798,6 @@ def setup(self):

# Set window title
self.set_window_title()
self.set_splash("")

# Toolbars
# TODO: Remove after finishing the migration
logger.info("Creating toolbars...")
toolbar = self.toolbar
self.file_toolbar = toolbar.get_application_toolbar("file_toolbar")

self.set_splash(_("Setting up main window..."))

def __getattr__(self, attr):
Expand Down Expand Up @@ -1048,7 +1037,7 @@ def createPopupMenu(self):

def closeEvent(self, event):
"""closeEvent reimplementation"""
if self.closing(True):
if self.closing(cancelable=True):
event.accept()
else:
event.ignore()
Expand Down Expand Up @@ -1138,6 +1127,19 @@ def closing(self, cancelable=False, close_immediately=False):
if reply == QMessageBox.No:
return False

# Save current project files here to be sure we do it as expected in
# case the Editor is closed before Projects below.
projects = self.get_plugin(Plugins.Projects, error=False)
if projects and projects.get_active_project_path():
editor = self.get_plugin(Plugins.Editor, error=False)
if editor:
projects.set_project_filenames(
[
finfo.filename
for finfo in editor.get_widget().editorstacks[0].data
]
)

can_close = self.plugin_registry.delete_all_plugins(
excluding={Plugins.Layout},
close_immediately=close_immediately)
Expand Down
44 changes: 34 additions & 10 deletions spyder/app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import pytest

# Spyder imports
from spyder.api.plugin_registration.registry import PLUGIN_REGISTRY
from spyder.api.plugins import Plugins
from spyder.app import start
from spyder.config.base import get_home_dir, running_in_ci
Expand All @@ -35,7 +36,6 @@
from spyder.utils.environ import (get_user_env, set_user_env,
amend_user_shell_init)


# =============================================================================
# ---- Constants
# =============================================================================
Expand Down Expand Up @@ -242,7 +242,7 @@ def preferences_dialog_helper(qtbot, main_window, section):
def generate_run_parameters(mainwindow, filename, selected=None,
executor=None):
"""Generate run configuration parameters for a given filename."""
file_uuid = mainwindow.editor.id_per_file[filename]
file_uuid = mainwindow.editor.get_widget().id_per_file[filename]
if executor is None:
executor = mainwindow.ipyconsole.NAME

Expand Down Expand Up @@ -281,10 +281,19 @@ def close_window():
if hasattr(main_window, 'window') and main_window.window is not None:
window = main_window.window
main_window.window = None
window.closing(close_immediately=True)
window.close()
window = None
CONF.reset_to_defaults(notification=False)
CONF.reset_manager()
PLUGIN_REGISTRY.reset()

if qapp.instance():
for widget in qapp.allWidgets():
try:
widget.close()
except RuntimeError:
pass
qapp.quit()

request.addfinalizer(close_window)
Expand Down Expand Up @@ -370,9 +379,6 @@ def main_window(request, tmpdir, qtbot):
QApplication.processEvents()

if not hasattr(main_window, 'window') or main_window.window is None:
from spyder.api.plugin_registration.registry import PLUGIN_REGISTRY
PLUGIN_REGISTRY.reset()

# Start the window
window = start.main()
main_window.window = window
Expand Down Expand Up @@ -444,9 +450,13 @@ def main_window(request, tmpdir, qtbot):
print('info_page')
print(client.info_page)
main_window.window = None
window.closing(close_immediately=True)
window.close()
window = None
CONF.reset_to_defaults(notification=False)
CONF.reset_manager()
PLUGIN_REGISTRY.reset()

else:
# Try to close used mainwindow directly on fixture
# after running test that uses the fixture
Expand All @@ -457,25 +467,27 @@ def main_window(request, tmpdir, qtbot):
'close_main_window')
if close_main_window:
main_window.window = None
window.closing(close_immediately=True)
window.close()
window = None
CONF.reset_to_defaults(notification=False)
CONF.reset_manager()
PLUGIN_REGISTRY.reset()
else:
try:
# Close or hide everything we can think of
window.switcher.hide()
window.switcher.on_close()

# Close editor related elements
window.editor.close_all_files()

# Force close all files
while window.editor.editorstacks[0].close_file(force=True):
editor_widget = window.editor.get_widget()
while editor_widget.editorstacks[0].close_file(force=True):
pass
for editorwindow in window.editor.editorwindows:
for editorwindow in editor_widget.editorwindows:
editorwindow.close()
editorstack = window.editor.get_current_editorstack()
if editorstack.switcher_plugin:
editorstack.switcher_plugin.on_close()

window.projects.close_project()

Expand All @@ -501,9 +513,12 @@ def main_window(request, tmpdir, qtbot):
window.ipyconsole.restart()
except Exception:
main_window.window = None
window.closing(close_immediately=True)
window.close()
window = None
CONF.reset_to_defaults(notification=False)
CONF.reset_manager()
PLUGIN_REGISTRY.reset()
return

if os.name == 'nt':
Expand Down Expand Up @@ -550,9 +565,12 @@ def threads_condition():
"\nThread " + str(threads) + ":\n")
traceback.print_stack(frame)
main_window.window = None
window.closing(close_immediately=True)
window.close()
window = None
CONF.reset_to_defaults(notification=False)
CONF.reset_manager()
PLUGIN_REGISTRY.reset()
raise

try:
Expand All @@ -563,9 +581,12 @@ def threads_condition():
subprocesses = [repr(f) for f in proc.children()]
show_diff(init_subprocesses, subprocesses, "processes")
main_window.window = None
window.closing(close_immediately=True)
window.close()
window = None
CONF.reset_to_defaults(notification=False)
CONF.reset_manager()
PLUGIN_REGISTRY.reset()
raise

try:
Expand All @@ -579,9 +600,12 @@ def threads_condition():
except Exception:
show_diff(init_files, files, "files")
main_window.window = None
window.closing(close_immediately=True)
window.close()
window = None
CONF.reset_to_defaults(notification=False)
CONF.reset_manager()
PLUGIN_REGISTRY.reset()
raise


Expand Down
Loading

0 comments on commit 00986c1

Please sign in to comment.