Skip to content

Commit c7050fb

Browse files
authored
Merge pull request #24826 from hlouzada/fix/remoteclient/config
PR: Update connection info after editing it and fix some bugs with remote consoles and file explorer (Remote client/IPython console/Files)
2 parents 595113c + fe55984 commit c7050fb

File tree

13 files changed

+194
-141
lines changed

13 files changed

+194
-141
lines changed

spyder/app/mainwindow.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1120,7 +1120,8 @@ def closing(self, cancelable=False, close_immediately=False):
11201120
if self.already_closed or self.is_starting_up:
11211121
return True
11221122

1123-
self.layouts.save_visible_plugins()
1123+
if self.layouts is not None:
1124+
self.layouts.save_visible_plugins()
11241125

11251126
self.plugin_registry = PLUGIN_REGISTRY
11261127

spyder/plugins/explorer/plugin.py

Lines changed: 29 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ def on_application_available(self):
242242
application = self.get_plugin(Plugins.Application)
243243
self.sig_open_file_requested.connect(application.open_file_in_plugin)
244244

245+
@on_plugin_available(plugin=Plugins.RemoteClient)
246+
def on_remote_client_available(self):
247+
remoteclient = self.get_plugin(Plugins.RemoteClient)
248+
remoteclient.sig_server_stopped.connect(self._on_server_stopped)
249+
245250
@on_plugin_teardown(plugin=Plugins.Editor)
246251
def on_editor_teardown(self):
247252
editor = self.get_plugin(Plugins.Editor)
@@ -282,12 +287,8 @@ def on_application_teardown(self):
282287

283288
@on_plugin_teardown(plugin=Plugins.RemoteClient)
284289
def on_remote_client_teardown(self):
285-
if len(self._file_managers):
286-
for file_manager in self._file_managers.values():
287-
AsyncDispatcher(
288-
loop=file_manager.session._loop, early_return=False
289-
)(file_manager.close)()
290-
self._file_managers = {}
290+
remoteclient = self.get_plugin(Plugins.RemoteClient)
291+
remoteclient.sig_server_stopped.disconnect(self._on_server_stopped)
291292

292293
def on_close(self, cancelable=False):
293294
if len(self._file_managers):
@@ -297,20 +298,6 @@ def on_close(self, cancelable=False):
297298
)(file_manager.close)()
298299
self._file_managers = {}
299300

300-
# ---- Private API
301-
# ------------------------------------------------------------------------
302-
@AsyncDispatcher(loop="explorer")
303-
async def _get_remote_files_manager(self, server_id):
304-
remoteclient = self.get_plugin(Plugins.RemoteClient, error=False)
305-
if not remoteclient:
306-
return
307-
if server_id not in self._file_managers:
308-
self._file_managers[server_id] = remoteclient.get_file_api(
309-
server_id
310-
)()
311-
await self._file_managers[server_id].connect()
312-
return self._file_managers.get(server_id, None)
313-
314301
# ---- Public API
315302
# ------------------------------------------------------------------------
316303
def chdir(self, directory, emit=True, server_id=None):
@@ -368,3 +355,25 @@ def _chdir_from_working_directory(
368355
if sender_plugin != self.NAME:
369356
self.chdir(directory, emit=False, server_id=server_id)
370357
self.refresh(directory)
358+
359+
@AsyncDispatcher(loop="explorer")
360+
async def _get_remote_files_manager(self, server_id):
361+
remoteclient = self.get_plugin(Plugins.RemoteClient, error=False)
362+
if not remoteclient:
363+
return
364+
if server_id not in self._file_managers:
365+
self._file_managers[server_id] = remoteclient.get_file_api(
366+
server_id
367+
)()
368+
await self._file_managers[server_id].connect()
369+
return self._file_managers.get(server_id, None)
370+
371+
def _on_server_stopped(self, server_id):
372+
file_manager = self._file_managers.get(server_id)
373+
if file_manager:
374+
AsyncDispatcher(
375+
loop=file_manager.session._loop, early_return=False
376+
)(file_manager.close)()
377+
self._file_managers.pop(server_id)
378+
379+
self.get_widget().reset_remote_treewidget(server_id)

spyder/plugins/explorer/widgets/main_widget.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
# Standard library imports
1212
import logging
1313
import os.path as osp
14-
import sys
1514

1615
# Third-party imports
1716
from qtpy.QtCore import Qt, Signal, Slot
@@ -506,6 +505,8 @@ def update_history(self, directory):
506505
"""
507506
self.treewidget.update_history(directory)
508507

508+
def reset_remote_treewidget(self, server_id):
509+
self.remote_treewidget.reset(server_id)
509510

510511
# =============================================================================
511512
# Tests

spyder/plugins/explorer/widgets/remote_explorer.py

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ def __init__(self, parent=None, class_parent=None, files=None):
108108

109109
# General attributes
110110
self.remote_files_manager = None
111-
self.server_id = None
112-
self.root_prefix = ""
111+
self.server_id: str | None = None
112+
self.root_prefix: dict[str, str] = {}
113113

114114
self.background_files_load = set()
115115
self.extra_files = []
@@ -559,7 +559,7 @@ async def _do_remote_upload_file(self, local_path):
559559
return
560560

561561
remote_file = posixpath.join(
562-
self.root_prefix, os.path.basename(local_path)
562+
self.root_prefix[self.server_id], os.path.basename(local_path)
563563
)
564564
file_content = None
565565

@@ -607,7 +607,9 @@ async def _do_remote_ls(self, path, server_id):
607607
init_files_display = self.get_conf("init_files_display")
608608
generator = self.remote_files_manager.ls(path)
609609
async for file in generator:
610-
file_name = os.path.relpath(file["name"], self.root_prefix)
610+
file_name = os.path.relpath(
611+
file["name"], self.root_prefix[self.server_id]
612+
)
611613
file_type = file["type"]
612614
if len(files) < init_files_display:
613615
if not self.get_conf(
@@ -655,7 +657,9 @@ async def _get_extra_files(self, generator, already_added):
655657
self.more_files_available = True
656658
break
657659

658-
file_name = os.path.relpath(file["name"], self.root_prefix)
660+
file_name = os.path.relpath(
661+
file["name"], self.root_prefix[self.server_id]
662+
)
659663
file_type = file["type"]
660664
if not self.get_conf("show_hidden") and file_name.startswith("."):
661665
continue
@@ -679,7 +683,7 @@ async def _get_extra_files(self, generator, already_added):
679683
)
680684

681685
def _new_item(self, new_name, for_file=False, with_content=False):
682-
new_path = posixpath.join(self.root_prefix, new_name)
686+
new_path = posixpath.join(self.root_prefix[self.server_id], new_name)
683687
if not with_content:
684688
self._do_remote_new(new_path, for_file=for_file).connect(
685689
self._on_remote_new
@@ -725,11 +729,12 @@ def chdir(
725729
self.history.append(directory)
726730
self.histindex = len(self.history) - 1
727731

728-
if directory == self.root_prefix:
732+
if directory == self.root_prefix.get(server_id):
729733
return
730-
self.root_prefix = directory
734+
731735
if server_id:
732736
self.server_id = server_id
737+
self.root_prefix[self.server_id] = directory
733738
if remote_files_manager:
734739
self.remote_files_manager = remote_files_manager
735740
self.refresh(force_current=True)
@@ -762,7 +767,7 @@ def set_files(self, files, reset=True):
762767

763768
for file in files:
764769
path = file["name"]
765-
name = os.path.relpath(path, self.root_prefix)
770+
name = os.path.relpath(path, self.root_prefix[self.server_id])
766771

767772
file_type = file["type"]
768773
icon = ima.icon("FileIcon")
@@ -827,16 +832,16 @@ def fetch_more_files(self):
827832
)
828833

829834
def set_current_folder(self, folder):
830-
self.root_prefix = folder
835+
self.root_prefix[self.server_id] = folder
831836
return self.model.invisibleRootItem()
832837

833838
def get_current_folder(self):
834-
return self.root_prefix
839+
return self.root_prefix[self.server_id]
835840

836841
def go_to_parent_directory(self):
837-
parent_directory = os.path.dirname(self.root_prefix)
842+
parent_directory = os.path.dirname(self.root_prefix[self.server_id])
838843
logger.debug(
839-
f"Going to parent directory of {self.root_prefix}: "
844+
f"Going to parent directory of {self.root_prefix[self.server_id]}: "
840845
f"{parent_directory}"
841846
)
842847
self.chdir(parent_directory)
@@ -859,7 +864,7 @@ def go_to_next_directory(self):
859864
def refresh(self, new_path=None, force_current=False):
860865
if force_current:
861866
if new_path is None:
862-
new_path = self.root_prefix
867+
new_path = self.root_prefix.get(self.server_id)
863868
self._do_remote_ls(new_path, self.server_id).connect(
864869
self._on_remote_ls
865870
)
@@ -941,7 +946,9 @@ def copy_paste_item(self):
941946
data = self.model.data(data_index, Qt.UserRole + 1)
942947
if data:
943948
old_path = data["name"]
944-
relpath = os.path.relpath(old_path, self.root_prefix)
949+
relpath = os.path.relpath(
950+
old_path, self.root_prefix[self.server_id]
951+
)
945952
new_relpath, valid = QInputDialog.getText(
946953
self,
947954
_("Copy and Paste"),
@@ -950,7 +957,9 @@ def copy_paste_item(self):
950957
relpath,
951958
)
952959
if valid:
953-
new_path = posixpath.join(self.root_prefix, new_relpath)
960+
new_path = posixpath.join(
961+
self.root_prefix[self.server_id], new_relpath
962+
)
954963
self._do_remote_copy_paste(old_path, new_path).connect(
955964
self._on_remote_copy_paste
956965
)
@@ -968,12 +977,16 @@ def rename_item(self):
968977
data = self.model.data(data_index, Qt.UserRole + 1)
969978
if data:
970979
old_path = data["name"]
971-
relpath = os.path.relpath(old_path, self.root_prefix)
980+
relpath = os.path.relpath(
981+
old_path, self.root_prefix[self.server_id]
982+
)
972983
new_relpath, valid = QInputDialog.getText(
973984
self, _("Rename"), _("New name:"), QLineEdit.Normal, relpath
974985
)
975986
if valid:
976-
new_path = posixpath.join(self.root_prefix, new_relpath)
987+
new_path = posixpath.join(
988+
self.root_prefix[self.server_id], new_relpath
989+
)
977990
self._do_remote_rename(old_path, str(new_path)).connect(
978991
self._on_remote_rename
979992
)
@@ -984,7 +997,7 @@ def copy_path(self):
984997
not self.view.currentIndex()
985998
or not self.view.currentIndex().isValid()
986999
):
987-
path = self.root_prefix
1000+
path = self.root_prefix[self.server_id]
9881001
else:
9891002
source_index = self.proxy_model.mapToSource(
9901003
self.view.currentIndex()
@@ -1009,7 +1022,7 @@ def delete_item(self):
10091022
data = self.model.data(data_index, Qt.UserRole + 1)
10101023
if data:
10111024
path = data["name"]
1012-
filename = os.path.relpath(path, self.root_prefix)
1025+
filename = os.path.relpath(path, self.root_prefix[self.server_id])
10131026
result = QMessageBox.warning(
10141027
self,
10151028
_("Delete"),
@@ -1037,7 +1050,7 @@ def download_item(self):
10371050
data = self.model.data(data_index, Qt.UserRole + 1)
10381051
if data:
10391052
path = data["name"]
1040-
filename = os.path.relpath(path, self.root_prefix)
1053+
filename = os.path.relpath(path, self.root_prefix[self.server_id])
10411054
is_file = data["type"] == "file"
10421055
remote_filename = filename if is_file else f"{filename}.zip"
10431056

@@ -1068,3 +1081,6 @@ def upload_file(self):
10681081
self._on_remote_upload_file
10691082
)
10701083
self.sig_start_spinner_requested.emit()
1084+
1085+
def reset(self, server_id):
1086+
self.root_prefix[server_id] = None

spyder/plugins/ipythonconsole/widgets/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ def finish_close(self, is_last_client, close_console, debugging):
673673
self.shutdown(is_last_client, close_console=close_console)
674674

675675
# Close jupyter api regardless of the kernel state
676-
if self.is_remote():
676+
if self.is_remote() and not self._jupyter_api.closed:
677677
AsyncDispatcher(
678678
loop=self._jupyter_api.session._loop, early_return=False
679679
)(self._jupyter_api.close)()

spyder/plugins/ipythonconsole/widgets/main_widget.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2186,13 +2186,20 @@ def get_related_clients(self, client, clients_list=None):
21862186
"""
21872187
Get all other clients that are connected to the same kernel as `client`
21882188
"""
2189+
# At the moment it's not possible to have two clients connected to the
2190+
# same remote kernel.
2191+
if client.is_remote():
2192+
return []
2193+
21892194
if clients_list is None:
21902195
clients_list = self.clients
2196+
21912197
related_clients = []
21922198
for cl in clients_list:
21932199
if (cl.connection_file == client.connection_file and
21942200
cl is not client):
21952201
related_clients.append(cl)
2202+
21962203
return related_clients
21972204

21982205
def close_related_clients(self, client):
@@ -2762,7 +2769,8 @@ def setup_remote_consoles_submenu(self, render=True):
27622769

27632770
if self._remote_consoles_menu is None:
27642771
self._remote_consoles_menu = self.create_menu(
2765-
RemoteConsolesMenus.RemoteConsoles, _("New console in remote server")
2772+
RemoteConsolesMenus.RemoteConsoles,
2773+
_("New console in remote server")
27662774
)
27672775

27682776
self._remote_consoles_menu.clear_actions()

spyder/plugins/remoteclient/api/manager/base.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ def __init__(
9393
if not get_debug_level():
9494
self.logger.setLevel(logging.DEBUG)
9595

96-
if self._plugin is not None:
96+
if self._plugin is not None and not self.logger.hasHandlers():
9797
self.logger.addHandler(SpyderRemoteAPILoggerHandler(self))
9898

9999
@AsyncDispatcher(loop="asyncssh", early_return=False)

spyder/plugins/remoteclient/api/modules/base.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,12 @@
77
from __future__ import annotations
88

99
import asyncio
10-
import contextlib
11-
import json
1210
import logging
13-
import struct
1411
import typing
1512
from abc import abstractmethod
16-
import uuid
1713

1814
import aiohttp
1915
import yarl
20-
import zmq.asyncio
2116

2217
from spyder.api.asyncdispatcher import AsyncDispatcher
2318
from spyder.api.utils import ABCMeta, abstract_attribute

0 commit comments

Comments
 (0)