Skip to content

Commit

Permalink
Merge pull request #23580 from ccordoba12/issue-22496
Browse files Browse the repository at this point in the history
PR: Fix setting custom run configurations for multiple executors (Run)
  • Loading branch information
dalthviz authored Feb 1, 2025
2 parents 877513f + 01ecdf5 commit 9de131c
Show file tree
Hide file tree
Showing 7 changed files with 358 additions and 67 deletions.
2 changes: 1 addition & 1 deletion spyder/app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def generate_run_parameters(mainwindow, filename, selected=None,
selected=selected,
)

return {file_uuid: file_run_params}
return {file_uuid: [file_run_params]}


def get_random_dockable_plugin(main_window, exclude=None):
Expand Down
135 changes: 126 additions & 9 deletions spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@
from qtpy import PYQT_VERSION, PYQT5
from qtpy.QtCore import QPoint, Qt, QTimer, QUrl
from qtpy.QtGui import QImage, QTextCursor
from qtpy.QtWidgets import QAction, QApplication, QInputDialog, QWidget
from qtpy.QtWidgets import (
QAction,
QApplication,
QDialogButtonBox,
QInputDialog,
QWidget,
)
from qtpy.QtWebEngineWidgets import WEBENGINE
from spyder_kernels.utils.pythonenv import is_conda_env

Expand Down Expand Up @@ -81,8 +87,13 @@
from spyder.plugins.layout.layouts import DefaultLayouts
from spyder.plugins.toolbar.api import ApplicationToolbars
from spyder.plugins.run.api import (
RunExecutionParameters, ExtendedRunExecutionParameters, WorkingDirOpts,
WorkingDirSource, RunContext)
ExtendedRunExecutionParameters,
RunActions,
RunContext,
RunExecutionParameters,
WorkingDirOpts,
WorkingDirSource,
)
from spyder.plugins.shortcuts.widgets.table import SEQUENCE
from spyder.py3compat import qbytearray_to_str, to_text_string
from spyder.utils.environ import set_user_env
Expand Down Expand Up @@ -737,7 +748,7 @@ def test_runconfig_workdir(main_window, qtbot, tmpdir):

# --- Set run options for this file ---
run_parameters = generate_run_parameters(main_window, test_file, exec_uuid)
CONF.set('run', 'last_used_parameters', run_parameters)
CONF.set('run', 'last_used_parameters_per_executor', run_parameters)

# --- Run test file ---
shell = main_window.ipyconsole.get_current_shellwidget()
Expand Down Expand Up @@ -787,8 +798,8 @@ def test_runconfig_workdir(main_window, qtbot, tmpdir):
@pytest.mark.order(1)
@pytest.mark.no_new_console
@pytest.mark.skipif(
sys.platform.startswith("linux"),
reason='Fails sometimes on Linux'
sys.platform.startswith("linux") and running_in_ci(),
reason='Fails sometimes on Linux and CIs'
)
def test_dedicated_consoles(main_window, qtbot):
"""Test running code in dedicated consoles."""
Expand Down Expand Up @@ -826,7 +837,7 @@ def test_dedicated_consoles(main_window, qtbot):

# --- Set run options for this file ---
run_parameters = generate_run_parameters(main_window, test_file, exec_uuid)
CONF.set('run', 'last_used_parameters', run_parameters)
CONF.set('run', 'last_used_parameters_per_executor', run_parameters)

# --- Run test file and assert that we get a dedicated console ---
qtbot.keyClick(code_editor, Qt.Key_F5)
Expand Down Expand Up @@ -882,7 +893,7 @@ def test_dedicated_consoles(main_window, qtbot):
# ---- Closing test file and resetting config ----
main_window.editor.close_file()
CONF.set('run', 'configurations', {})
CONF.set('run', 'last_used_parameters', {})
CONF.set('run', 'last_used_parameters_per_executor', {})


@flaky(max_runs=3)
Expand Down Expand Up @@ -946,7 +957,7 @@ def test_shell_execution(main_window, qtbot, tmpdir):
# --- Set run options for this file ---
run_parameters = generate_run_parameters(
main_window, test_file, exec_uuid, external_terminal.NAME)
CONF.set('run', 'last_used_parameters', run_parameters)
CONF.set('run', 'last_used_parameters_per_executor', run_parameters)

# --- Run test file and assert that the script gets executed ---
qtbot.keyClick(code_editor, Qt.Key_F5)
Expand Down Expand Up @@ -7109,5 +7120,111 @@ def test_editor_window_outline_and_toolbars(main_window, qtbot):
debug_toolbar_action.trigger()


@flaky(max_runs=3)
def test_custom_run_config_for_multiple_executors(
main_window, qtbot, tmp_path
):
"""
Check that we correctly use custom run configurations for multiple
executors.
This is a regression test for issue spyder-ide/spyder#22496
"""
# Auxiliary function
def change_run_options(
executor: str, change_cwd: bool, dedicated_console: bool
):
# Select executor
dialog = main_window.run.get_container().dialog
dialog.select_executor(executor)

# Use a fixed path for cwd
if change_cwd:
dialog.fixed_dir_radio.setChecked(True)
dialog.wd_edit.setText(str(tmp_path))

if dedicated_console:
dialog.current_widget.dedicated_radio.setChecked(True)

# Accept changes
ok_btn = dialog.bbox.button(QDialogButtonBox.Ok)
ok_btn.animateClick()

# Wait for a bit until changes are saved to disk
qtbot.wait(500)

# Wait until the window is fully up
shell = main_window.ipyconsole.get_current_shellwidget()
control = shell._control
qtbot.waitUntil(
lambda: shell.spyder_kernel_ready and shell._prompt_html is not None,
timeout=SHELL_TIMEOUT
)

# Open test file
main_window.editor.load(osp.join(LOCATION, 'script.py'))

# Configure debugger with custom options
run_config_action = main_window.run.get_action(RunActions.Configure)
run_config_action.trigger()
change_run_options(
executor=Plugins.Debugger,
change_cwd=True,
dedicated_console=False,
)

# Run test file
run_action = main_window.run.get_action(RunActions.Run)
with qtbot.waitSignal(shell.executed):
run_action.trigger()

# Check we used the default executor, i.e. we ran the file instead of
# debugging it (the run config per file dialog must be used for
# configuration only, not to decide what plugin gets associated to the
# Run file, cell, selection actions).
assert "runfile" in control.toPlainText()

# Check we didn't change the cwd
cwd = (
str(tmp_path).replace("\\", "/") if os.name == "nt" else str(tmp_path)
)
assert cwd not in control.toPlainText()

# Configure IPython console with custom options
run_config_action.trigger()
change_run_options(
executor=Plugins.IPythonConsole,
change_cwd=False,
dedicated_console=True,
)

# Debug file
debug_action = main_window.run.get_action(
"run file in debugger"
)
with qtbot.waitSignal(shell.executed):
debug_action.trigger()

# Check debugging happened in the same console and not in a new one
assert (
"debugfile" in control.toPlainText()
and "runfile" in control.toPlainText()
)

# Check we used the selected cwd for debugging
assert cwd in control.toPlainText()

# Run file again
run_action.trigger()

# Check a new console was created
ipyconsole_widget = main_window.ipyconsole.get_widget()
qtbot.waitUntil(lambda: len(ipyconsole_widget.clients) == 2)

# Check it's a dedicated console for the file we're running
client = main_window.ipyconsole.get_current_client()
assert "script.py" in client.get_name()


if __name__ == "__main__":
pytest.main()
6 changes: 4 additions & 2 deletions spyder/config/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,8 +651,10 @@
'breakpoints',
'configurations',
'default/wdir/fixed_directory',
'last_used_parameters',
'parameters'
'parameters',
'last_used_parameters', # Needed for Spyder 6.0.0 to 6.0.3
'last_used_parameters_per_executor',
'last_configured_executor',
]
),
('workingdir', [
Expand Down
18 changes: 17 additions & 1 deletion spyder/plugins/externalterminal/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
ExtTerminalPyConfiguration, ExtTerminalShConfiguration)
from spyder.plugins.externalterminal.widgets.run_conf import (
ExternalTerminalPyConfiguration, ExternalTerminalShConfiguration)
from spyder.plugins.mainmenu.api import ApplicationMenus, RunMenuSections
from spyder.plugins.run.api import (
RunContext, run_execute, RunConfiguration, ExtendedRunExecutionParameters,
RunResult, RunExecutor)
Expand Down Expand Up @@ -175,6 +176,19 @@ def on_run_available(self):
run = self.get_plugin(Plugins.Run)
run.register_executor_configuration(self, self.executor_configuration)

run.create_run_in_executor_button(
RunContext.File,
self.NAME,
text=_("Run in external terminal"),
tip=_("Run in an operating system terminal"),
icon=self.get_icon(),
register_shortcut=False,
add_to_menu={
"menu": ApplicationMenus.Run,
"section": RunMenuSections.RunInExecutors
},
)

@on_plugin_available(plugin=Plugins.Editor)
def on_editor_available(self):
editor = self.get_plugin(Plugins.Editor)
Expand All @@ -185,7 +199,9 @@ def on_editor_available(self):
def on_run_teardown(self):
run = self.get_plugin(Plugins.Run)
run.deregister_executor_configuration(
self, self.executor_configuration)
self, self.executor_configuration
)
run.destroy_run_in_executor_button(RunContext.File, self.NAME)

@on_plugin_teardown(plugin=Plugins.Editor)
def on_editor_teardown(self):
Expand Down
3 changes: 3 additions & 0 deletions spyder/plugins/ipythonconsole/tests/test_ipythonconsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,9 @@ def test_recursive_pdb(ipyconsole, qtbot):
assert control.toPlainText().split()[-2:] == ["In", "[3]:"]


@pytest.mark.skipif(
sys.version_info[:2] == (3, 8), reason="Fails in Python 3.8"
)
def test_pdb_magics_are_recursive(ipyconsole, qtbot, tmp_path):
"""
Check that calls to Pdb magics start a recursive debugger when called in
Expand Down
Loading

0 comments on commit 9de131c

Please sign in to comment.