cecbb557b3c201b38b96956bd9406848bbba28ea — Stefan Schwarzer 8 years ago f5c41ed
Use original exception message in `FTPError` (ticket #76).

If we create an `FTPError` instance based on a previously raised
`ftplib` or `socket` exception, take the string representation of this
original exception as the string representation of the new `FTPError`

The `*args` passed to `FTPError` aren't necessarily enough to
guarantee this. At least for some socket exceptions, `args` is a tuple
of an integer and a string and using only `args[0]` to form the
exception message keeps only the integer.
2 files changed, 29 insertions(+), 8 deletions(-)

M ftputil/error.py
M test/test_error.py
M ftputil/error.py => ftputil/error.py +13 -7
@@ 1,4 1,4 @@
# Copyright (C) 2003-2013, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# Copyright (C) 2003-2014, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# See the file LICENSE for licensing terms.


@@ 30,17 30,23 @@ __all__ = [

class FTPError(Exception):
    """General ftputil error class."""

    def __init__(self, *args):
    # In Python 2, we can't use a keyword argument after `*args`, so
    # `pop` from `**kwargs`.
    def __init__(self, *args, **kwargs):
        super(FTPError, self).__init__(*args)
        # Don't use `args[0]` directly because `args` may be empty.
        if args:
            self.strerror = self.args[0]
        if "original_exception" in kwargs:
            self.strerror = ftputil.compat.unicode_type(
        elif args:
            # If there was no `original_exception` argument, assume
            # the first argument is a unicode string.
            self.strerror = args[0]
            self.strerror = ""

@@ 130,7 136,7 @@ class FtplibErrorToFTPOSError(object):
                raise PermanentError(*exc_value.args)
        elif isinstance(exc_value, ftplib.all_errors):
            raise FTPOSError(*exc_value.args)
            raise FTPOSError(*exc_value.args, original_exception=exc_value)

M test/test_error.py => test/test_error.py +16 -1
@@ 1,5 1,5 @@
# encoding: utf-8
# Copyright (C) 2002-2013, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# Copyright (C) 2002-2014, Stefan Schwarzer <sschwarzer@sschwarzer.net>
# See the file LICENSE for licensing terms.

from __future__ import unicode_literals

@@ 62,6 62,21 @@ class TestErrorConversion(unittest.TestCase):
            # We shouldn't come here.

    def test_error_message_reuse(self):
        Test if the error message string is retained if the caugt
        exception has more than one element in `args`.
        # See ticket #76.
            # Format "host:port" doesn't work.
            host = ftputil.FTPHost("localhost:21", "", "")
        except ftputil.error.FTPOSError as exc:
            # The error message might change for future Python
            # versions, so possibly relax the assertion later.
            self.assertTrue("[Errno -2] Name or service not known" in

if __name__ == "__main__":