Skip to content
Open
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
8 changes: 8 additions & 0 deletions doc/exceptions.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
Exceptions
==========

Exceptions initialized with a printf-style message and its values have a
human-readable string representation while retaining the original values in
``args``. For example::

error = AuthenticationError("Authentication failed for %s", "host.example.com")
print(error)
# Authentication failed for host.example.com

.. automodule:: pssh.exceptions
:members:
:undoc-members:
Expand Down
40 changes: 26 additions & 14 deletions pssh/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,19 @@
"""Exceptions raised by parallel-ssh classes."""


class NoIPv6AddressFoundError(Exception):
class _PSSHError(Exception):
"""Base class for exceptions with printf-style message arguments."""

def __str__(self):
if len(self.args) < 2 or not isinstance(self.args[0], str):
return super().__str__()
try:
return self.args[0] % self.args[1:]
except (KeyError, TypeError, ValueError):
return super().__str__()


class NoIPv6AddressFoundError(_PSSHError):
"""Raised when an IPV6 only address was requested but none are
available for a host.

Expand All @@ -29,7 +41,7 @@ class NoIPv6AddressFoundError(Exception):
"""


class UnknownHostError(Exception):
class UnknownHostError(_PSSHError):
"""Raised when a host is unknown (dns failure)"""
pass

Expand All @@ -39,36 +51,36 @@ class UnknownHostError(Exception):
ConnectionErrorException = ConnectionError


class AuthenticationError(Exception):
class AuthenticationError(_PSSHError):
"""Raised on authentication error (user/password/ssh key error)"""
pass


AuthenticationException = AuthenticationError


class SSHError(Exception):
class SSHError(_PSSHError):
"""Raised on error authenticating with SSH server"""
pass


SSHException = SSHError


class HostArgumentError(Exception):
class HostArgumentError(_PSSHError):
"""Raised on errors with per-host arguments to parallel functions"""
pass


HostArgumentException = HostArgumentError


class SessionError(Exception):
class SessionError(_PSSHError):
"""Raised on errors establishing SSH session"""
pass


class SFTPError(Exception):
class SFTPError(_PSSHError):
"""Raised on SFTP errors"""
pass

Expand All @@ -78,29 +90,29 @@ class SFTPIOError(SFTPError):
pass


class ProxyError(Exception):
class ProxyError(_PSSHError):
"""Raised on proxy errors"""


class Timeout(Exception):
class Timeout(_PSSHError):
"""Raised on timeout requested and reached"""


class SCPError(Exception):
class SCPError(_PSSHError):
"""Raised on errors copying file via SCP"""


class PKeyFileError(Exception):
class PKeyFileError(_PSSHError):
"""Raised on errors finding private key file"""


class ShellError(Exception):
class ShellError(_PSSHError):
"""Raised on errors running command on interactive shell"""


class HostConfigError(Exception):
class HostConfigError(_PSSHError):
"""Raised on invalid host configuration"""


class InvalidAPIUseError(Exception):
class InvalidAPIUseError(_PSSHError):
"""Raised on invalid use of library API"""
23 changes: 23 additions & 0 deletions tests/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from pssh.exceptions import AuthenticationError, AuthenticationException, UnknownHostError, \
UnknownHostException, ConnectionError, ConnectionErrorException, SSHError, SSHException, \
HostArgumentError, HostArgumentException
from pssh.exceptions import SCPError, Timeout


class ParallelSSHUtilsTest(unittest.TestCase):
Expand Down Expand Up @@ -67,3 +68,25 @@ def test_errors(self):
raise HostArgumentError
except HostArgumentException:
pass

def test_formatted_error_string(self):
message = "Authentication error while connecting to %s:%s - %s - retries %s/%s"
cause = AuthenticationError("No authentication methods succeeded")
error = AuthenticationError(
message, "host.example.com", 22, cause, 3, 3)

self.assertEqual(
str(error),
"Authentication error while connecting to host.example.com:22 - "
"No authentication methods succeeded - retries 3/3")
self.assertEqual(
error.args,
(message, "host.example.com", 22, cause, 3, 3))

def test_invalid_formatted_error_falls_back_to_default(self):
error = Timeout("Timeout after %s seconds: %s", 10)

self.assertEqual(str(error), str(Exception(*error.args)))

def test_single_argument_error_string_is_unchanged(self):
self.assertEqual(str(SCPError("copy failed")), "copy failed")