diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 77dcf45621..3774f94ddd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -126,9 +126,6 @@ repos: src/aiida/tools/data/orbital/orbital.py| src/aiida/tools/data/orbital/realhydrogen.py| src/aiida/tools/dbimporters/plugins/.*| - src/aiida/transports/cli.py| - src/aiida/transports/plugins/local.py| - src/aiida/transports/plugins/ssh.py| )$ - id: generate-conda-environment diff --git a/src/aiida/transports/cli.py b/src/aiida/transports/cli.py index 9ce06d8f75..9abf28b971 100644 --- a/src/aiida/transports/cli.py +++ b/src/aiida/transports/cli.py @@ -41,6 +41,8 @@ def configure_computer_main(computer, user, **kwargs): user = user or orm.User.collection.get_default() + assert user is not None + echo.echo_report(f'Configuring computer {computer.label} for user {user.email}.') if not user.is_default: echo.echo_report('Configuring different user, defaults may not be appropriate.') @@ -85,7 +87,7 @@ def get_default(ctx): user = ctx.params.get('user', None) or orm.User.collection.get_default() computer = ctx.params.get('computer', None) - if computer is None: + if computer is None or user is None: return None try: @@ -123,7 +125,7 @@ def create_option(name, spec): if existing_option: return existing_option(**kwargs) - return click.option(option_name, **kwargs) + return click.option(option_name, **kwargs) # type: ignore[arg-type] def list_transport_options(transport_type): diff --git a/src/aiida/transports/plugins/local.py b/src/aiida/transports/plugins/local.py index 1bd6501ad7..8f9e5a35fa 100644 --- a/src/aiida/transports/plugins/local.py +++ b/src/aiida/transports/plugins/local.py @@ -90,6 +90,7 @@ def curdir(self): If possible, use getcwd() instead! """ if self._is_open: + assert self._internal_dir is not None return os.path.realpath(self._internal_dir) raise TransportInternalError('Error, local method called for LocalTransport without opening the channel first') @@ -816,7 +817,9 @@ def exec_command_wait_bytes(self, command, stdin=None, workdir: TransportPath | # Also, if I get a StringIO, I just read it all in memory and put it into a BytesIO. # Clearly not memory effective - in this case do not use a StringIO, but pass directly a BytesIO # that will be read line by line, if you have a huge stdin and care about memory usage. - if isinstance(stdin, str): + if isinstance(stdin, io.BufferedIOBase): + filelike_stdin = stdin + elif isinstance(stdin, str): filelike_stdin = io.BytesIO(stdin.encode('utf-8')) elif isinstance(stdin, bytes): filelike_stdin = io.BytesIO(stdin) @@ -832,8 +835,6 @@ def line_encoder(iterator, encoding='utf-8'): yield line.encode(encoding) filelike_stdin = line_encoder(stdin) - elif isinstance(stdin, io.BufferedIOBase): - filelike_stdin = stdin else: raise ValueError('You can only pass strings, bytes, BytesIO or StringIO objects') diff --git a/src/aiida/transports/plugins/ssh.py b/src/aiida/transports/plugins/ssh.py index 138aea8057..72af896dbd 100644 --- a/src/aiida/transports/plugins/ssh.py +++ b/src/aiida/transports/plugins/ssh.py @@ -441,7 +441,7 @@ def open(self): matcher = re.compile(r'^(?:(?P[^@]+)@)?(?P[^@:]+)(?::(?P\d+))?\s*$') try: # don't use a generator here to have everything evaluated - proxies = [matcher.match(s).groupdict() for s in proxyjumpstring.split(',')] + proxies = [matcher.match(s).groupdict() for s in proxyjumpstring.split(',')] # type: ignore[union-attr] except AttributeError: raise ValueError('The given configuration for the SSH proxy jump option could not be parsed') @@ -486,7 +486,7 @@ def open(self): ) self._close_proxies() # close all since we're going to start anew on the next open() (if any) raise - connection_arguments['sock'] = proxy_client.get_transport().open_channel( + connection_arguments['sock'] = proxy_client.get_transport().open_channel( # type: ignore[union-attr] 'direct-tcpip', (target['host'], target['port']), ('', 0) ) self._proxies.append(proxy_client) @@ -540,13 +540,16 @@ def close(self): :todo: correctly manage exceptions - :raise aiida.common.InvalidOperation: if the channel is already open + :raise aiida.common.InvalidOperation: if the channel is already closed """ from aiida.common.exceptions import InvalidOperation if not self._is_open: raise InvalidOperation('Cannot close the transport: it is already closed') + if self._sftp is None: + raise InvalidOperation('Cannot close the transport: it has never been opened') + self._sftp.close() self._client.close() self._close_proxies() @@ -1400,7 +1403,14 @@ def isfile(self, path: TransportPath): return False raise # Typically if I don't have permissions (errno=13) - def _exec_command_internal(self, command, combine_stderr=False, bufsize=-1, workdir=None): + def _exec_command_internal( + self, + command: str, + workdir: TransportPath | None = None, + combine_stderr: bool = False, + bufsize: int = -1, + **kwargs, + ): """Executes the specified command in bash login shell. @@ -1449,7 +1459,14 @@ def _exec_command_internal(self, command, combine_stderr=False, bufsize=-1, work return stdin, stdout, stderr, channel def exec_command_wait_bytes( - self, command, stdin=None, combine_stderr=False, bufsize=-1, timeout=0.01, workdir: TransportPath = None + self, + command: str, + stdin=None, + workdir: TransportPath | None = None, + combine_stderr: bool = False, + bufsize: int = -1, + timeout: float = 0.01, + **kwargs, ): """Executes the specified command and waits for it to finish. @@ -1472,18 +1489,18 @@ def exec_command_wait_bytes( workdir = str(workdir) ssh_stdin, stdout, stderr, channel = self._exec_command_internal( - command, combine_stderr, bufsize=bufsize, workdir=workdir + command, workdir, combine_stderr=combine_stderr, bufsize=bufsize ) if stdin is not None: - if isinstance(stdin, str): - filelike_stdin = io.StringIO(stdin) - elif isinstance(stdin, bytes): - filelike_stdin = io.BytesIO(stdin) - elif isinstance(stdin, (io.BufferedIOBase, io.TextIOBase)): + if isinstance(stdin, (io.BufferedIOBase, io.TextIOBase)): # It seems both StringIO and BytesIO work correctly when doing ssh_stdin.write(line)? # (The ChannelFile is opened with mode 'b', but until now it always has been a StringIO) filelike_stdin = stdin + elif isinstance(stdin, str): + filelike_stdin = io.StringIO(stdin) + elif isinstance(stdin, bytes): + filelike_stdin = io.BytesIO(stdin) else: raise ValueError('You can only pass strings, bytes, BytesIO or StringIO objects')