source: ftp_error.py @ 772:2166f443d615

Last change on this file since 772:2166f443d615 was 772:2166f443d615, checked in by Stefan Schwarzer <sschwarzer@…>, 12 years ago
Make the `ftp_error` module more robust with respect to `ftplib` exceptions containing bytestrings or unicode strings or no strings at all. Normally, `ftplib` exceptions will only contain bytestrings but that may not be true for subclasses of `ftplib` exception classes in third-party modules.
File size: 5.5 KB
Line 
1# Copyright (C) 2003-2009, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met:
7#
8# - Redistributions of source code must retain the above copyright
9#   notice, this list of conditions and the following disclaimer.
10#
11# - Redistributions in binary form must reproduce the above copyright
12#   notice, this list of conditions and the following disclaimer in the
13#   documentation and/or other materials provided with the distribution.
14#
15# - Neither the name of the above author nor the names of the
16#   contributors to the software may be used to endorse or promote
17#   products derived from this software without specific prior written
18#   permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR
24# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32"""
33ftp_error.py - exception classes and wrappers
34"""
35
36# $Id$
37
38# "Too many ancestors"
39# pylint: disable-msg = R0901
40
41import ftplib
42import sys
43import warnings
44
45import ftputil_version
46
47
48class FTPError(Exception):
49    """General error class."""
50
51    def __init__(self, *args):
52        # contrary to what `ftplib`'s documentation says, `all_errors`
53        #  does _not_ contain the subclasses, so I explicitly add them
54        if args and args[0].__class__ in ftplib.all_errors + \
55                                         tuple(ftplib.Error.__subclasses__()):
56            warnings.warn(("Passing exception objects into the FTPError "
57              "constructor is deprecated and will be disabled in ftputil 2.6"),
58              DeprecationWarning, stacklevel=2)
59        super(FTPError, self).__init__(*args)
60        # don't use `args[0]` because `args` may be empty
61        if args:
62            self.strerror = self.args[0]
63        else:
64            self.strerror = ""
65        try:
66            self.errno = int(self.strerror[:3])
67        except (TypeError, IndexError, ValueError):
68            self.errno = None
69        self.filename = None
70
71    def __str__(self):
72        return "%s\nDebugging info: %s" % \
73               (self.strerror, ftputil_version.version_info)
74
75# internal errors are those that have more to do with the inner
76#  workings of ftputil than with errors on the server side
77class InternalError(FTPError):
78    """Internal error."""
79    pass
80
81class RootDirError(InternalError):
82    """Raised for generic stat calls on the remote root directory."""
83    pass
84
85class InaccessibleLoginDirError(InternalError):
86    """May be raised if the login directory isn't accessible."""
87    pass
88
89class TimeShiftError(InternalError):
90    """Raised for invalid time shift values."""
91    pass
92
93class ParserError(InternalError):
94    """Raised if a line of a remote directory can't be parsed."""
95    pass
96
97# currently not used
98class KeepAliveError(InternalError):
99    """Raised if the keep-alive feature failed."""
100    pass
101
102class FTPOSError(FTPError, OSError):
103    """Generic FTP error related to `OSError`."""
104    pass
105
106class TemporaryError(FTPOSError):
107    """Raised for temporary FTP errors (4xx)."""
108    pass
109
110class PermanentError(FTPOSError):
111    """Raised for permanent FTP errors (5xx)."""
112    pass
113
114class CommandNotImplementedError(PermanentError):
115    """Raised if the server doesn't implement a certain feature (502)."""
116    pass
117
118# currently not used
119class SyncError(PermanentError):
120    """Raised for problems specific to syncing directories."""
121    pass
122
123
124#XXX Do you know better names for `_try_with_oserror` and
125#    `_try_with_ioerror`?
126def _try_with_oserror(callee, *args, **kwargs):
127    """
128    Try the callee with the given arguments and map resulting
129    exceptions from `ftplib.all_errors` to `FTPOSError` and its
130    derived classes.
131    """
132    # use `*exc.args` instead of `str(args)` because args might be
133    #  a unicode string with non-ascii characters
134    try:
135        return callee(*args, **kwargs)
136    except ftplib.error_temp, exc:
137        raise TemporaryError(*exc.args)
138    except ftplib.error_perm, exc:
139        # if `exc.args` is present, assume it's a byte or unicode string
140        if exc.args and exc.args[0].startswith("502"):
141            raise CommandNotImplementedError(*exc.args)
142        else:
143            raise PermanentError(*exc.args)
144    except ftplib.all_errors:
145        exc = sys.exc_info()[1]
146        raise FTPOSError(*exc.args)
147
148class FTPIOError(FTPError, IOError):
149    """Generic FTP error related to `IOError`."""
150    pass
151
152
153def _try_with_ioerror(callee, *args, **kwargs):
154    """
155    Try the callee with the given arguments and map resulting
156    exceptions from `ftplib.all_errors` to `FTPIOError`.
157    """
158    try:
159        return callee(*args, **kwargs)
160    except ftplib.all_errors:
161        exc = sys.exc_info()[1]
162        # use `*exc.args` instead of `str(args)` because args might be
163        #  a unicode string with non-ascii characters
164        raise FTPIOError(*exc.args)
165
Note: See TracBrowser for help on using the repository browser.