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 | """ |
---|
6 | ftputil.error - exception classes and wrappers |
---|
7 | """ |
---|
8 | |
---|
9 | # pylint: disable=too-many-ancestors |
---|
10 | |
---|
11 | import ftplib |
---|
12 | |
---|
13 | import ftputil.tool |
---|
14 | import 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 | |
---|
35 | class 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 "{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. |
---|
67 | class InternalError(FTPError): |
---|
68 | """Internal error.""" |
---|
69 | pass |
---|
70 | |
---|
71 | class RootDirError(InternalError): |
---|
72 | """Raised for generic stat calls on the remote root directory.""" |
---|
73 | pass |
---|
74 | |
---|
75 | class InaccessibleLoginDirError(InternalError): |
---|
76 | """May be raised if the login directory isn't accessible.""" |
---|
77 | pass |
---|
78 | |
---|
79 | class TimeShiftError(InternalError): |
---|
80 | """Raised for invalid time shift values.""" |
---|
81 | pass |
---|
82 | |
---|
83 | class ParserError(InternalError): |
---|
84 | """Raised if a line of a remote directory can't be parsed.""" |
---|
85 | pass |
---|
86 | |
---|
87 | class CacheMissError(InternalError): |
---|
88 | """Raised if a path isn't found in the cache.""" |
---|
89 | pass |
---|
90 | |
---|
91 | # Currently not used |
---|
92 | class KeepAliveError(InternalError): |
---|
93 | """Raised if the keep-alive feature failed.""" |
---|
94 | pass |
---|
95 | |
---|
96 | class FTPOSError(FTPError, OSError): |
---|
97 | """Generic FTP error related to `OSError`.""" |
---|
98 | pass |
---|
99 | |
---|
100 | class TemporaryError(FTPOSError): |
---|
101 | """Raised for temporary FTP errors (4xx).""" |
---|
102 | pass |
---|
103 | |
---|
104 | class PermanentError(FTPOSError): |
---|
105 | """Raised for permanent FTP errors (5xx).""" |
---|
106 | pass |
---|
107 | |
---|
108 | class CommandNotImplementedError(PermanentError): |
---|
109 | """Raised if the server doesn't implement a certain feature (502).""" |
---|
110 | pass |
---|
111 | |
---|
112 | class RecursiveLinksError(PermanentError): |
---|
113 | """Raised if an infinite link structure is detected.""" |
---|
114 | pass |
---|
115 | |
---|
116 | # Currently not used |
---|
117 | class SyncError(PermanentError): |
---|
118 | """Raised for problems specific to syncing directories.""" |
---|
119 | pass |
---|
120 | |
---|
121 | |
---|
122 | class FtplibErrorToFTPOSError(object): |
---|
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 | |
---|
153 | ftplib_error_to_ftp_os_error = FtplibErrorToFTPOSError() |
---|
154 | |
---|
155 | |
---|
156 | class FTPIOError(FTPError, IOError): |
---|
157 | """Generic FTP error related to `IOError`.""" |
---|
158 | pass |
---|
159 | |
---|
160 | |
---|
161 | class FtplibErrorToFTPIOError(object): |
---|
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 | |
---|
179 | ftplib_error_to_ftp_io_error = FtplibErrorToFTPIOError() |
---|