Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
18 changes: 9 additions & 9 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ This methods are almost the same for `SSHCleint` and `Subprocess`, except specif
result = helper.execute(
command, # type: str
verbose=False, # type: bool
timeout=1 * 60 * 60, # type: typing.Optional[int]
timeout=1 * 60 * 60, # type: typing.Union[int, float, None]
**kwargs
)

Expand All @@ -139,7 +139,7 @@ This methods are almost the same for `SSHCleint` and `Subprocess`, except specif
result = helper.check_call(
command, # type: str
verbose=False, # type: bool
timeout=1 * 60 * 60, # type: typing.Optional[int]
timeout=1 * 60 * 60, # type: type: typing.Union[int, float, None]
error_info=None, # type: typing.Optional[str]
expected=None, # type: typing.Optional[typing.Iterable[int]]
raise_on_err=True, # type: bool
Expand All @@ -151,7 +151,7 @@ This methods are almost the same for `SSHCleint` and `Subprocess`, except specif
result = helper.check_stderr(
command, # type: str
verbose=False, # type: bool
timeout=1 * 60 * 60, # type: typing.Optional[int]
timeout=1 * 60 * 60, # type: type: typing.Union[int, float, None]
error_info=None, # type: typing.Optional[str]
raise_on_err=True, # type: bool
)
Expand Down Expand Up @@ -187,10 +187,10 @@ Execution result object has a set of useful properties:
* `stderr` -> `typing.Tuple[bytes]`. Raw stderr output.
* `stdout_bin` -> `bytearray`. Binary stdout output.
* `stderr_bin` -> `bytearray`. Binary stderr output.
* `stdout_str` -> `six.text_types`. Text representation of output.
* `stderr_str` -> `six.text_types`. Text representation of output.
* `stdout_brief` -> `six.text_types`. Up to 7 lines from stdout (3 first and 3 last if >7 lines).
* `stderr_brief` -> `six.text_types`. Up to 7 lines from stderr (3 first and 3 last if >7 lines).
* `stdout_str` -> `str`. Text representation of output.
* `stderr_str` -> `str`. Text representation of output.
* `stdout_brief` -> `str`. Up to 7 lines from stdout (3 first and 3 last if >7 lines).
* `stderr_brief` -> `str`. Up to 7 lines from stderr (3 first and 3 last if >7 lines).

* `stdout_json` - STDOUT decoded as JSON.

Expand All @@ -211,7 +211,7 @@ Possible to call commands in parallel on multiple hosts if it's not produce huge
results = SSHClient.execute_together(
remotes, # type: typing.Iterable[SSHClient]
command, # type: str
timeout=1 * 60 * 60, # type: typing.Optional[int]
timeout=1 * 60 * 60, # type: type: typing.Union[int, float, None]
expected=None, # type: typing.Optional[typing.Iterable[int]]
raise_on_err=True # type: bool
)
Expand All @@ -229,7 +229,7 @@ For execute through SSH host can be used `execute_through_host` method:
command, # type: str
auth=None, # type: typing.Optional[SSHAuth]
target_port=22, # type: int
timeout=1 * 60 * 60, # type: typing.Optional[int]
timeout=1 * 60 * 60, # type: type: typing.Union[int, float, None]
verbose=False, # type: bool
get_pty=False, # type: bool
)
Expand Down
16 changes: 8 additions & 8 deletions doc/source/ExecResult.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ API: ExecResult
:param cmd: command
:type cmd: ``str``
:param stdin: STDIN
:type stdin: ``typing.Optional[str]``
:type stdin: ``typing.Union[bytes, str, bytearray, None]``
:param stdout: binary STDOUT
:type stdout: ``typing.Optional[typing.Iterable[bytes]]``
:param stderr: binary STDERR
Expand All @@ -40,17 +40,17 @@ API: ExecResult

.. py:attribute:: stdin

``typing.Text``
``typing.Optional[str]``
Stdin input as string.

.. py:attribute:: stdout

``typing.Tuple[bytes]``
``typing.Tuple[bytes, ...]``
Stdout output as list of binaries.

.. py:attribute:: stderr

``typing.Tuple[bytes]``
``typing.Tuple[bytes, ...]``
Stderr output as list of binaries.

.. py:attribute:: stdout_bin
Expand All @@ -65,22 +65,22 @@ API: ExecResult

.. py:attribute:: stdout_str

``typing.Text``
``str``
Stdout output as string.

.. py:attribute:: stderr_str

``typing.Text``
``str``
Stderr output as string.

.. py:attribute:: stdout_brief

``typing.Text``
``str``
Brief stdout output (mostly for exceptions).

.. py:attribute:: stderr_brief

``typing.Text``
``str``
Brief stderr output (mostly for exceptions).

.. py:attribute:: exit_code
Expand Down
12 changes: 6 additions & 6 deletions doc/source/SSHClient.rst
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ API: SSHClient and SSHAuth.
:param command: Command for execution
:type command: ``str``
:param stdin: pass STDIN text to the process
:type stdin: ``typing.Union[typing.AnyStr, bytearray, None]``
:type stdin: ``typing.Union[str, bytes, bytearray, None]``
:param open_stdout: open STDOUT stream for read
:type open_stdout: bool
:param open_stderr: open STDERR stream for read
Expand All @@ -153,7 +153,7 @@ API: SSHClient and SSHAuth.
:param verbose: Produce log.info records for command call and output
:type verbose: ``bool``
:param timeout: Timeout for command execution.
:type timeout: ``typing.Union[int, None]``
:type timeout: ``typing.Union[int, float, None]``
:rtype: ExecResult
:raises ExecHelperTimeoutError: Timeout exceeded

Expand All @@ -168,7 +168,7 @@ API: SSHClient and SSHAuth.
:param verbose: Produce log.info records for command call and output
:type verbose: ``bool``
:param timeout: Timeout for command execution.
:type timeout: ``typing.Union[int, None]``
:type timeout: ``typing.Union[int, float, None]``
:param error_info: Text for error details, if fail happens
:type error_info: ``typing.Optional[str]``
:param expected: expected return codes (0 by default)
Expand All @@ -190,7 +190,7 @@ API: SSHClient and SSHAuth.
:param verbose: Produce log.info records for command call and output
:type verbose: ``bool``
:param timeout: Timeout for command execution.
:type timeout: ``typing.Union[int, None]``
:type timeout: ``typing.Union[int, float, None]``
:param error_info: Text for error details, if fail happens
:type error_info: ``typing.Optional[str]``
:param raise_on_err: Raise exception on unexpected return code
Expand All @@ -217,7 +217,7 @@ API: SSHClient and SSHAuth.
:param verbose: Produce log.info records for command call and output
:type verbose: ``bool``
:param timeout: Timeout for command execution.
:type timeout: ``typing.Union[int, None]``
:type timeout: ``typing.Union[int, float, None]``
:param get_pty: open PTY on target machine
:type get_pty: ``bool``
:rtype: ExecResult
Expand All @@ -234,7 +234,7 @@ API: SSHClient and SSHAuth.
:param command: Command for execution
:type command: ``str``
:param timeout: Timeout for command execution.
:type timeout: ``typing.Union[int, None]``
:type timeout: ``typing.Union[int, float, None]``
:param expected: expected return codes (0 by default)
:type expected: ``typing.Optional[typing.Iterable[]]``
:param raise_on_err: Raise exception on unexpected return code
Expand Down
8 changes: 4 additions & 4 deletions doc/source/Subprocess.rst
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ API: Subprocess
:param command: Command for execution
:type command: str
:param stdin: pass STDIN text to the process
:type stdin: ``typing.Union[typing.AnyStr, bytearray, None]``
:type stdin: ``typing.Union[str, bytes, bytearray, None]``
:param open_stdout: open STDOUT stream for read
:type open_stdout: ``bool``
:param open_stderr: open STDERR stream for read
Expand All @@ -69,7 +69,7 @@ API: Subprocess
:param verbose: Produce log.info records for command call and output
:type verbose: ``bool``
:param timeout: Timeout for command execution.
:type timeout: ``typing.Union[int, None]``
:type timeout: ``typing.Union[int, float, None]``
:rtype: ExecResult
:raises ExecHelperTimeoutError: Timeout exceeded

Expand All @@ -88,7 +88,7 @@ API: Subprocess
:param verbose: Produce log.info records for command call and output
:type verbose: ``bool``
:param timeout: Timeout for command execution.
:type timeout: ``typing.Union[int, None]``
:type timeout: ``typing.Union[int, float, None]``
:param error_info: Text for error details, if fail happens
:type error_info: ``typing.Optional[str]``
:param expected: expected return codes (0 by default)
Expand All @@ -111,7 +111,7 @@ API: Subprocess
:param verbose: Produce log.info records for command call and output
:type verbose: ``bool``
:param timeout: Timeout for command execution.
:type timeout: ``typing.Union[int, None]``
:type timeout: ``typing.Union[int, float, None]``
:param error_info: Text for error details, if fail happens
:type error_info: ``typing.Optional[str]``
:param raise_on_err: Raise exception on unexpected return code
Expand Down
10 changes: 5 additions & 5 deletions doc/source/exceptions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ API: exceptions

.. py:attribute:: timeout

``int``
``typing.Union[int, float]``

.. py:attribute:: result

Expand All @@ -46,12 +46,12 @@ API: exceptions

.. py:attribute:: stdout

``typing.Text``
``str``
stdout string or brief string

.. py:attribute:: stderr

``typing.Text``
``str``
stdout string or brief string

.. py:exception:: CalledProcessError(ExecCalledProcessError)
Expand Down Expand Up @@ -92,12 +92,12 @@ API: exceptions

.. py:attribute:: stdout

``typing.Text``
``str``
stdout string or brief string

.. py:attribute:: stderr

``typing.Text``
``str``
stdout string or brief string

.. py:exception:: ParallelCallExceptions(ExecCalledProcessError)
Expand Down
40 changes: 26 additions & 14 deletions exec_helpers/_ssh_client_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

"""SSH client helper based on Paramiko. Base class."""

import abc
import base64
import collections
import concurrent.futures
Expand Down Expand Up @@ -58,7 +59,7 @@
CPYTHON = 'CPython' == platform.python_implementation()


class _MemorizedSSH(type):
class _MemorizedSSH(abc.ABCMeta):
"""Memorize metaclass for SSHClient.

This class implements caching and managing of SSHClient connections.
Expand Down Expand Up @@ -112,16 +113,23 @@ def __call__( # type: ignore
auth: typing.Optional[ssh_auth.SSHAuth] = None,
verbose: bool = True,
) -> 'SSHClientBase':
"""Main memorize method: check for cached instance and return it.
"""Main memorize method: check for cached instance and return it. API follows target __init__.

:param host: remote hostname
:type host: str
:param port: remote ssh port
:type port: int
:type username: str
:type password: str
:type private_keys: list
:type auth: ssh_auth.SSHAuth
:param username: remote username.
:type username: typing.Optional[str]
:param password: remote password
:type password: typing.Optional[str]
:param private_keys: private keys for connection
:type private_keys: typing.Optional[typing.Iterable[paramiko.RSAKey]]
:param auth: credentials for connection
:type auth: typing.Optional[ssh_auth.SSHAuth]
:param verbose: show additional error/warning messages
:type verbose: bool
:rtype: SSHClient
:rtype: SSHClientBase
"""
if (host, port) in cls.__cache:
key = host, port
Expand Down Expand Up @@ -164,7 +172,7 @@ def __call__( # type: ignore
def clear_cache(mcs: typing.Type['_MemorizedSSH']) -> None:
"""Clear cached connections for initialize new instance on next call.

getrefcount is used to check for usage.
getrefcount is used to check for usage, so connections closed on CPYTHON only.
"""
n_count = 3
# PY3: cache, ssh, temporary
Expand Down Expand Up @@ -210,8 +218,8 @@ def __init__(
) -> None:
"""Context manager for call commands with sudo.

:type ssh: SSHClient
:type enforce: bool
:type ssh: SSHClientBase
:type enforce: typing.Optional[bool]
"""
self.__ssh = ssh
self.__sudo_status = ssh.sudo_mode
Expand Down Expand Up @@ -241,7 +249,7 @@ def __init__(
) -> None:
"""Context manager for keepalive management.

:type ssh: SSHClient
:type ssh: SSHClientBase
:type enforce: bool
:param enforce: Keep connection alive after context manager exit
"""
Expand Down Expand Up @@ -465,8 +473,8 @@ def _clear_cache(cls: typing.Type['SSHClientBase']) -> None:
def __del__(self) -> None:
"""Destructor helper: close channel and threads BEFORE closing others.

Due to threading in paramiko, default destructor could generate asserts
on close, so we calling channel close before closing main ssh object.
Due to threading in paramiko, default destructor could generate asserts on close,
so we calling channel close before closing main ssh object.
"""
try:
self.__ssh.close()
Expand Down Expand Up @@ -540,6 +548,7 @@ def sudo(

:param enforce: Enforce sudo enabled or disabled. By default: None
:type enforce: typing.Optional[bool]
:rtype: typing.ContextManager
"""
return self.__get_sudo(ssh=self, enforce=enforce)

Expand All @@ -551,6 +560,7 @@ def keepalive(

:param enforce: Enforce keepalive enabled or disabled.
:type enforce: bool
:rtype: typing.ContextManager

.. Note:: Enter and exit ssh context manager is produced as well.
.. versionadded:: 1.2.1
Expand All @@ -572,7 +582,7 @@ def execute_async(
:param command: Command for execution
:type command: str
:param stdin: pass STDIN text to the process
:type stdin: typing.Union[str, bytes, bytearray, None]
:type stdin: typing.Union[bytes, str, bytearray, None]
:param open_stdout: open STDOUT stream for read
:type open_stdout: bool
:param open_stderr: open STDERR stream for read
Expand Down Expand Up @@ -623,6 +633,7 @@ def execute_async(
cmd = "sudo -S bash -c 'eval \"$(base64 -d <(echo \"{0}\"))\"'".format(encoded_cmd)
chan.exec_command(cmd) # nosec # Sanitize on caller side
if stdout.channel.closed is False:
# noinspection PyTypeChecker
self.auth.enter_password(_stdin)
_stdin.flush()
else:
Expand Down Expand Up @@ -716,6 +727,7 @@ def poll_pipes(stop: threading.Event) -> None:
stop_event = threading.Event()

# pylint: disable=assignment-from-no-return
# noinspection PyNoneFunctionAssignment
future = poll_pipes(stop=stop_event) # type: concurrent.futures.Future
# pylint: enable=assignment-from-no-return

Expand Down
Loading