source: ftputil/error.py @ 1682:ec059f9d9dc8

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