Ticket #85: error.py

File error.py, 4.9 KB (added by schwa, 7 years ago)

Patched error.py

Line 
1# Copyright (C) 2003-2014, Stefan Schwarzer <sschwarzer@sschwarzer.net>
2# See the file LICENSE for licensing terms.
3
4"""
5ftputil.error - exception classes and wrappers
6"""
7
8# pylint: disable=too-many-ancestors
9
10from __future__ import unicode_literals
11
12import ftplib
13
14import ftputil.version
15
16
17# You _can_ import these with `from ftputil.error import *`, - but
18# it's _not_ recommended.
19__all__ = [
20  "FTPError",
21  "InternalError",
22  "RootDirError",
23  "InaccessibleLoginDirError",
24  "TimeShiftError",
25  "ParserError",
26  "KeepAliveError",
27  "FTPOSError",
28  "TemporaryError",
29  "PermanentError",
30  "CommandNotImplementedError",
31  "SyncError",
32  "FTPIOError",
33]
34
35
36class FTPError(Exception):
37    """General ftputil error class."""
38
39    # In Python 2, we can't use a keyword argument after `*args`, so
40    # `pop` from `**kwargs`.
41    def __init__(self, *args, **kwargs):
42        super(FTPError, self).__init__(*args)
43        if "original_exception" in kwargs:
44            self.strerror = ftputil.tool.as_unicode(
45                              kwargs.pop("original_exception"))
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 (TypeError, IndexError, ValueError):
56            # TODO: List where all these exceptions may come from.
57            self.errno = None
58        self.file_name = None
59
60    def __str__(self):
61        return "{0}\nDebugging info: {1}".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
112# Currently not used
113class SyncError(PermanentError):
114    """Raised for problems specific to syncing directories."""
115    pass
116
117
118class FtplibErrorToFTPOSError(object):
119    """
120    Context manager to convert `ftplib` exceptions to exceptions
121    derived from `FTPOSError`.
122    """
123
124    def __enter__(self):
125        pass
126
127    def __exit__(self, exc_type, exc_value, traceback):
128        if exc_type is None:
129            # No exception
130            return
131        if isinstance(exc_value, ftplib.error_temp):
132            raise TemporaryError(*exc_value.args, original_exception=exc_value)
133        elif isinstance(exc_value, ftplib.error_perm):
134            # If `exc_value.args[0]` is present, assume it's a byte or
135            # unicode string.
136            if (
137              exc_value.args and
138              ftputil.tool.as_unicode(exc_value.args[0]).startswith("502")
139            ):
140                raise CommandNotImplementedError(*exc_value.args)
141            else:
142                raise PermanentError(*exc_value.args,
143                                     original_exception=exc_value)
144        elif isinstance(exc_value, ftplib.all_errors):
145            raise FTPOSError(*exc_value.args, original_exception=exc_value)
146        else:
147            raise
148
149ftplib_error_to_ftp_os_error = FtplibErrorToFTPOSError()
150
151
152class FTPIOError(FTPError, IOError):
153    """Generic FTP error related to `IOError`."""
154    pass
155
156
157class FtplibErrorToFTPIOError(object):
158    """
159    Context manager to convert `ftplib` exceptions to `FTPIOError`
160    exceptions.
161    """
162
163    def __enter__(self):
164        pass
165
166    def __exit__(self, exc_type, exc_value, traceback):
167        if exc_type is None:
168            # No exception
169            return
170        if isinstance(exc_value, ftplib.all_errors):
171            raise FTPIOError(*exc_value.args, original_exception=exc_value)
172        else:
173            raise
174
175ftplib_error_to_ftp_io_error = FtplibErrorToFTPIOError()