From 5d8d5536cb91b5ca5028d2a12a25f49889545911 Mon Sep 17 00:00:00 2001 From: Hendrik Dumith Louzada Date: Wed, 22 Jan 2025 13:26:55 -0300 Subject: [PATCH] refac: fix line length with black --- spyder/plugins/remoteclient/__init__.py | 4 +- spyder/plugins/remoteclient/api/__init__.py | 91 +++++++++++++------ .../plugins/remoteclient/api/modules/base.py | 76 ++++++++++++---- .../remoteclient/api/modules/file_services.py | 25 +++-- spyder/plugins/remoteclient/api/ssh.py | 8 +- spyder/plugins/remoteclient/plugin.py | 20 +++- spyder/plugins/remoteclient/tests/conftest.py | 8 +- .../plugins/remoteclient/tests/test_files.py | 20 ++-- .../remoteclient/tests/test_ipythonconsole.py | 19 +++- .../remoteclient/utils/installation.py | 8 +- .../remoteclient/widgets/connectiondialog.py | 88 +++++++++++++----- .../remoteclient/widgets/connectionstatus.py | 12 ++- .../plugins/remoteclient/widgets/container.py | 24 +++-- 13 files changed, 291 insertions(+), 112 deletions(-) diff --git a/spyder/plugins/remoteclient/__init__.py b/spyder/plugins/remoteclient/__init__.py index cb26f7cf8f0..3642cd9706f 100644 --- a/spyder/plugins/remoteclient/__init__.py +++ b/spyder/plugins/remoteclient/__init__.py @@ -14,4 +14,6 @@ # Required version of spyder-remote-services SPYDER_REMOTE_MIN_VERSION = "1.0.0" SPYDER_REMOTE_MAX_VERSION = "2.0.0" -SPYDER_REMOTE_VERSION = f">={SPYDER_REMOTE_MIN_VERSION},<{SPYDER_REMOTE_MAX_VERSION}" +SPYDER_REMOTE_VERSION = ( + f">={SPYDER_REMOTE_MIN_VERSION},<{SPYDER_REMOTE_MAX_VERSION}" +) diff --git a/spyder/plugins/remoteclient/api/__init__.py b/spyder/plugins/remoteclient/api/__init__.py index 474ad3565cf..80185fc88a5 100644 --- a/spyder/plugins/remoteclient/api/__init__.py +++ b/spyder/plugins/remoteclient/api/__init__.py @@ -45,7 +45,9 @@ ) if typing.TYPE_CHECKING: - from spyder.plugins.remoteclient.api.modules.base import SpyderBaseJupyterAPIType + from spyder.plugins.remoteclient.api.modules.base import ( + SpyderBaseJupyterAPIType, + ) # ---- Constants # ----------------------------------------------------------------------------- @@ -99,9 +101,7 @@ class SpyderRemoteAPIManager: START_SERVER_COMMAND = ( f"/${{HOME}}/.local/bin/micromamba run -n {SERVER_ENV} spyder-server" ) - GET_SERVER_INFO_COMMAND = ( - f"/${{HOME}}/.local/bin/micromamba run -n {SERVER_ENV} spyder-server info" - ) + GET_SERVER_INFO_COMMAND = f"/${{HOME}}/.local/bin/micromamba run -n {SERVER_ENV} spyder-server info" def __init__(self, conf_id, options: SSHClientOptions, _plugin=None): self._config_id = conf_id @@ -135,7 +135,9 @@ def __init__(self, conf_id, options: SSHClientOptions, _plugin=None): def __emit_connection_status(self, status, message): if self._plugin is not None: self._plugin.sig_connection_status_changed.emit( - ConnectionInfo(id=self.config_id, status=status, message=message) + ConnectionInfo( + id=self.config_id, status=status, message=message + ) ) def __emit_version_mismatch(self, version: str): @@ -161,7 +163,10 @@ def server_started(self): @property def ssh_is_connected(self): """Check if SSH connection is open.""" - return self.__connection_established.is_set() and not self.__creating_connection + return ( + self.__connection_established.is_set() + and not self.__creating_connection + ) @property def port_is_forwarded(self): @@ -374,7 +379,8 @@ async def __start_remote_server(self): if self._server_info != info: self._server_info = info self.logger.info( - "Different server info, updating info " f"for {self.peer_host}" + "Different server info, updating info " + f"for {self.peer_host}" ) if await self.forward_local_port(): self.__emit_connection_status( @@ -384,7 +390,8 @@ async def __start_remote_server(self): return True self.logger.error( - "Error forwarding local port, server might not be " "reachable" + "Error forwarding local port, server might not be " + "reachable" ) self.__emit_connection_status( ConnectionStatus.Error, @@ -400,9 +407,11 @@ async def __start_remote_server(self): self.logger.debug(f"Starting remote server for {self.peer_host}") try: - self._remote_server_process = await self._ssh_connection.create_process( - self.START_SERVER_COMMAND, - stderr=asyncssh.STDOUT, + self._remote_server_process = ( + await self._ssh_connection.create_process( + self.START_SERVER_COMMAND, + stderr=asyncssh.STDOUT, + ) ) except (OSError, asyncssh.Error, ValueError) as e: self.logger.error(f"Error starting remote server: {e}") @@ -431,7 +440,8 @@ async def __start_remote_server(self): self._server_info = info self.logger.info( - f"Remote server started for {self.peer_host} at port " f"{self.server_port}" + f"Remote server started for {self.peer_host} at port " + f"{self.server_port}" ) if await self.forward_local_port(): @@ -527,7 +537,9 @@ async def __install_remote_server(self): ) return False - self.logger.debug(f"Installing spyder-remote-server on {self.peer_host}") + self.logger.debug( + f"Installing spyder-remote-server on {self.peer_host}" + ) try: command = get_installer_command(self.options["platform"]) @@ -608,7 +620,9 @@ async def __create_new_connection(self) -> bool: ) connect_kwargs = { - k: v for k, v in self.options.items() if k not in self._extra_options + k: v + for k, v in self.options.items() + if k not in self._extra_options } self.logger.debug("Opening SSH connection") try: @@ -695,7 +709,9 @@ async def close_port_forwarder(self): async def stop_remote_server(self): """Close remote server.""" if not self.server_started: - self.logger.warning(f"Remote server is not running for {self.peer_host}") + self.logger.warning( + f"Remote server is not running for {self.peer_host}" + ) return False if not self.ssh_is_connected: @@ -712,12 +728,17 @@ async def stop_remote_server(self): f"{self._server_info['pid']}" ) try: - async with JupyterAPI(self.server_url, api_token=self.api_token) as jupyter: + async with JupyterAPI( + self.server_url, api_token=self.api_token + ) as jupyter: await jupyter.shutdown_server() except Exception as err: self.logger.exception("Error stopping remote server", exc_info=err) - if self._remote_server_process and not self._remote_server_process.is_closing(): + if ( + self._remote_server_process + and not self._remote_server_process.is_closing() + ): self._remote_server_process.terminate() await self._remote_server_process.wait_closed() @@ -744,7 +765,9 @@ async def close_ssh_connection(self): ) # --- Kernel Management - async def start_new_kernel_ensure_server(self, _retries=5) -> KernelConnectionInfo: + async def start_new_kernel_ensure_server( + self, _retries=5 + ) -> KernelConnectionInfo: """Launch a new kernel ensuring the remote server is running. Parameters @@ -758,7 +781,9 @@ async def start_new_kernel_ensure_server(self, _retries=5) -> KernelConnectionIn The kernel connection information. """ if not await self.ensure_connection_and_server(): - self.logger.error("Cannot launch kernel, remote server is not running") + self.logger.error( + "Cannot launch kernel, remote server is not running" + ) return {} # This is necessary to avoid an error when the server has not started @@ -794,7 +819,9 @@ async def get_kernel_info_ensure_server( The kernel connection information. """ if not await self.ensure_connection_and_server(): - self.logger.error("Cannot launch kernel, remote server is not running") + self.logger.error( + "Cannot launch kernel, remote server is not running" + ) return {} # This is necessary to avoid an error when the server has not started @@ -816,14 +843,18 @@ async def get_kernel_info_ensure_server( async def start_new_kernel(self, kernel_spec=None) -> KernelInfo: """Start new kernel.""" - async with JupyterAPI(self.server_url, api_token=self.api_token) as jupyter: + async with JupyterAPI( + self.server_url, api_token=self.api_token + ) as jupyter: response = await jupyter.create_kernel(kernel_spec=kernel_spec) self.logger.info(f"Kernel started with ID {response['id']}") return response async def list_kernels(self) -> list[KernelInfo]: """List kernels.""" - async with JupyterAPI(self.server_url, api_token=self.api_token) as jupyter: + async with JupyterAPI( + self.server_url, api_token=self.api_token + ) as jupyter: response = await jupyter.list_kernels() self.logger.info(f"Kernels listed for {self.peer_host}") @@ -831,7 +862,9 @@ async def list_kernels(self) -> list[KernelInfo]: async def get_kernel_info(self, kernel_id) -> KernelInfo: """Get kernel info.""" - async with JupyterAPI(self.server_url, api_token=self.api_token) as jupyter: + async with JupyterAPI( + self.server_url, api_token=self.api_token + ) as jupyter: response = await jupyter.get_kernel(kernel_id=kernel_id) self.logger.info(f"Kernel info retrieved for ID {kernel_id}") @@ -839,7 +872,9 @@ async def get_kernel_info(self, kernel_id) -> KernelInfo: async def terminate_kernel(self, kernel_id) -> bool: """Terminate kernel.""" - async with JupyterAPI(self.server_url, api_token=self.api_token) as jupyter: + async with JupyterAPI( + self.server_url, api_token=self.api_token + ) as jupyter: response = await jupyter.delete_kernel(kernel_id=kernel_id) self.logger.info(f"Kernel terminated for ID {kernel_id}") @@ -847,7 +882,9 @@ async def terminate_kernel(self, kernel_id) -> bool: async def interrupt_kernel(self, kernel_id) -> bool: """Interrupt kernel.""" - async with JupyterAPI(self.server_url, api_token=self.api_token) as jupyter: + async with JupyterAPI( + self.server_url, api_token=self.api_token + ) as jupyter: response = await jupyter.interrupt_kernel(kernel_id=kernel_id) self.logger.info(f"Kernel interrupted for ID {kernel_id}") @@ -855,7 +892,9 @@ async def interrupt_kernel(self, kernel_id) -> bool: async def restart_kernel(self, kernel_id) -> bool: """Restart kernel.""" - async with JupyterAPI(self.server_url, api_token=self.api_token) as jupyter: + async with JupyterAPI( + self.server_url, api_token=self.api_token + ) as jupyter: response = await jupyter.restart_kernel(kernel_id=kernel_id) self.logger.info(f"Kernel restarted for ID {kernel_id}") diff --git a/spyder/plugins/remoteclient/api/modules/base.py b/spyder/plugins/remoteclient/api/modules/base.py index d65d094d07c..699e1535cb3 100644 --- a/spyder/plugins/remoteclient/api/modules/base.py +++ b/spyder/plugins/remoteclient/api/modules/base.py @@ -57,7 +57,9 @@ async def basic_authentication(hub_url, username, password, verify_ssl=True): return session -async def keycloak_authentication(hub_url, username, password, verify_ssl=True): +async def keycloak_authentication( + hub_url, username, password, verify_ssl=True +): session = aiohttp.ClientSession( headers={"Referer": str(yarl.URL(hub_url) / "hub" / "api")}, connector=aiohttp.TCPConnector(ssl=None if verify_ssl else False), @@ -106,7 +108,9 @@ async def __aenter__(self): ) self.api_token = await self.create_token(self.username) await self.session.close() - logger.debug("upgrading basic authentication to token authentication") + logger.debug( + "upgrading basic authentication to token authentication" + ) self.session = await token_authentication( self.api_token, verify_ssl=self.verify_ssl ) @@ -119,7 +123,9 @@ async def __aenter__(self): ) self.api_token = await self.create_token(self.username) await self.session.close() - logger.debug("upgrading keycloak authentication to token authentication") + logger.debug( + "upgrading keycloak authentication to token authentication" + ) self.session = await token_authentication( self.api_token, verify_ssl=self.verify_ssl ) @@ -142,7 +148,9 @@ async def ensure_user(self, username, create_user=False): return user async def get_user(self, username): - async with self.session.get(self.api_url / "users" / username) as response: + async with self.session.get( + self.api_url / "users" / username + ) as response: if response.status == 200: return await response.json() elif response.status == 404: @@ -150,7 +158,9 @@ async def get_user(self, username): return None async def create_user(self, username): - async with self.session.post(self.api_url / "users" / username) as response: + async with self.session.post( + self.api_url / "users" / username + ) as response: if response.status == 201: logger.info(f"created username={username}") response = await response.json() @@ -160,11 +170,15 @@ async def create_user(self, username): raise ValueError(f"username={username} already exists") async def delete_user(self, username): - async with self.session.delete(self.api_url / "users" / username) as response: + async with self.session.delete( + self.api_url / "users" / username + ) as response: if response.status == 204: logger.info(f"deleted username={username}") elif response.status == 404: - raise ValueError(f"username={username} does not exist cannot delete") + raise ValueError( + f"username={username} does not exist cannot delete" + ) async def ensure_server( self, username, timeout, user_options=None, create_user=False @@ -186,12 +200,16 @@ async def ensure_server( await asyncio.sleep(5) total_time = time.time() - start_time if total_time > timeout: - logger.error(f"jupyterhub server creation timeout={timeout:.0f} [s]") + logger.error( + f"jupyterhub server creation timeout={timeout:.0f} [s]" + ) raise TimeoutError( f"jupyterhub server creation timeout={timeout:.0f} [s]" ) - logger.info(f"pending spawn polling for seconds={total_time:.0f} [s]") + logger.info( + f"pending spawn polling for seconds={total_time:.0f} [s]" + ) async def ensure_server_deleted(self, username, timeout): user = await self.get_user(username) @@ -207,12 +225,16 @@ async def ensure_server_deleted(self, username, timeout): await asyncio.sleep(5) total_time = time.time() - start_time if total_time > timeout: - logger.error(f"jupyterhub server deletion timeout={timeout:.0f} [s]") + logger.error( + f"jupyterhub server deletion timeout={timeout:.0f} [s]" + ) raise TimeoutError( f"jupyterhub server deletion timeout={timeout:.0f} [s]" ) - logger.info(f"pending deletion polling for seconds={total_time:.0f} [s]") + logger.info( + f"pending deletion polling for seconds={total_time:.0f} [s]" + ) async def create_token(self, username, token_name=None): token_name = token_name or "jhub-client" @@ -229,10 +251,13 @@ async def create_server(self, username, user_options=None): self.api_url / "users" / username / "server", json=user_options ) as response: logger.info( - f"creating cluster username={username} " f"user_options={user_options}" + f"creating cluster username={username} " + f"user_options={user_options}" ) if response.status == 400: - raise ValueError(f"server for username={username} is already running") + raise ValueError( + f"server for username={username} is already running" + ) elif response.status == 201: logger.info( f"created server for username={username} with " @@ -315,7 +340,9 @@ def __init__(self, notebook_url, api_token, verify_ssl=True): async def __aenter__(self): self.session = aiohttp.ClientSession( headers={"Authorization": f"token {self.api_token}"}, - connector=aiohttp.TCPConnector(ssl=None if self.verify_ssl else False), + connector=aiohttp.TCPConnector( + ssl=None if self.verify_ssl else False + ), timeout=aiohttp.ClientTimeout(total=REQUEST_TIMEOUT), ) return self @@ -328,7 +355,9 @@ async def create_kernel(self, kernel_spec=None): if kernel_spec: data["name"] = kernel_spec - async with self.session.post(self.api_url / "kernels", json=data) as response: + async with self.session.post( + self.api_url / "kernels", json=data + ) as response: if response.status != 201: logger.error(f"failed to create kernel_spec={kernel_spec}") raise ValueError(await response.text()) @@ -366,7 +395,9 @@ async def ensure_kernel(self, kernel_spec=None): ) async def get_kernel(self, kernel_id): - async with self.session.get(self.api_url / "kernels" / kernel_id) as response: + async with self.session.get( + self.api_url / "kernels" / kernel_id + ) as response: if response.status == 404: return {} elif response.status == 200: @@ -428,9 +459,13 @@ def __init__(self, kernel_url, api_token, verify_ssl=True): async def __aenter__(self): self.session = aiohttp.ClientSession( headers={"Authorization": f"token {self.api_token}"}, - connector=aiohttp.TCPConnector(ssl=None if self.verify_ssl else False), + connector=aiohttp.TCPConnector( + ssl=None if self.verify_ssl else False + ), + ) + self.websocket = await self.session.ws_connect( + self.kernel_url / "channels" ) - self.websocket = await self.session.ws_connect(self.kernel_url / "channels") return self async def __aexit__(self, exc_type, exc, tb): @@ -476,7 +511,10 @@ async def send_code(self, username, code, wait=True, timeout=None): msg = msg_text.json() - if "parent_header" in msg and msg["parent_header"].get("msg_id") == msg_id: + if ( + "parent_header" in msg + and msg["parent_header"].get("msg_id") == msg_id + ): # These are responses to our request if msg["channel"] == "iopub": if msg["msg_type"] == "execute_result": diff --git a/spyder/plugins/remoteclient/api/modules/file_services.py b/spyder/plugins/remoteclient/api/modules/file_services.py index 61109482455..420608a8c31 100644 --- a/spyder/plugins/remoteclient/api/modules/file_services.py +++ b/spyder/plugins/remoteclient/api/modules/file_services.py @@ -10,9 +10,7 @@ from spyder.plugins.remoteclient.api.modules.base import SpyderBaseJupyterAPI from spyder.plugins.remoteclient.api import SpyderRemoteAPIManager -SPYDER_PLUGIN_NAME = ( - "spyder-services" # jupyter server's extension name for spyder-remote-services -) +SPYDER_PLUGIN_NAME = "spyder-services" # jupyter server's extension name for spyder-remote-services class SpyderServicesError(Exception): ... @@ -26,7 +24,9 @@ def __init__(self, type, message, url, tracebacks): self.tracebacks = tracebacks def __str__(self): - return f"(type='{self.type}', message='{self.message}', url='{self.url}')" + return ( + f"(type='{self.type}', message='{self.message}', url='{self.url}')" + ) class RemoteOSError(OSError, RemoteFileServicesError): @@ -96,7 +96,10 @@ async def _check_connection(self): await self._websocket.close() if status.data == 1002: data = json.loads(status.extra) - if data["status"] in (HTTPStatus.LOCKED, HTTPStatus.EXPECTATION_FAILED): + if data["status"] in ( + HTTPStatus.LOCKED, + HTTPStatus.EXPECTATION_FAILED, + ): raise RemoteOSError.from_json( data, url=self._websocket._response.url ) @@ -151,7 +154,9 @@ async def _send_request(self, method: str, **args): await self._websocket.send_json({"method": method, **args}) async def _get_response(self, timeout=None): - message = json.loads(await self._websocket.receive_bytes(timeout=timeout)) + message = json.loads( + await self._websocket.receive_bytes(timeout=timeout) + ) if message["status"] > 400: if message["status"] == HTTPStatus.EXPECTATION_FAILED: @@ -207,7 +212,9 @@ async def readall(self): return await self.read(size=-1) async def readinto(self, b) -> int: - raise NotImplementedError("readinto() is not supported by the remote file API") + raise NotImplementedError( + "readinto() is not supported by the remote file API" + ) async def seek(self, pos: int, whence: int = 0) -> int: """Seek to a new position in the file.""" @@ -377,7 +384,9 @@ async def touch(self, path: Path, truncate: bool = True): ) as response: return await response.json() - async def open(self, path, mode="r", atomic=False, lock=False, encoding="utf-8"): + async def open( + self, path, mode="r", atomic=False, lock=False, encoding="utf-8" + ): file = SpyderRemoteFileIOAPI( path, mode, atomic, lock, encoding, manager=self.manager ) diff --git a/spyder/plugins/remoteclient/api/ssh.py b/spyder/plugins/remoteclient/api/ssh.py index d718bc58b8f..7606c6aeb7d 100644 --- a/spyder/plugins/remoteclient/api/ssh.py +++ b/spyder/plugins/remoteclient/api/ssh.py @@ -31,7 +31,9 @@ def connection_made(self, conn: SSHClientConnection) -> None: """ if self.client._plugin: - self.client._plugin.sig_connection_established.emit(self.client.config_id) + self.client._plugin.sig_connection_established.emit( + self.client.config_id + ) def connection_lost(self, exc: Optional[Exception]) -> None: """Called when a connection is lost or closed @@ -51,7 +53,9 @@ def connection_lost(self, exc: Optional[Exception]) -> None: self.client._plugin.sig_connection_lost.emit(self.client.config_id) self.client._handle_connection_lost(exc) - def debug_msg_received(self, msg: str, lang: str, always_display: bool) -> None: + def debug_msg_received( + self, msg: str, lang: str, always_display: bool + ) -> None: """A debug message was received on this connection This method is called when the other end of the connection sends diff --git a/spyder/plugins/remoteclient/plugin.py b/spyder/plugins/remoteclient/plugin.py index 838025df823..3b2af18a501 100644 --- a/spyder/plugins/remoteclient/plugin.py +++ b/spyder/plugins/remoteclient/plugin.py @@ -107,13 +107,17 @@ def on_initialize(self): self.create_ipyclient_for_server ) container.sig_shutdown_kernel_requested.connect(self._shutdown_kernel) - container.sig_interrupt_kernel_requested.connect(self._interrupt_kernel) + container.sig_interrupt_kernel_requested.connect( + self._interrupt_kernel + ) # Plugin signals self.sig_connection_status_changed.connect( container.sig_connection_status_changed ) - self.sig_client_message_logged.connect(container.sig_client_message_logged) + self.sig_client_message_logged.connect( + container.sig_client_message_logged + ) self.sig_version_mismatch.connect(container.on_server_version_mismatch) self._sig_kernel_started.connect(container.on_kernel_started) @@ -123,7 +127,9 @@ def on_first_registration(self): def on_close(self, cancellable=True): """Stops remote server and close any opened connection.""" for client in self._remote_clients.values(): - AsyncDispatcher(client.close, loop="asyncssh", early_return=False)() + AsyncDispatcher( + client.close, loop="asyncssh", early_return=False + )() @on_plugin_available(plugin=Plugins.MainMenu) def on_mainmenu_available(self): @@ -222,7 +228,9 @@ def load_client(self, config_id: str, options: SSHClientOptions): def load_conf(self, config_id): """Load remote server configuration.""" - options = self.get_conf(self.CONF_SECTION_SERVERS, {}).get(config_id, {}) + options = self.get_conf(self.CONF_SECTION_SERVERS, {}).get( + config_id, {} + ) # We couldn't find saved options for config_id if not options: @@ -289,7 +297,9 @@ def create_ipyclient_for_server(self, config_id): # and odd issues (e.g. the Variable Explorer not working). future = self._start_new_kernel(config_id) future.add_done_callback( - lambda future: self._sig_kernel_started.emit(ipyclient, future.result()) + lambda future: self._sig_kernel_started.emit( + ipyclient, future.result() + ) ) @staticmethod diff --git a/spyder/plugins/remoteclient/tests/conftest.py b/spyder/plugins/remoteclient/tests/conftest.py index 5c96b18a562..295715eaa5a 100644 --- a/spyder/plugins/remoteclient/tests/conftest.py +++ b/spyder/plugins/remoteclient/tests/conftest.py @@ -82,7 +82,9 @@ def register_plugin(self, plugin_class): @staticmethod def unregister_plugin(plugin): - assert PLUGIN_REGISTRY.delete_plugin(plugin.NAME), f"{plugin.NAME} not deleted" + assert PLUGIN_REGISTRY.delete_plugin( + plugin.NAME + ), f"{plugin.NAME} not deleted" plugin._unregister() @staticmethod @@ -132,7 +134,9 @@ def remote_client_id( config_id = str(uuid.uuid4()) # Options Required by container widget - remote_client.set_conf(f"{config_id}/auth_method", AuthenticationMethod.Password) + remote_client.set_conf( + f"{config_id}/auth_method", AuthenticationMethod.Password + ) remote_client.set_conf( f"{config_id}/{AuthenticationMethod.Password}/name", "test-server" ) diff --git a/spyder/plugins/remoteclient/tests/test_files.py b/spyder/plugins/remoteclient/tests/test_files.py index d0e15b3306d..5d9c4f195a8 100644 --- a/spyder/plugins/remoteclient/tests/test_files.py +++ b/spyder/plugins/remoteclient/tests/test_files.py @@ -28,7 +28,9 @@ async def test_create_dir( assert file_api_class is not None async with file_api_class() as file_api: - assert await file_api.mkdir(self.remote_temp_dir) == {"success": True} + assert await file_api.mkdir(self.remote_temp_dir) == { + "success": True + } @AsyncDispatcher.dispatch(early_return=False) async def test_list_dir( @@ -121,12 +123,12 @@ async def test_rm_file( assert file_api_class is not None async with file_api_class() as file_api: - assert await file_api.unlink(self.remote_temp_dir + "/test.txt") == { - "success": True - } - assert await file_api.unlink(self.remote_temp_dir + "/test2.txt") == { - "success": True - } + assert await file_api.unlink( + self.remote_temp_dir + "/test.txt" + ) == {"success": True} + assert await file_api.unlink( + self.remote_temp_dir + "/test2.txt" + ) == {"success": True} @AsyncDispatcher.dispatch(early_return=False) async def test_rm_dir( @@ -139,7 +141,9 @@ async def test_rm_dir( assert file_api_class is not None async with file_api_class() as file_api: - assert await file_api.rmdir(self.remote_temp_dir) == {"success": True} + assert await file_api.rmdir(self.remote_temp_dir) == { + "success": True + } if __name__ == "__main__": diff --git a/spyder/plugins/remoteclient/tests/test_ipythonconsole.py b/spyder/plugins/remoteclient/tests/test_ipythonconsole.py index b9c9817fc0e..9cd6263565c 100644 --- a/spyder/plugins/remoteclient/tests/test_ipythonconsole.py +++ b/spyder/plugins/remoteclient/tests/test_ipythonconsole.py @@ -17,20 +17,26 @@ class TestIpythonConsole: @flaky(max_runs=3, min_passes=1) - def test_shutdown_kernel(self, ipyconsole, remote_client, remote_client_id, qtbot): + def test_shutdown_kernel( + self, ipyconsole, remote_client, remote_client_id, qtbot + ): """Starts and stops a kernel on the remote server.""" remote_client.create_ipyclient_for_server(remote_client_id) shell = ipyconsole.get_current_shellwidget() qtbot.waitUntil( - lambda: shell.spyder_kernel_ready and shell._prompt_html is not None, + lambda: shell.spyder_kernel_ready + and shell._prompt_html is not None, timeout=180000, # longer timeout for installation ) ipyconsole.get_widget().close_client() assert ( - await_future(remote_client.get_kernels(remote_client_id), timeout=10) == [] + await_future( + remote_client.get_kernels(remote_client_id), timeout=10 + ) + == [] ) def test_restart_kernel(self, shell, ipyconsole, qtbot): @@ -42,7 +48,8 @@ def test_restart_kernel(self, shell, ipyconsole, qtbot): shell._prompt_html = None ipyconsole.get_widget().restart_action.trigger() qtbot.waitUntil( - lambda: shell.spyder_kernel_ready and shell._prompt_html is not None, + lambda: shell.spyder_kernel_ready + and shell._prompt_html is not None, timeout=4000, ) @@ -74,7 +81,9 @@ def test_interrupt_kernel(self, shell, qtbot): def test_kernel_kill(self, shell, qtbot): """Test that the kernel correctly restarts after a kill.""" - crash_string = "import os, signal; os.kill(os.getpid(), signal.SIGTERM)" + crash_string = ( + "import os, signal; os.kill(os.getpid(), signal.SIGTERM)" + ) # Since the heartbeat and the tunnels are running in separate threads, # we need to make sure that the heartbeat thread has "higher" priority diff --git a/spyder/plugins/remoteclient/utils/installation.py b/spyder/plugins/remoteclient/utils/installation.py index 9310e84f54f..66a6be3d936 100644 --- a/spyder/plugins/remoteclient/utils/installation.py +++ b/spyder/plugins/remoteclient/utils/installation.py @@ -11,9 +11,7 @@ SERVER_ENV = "spyder-remote" PACKAGE_NAME = "spyder-remote-services" -SCRIPT_URL = ( - f"https://raw.githubusercontent.com/spyder-ide/{PACKAGE_NAME}/master/scripts" -) +SCRIPT_URL = f"https://raw.githubusercontent.com/spyder-ide/{PACKAGE_NAME}/master/scripts" def get_installer_command(platform: str) -> str: @@ -21,7 +19,9 @@ def get_installer_command(platform: str) -> str: raise NotImplementedError("Windows is not supported yet") if running_remoteclient_tests(): - return "\n" # server should be aready installed in the test environment + return ( + "\n" # server should be aready installed in the test environment + ) return ( f'"${{SHELL}}" <(curl -L {SCRIPT_URL}/installer.sh) ' diff --git a/spyder/plugins/remoteclient/widgets/connectiondialog.py b/spyder/plugins/remoteclient/widgets/connectiondialog.py index e19de98aa4d..3775910ba13 100644 --- a/spyder/plugins/remoteclient/widgets/connectiondialog.py +++ b/spyder/plugins/remoteclient/widgets/connectiondialog.py @@ -103,14 +103,20 @@ def set_text(self, reasons: ValidationReasons): if reasons.get("repeated_name"): text += ( prefix - + _("The name you selected is already used by another " "connection.") + + _( + "The name you selected is already used by another " + "connection." + ) + suffix ) if reasons.get("invalid_address"): text += ( prefix - + _("The address you provided is not a valid IP or domain " "name.") + + _( + "The address you provided is not a valid IP or domain " + "name." + ) + suffix ) @@ -310,7 +316,9 @@ def _create_common_elements(self, auth_method): address = self.create_lineedit( text=_("Remote address *"), option=f"{self.host_id}/{auth_method}/address", - tip=_("This is the IP address or domain name of your remote machine"), + tip=_( + "This is the IP address or domain name of your remote machine" + ), validate_callback=self._validate_address, validate_reason=_("The address is not a valid IP or domain name"), ) @@ -385,9 +393,13 @@ def _create_password_subpage(self): validation_label = ValidationLabel(self) # Add widgets to their required dicts - self._widgets_for_validation[AuthenticationMethod.Password].append(password) + self._widgets_for_validation[AuthenticationMethod.Password].append( + password + ) - self._validation_labels[AuthenticationMethod.Password] = validation_label + self._validation_labels[AuthenticationMethod.Password] = ( + validation_label + ) # Layout password_layout = QVBoxLayout() @@ -436,9 +448,13 @@ def _create_keyfile_subpage(self): validation_label = ValidationLabel(self) # Add widgets to their required dicts - self._widgets_for_validation[AuthenticationMethod.KeyFile].append(keyfile) + self._widgets_for_validation[AuthenticationMethod.KeyFile].append( + keyfile + ) - self._validation_labels[AuthenticationMethod.KeyFile] = validation_label + self._validation_labels[AuthenticationMethod.KeyFile] = ( + validation_label + ) # Layout keyfile_layout = QVBoxLayout() @@ -486,7 +502,9 @@ def _create_configfile_subpage(self): name, configfile, ] - self._validation_labels[AuthenticationMethod.ConfigFile] = validation_label + self._validation_labels[AuthenticationMethod.ConfigFile] = ( + validation_label + ) # Layout configfile_layout = QVBoxLayout() @@ -535,7 +553,9 @@ def _validate_address(self, address): ipv6_pattern = r"^([\da-fA-F]{1,4}:){7}[\da-fA-F]{1,4}$" # Combined pattern to check all three formats - combined_pattern = f"({domain_pattern})|({ipv4_pattern})|({ipv6_pattern})" + combined_pattern = ( + f"({domain_pattern})|({ipv4_pattern})|({ipv6_pattern})" + ) address_re = re.compile(combined_pattern) return True if address_re.match(address) else False @@ -585,7 +605,9 @@ def reset_page(self, clear=False): for widgets in [self.comboboxes, self.lineedits, self.spinboxes]: for widget in widgets: section, option, default = widgets[widget] - new_option = "/".join([self.host_id] + option.split("/")[1:]) + new_option = "/".join( + [self.host_id] + option.split("/")[1:] + ) widgets[widget] = (section, new_option, default) @@ -617,9 +639,13 @@ def save_server_info(self): # Mapping from options in our config system to those accepted by # asyncssh options = SSHClientOptions( - host=self.get_option(f"{self.host_id}/{self.auth_method()}/address"), + host=self.get_option( + f"{self.host_id}/{self.auth_method()}/address" + ), port=self.get_option(f"{self.host_id}/{self.auth_method()}/port"), - username=self.get_option(f"{self.host_id}/{self.auth_method()}/username"), + username=self.get_option( + f"{self.host_id}/{self.auth_method()}/username" + ), client_keys=self.get_option(f"{self.host_id}/keyfile"), config=self.get_option(f"{self.host_id}/configfile"), ) @@ -657,7 +683,9 @@ def remove_config_options(self): # One of these options was saved securely and other as empty in our # config system, so we try to remove them both. for secure in [True, False]: - self.remove_option(f"{self.host_id}/{secure_option}", secure=secure) + self.remove_option( + f"{self.host_id}/{secure_option}", secure=secure + ) def update_status(self, info: ConnectionInfo): if info["id"] == self.host_id: @@ -730,16 +758,26 @@ def create_buttons(self): bbox = SpyderDialogButtonBox(QDialogButtonBox.Cancel) self._button_save_connection = QPushButton(_("Save connection")) - self._button_save_connection.clicked.connect(self._save_connection_info) - bbox.addButton(self._button_save_connection, QDialogButtonBox.ResetRole) + self._button_save_connection.clicked.connect( + self._save_connection_info + ) + bbox.addButton( + self._button_save_connection, QDialogButtonBox.ResetRole + ) self._button_remove_connection = QPushButton(_("Remove connection")) - self._button_remove_connection.clicked.connect(self._remove_connection_info) - bbox.addButton(self._button_remove_connection, QDialogButtonBox.ResetRole) + self._button_remove_connection.clicked.connect( + self._remove_connection_info + ) + bbox.addButton( + self._button_remove_connection, QDialogButtonBox.ResetRole + ) self._button_clear_settings = QPushButton(_("Clear settings")) self._button_clear_settings.clicked.connect(self._clear_settings) - bbox.addButton(self._button_clear_settings, QDialogButtonBox.ActionRole) + bbox.addButton( + self._button_clear_settings, QDialogButtonBox.ActionRole + ) self._button_connect = QPushButton(_("Connect")) self._button_connect.clicked.connect(self._start_server) @@ -846,9 +884,9 @@ def _remove_connection_info(self): reply = QMessageBox.question( self, _("Remove connection"), - _("Do you want to remove the connection called {}?").format( - page.get_name() - ), + _( + "Do you want to remove the connection called {}?" + ).format(page.get_name()), QMessageBox.Yes, QMessageBox.No, ) @@ -903,7 +941,9 @@ def _add_connection_page(self, host_id: str, new: bool): # This is necessary to make button_save_connection enabled when there # are config changes in the page - page.apply_button_enabled.connect(self._update_button_save_connection_state) + page.apply_button_enabled.connect( + self._update_button_save_connection_state + ) if new: page.save_server_info() @@ -915,7 +955,9 @@ def _add_connection_page(self, host_id: str, new: bool): page.add_logs(self._container.client_logs.get(host_id, [])) # This updates the info shown in the "Connection info" tab of pages - self._container.sig_connection_status_changed.connect(page.update_status) + self._container.sig_connection_status_changed.connect( + page.update_status + ) self._container.sig_client_message_logged.connect(page.add_log) def _add_saved_connection_pages(self): diff --git a/spyder/plugins/remoteclient/widgets/connectionstatus.py b/spyder/plugins/remoteclient/widgets/connectionstatus.py index a075fa31345..3703d6f7e6b 100644 --- a/spyder/plugins/remoteclient/widgets/connectionstatus.py +++ b/spyder/plugins/remoteclient/widgets/connectionstatus.py @@ -99,7 +99,9 @@ def __init__(self, parent, host_id): # TODO: Address this for configfile login if self._auth_method != AuthenticationMethod.ConfigFile: - self.address = self.get_conf(f"{host_id}/{self._auth_method}/address") + self.address = self.get_conf( + f"{host_id}/{self._auth_method}/address" + ) username = self.get_conf(f"{host_id}/{self._auth_method}/username") else: self.address = "" @@ -225,7 +227,9 @@ def _set_stylesheet(self): # -- Style of other info labels other_info_labels_css = qstylizer.style.StyleSheet() - other_info_labels_css.setValues(marginLeft=f"{9 * AppStyle.MarginSize}px") + other_info_labels_css.setValues( + marginLeft=f"{9 * AppStyle.MarginSize}px" + ) for label in [self._status_label, self._user_label]: label.setStyleSheet(other_info_labels_css.toString()) @@ -291,7 +295,9 @@ def _set_text_in_labels(self, status): ) self._status_label.setText( - _('Status: {}').format(color, localized_status) + _('Status: {}').format( + color, localized_status + ) ) def _set_icon(self, status): diff --git a/spyder/plugins/remoteclient/widgets/container.py b/spyder/plugins/remoteclient/widgets/container.py index 495a5db98b2..402b17f47aa 100644 --- a/spyder/plugins/remoteclient/widgets/container.py +++ b/spyder/plugins/remoteclient/widgets/container.py @@ -171,7 +171,9 @@ def setup(self): ) # Signals - self.sig_connection_status_changed.connect(self._on_connection_status_changed) + self.sig_connection_status_changed.connect( + self._on_connection_status_changed + ) self.sig_client_message_logged.connect(self._on_client_message_logged) self._sig_kernel_restarted.connect(self._on_kernel_restarted) self._sig_kernel_info_replied.connect(self._on_kernel_info_reply) @@ -332,7 +334,9 @@ def _connect_ipyclient_signals(self, ipyclient): ipyclient.sig_restart_kernel_requested.connect( lambda: self._request_kernel_restart(ipyclient) ) - ipyclient.sig_kernel_died.connect(lambda: self._request_kernel_info(ipyclient)) + ipyclient.sig_kernel_died.connect( + lambda: self._request_kernel_info(ipyclient) + ) def _request_kernel_restart(self, ipyclient): """ @@ -342,12 +346,16 @@ def _request_kernel_restart(self, ipyclient): if self.__requested_restart: return - future = self._plugin._restart_kernel(ipyclient.server_id, ipyclient.kernel_id) + future = self._plugin._restart_kernel( + ipyclient.server_id, ipyclient.kernel_id + ) self.__requested_restart = True future.add_done_callback( - lambda future: self._sig_kernel_restarted.emit(ipyclient, future.result()) + lambda future: self._sig_kernel_restarted.emit( + ipyclient, future.result() + ) ) def _request_kernel_info(self, ipyclient): @@ -358,7 +366,9 @@ def _request_kernel_info(self, ipyclient): if self.__requested_info: return - future = self._plugin._get_kernel_info(ipyclient.server_id, ipyclient.kernel_id) + future = self._plugin._get_kernel_info( + ipyclient.server_id, ipyclient.kernel_id + ) self.__requested_info = True @@ -395,7 +405,9 @@ def _on_kernel_info_reply(self, ipyclient, kernel_info): ) kernel_handler.set_time_to_dead(1.0) except Exception as err: - ipyclient.remote_kernel_restarted_failure_message(err, shutdown=True) + ipyclient.remote_kernel_restarted_failure_message( + err, shutdown=True + ) else: ipyclient.replace_kernel( kernel_handler, shutdown_kernel=False, clear=False