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: Enable running renamed and IPython files again #20762

Merged
merged 22 commits into from
Jun 25, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
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
4 changes: 2 additions & 2 deletions spyder/app/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}

Expand Down
91 changes: 90 additions & 1 deletion spyder/app/tests/test_mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -6331,5 +6331,94 @@ 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,
and ipy files.
"""
# create a file with a funky name
path = "a'b\"c\\.py"
file = tmpdir.join(path)
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()

# 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()

# 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__":
pytest.main()
8 changes: 4 additions & 4 deletions spyder/plugins/debugger/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def on_initialize(self):

self.python_editor_run_configuration = {
'origin': self.NAME,
'extension': 'py',
'extension': ['py', 'ipy'],
'contexts': [
{
'name': 'File'
Expand All @@ -92,7 +92,7 @@ def on_initialize(self):

self.executor_configuration = [
{
'input_extension': 'py',
'input_extension': ['py', 'ipy'],
'context': {
'name': 'File'
},
Expand All @@ -102,7 +102,7 @@ def on_initialize(self):
'priority': 10
},
{
'input_extension': 'py',
'input_extension': ['py', 'ipy'],
'context': {
'name': 'Cell'
},
Expand All @@ -112,7 +112,7 @@ def on_initialize(self):
'priority': 10
},
{
'input_extension': 'py',
'input_extension': ['py', 'ipy'],
'context': {
'name': 'Selection'
},
Expand Down
160 changes: 94 additions & 66 deletions spyder/plugins/editor/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,13 +263,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}
]
}
},
]

run = self.main.get_plugin(Plugins.Run, error=False)
Expand Down Expand Up @@ -473,7 +473,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 = self.get_conf('file_uuids', default={})
file_id = all_uuids.get(filename, str(uuid.uuid4()))
Expand All @@ -489,7 +489,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
Expand All @@ -499,6 +499,36 @@ 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)

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.get(old_filename, None)
)
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):
Expand All @@ -517,7 +547,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:
Expand Down Expand Up @@ -1724,8 +1754,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(
Expand Down Expand Up @@ -1789,13 +1818,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:]
Expand All @@ -1817,14 +1841,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"""
Expand Down Expand Up @@ -2659,7 +2675,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.

Expand All @@ -2669,10 +2687,16 @@ 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.change_register_file_run_metadata(filename, dest)

for editorstack in self.editorstacks:
editorstack.rename_in_data(filename,
new_filename=to_text_string(dest))
if (
editorstack_id_str is None or
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))

Expand Down Expand Up @@ -3075,48 +3099,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, filename_ext)
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,
Expand Down
Loading