source: ftputil/error.py @ 1723:7d731aea5360

Last change on this file since 1723:7d731aea5360 was 1723:7d731aea5360, checked in by Stefan Schwarzer <sschwarzer@…>, 8 months ago
Remove positional argument specifiers for `format` In strings for `format` calls, remove the digits for positional arguments. For example, "{0}, {1}" becomes "{}, {}".
File size: 5.0 KB
Line 
1# Copyright (C) 2003-2018, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# and ftputil contributors (see `doc/contributors.txt`)
3# See the file LICENSE for licensing terms.
4
5"""
6ftputil.error - exception classes and wrappers
7"""
8
9# pylint: disable=too-many-ancestors
10
11import ftplib
12
13import ftputil.tool
14import ftputil.version
15
16
17# You _can_ import these with `from ftputil.error import *`, - but
18# it's _not_ recommended.
19__all__ = [
20  "InternalError",
21  "RootDirError",
22  "InaccessibleLoginDirError",
23  "TimeShiftError",
24  "ParserError",
25  "KeepAliveError",
26  "FTPOSError",
27  "TemporaryError",
28  "PermanentError",
29  "CommandNotImplementedError",
30  "SyncError",
31  "FTPIOError",
32]
33
34
35class FTPError(Exception):
36    """General ftputil error class."""
37
38    # In Python 2, we can't use a keyword argument after `*args`, so
39    # `pop` from `**kwargs`.
40    def __init__(self, *args, **kwargs):
41        super(FTPError, self).__init__(*args)
42        if "original_exception" in kwargs:
43            # Byte string under Python 2.
44            exception_string = str(kwargs.pop("original_exception"))
45            self.strerror = ftputil.tool.as_unicode(exception_string)
46        elif args:
47            # If there was no `original_exception` argument, assume
48            # the first argument is a string. It may be a byte string
49            # though.
50            self.strerror = ftputil.tool.as_unicode(args[0])
51        else:
52            self.strerror = ""
53        try:
54            self.errno = int(self.strerror[:3])
55        except ValueError:
56            # `int()` argument couldn't be converted to an integer.
57            self.errno = None
58        self.file_name = None
59
60    def __str__(self):
61        return "{}\nDebugging info: {}".format(self.strerror,
62                                               ftputil.version.version_info)
63
64
65# Internal errors are those that have more to do with the inner
66# workings of ftputil than with errors on the server side.
67class InternalError(FTPError):
68    """Internal error."""
69    pass
70
71class RootDirError(InternalError):
72    """Raised for generic stat calls on the remote root directory."""
73    pass
74
75class InaccessibleLoginDirError(InternalError):
76    """May be raised if the login directory isn't accessible."""
77    pass
78
79class TimeShiftError(InternalError):
80    """Raised for invalid time shift values."""
81    pass
82
83class ParserError(InternalError):
84    """Raised if a line of a remote directory can't be parsed."""
85    pass
86
87class CacheMissError(InternalError):
88    """Raised if a path isn't found in the cache."""
89    pass
90
91# Currently not used
92class KeepAliveError(InternalError):
93    """Raised if the keep-alive feature failed."""
94    pass
95
96class FTPOSError(FTPError, OSError):
97    """Generic FTP error related to `OSError`."""
98    pass
99
100class TemporaryError(FTPOSError):
101    """Raised for temporary FTP errors (4xx)."""
102    pass
103
104class PermanentError(FTPOSError):
105    """Raised for permanent FTP errors (5xx)."""
106    pass
107
108class CommandNotImplementedError(PermanentError):
109    """Raised if the server doesn't implement a certain feature (502)."""
110    pass
111
112class RecursiveLinksError(PermanentError):
113    """Raised if an infinite link structure is detected."""
114    pass
115
116# Currently not used
117class SyncError(PermanentError):
118    """Raised for problems specific to syncing directories."""
119    pass
120
121
122class FtplibErrorToFTPOSError:
123    """
124    Context manager to convert `ftplib` exceptions to exceptions
125    derived from `FTPOSError`.
126    """
127
128    def __enter__(self):
129        pass
130
131    def __exit__(self, exc_type, exc_value, traceback):
132        if exc_type is None:
133            # No exception
134            return
135        if isinstance(exc_value, ftplib.error_temp):
136            raise TemporaryError(*exc_value.args, original_exception=exc_value)
137        elif isinstance(exc_value, ftplib.error_perm):
138            # If `exc_value.args[0]` is present, assume it's a byte or
139            # unicode string.
140            if (
141              exc_value.args and
142              ftputil.tool.as_unicode(exc_value.args[0]).startswith("502")
143            ):
144                raise CommandNotImplementedError(*exc_value.args)
145            else:
146                raise PermanentError(*exc_value.args,
147                                     original_exception=exc_value)
148        elif isinstance(exc_value, ftplib.all_errors):
149            raise FTPOSError(*exc_value.args, original_exception=exc_value)
150        else:
151            raise
152
153ftplib_error_to_ftp_os_error = FtplibErrorToFTPOSError()
154
155
156class FTPIOError(FTPError, IOError):
157    """Generic FTP error related to `IOError`."""
158    pass
159
160
161class FtplibErrorToFTPIOError:
162    """
163    Context manager to convert `ftplib` exceptions to `FTPIOError`
164    exceptions.
165    """
166
167    def __enter__(self):
168        pass
169
170    def __exit__(self, exc_type, exc_value, traceback):
171        if exc_type is None:
172            # No exception
173            return
174        if isinstance(exc_value, ftplib.all_errors):
175            raise FTPIOError(*exc_value.args, original_exception=exc_value)
176        else:
177            raise
178
179ftplib_error_to_ftp_io_error = FtplibErrorToFTPIOError()
Note: See TracBrowser for help on using the repository browser.