From 4757294fb9ae37f55127f4057b4d06dbd64bc0c9 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sat, 1 Apr 2023 10:41:32 +0200 Subject: [PATCH 01/16] add test quotes --- spyder/app/tests/test_mainwindow.py | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index c5d200139b8..b207f45bb14 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -6333,5 +6333,36 @@ def test_runfile_namespace(main_window, qtbot, tmpdir): assert "test_globals True" in control.toPlainText() +def test_quotes_in_filename(main_window, qtbot, tmpdir): + """Test that we can run files with qotes in name""" + # create a file with a funky name + file = tmpdir.join("a'b\"c\\.py") + file.write("print(23 + 780)") + path = to_text_string(file) + main_window.editor.load(path) + + # Run file + 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) + + with qtbot.waitSignal(shell.executed): + qtbot.mouseClick(main_window.run_button, Qt.LeftButton) + + assert "803" in control.toPlainText() + assert "error" not in control.toPlainText() + + code_editor = main_window.editor.get_focus_widget() + code_editor.set_text("print(22 + 780)") + + with qtbot.waitSignal(shell.executed): + qtbot.mouseClick(main_window.run_cell_button, Qt.LeftButton) + + assert "802" in control.toPlainText() + assert "error" not in control.toPlainText() + + if __name__ == "__main__": pytest.main() From 6ff6dfdbf8445972f437a7a322546a7ebc6a1670 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sat, 1 Apr 2023 12:30:08 +0200 Subject: [PATCH 02/16] Update run plugin --- spyder/app/tests/conftest.py | 4 ++-- spyder/plugins/editor/plugin.py | 31 +++++++++++++++++++--------- spyder/plugins/run/api.py | 4 ---- spyder/plugins/run/container.py | 15 ++++++-------- spyder/plugins/run/tests/test_run.py | 2 -- 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/spyder/app/tests/conftest.py b/spyder/app/tests/conftest.py index 580a975cff2..3175a6c609f 100755 --- a/spyder/app/tests/conftest.py +++ b/spyder/app/tests/conftest.py @@ -252,8 +252,8 @@ def generate_run_parameters(mainwindow, filename, selected=None, file_run_params = StoredRunConfigurationExecutor( executor=executor, selected=selected, - display_dialog=False, - first_execution=False) + display_dialog=False + ) return {file_uuid: file_run_params} diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 014d2fe746e..cc397cb648f 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -474,7 +474,7 @@ def update_run_focus_file(self): file_id = self.id_per_file.get(filename, None) self.sig_editor_focus_changed_uuid.emit(file_id) - def register_file_run_metadata(self, filename, filename_ext): + def register_file_run_metadata(self, filename): """Register opened files with the Run plugin.""" all_uuids = CONF.get('editor', 'file_uuids', default={}) file_id = all_uuids.get(filename, str(uuid.uuid4())) @@ -490,7 +490,7 @@ def register_file_run_metadata(self, filename, filename_ext): 'context': { 'name': 'File' }, - 'input_extension': filename_ext + 'input_extension': osp.splitext(filename)[1][1:] } self.file_per_id[file_id] = filename @@ -500,6 +500,19 @@ def register_file_run_metadata(self, filename, filename_ext): run = self.main.get_plugin(Plugins.Run, error=False) if run: run.register_run_configuration_metadata(self, metadata) + + def deregister_file_run_metadata(self, filename): + """Unregister opened files with the Run plugin.""" + if filename not in self.id_per_file: + return + + file_id = self.id_per_file.pop(filename) + self.file_per_id.pop(file_id) + self.metadata_per_id.pop(file_id) + + run = self.main.get_plugin(Plugins.Run, error=False) + if run is not None: + run.deregister_run_configuration_metadata(file_id) @Slot(dict) def report_open_file(self, options): @@ -518,7 +531,7 @@ def report_open_file(self, options): filename not in self.id_per_file and RunContext.File in ext_contexts ): - self.register_file_run_metadata(filename, filename_ext) + self.register_file_run_metadata(filename) able_to_run_file = True if not able_to_run_file: @@ -1778,13 +1791,8 @@ def clone_editorstack(self, editorstack): @Slot(str, str) def close_file_in_all_editorstacks(self, editorstack_id_str, filename): - run = self.main.get_plugin(Plugins.Run, error=False) if filename in self.id_per_file: - file_id = self.id_per_file.pop(filename) - self.file_per_id.pop(file_id) - self.metadata_per_id.pop(file_id) - if run is not None: - run.deregister_run_configuration_metadata(file_id) + self.deregister_file_run_metadata(filename) else: _, filename_ext = osp.splitext(filename) filename_ext = filename_ext[1:] @@ -2654,6 +2662,9 @@ def renamed(self, source, dest): filename = osp.abspath(to_text_string(source)) index = self.get_filename_index(filename) if index is not None: + self.deregister_file_run_metadata(filename) + self.register_file_run_metadata(dest) + for editorstack in self.editorstacks: editorstack.rename_in_data(filename, new_filename=to_text_string(dest)) @@ -3092,7 +3103,7 @@ def add_supported_run_configuration(self, config: EditorRunConfiguration): for filename, filename_ext in list(self.pending_run_files): if filename_ext == extension and file_enabled: - self.register_file_run_metadata(filename, filename_ext) + self.register_file_run_metadata(filename) else: self.pending_run_files -= {(filename, filename_ext)} diff --git a/spyder/plugins/run/api.py b/spyder/plugins/run/api.py index bd258dac0d5..1881299b2d4 100644 --- a/spyder/plugins/run/api.py +++ b/spyder/plugins/run/api.py @@ -271,10 +271,6 @@ class StoredRunConfigurationExecutor(TypedDict): # configuration is executed. Otherwise not. display_dialog: bool - # True if the configuration has been executed in the past, False - # otherwise. - first_execution: bool - class RunConfigurationProvider(QObject): """ diff --git a/spyder/plugins/run/container.py b/spyder/plugins/run/container.py index 65f2843ecd5..9776f1b5cde 100644 --- a/spyder/plugins/run/container.py +++ b/spyder/plugins/run/container.py @@ -204,9 +204,7 @@ def run_file(self, selected_uuid=None, selected_executor=None): exec_params = self.get_last_used_executor_parameters( self.currently_selected_configuration) - first_execution = exec_params['first_execution'] display_dialog = exec_params['display_dialog'] - display_dialog = display_dialog or first_execution self.edit_run_configurations( display_dialog=display_dialog, @@ -258,7 +256,7 @@ def process_run_dialog_result(self, result): last_used_conf = StoredRunConfigurationExecutor( executor=executor_name, selected=ext_params['uuid'], - display_dialog=open_dialog, first_execution=False) + display_dialog=open_dialog) self.set_last_used_execution_params(uuid, last_used_conf) @@ -936,10 +934,10 @@ def get_last_used_executor_parameters( last_used_params = mru_executors_uuids.get( uuid, StoredRunConfigurationExecutor( - executor=None, - selected=None, - display_dialog=False, - first_execution=True) + executor=None, + selected=None, + display_dialog=False, + ) ) return last_used_params @@ -976,8 +974,7 @@ def get_last_used_execution_params( default = StoredRunConfigurationExecutor( executor=executor_name, selected=None, - display_dialog=False, - first_execution=True + display_dialog=False ) params = mru_executors_uuids.get(uuid, default) diff --git a/spyder/plugins/run/tests/test_run.py b/spyder/plugins/run/tests/test_run.py index 53b25b760d7..a4f35592bd7 100644 --- a/spyder/plugins/run/tests/test_run.py +++ b/spyder/plugins/run/tests/test_run.py @@ -617,7 +617,6 @@ def test_run_plugin(qtbot, run_mock): assert stored_run_params['executor'] == test_executor_name assert stored_run_params['selected'] is None assert not stored_run_params['display_dialog'] - assert not stored_run_params['first_execution'] # The configuration gets run again with qtbot.waitSignal(executor_1.sig_run_invocation) as sig: @@ -780,7 +779,6 @@ def test_run_plugin(qtbot, run_mock): assert stored_run_params['executor'] == executor_name assert stored_run_params['selected'] == exec_conf_uuid assert not stored_run_params['display_dialog'] - assert not stored_run_params['first_execution'] # Test teardown functions executor_1.on_run_teardown(run) From c46a1b1356482bb53187ceca7a4422c2691abf6e Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 2 Apr 2023 07:32:26 +0200 Subject: [PATCH 03/16] Add ipy extension --- spyder/plugins/debugger/plugin.py | 30 ++++++++++++++++++++++++ spyder/plugins/editor/plugin.py | 8 +++++++ spyder/plugins/externalconsole/plugin.py | 10 ++++++++ spyder/plugins/ipythonconsole/plugin.py | 30 ++++++++++++++++++++++++ spyder/plugins/profiler/plugin.py | 10 ++++++++ spyder/plugins/pylint/plugin.py | 10 ++++++++ 6 files changed, 98 insertions(+) diff --git a/spyder/plugins/debugger/plugin.py b/spyder/plugins/debugger/plugin.py index 65ff1e89690..f6e99eeb98f 100644 --- a/spyder/plugins/debugger/plugin.py +++ b/spyder/plugins/debugger/plugin.py @@ -121,6 +121,36 @@ def on_initialize(self): 'requires_cwd': True, 'priority': 10 }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'File' + }, + 'output_formats': [], + 'configuration_widget': IPythonConfigOptions, + 'requires_cwd': True, + 'priority': 10 + }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'Cell' + }, + 'output_formats': [], + 'configuration_widget': None, + 'requires_cwd': True, + 'priority': 10 + }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'Selection' + }, + 'output_formats': [], + 'configuration_widget': None, + 'requires_cwd': True, + 'priority': 10 + }, ] @on_plugin_available(plugin=Plugins.Run) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index cc397cb648f..42d4d05fc2a 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -270,6 +270,14 @@ def __init__(self, parent, ignore_last_opened_files=False): {'context': {'name': 'Selection'}, 'is_super': False}, {'context': {'name': 'Cell'}, 'is_super': False} ] + }, + { + 'input_extension': 'ipy', + 'contexts': [ + {'context': {'name': 'File'}, 'is_super': True}, + {'context': {'name': 'Selection'}, 'is_super': False}, + {'context': {'name': 'Cell'}, 'is_super': False} + ] } ] diff --git a/spyder/plugins/externalconsole/plugin.py b/spyder/plugins/externalconsole/plugin.py index 475f5c1418e..7381bd86376 100644 --- a/spyder/plugins/externalconsole/plugin.py +++ b/spyder/plugins/externalconsole/plugin.py @@ -78,6 +78,16 @@ def on_initialize(self): 'configuration_widget': ExternalConsolePyConfiguration, 'requires_cwd': True, 'priority': 2 + }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'File' + }, + 'output_formats': [], + 'configuration_widget': ExternalConsolePyConfiguration, + 'requires_cwd': True, + 'priority': 2 } ] diff --git a/spyder/plugins/ipythonconsole/plugin.py b/spyder/plugins/ipythonconsole/plugin.py index bc2b7a1f249..2482e0a1ec8 100644 --- a/spyder/plugins/ipythonconsole/plugin.py +++ b/spyder/plugins/ipythonconsole/plugin.py @@ -274,6 +274,36 @@ def on_initialize(self): 'requires_cwd': True, 'priority': 0 }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'File' + }, + 'output_formats': [], + 'configuration_widget': IPythonConfigOptions, + 'requires_cwd': True, + 'priority': 0 + }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'Cell' + }, + 'output_formats': [], + 'configuration_widget': None, + 'requires_cwd': True, + 'priority': 0 + }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'Selection' + }, + 'output_formats': [], + 'configuration_widget': None, + 'requires_cwd': True, + 'priority': 0 + }, { 'input_extension': 'pyx', 'context': { diff --git a/spyder/plugins/profiler/plugin.py b/spyder/plugins/profiler/plugin.py index 891f89df33c..667ed0ae811 100644 --- a/spyder/plugins/profiler/plugin.py +++ b/spyder/plugins/profiler/plugin.py @@ -81,6 +81,16 @@ def on_initialize(self): 'configuration_widget': ProfilerPyConfigurationGroup, 'requires_cwd': True, 'priority': 3 + }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'File' + }, + 'output_formats': [], + 'configuration_widget': ProfilerPyConfigurationGroup, + 'requires_cwd': True, + 'priority': 3 } ] diff --git a/spyder/plugins/pylint/plugin.py b/spyder/plugins/pylint/plugin.py index 6319f2a00e9..3e14dd10756 100644 --- a/spyder/plugins/pylint/plugin.py +++ b/spyder/plugins/pylint/plugin.py @@ -94,6 +94,16 @@ def on_initialize(self): 'configuration_widget': None, 'requires_cwd': False, 'priority': 4 + }, + { + 'input_extension': 'ipy', + 'context': { + 'name': 'File' + }, + 'output_formats': [], + 'configuration_widget': None, + 'requires_cwd': False, + 'priority': 4 } ] From 6d7660af58bf55ac4a73204415eaa1c760f013dd Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 2 Apr 2023 08:26:14 +0200 Subject: [PATCH 04/16] use lists --- spyder/plugins/debugger/plugin.py | 38 +--------- spyder/plugins/editor/plugin.py | 96 ++++++++++++------------ spyder/plugins/externalconsole/plugin.py | 14 +--- spyder/plugins/ipythonconsole/plugin.py | 38 +--------- spyder/plugins/profiler/plugin.py | 12 +-- spyder/plugins/pylint/plugin.py | 12 +-- spyder/plugins/run/container.py | 28 ++++--- spyder/plugins/run/models.py | 3 + spyder/plugins/run/plugin.py | 13 ---- 9 files changed, 79 insertions(+), 175 deletions(-) diff --git a/spyder/plugins/debugger/plugin.py b/spyder/plugins/debugger/plugin.py index f6e99eeb98f..fe469cc6693 100644 --- a/spyder/plugins/debugger/plugin.py +++ b/spyder/plugins/debugger/plugin.py @@ -76,7 +76,7 @@ def on_initialize(self): self.python_editor_run_configuration = { 'origin': self.NAME, - 'extension': 'py', + 'extension': ['py', 'ipy'], 'contexts': [ { 'name': 'File' @@ -92,7 +92,7 @@ def on_initialize(self): self.executor_configuration = [ { - 'input_extension': 'py', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'File' }, @@ -102,7 +102,7 @@ def on_initialize(self): 'priority': 10 }, { - 'input_extension': 'py', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'Cell' }, @@ -112,37 +112,7 @@ def on_initialize(self): 'priority': 10 }, { - 'input_extension': 'py', - 'context': { - 'name': 'Selection' - }, - 'output_formats': [], - 'configuration_widget': None, - 'requires_cwd': True, - 'priority': 10 - }, - { - 'input_extension': 'ipy', - 'context': { - 'name': 'File' - }, - 'output_formats': [], - 'configuration_widget': IPythonConfigOptions, - 'requires_cwd': True, - 'priority': 10 - }, - { - 'input_extension': 'ipy', - 'context': { - 'name': 'Cell' - }, - 'output_formats': [], - 'configuration_widget': None, - 'requires_cwd': True, - 'priority': 10 - }, - { - 'input_extension': 'ipy', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'Selection' }, diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 42d4d05fc2a..7da55e8e043 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -264,21 +264,13 @@ def __init__(self, parent, ignore_last_opened_files=False): self.supported_run_extensions = [ { - 'input_extension': 'py', + 'input_extension': ['py', 'ipy'], 'contexts': [ {'context': {'name': 'File'}, 'is_super': True}, {'context': {'name': 'Selection'}, 'is_super': False}, {'context': {'name': 'Cell'}, 'is_super': False} ] }, - { - 'input_extension': 'ipy', - 'contexts': [ - {'context': {'name': 'File'}, 'is_super': True}, - {'context': {'name': 'Selection'}, 'is_super': False}, - {'context': {'name': 'Cell'}, 'is_super': False} - ] - } ] run = self.main.get_plugin(Plugins.Run, error=False) @@ -3072,48 +3064,52 @@ def handle_get_file_code(self, filename, save_all=True): # ------ Run files def add_supported_run_configuration(self, config: EditorRunConfiguration): origin = config['origin'] - extension = config['extension'] + extensions = config['extension'] contexts = config['contexts'] - - ext_contexts = [] - for context in contexts: - is_super = RunContext[context['name']] == RunContext.File - ext_contexts.append( - ExtendedContext(context=context, is_super=is_super)) - supported_extension = SupportedExtensionContexts( - input_extension=extension, contexts=ext_contexts) - self.supported_run_extensions.append(supported_extension) - - run = self.main.get_plugin(Plugins.Run, error=False) - if run: - run.register_run_configuration_provider( - self.NAME, [supported_extension]) - - actual_contexts = set({}) - ext_origins = self.run_configurations_per_origin.get(extension, {}) - - file_enabled = False - for context in contexts: - context_name = context['name'] - context_id = getattr(RunContext, context_name) - actual_contexts |= {context_id} - context_origins = ext_origins.get(context_id, set({})) - context_origins |= {origin} - ext_origins[context_id] = context_origins - if context_id == RunContext.File: - file_enabled = True - - ext_contexts = self.supported_run_configurations.get( - extension, set({})) - ext_contexts |= actual_contexts - self.supported_run_configurations[extension] = ext_contexts - self.run_configurations_per_origin[extension] = ext_origins - - for filename, filename_ext in list(self.pending_run_files): - if filename_ext == extension and file_enabled: - self.register_file_run_metadata(filename) - else: - self.pending_run_files -= {(filename, filename_ext)} + + if not isinstance(extensions, list): + extensions = [extensions] + + for extension in extensions: + ext_contexts = [] + for context in contexts: + is_super = RunContext[context['name']] == RunContext.File + ext_contexts.append( + ExtendedContext(context=context, is_super=is_super)) + supported_extension = SupportedExtensionContexts( + input_extension=extension, contexts=ext_contexts) + self.supported_run_extensions.append(supported_extension) + + run = self.main.get_plugin(Plugins.Run, error=False) + if run: + run.register_run_configuration_provider( + self.NAME, [supported_extension]) + + actual_contexts = set({}) + ext_origins = self.run_configurations_per_origin.get(extension, {}) + + file_enabled = False + for context in contexts: + context_name = context['name'] + context_id = getattr(RunContext, context_name) + actual_contexts |= {context_id} + context_origins = ext_origins.get(context_id, set({})) + context_origins |= {origin} + ext_origins[context_id] = context_origins + if context_id == RunContext.File: + file_enabled = True + + ext_contexts = self.supported_run_configurations.get( + extension, set({})) + ext_contexts |= actual_contexts + self.supported_run_configurations[extension] = ext_contexts + self.run_configurations_per_origin[extension] = ext_origins + + for filename, filename_ext in list(self.pending_run_files): + if filename_ext == extension and file_enabled: + self.register_file_run_metadata(filename) + else: + self.pending_run_files -= {(filename, filename_ext)} def remove_supported_run_configuration( self, diff --git a/spyder/plugins/externalconsole/plugin.py b/spyder/plugins/externalconsole/plugin.py index 7381bd86376..9ad98b5cfcb 100644 --- a/spyder/plugins/externalconsole/plugin.py +++ b/spyder/plugins/externalconsole/plugin.py @@ -59,7 +59,7 @@ def on_initialize(self): self.editor_configurations = [ { 'origin': self.NAME, - 'extension': 'py', + 'extension': ['py', 'ipy'], 'contexts': [ { 'name': 'File' @@ -70,7 +70,7 @@ def on_initialize(self): self.executor_configuration = [ { - 'input_extension': 'py', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'File' }, @@ -79,16 +79,6 @@ def on_initialize(self): 'requires_cwd': True, 'priority': 2 }, - { - 'input_extension': 'ipy', - 'context': { - 'name': 'File' - }, - 'output_formats': [], - 'configuration_widget': ExternalConsolePyConfiguration, - 'requires_cwd': True, - 'priority': 2 - } ] if os.name == 'nt': diff --git a/spyder/plugins/ipythonconsole/plugin.py b/spyder/plugins/ipythonconsole/plugin.py index 2482e0a1ec8..231ecc543b7 100644 --- a/spyder/plugins/ipythonconsole/plugin.py +++ b/spyder/plugins/ipythonconsole/plugin.py @@ -229,7 +229,7 @@ def on_initialize(self): self.python_editor_run_configuration = { 'origin': self.NAME, - 'extension': 'py', + 'extension': ['py', 'ipy'], 'contexts': [ { 'name': 'File' @@ -245,7 +245,7 @@ def on_initialize(self): self.executor_configuration = [ { - 'input_extension': 'py', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'File' }, @@ -255,7 +255,7 @@ def on_initialize(self): 'priority': 0 }, { - 'input_extension': 'py', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'Cell' }, @@ -265,37 +265,7 @@ def on_initialize(self): 'priority': 0 }, { - 'input_extension': 'py', - 'context': { - 'name': 'Selection' - }, - 'output_formats': [], - 'configuration_widget': None, - 'requires_cwd': True, - 'priority': 0 - }, - { - 'input_extension': 'ipy', - 'context': { - 'name': 'File' - }, - 'output_formats': [], - 'configuration_widget': IPythonConfigOptions, - 'requires_cwd': True, - 'priority': 0 - }, - { - 'input_extension': 'ipy', - 'context': { - 'name': 'Cell' - }, - 'output_formats': [], - 'configuration_widget': None, - 'requires_cwd': True, - 'priority': 0 - }, - { - 'input_extension': 'ipy', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'Selection' }, diff --git a/spyder/plugins/profiler/plugin.py b/spyder/plugins/profiler/plugin.py index 667ed0ae811..cfaf3895dbc 100644 --- a/spyder/plugins/profiler/plugin.py +++ b/spyder/plugins/profiler/plugin.py @@ -73,7 +73,7 @@ def on_initialize(self): self.executor_configuration = [ { - 'input_extension': 'py', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'File' }, @@ -82,16 +82,6 @@ def on_initialize(self): 'requires_cwd': True, 'priority': 3 }, - { - 'input_extension': 'ipy', - 'context': { - 'name': 'File' - }, - 'output_formats': [], - 'configuration_widget': ProfilerPyConfigurationGroup, - 'requires_cwd': True, - 'priority': 3 - } ] @on_plugin_available(plugin=Plugins.Editor) diff --git a/spyder/plugins/pylint/plugin.py b/spyder/plugins/pylint/plugin.py index 3e14dd10756..3b8cdc2a7bf 100644 --- a/spyder/plugins/pylint/plugin.py +++ b/spyder/plugins/pylint/plugin.py @@ -86,7 +86,7 @@ def on_initialize(self): # Run configuration self.executor_configuration = [ { - 'input_extension': 'py', + 'input_extension': ['py', 'ipy'], 'context': { 'name': 'File' }, @@ -95,16 +95,6 @@ def on_initialize(self): 'requires_cwd': False, 'priority': 4 }, - { - 'input_extension': 'ipy', - 'context': { - 'name': 'File' - }, - 'output_formats': [], - 'configuration_widget': None, - 'requires_cwd': False, - 'priority': 4 - } ] @on_plugin_available(plugin=Plugins.Editor) diff --git a/spyder/plugins/run/container.py b/spyder/plugins/run/container.py index 9776f1b5cde..5c7557ca6d9 100644 --- a/spyder/plugins/run/container.py +++ b/spyder/plugins/run/container.py @@ -57,7 +57,6 @@ def setup(self): self.executor_use_count: Dict[str, int] = {} self.viewers_per_output: Dict[str, Set[str]] = {} - self.currently_selected_configuration: Optional[str] = None self.run_action = self.create_action( RunActions.Run, @@ -283,13 +282,16 @@ def process_run_dialog_result(self, result): def re_run_file(self): self.run_file(self.last_executed_file, selected_executor=None) + + @property + def currently_selected_configuration(self): + return self.metadata_model.get_current_run_configuration() def switch_focused_run_configuration(self, uuid: Optional[str]): uuid = uuid or None + self.metadata_model.set_current_run_configuration(uuid) if uuid is not None and uuid != self.currently_selected_configuration: self.run_action.setEnabled(True) - self.currently_selected_configuration = uuid - self.metadata_model.set_current_run_configuration(uuid) metadata = self.metadata_model[uuid] self.current_input_provider = metadata['source'] @@ -300,7 +302,7 @@ def switch_focused_run_configuration(self, uuid: Optional[str]): self.set_actions_status() elif uuid is None: self.run_action.setEnabled(False) - + for context, act, mod in self.context_actions: action, __ = self.context_actions[(context, act, mod)] action.setEnabled(False) @@ -579,7 +581,6 @@ def register_run_configuration_provider( provider_name, set({})) for supported_extension_contexts in supported_extensions_contexts: - ext = supported_extension_contexts['input_extension'] for ext_context in supported_extension_contexts['contexts']: context = ext_context['context'] is_super = ext_context['is_super'] @@ -589,7 +590,11 @@ def register_run_configuration_provider( context_identifier = camel_case_to_snake_case(context_name) context['identifier'] = context_identifier setattr(RunContext, context_name, context_identifier) - provider_extensions_contexts |= {(ext, context_identifier)} + ext_list = supported_extension_contexts['input_extension'] + if not isinstance(ext_list, list): + ext_list = [ext_list] + for ext in ext_list: + provider_extensions_contexts |= {(ext, context_identifier)} if is_super: self.super_contexts |= {context_identifier} @@ -717,7 +722,6 @@ def register_executor_configuration( executor_count = self.executor_use_count.get(executor_id, 0) for config in configuration: - ext = config['input_extension'] context = config['context'] context_name = context['name'] context_id = context.get('identifier', None) @@ -736,9 +740,13 @@ def register_executor_configuration( output_formats.append(updated_out) config['output_formats'] = output_formats - self.executor_model.add_input_executor_configuration( - ext, context_id, executor_id, config) - executor_count += 1 + ext_list = config['input_extension'] + if not isinstance(ext_list, list): + ext_list = [ext_list] + for ext in ext_list: + self.executor_model.add_input_executor_configuration( + ext, context_id, executor_id, config) + executor_count += 1 self.executor_use_count[executor_id] = executor_count self.executor_model.set_executor_name(executor_id, executor_name) diff --git a/spyder/plugins/run/models.py b/spyder/plugins/run/models.py index 7dccce0eae5..9e31cc9af29 100644 --- a/spyder/plugins/run/models.py +++ b/spyder/plugins/run/models.py @@ -175,6 +175,9 @@ def get_metadata_context_extension(self, uuid: str): def set_current_run_configuration(self, uuid: str): self.current_configuration = uuid + + def get_current_run_configuration(self): + return self.current_configuration def get_initial_index(self) -> int: return self.inverted_index[self.current_configuration] diff --git a/spyder/plugins/run/plugin.py b/spyder/plugins/run/plugin.py index a5d7f070622..167f9dffc16 100644 --- a/spyder/plugins/run/plugin.py +++ b/spyder/plugins/run/plugin.py @@ -62,16 +62,6 @@ class Run(SpyderPluginV2): focused `RunConfigurationProvider` """ - sig_switch_run_configuration_focus = Signal(str) - """ - Change the current run configuration to the one that is focused. - - Arguments - --------- - uuid: str - The run configuration identifier. - """ - # ---- SpyderPluginV2 API # ------------------------------------------------------------------------- @staticmethod @@ -94,9 +84,6 @@ def on_initialize(self): self.shortcut_actions = {} self.action_lock = Lock() - self.sig_switch_run_configuration_focus.connect( - self.switch_focused_run_configuration) - container = self.get_container() container.sig_run_action_created.connect( self.register_action_shortcuts) From 5b3534bcec1ec6f8409b5683f312480ac6d73501 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Sun, 2 Apr 2023 09:41:57 +0200 Subject: [PATCH 05/16] add test --- spyder/app/tests/test_mainwindow.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index b207f45bb14..d1cd4cad277 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -69,7 +69,7 @@ WorkingDirSource, RunContext) from spyder.py3compat import qbytearray_to_str, to_text_string from spyder.utils.environ import set_user_env -from spyder.utils.misc import remove_backslashes +from spyder.utils.misc import remove_backslashes, rename_file from spyder.utils.clipboard_helper import CLIPBOARD_HELPER from spyder.utils.programs import find_program from spyder.widgets.dock import DockTitleBar @@ -6362,6 +6362,26 @@ def test_quotes_in_filename(main_window, qtbot, tmpdir): assert "802" in control.toPlainText() assert "error" not in control.toPlainText() + + # Make sure this works with ipy and renamed files too + + # Rename the file to ipython and send the signal + rename_file(path, path[:-2] + "ipy") + explorer = main_window.get_plugin(Plugins.Explorer) + explorer.sig_file_renamed.emit(path, path[:-2] + "ipy") + + code_editor.set_text("print(21 + 780)") + + with qtbot.waitSignal(shell.executed): + qtbot.mouseClick(main_window.run_button, Qt.LeftButton) + + assert "801" in control.toPlainText() + assert "error" not in control.toPlainText() + assert "\\.ipy" in control.toPlainText() + + + + if __name__ == "__main__": From ac86cf5aa4d5ad14eb7d25e7d2a94827e014e4b6 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 3 Apr 2023 07:55:10 +0200 Subject: [PATCH 06/16] fix test --- spyder/plugins/run/container.py | 38 +++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/spyder/plugins/run/container.py b/spyder/plugins/run/container.py index 5c7557ca6d9..d1193ee00a3 100644 --- a/spyder/plugins/run/container.py +++ b/spyder/plugins/run/container.py @@ -289,8 +289,12 @@ def currently_selected_configuration(self): def switch_focused_run_configuration(self, uuid: Optional[str]): uuid = uuid or None + if uuid == self.currently_selected_configuration: + return + self.metadata_model.set_current_run_configuration(uuid) - if uuid is not None and uuid != self.currently_selected_configuration: + + if uuid is not None: self.run_action.setEnabled(True) metadata = self.metadata_model[uuid] @@ -300,21 +304,23 @@ def switch_focused_run_configuration(self, uuid: Optional[str]): input_provider = self.run_metadata_provider[uuid] input_provider.focus_run_configuration(uuid) self.set_actions_status() - elif uuid is None: - self.run_action.setEnabled(False) - - for context, act, mod in self.context_actions: - action, __ = self.context_actions[(context, act, mod)] - action.setEnabled(False) - - for context, act, mod in self.re_run_actions: - action, __ = self.re_run_actions[(context, act, mod)] - action.setEnabled(False) - - for context_name, executor_name in self.run_executor_actions: - action, __ = self.run_executor_actions[ - (context_name, executor_name)] - action.setEnabled(False) + + return + + self.run_action.setEnabled(False) + + for context, act, mod in self.context_actions: + action, __ = self.context_actions[(context, act, mod)] + action.setEnabled(False) + + for context, act, mod in self.re_run_actions: + action, __ = self.re_run_actions[(context, act, mod)] + action.setEnabled(False) + + for context_name, executor_name in self.run_executor_actions: + action, __ = self.run_executor_actions[ + (context_name, executor_name)] + action.setEnabled(False) def set_actions_status(self): if self.current_input_provider is None: From 6194986b285ea8a148f6c6c924f2efb3d3d4397c Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 3 Apr 2023 08:59:19 +0200 Subject: [PATCH 07/16] fix test --- spyder/app/tests/test_mainwindow.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index d1cd4cad277..aa7a7cc5b5c 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -6333,10 +6333,17 @@ def test_runfile_namespace(main_window, qtbot, tmpdir): assert "test_globals True" in control.toPlainText() -def test_quotes_in_filename(main_window, qtbot, tmpdir): - """Test that we can run files with qotes in name""" +def test_quotes_rename_ipy(main_window, qtbot, tmpdir): + """ + Test that we can run files with quotes in name, renamed files, + and ipy files. + """ # create a file with a funky name - file = tmpdir.join("a'b\"c\\.py") + path = "a'b\"c\\.py" + if os.name == 'nt': + # path is not a valid file name on Windows + path = "abc.py" + file = tmpdir.join(path) file.write("print(23 + 780)") path = to_text_string(file) main_window.editor.load(path) @@ -6362,26 +6369,22 @@ def test_quotes_in_filename(main_window, qtbot, tmpdir): assert "802" in control.toPlainText() assert "error" not in control.toPlainText() - + # Make sure this works with ipy and renamed files too - + # Rename the file to ipython and send the signal rename_file(path, path[:-2] + "ipy") explorer = main_window.get_plugin(Plugins.Explorer) explorer.sig_file_renamed.emit(path, path[:-2] + "ipy") - + code_editor.set_text("print(21 + 780)") - + with qtbot.waitSignal(shell.executed): qtbot.mouseClick(main_window.run_button, Qt.LeftButton) assert "801" in control.toPlainText() assert "error" not in control.toPlainText() assert "\\.ipy" in control.toPlainText() - - - - if __name__ == "__main__": From 3a15f25f74caf3ecf46e975aaa48973219148bf1 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Mon, 3 Apr 2023 23:09:57 +0200 Subject: [PATCH 08/16] skip window --- spyder/app/tests/test_mainwindow.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index aa7a7cc5b5c..f70cd993189 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -6333,6 +6333,10 @@ def test_runfile_namespace(main_window, qtbot, tmpdir): assert "test_globals True" in control.toPlainText() +@pytest.mark.skipif( + os.name == 'nt', + reason="No quotes in windows filepath" +) def test_quotes_rename_ipy(main_window, qtbot, tmpdir): """ Test that we can run files with quotes in name, renamed files, @@ -6340,9 +6344,6 @@ def test_quotes_rename_ipy(main_window, qtbot, tmpdir): """ # create a file with a funky name path = "a'b\"c\\.py" - if os.name == 'nt': - # path is not a valid file name on Windows - path = "abc.py" file = tmpdir.join(path) file.write("print(23 + 780)") path = to_text_string(file) From a027a24cc70ec5552b7876f7e3ceb3aafbf0b0bb Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Tue, 20 Jun 2023 06:37:14 +0200 Subject: [PATCH 09/16] fix rename bug --- spyder/plugins/editor/plugin.py | 25 +++++++++++-------------- spyder/plugins/editor/widgets/editor.py | 8 ++++---- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 584b0139437..c740ddf4815 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -1737,8 +1737,7 @@ def register_editorstack(self, editorstack): editorstack.sig_close_file.connect(self.close_file_in_all_editorstacks) editorstack.sig_close_file.connect(self.remove_file_cursor_history) editorstack.file_saved.connect(self.file_saved_in_editorstack) - editorstack.file_renamed_in_data.connect( - self.file_renamed_in_data_in_editorstack) + editorstack.file_renamed_in_data.connect(self.renamed) editorstack.opened_files_list_changed.connect( self.opened_files_list_changed) editorstack.active_languages_stats.connect( @@ -1825,14 +1824,6 @@ def file_saved_in_editorstack(self, editorstack_id_str, editorstack.file_saved_in_other_editorstack(original_filename, filename) - @Slot(str, str, str) - def file_renamed_in_data_in_editorstack(self, editorstack_id_str, - original_filename, filename): - """A file was renamed in data in editorstack, this notifies others""" - for editorstack in self.editorstacks: - if str(id(editorstack)) != editorstack_id_str: - editorstack.rename_in_data(original_filename, filename) - #------ Handling editor windows def setup_other_windows(self): """Setup toolbars and menus for 'New window' instances""" @@ -2667,7 +2658,9 @@ def removed_tree(self, dirname): if osp.abspath(fname).startswith(dirname): self.close_file_from_name(fname) - def renamed(self, source, dest): + @Slot(str, str) + @Slot(str, str, str) + def renamed(self, source, dest, editorstack_id_str=None): """ Propagate file rename to editor stacks and autosave component. @@ -2677,13 +2670,17 @@ def renamed(self, source, dest): """ filename = osp.abspath(to_text_string(source)) index = self.get_filename_index(filename) - if index is not None: + if index is not None or editorstack_id_str is not None: self.deregister_file_run_metadata(filename) self.register_file_run_metadata(dest) for editorstack in self.editorstacks: - editorstack.rename_in_data(filename, - new_filename=to_text_string(dest)) + if ( + editorstack_id_str is not None and + str(id(editorstack)) != editorstack_id_str + ): + editorstack.rename_in_data( + filename, new_filename=to_text_string(dest)) self.editorstacks[0].autosave.file_renamed( filename, to_text_string(dest)) diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py index 08315c23dc1..a6293d3eb3b 100644 --- a/spyder/plugins/editor/widgets/editor.py +++ b/spyder/plugins/editor/widgets/editor.py @@ -1898,8 +1898,8 @@ def save_as(self, index=None): # depend on the platform: long for 64bit, int for 32bit. Replacing # by long all the time is not working on some 32bit platforms # See spyder-ide/spyder#1094 and spyder-ide/spyder#1098. - self.file_renamed_in_data.emit(str(id(self)), - original_filename, filename) + self.file_renamed_in_data.emit( + original_filename, filename, str(id(self))) ok = self.save(index=new_index, force=True) self.refresh(new_index) @@ -3546,8 +3546,8 @@ def file_saved_in_editorstack(self, editorstack_id_str, # This method is never called in this plugin example. It's here only # to show how to use the file_saved signal (see above). @Slot(str, str, str) - def file_renamed_in_data_in_editorstack(self, editorstack_id_str, - original_filename, filename): + def file_renamed_in_data_in_editorstack( + self, original_filename, filename, editorstack_id_str): """A file was renamed in data in editorstack, this notifies others""" for editorstack in self.editorstacks: if str(id(editorstack)) != editorstack_id_str: From b35b9d058e7a1718062ac5244f4be1185ebabc57 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Tue, 20 Jun 2023 08:57:06 +0200 Subject: [PATCH 10/16] reset current configuration --- spyder/plugins/editor/plugin.py | 2 +- spyder/plugins/run/models.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index c740ddf4815..0e160919049 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -2676,7 +2676,7 @@ def renamed(self, source, dest, editorstack_id_str=None): for editorstack in self.editorstacks: if ( - editorstack_id_str is not None and + editorstack_id_str is None or str(id(editorstack)) != editorstack_id_str ): editorstack.rename_in_data( diff --git a/spyder/plugins/run/models.py b/spyder/plugins/run/models.py index 9e31cc9af29..ac1e4d44f2c 100644 --- a/spyder/plugins/run/models.py +++ b/spyder/plugins/run/models.py @@ -240,6 +240,8 @@ def pop(self, uuid: str) -> RunConfigurationMetadata: item = self.run_configurations.pop(uuid) self.metadata_index = dict(enumerate(self.run_configurations)) self.inverted_index = {v: k for k, v in self.metadata_index.items()} + if self.current_configuration not in self.inverted_index: + self.current_configuration = None self.dataChanged.emit(self.createIndex(0, 0), self.createIndex(len(self.metadata_index), 0)) return item @@ -257,6 +259,8 @@ def __setitem__(self, uuid: str, metadata: RunConfigurationMetadata): self.run_configurations[uuid] = metadata self.metadata_index[len(self.metadata_index)] = uuid self.inverted_index[uuid] = len(self.inverted_index) + if self.current_configuration is None: + self.current_configuration = uuid self.dataChanged.emit(self.createIndex(0, 0), self.createIndex(len(self.metadata_index), 0)) From bdd8f471374630f28ae9901b9b38d0c6c4a4e74b Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Tue, 20 Jun 2023 09:13:06 +0200 Subject: [PATCH 11/16] add test unsaved files --- spyder/app/tests/test_mainwindow.py | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index 19c1c26f9dc..f2ac9bcac7e 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -6384,6 +6384,40 @@ def test_quotes_rename_ipy(main_window, qtbot, tmpdir): assert "801" in control.toPlainText() assert "error" not in control.toPlainText() assert "\\.ipy" in control.toPlainText() + + # Create an untiliteled file + main_window.editor.new() + + assert "untitled" in main_window.editor.get_current_filename() + + code_editor = main_window.editor.get_focus_widget() + code_editor.set_text("print(20 + 780)") + + with qtbot.waitSignal(shell.executed): + qtbot.mouseClick(main_window.run_cell_button, Qt.LeftButton) + + assert "800" in control.toPlainText() + assert "error" not in control.toPlainText() + assert "untitled" in control.toPlainText() + + # save in a new folder + code_editor.set_text("print(19 + 780)") + + with tempfile.TemporaryDirectory() as td: + + editorstack = main_window.editor.get_current_editorstack() + editorstack.select_savename = lambda fn: os.path.join(td, "fn.ipy") + main_window.editor.save() + + + with qtbot.waitSignal(shell.executed): + qtbot.mouseClick(main_window.run_cell_button, Qt.LeftButton) + + assert "799" in control.toPlainText() + assert "error" not in control.toPlainText() + assert "fn.ipy" in control.toPlainText() + main_window.editor.close_file() + if __name__ == "__main__": From da73465935e94b4058b698af4dd09dcb7c5736ba Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Tue, 20 Jun 2023 12:34:02 +0200 Subject: [PATCH 12/16] change focused run configuration --- spyder/plugins/editor/plugin.py | 21 ++++++++++++++++++--- spyder/plugins/run/models.py | 2 -- spyder/plugins/run/plugin.py | 8 ++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 0e160919049..31cd8d312ad 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -512,6 +512,22 @@ def deregister_file_run_metadata(self, filename): run = self.main.get_plugin(Plugins.Run, error=False) if run is not None: run.deregister_run_configuration_metadata(file_id) + + def change_register_file_run_metadata(self, old_filename, new_filename): + """Change the register name""" + is_selected = False + run = self.main.get_plugin(Plugins.Run, error=False) + if run is not None: + is_selected = ( + run.get_currently_selected_configuration() + == self.id_per_file[old_filename]) + self.deregister_file_run_metadata(old_filename) + self.register_file_run_metadata(new_filename) + + if is_selected: + run.switch_focused_run_configuration( + self.id_per_file[new_filename]) + @Slot(dict) def report_open_file(self, options): @@ -2671,9 +2687,8 @@ def renamed(self, source, dest, editorstack_id_str=None): filename = osp.abspath(to_text_string(source)) index = self.get_filename_index(filename) if index is not None or editorstack_id_str is not None: - self.deregister_file_run_metadata(filename) - self.register_file_run_metadata(dest) - + self.change_register_file_run_metadata(filename, dest) + for editorstack in self.editorstacks: if ( editorstack_id_str is None or diff --git a/spyder/plugins/run/models.py b/spyder/plugins/run/models.py index ac1e4d44f2c..b713675c137 100644 --- a/spyder/plugins/run/models.py +++ b/spyder/plugins/run/models.py @@ -259,8 +259,6 @@ def __setitem__(self, uuid: str, metadata: RunConfigurationMetadata): self.run_configurations[uuid] = metadata self.metadata_index[len(self.metadata_index)] = uuid self.inverted_index[uuid] = len(self.inverted_index) - if self.current_configuration is None: - self.current_configuration = uuid self.dataChanged.emit(self.createIndex(0, 0), self.createIndex(len(self.metadata_index), 0)) diff --git a/spyder/plugins/run/plugin.py b/spyder/plugins/run/plugin.py index 167f9dffc16..d72bd578af5 100644 --- a/spyder/plugins/run/plugin.py +++ b/spyder/plugins/run/plugin.py @@ -261,6 +261,14 @@ def register_run_configuration_metadata( """ self.get_container().register_run_configuration_metadata( provider, metadata) + + def get_currently_selected_configuration( + self + ): + """ + Get currently selected configuration + """ + return self.get_container().currently_selected_configuration def deregister_run_configuration_metadata(self, uuid: str): """ From 7389a7fe20a19fd32fd379514bf70e73fb7eac96 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Tue, 20 Jun 2023 15:17:16 +0200 Subject: [PATCH 13/16] fix not in list --- spyder/plugins/editor/plugin.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 31cd8d312ad..7ef604e17a3 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -520,7 +520,8 @@ def change_register_file_run_metadata(self, old_filename, new_filename): if run is not None: is_selected = ( run.get_currently_selected_configuration() - == self.id_per_file[old_filename]) + == self.id_per_file.get(old_filename, None) + ) self.deregister_file_run_metadata(old_filename) self.register_file_run_metadata(new_filename) From d9344d83366631899c90001b23368fa4393e9104 Mon Sep 17 00:00:00 2001 From: Quentin Peter Date: Thu, 22 Jun 2023 20:36:54 +0200 Subject: [PATCH 14/16] Apply suggestions from code review Co-authored-by: Carlos Cordoba --- spyder/app/tests/test_mainwindow.py | 10 ++++------ spyder/plugins/editor/plugin.py | 2 +- spyder/plugins/editor/widgets/editor.py | 3 ++- spyder/plugins/run/plugin.py | 4 +--- 4 files changed, 8 insertions(+), 11 deletions(-) diff --git a/spyder/app/tests/test_mainwindow.py b/spyder/app/tests/test_mainwindow.py index f2ac9bcac7e..c2820eb870b 100644 --- a/spyder/app/tests/test_mainwindow.py +++ b/spyder/app/tests/test_mainwindow.py @@ -6333,7 +6333,7 @@ def test_runfile_namespace(main_window, qtbot, tmpdir): @pytest.mark.skipif( os.name == 'nt', - reason="No quotes in windows filepath" + reason="No quotes on Windows file paths" ) def test_quotes_rename_ipy(main_window, qtbot, tmpdir): """ @@ -6371,7 +6371,7 @@ def test_quotes_rename_ipy(main_window, qtbot, tmpdir): # Make sure this works with ipy and renamed files too - # Rename the file to ipython and send the signal + # Rename the file to IPython and emit the signal for that rename_file(path, path[:-2] + "ipy") explorer = main_window.get_plugin(Plugins.Explorer) explorer.sig_file_renamed.emit(path, path[:-2] + "ipy") @@ -6385,7 +6385,7 @@ def test_quotes_rename_ipy(main_window, qtbot, tmpdir): assert "error" not in control.toPlainText() assert "\\.ipy" in control.toPlainText() - # Create an untiliteled file + # Create an untitled file main_window.editor.new() assert "untitled" in main_window.editor.get_current_filename() @@ -6400,7 +6400,7 @@ def test_quotes_rename_ipy(main_window, qtbot, tmpdir): assert "error" not in control.toPlainText() assert "untitled" in control.toPlainText() - # save in a new folder + # Save file in a new folder code_editor.set_text("print(19 + 780)") with tempfile.TemporaryDirectory() as td: @@ -6408,8 +6408,6 @@ def test_quotes_rename_ipy(main_window, qtbot, tmpdir): editorstack = main_window.editor.get_current_editorstack() editorstack.select_savename = lambda fn: os.path.join(td, "fn.ipy") main_window.editor.save() - - with qtbot.waitSignal(shell.executed): qtbot.mouseClick(main_window.run_cell_button, Qt.LeftButton) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 7ef604e17a3..96d03ac7423 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -514,7 +514,7 @@ def deregister_file_run_metadata(self, filename): run.deregister_run_configuration_metadata(file_id) def change_register_file_run_metadata(self, old_filename, new_filename): - """Change the register name""" + """Change the register name.""" is_selected = False run = self.main.get_plugin(Plugins.Run, error=False) if run is not None: diff --git a/spyder/plugins/editor/widgets/editor.py b/spyder/plugins/editor/widgets/editor.py index a6293d3eb3b..ca0b773b234 100644 --- a/spyder/plugins/editor/widgets/editor.py +++ b/spyder/plugins/editor/widgets/editor.py @@ -3547,7 +3547,8 @@ def file_saved_in_editorstack(self, editorstack_id_str, # to show how to use the file_saved signal (see above). @Slot(str, str, str) def file_renamed_in_data_in_editorstack( - self, original_filename, filename, editorstack_id_str): + self, original_filename, filename, editorstack_id_str + ): """A file was renamed in data in editorstack, this notifies others""" for editorstack in self.editorstacks: if str(id(editorstack)) != editorstack_id_str: diff --git a/spyder/plugins/run/plugin.py b/spyder/plugins/run/plugin.py index d72bd578af5..c70fe90b08a 100644 --- a/spyder/plugins/run/plugin.py +++ b/spyder/plugins/run/plugin.py @@ -262,9 +262,7 @@ def register_run_configuration_metadata( self.get_container().register_run_configuration_metadata( provider, metadata) - def get_currently_selected_configuration( - self - ): + def get_currently_selected_configuration(self): """ Get currently selected configuration """ From 3057cd6c51b71a34c065ab5f93fa0751f724223b Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 24 Jun 2023 11:59:33 -0500 Subject: [PATCH 15/16] Don't register IPython files to be run with Pylint and External console - That's because those plugins can't run them. - Also, transform a logging error into a debug message because there's nothing users can do about it. --- spyder/plugins/editor/utils/autosave.py | 2 +- spyder/plugins/externalconsole/plugin.py | 4 ++-- spyder/plugins/pylint/plugin.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spyder/plugins/editor/utils/autosave.py b/spyder/plugins/editor/utils/autosave.py index 7958ac8187b..f3629ddfad8 100644 --- a/spyder/plugins/editor/utils/autosave.py +++ b/spyder/plugins/editor/utils/autosave.py @@ -434,7 +434,7 @@ def file_renamed(self, old_name, new_name): old_hash = self.file_hashes[old_name] except KeyError: # This should not happen, but it does: spyder-ide/spyder#12396 - logger.error('KeyError when handling rename %s -> %s', + logger.debug('KeyError when handling rename %s -> %s', old_name, new_name) old_hash = None self.remove_autosave_file(old_name) diff --git a/spyder/plugins/externalconsole/plugin.py b/spyder/plugins/externalconsole/plugin.py index 9ad98b5cfcb..dd8cf899e45 100644 --- a/spyder/plugins/externalconsole/plugin.py +++ b/spyder/plugins/externalconsole/plugin.py @@ -59,7 +59,7 @@ def on_initialize(self): self.editor_configurations = [ { 'origin': self.NAME, - 'extension': ['py', 'ipy'], + 'extension': ['py'], 'contexts': [ { 'name': 'File' @@ -70,7 +70,7 @@ def on_initialize(self): self.executor_configuration = [ { - 'input_extension': ['py', 'ipy'], + 'input_extension': ['py'], 'context': { 'name': 'File' }, diff --git a/spyder/plugins/pylint/plugin.py b/spyder/plugins/pylint/plugin.py index ba39119cc26..1ecd2004459 100644 --- a/spyder/plugins/pylint/plugin.py +++ b/spyder/plugins/pylint/plugin.py @@ -86,7 +86,7 @@ def on_initialize(self): # Run configuration self.executor_configuration = [ { - 'input_extension': ['py', 'ipy'], + 'input_extension': ['py'], 'context': { 'name': 'File' }, From 33568c33bd569691b443bf1ce7dddc9b56f5c920 Mon Sep 17 00:00:00 2001 From: Carlos Cordoba Date: Sat, 24 Jun 2023 16:24:04 -0500 Subject: [PATCH 16/16] Editor: Avoid registering run metadata of new files twice on renames - That can happen for some rename operations. - Also, improve some docstrings, fix minor style issues and replace the usage of to_text_string by the str builtin. --- spyder/plugins/editor/plugin.py | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/spyder/plugins/editor/plugin.py b/spyder/plugins/editor/plugin.py index 96d03ac7423..731d522db03 100644 --- a/spyder/plugins/editor/plugin.py +++ b/spyder/plugins/editor/plugin.py @@ -501,10 +501,10 @@ def register_file_run_metadata(self, filename): run.register_run_configuration_metadata(self, metadata) def deregister_file_run_metadata(self, filename): - """Unregister opened files with the Run plugin.""" + """Unregister files with the Run plugin.""" if filename not in self.id_per_file: return - + file_id = self.id_per_file.pop(filename) self.file_per_id.pop(file_id) self.metadata_per_id.pop(file_id) @@ -514,21 +514,26 @@ def deregister_file_run_metadata(self, filename): run.deregister_run_configuration_metadata(file_id) def change_register_file_run_metadata(self, old_filename, new_filename): - """Change the register name.""" + """Change registered run metadata when renaming files.""" is_selected = False run = self.main.get_plugin(Plugins.Run, error=False) + if run is not None: is_selected = ( run.get_currently_selected_configuration() == self.id_per_file.get(old_filename, None) ) + self.deregister_file_run_metadata(old_filename) - self.register_file_run_metadata(new_filename) - + + # This avoids to register the run metadata of new_filename twice, which + # can happen for some rename operations. + if not self.id_per_file.get(new_filename): + self.register_file_run_metadata(new_filename) + if is_selected: run.switch_focused_run_configuration( self.id_per_file[new_filename]) - @Slot(dict) def report_open_file(self, options): @@ -1756,7 +1761,7 @@ def register_editorstack(self, editorstack): editorstack.file_saved.connect(self.file_saved_in_editorstack) editorstack.file_renamed_in_data.connect(self.renamed) editorstack.opened_files_list_changed.connect( - self.opened_files_list_changed) + self.opened_files_list_changed) editorstack.active_languages_stats.connect( self.update_active_languages) editorstack.sig_go_to_definition.connect( @@ -1770,7 +1775,7 @@ def register_editorstack(self, editorstack): editorstack.sig_update_code_analysis_actions.connect( self.update_todo_actions) editorstack.refresh_file_dependent_actions.connect( - self.refresh_file_dependent_actions) + self.refresh_file_dependent_actions) editorstack.refresh_save_all_action.connect(self.refresh_save_all_action) editorstack.sig_refresh_eol_chars.connect(self.refresh_eol_chars) editorstack.sig_refresh_formatting.connect(self.refresh_formatting) @@ -2687,6 +2692,7 @@ def renamed(self, source, dest, editorstack_id_str=None): """ filename = osp.abspath(to_text_string(source)) index = self.get_filename_index(filename) + if index is not None or editorstack_id_str is not None: self.change_register_file_run_metadata(filename, dest) @@ -2696,9 +2702,10 @@ def renamed(self, source, dest, editorstack_id_str=None): str(id(editorstack)) != editorstack_id_str ): editorstack.rename_in_data( - filename, new_filename=to_text_string(dest)) - self.editorstacks[0].autosave.file_renamed( - filename, to_text_string(dest)) + filename, new_filename=str(dest) + ) + + self.editorstacks[0].autosave.file_renamed(filename, str(dest)) def renamed_tree(self, source, dest): """Directory was renamed in file explorer or in project explorer."""