summaryrefslogtreecommitdiff
path: root/fs/errors.py
blob: 076de4bffe0aabcfc65d32a60a652795397e44b2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
"""
Defines the Exception classes thrown by PyFilesystem objects. Exceptions relating
to the underlying filesystem are translated in to one of the following Exceptions.
Exceptions that relate to a path store that path in `self.path`.

All Exception classes are derived from `FSError` which can be used as a
catch-all exception.

"""

__all__ = ['FSError',
           'CreateFailedError',
           'PathError',
           'InvalidPathError',
           'InvalidCharsInPathError',
           'OperationFailedError',
           'UnsupportedError',
           'RemoteConnectionError',
           'StorageSpaceError',
           'PermissionDeniedError',
           'FSClosedError',
           'OperationTimeoutError',
           'RemoveRootError',
           'ResourceError',
           'NoSysPathError',
           'NoMetaError',
           'NoPathURLError',
           'ResourceNotFoundError',
           'ResourceInvalidError',
           'DestinationExistsError',
           'DirectoryNotEmptyError',
           'ParentDirectoryMissingError',
           'ResourceLockedError',
           'NoMMapError',
           'BackReferenceError',
           'convert_fs_errors',
           'convert_os_errors',
           ]

import sys
import errno

from fs.path import *
from fs.local_functools import wraps


class FSError(Exception):
    """Base exception class for the FS module."""
    default_message = "Unspecified error"

    def __init__(self,msg=None,details=None):
        if msg is None:
            msg = self.default_message
        self.msg = msg
        self.details = details

    def __str__(self):
        keys = {}
        for k,v in self.__dict__.iteritems():
            if isinstance(v,unicode):
                v = v.encode(sys.getfilesystemencoding())
            keys[k] = v
        return str(self.msg % keys)

    def __unicode__(self):
        return unicode(self.msg) % self.__dict__

    def __reduce__(self):
        return (self.__class__,(),self.__dict__.copy(),)


class CreateFailedError(FSError):
    """An exception thrown when a FS could not be created"""
    default_message = "Unable to create filesystem"


class PathError(FSError):
    """Exception for errors to do with a path string.
    """
    default_message = "Path is invalid: %(path)s"

    def __init__(self,path="",**kwds):
        self.path = path
        super(PathError,self).__init__(**kwds)


class InvalidPathError(PathError):
    """Base exception for fs paths that can't be mapped on to the underlaying filesystem."""
    default_message = "Path is invalid on this filesystem %(path)s"


class InvalidCharsInPathError(InvalidPathError):
    """The path contains characters that are invalid on this filesystem"""
    default_message = "Path contains invalid characters: %(path)s"


class OperationFailedError(FSError):
    """Base exception class for errors associated with a specific operation."""
    default_message = "Unable to %(opname)s: unspecified error [%(errno)s - %(details)s]"

    def __init__(self,opname="",path=None,**kwds):
        self.opname = opname
        self.path = path
        self.errno = getattr(kwds.get("details",None),"errno",None)
        super(OperationFailedError,self).__init__(**kwds)


class UnsupportedError(OperationFailedError):
    """Exception raised for operations that are not supported by the FS."""
    default_message = "Unable to %(opname)s: not supported by this filesystem"


class RemoteConnectionError(OperationFailedError):
    """Exception raised when operations encounter remote connection trouble."""
    default_message = "%(opname)s: remote connection errror"


class StorageSpaceError(OperationFailedError):
    """Exception raised when operations encounter storage space trouble."""
    default_message = "Unable to %(opname)s: insufficient storage space"


class PermissionDeniedError(OperationFailedError):
    default_message = "Unable to %(opname)s: permission denied"


class FSClosedError(OperationFailedError):
    default_message = "Unable to %(opname)s: the FS has been closed"


class OperationTimeoutError(OperationFailedError):
    default_message = "Unable to %(opname)s: operation timed out"


class RemoveRootError(OperationFailedError):
    default_message = "Can't remove root dir"


class ResourceError(FSError):
    """Base exception class for error associated with a specific resource."""
    default_message = "Unspecified resource error: %(path)s"

    def __init__(self,path="",**kwds):
        self.path = path
        self.opname = kwds.pop("opname",None)
        super(ResourceError,self).__init__(**kwds)


class NoSysPathError(ResourceError):
    """Exception raised when there is no syspath for a given path."""
    default_message = "No mapping to OS filesystem: %(path)s"


class NoMetaError(FSError):
    """Exception raised when there is no meta value available."""
    default_message = "No meta value named '%(meta_name)s' could be retrieved"
    def __init__(self, meta_name, msg=None):
        self.meta_name = meta_name
        super(NoMetaError, self).__init__(msg)
    def __reduce__(self):
        return (self.__class__,(self.meta_name,),self.__dict__.copy(),)


class NoPathURLError(ResourceError):
    """Exception raised when there is no URL form for a given path."""
    default_message = "No URL form: %(path)s"


class ResourceNotFoundError(ResourceError):
    """Exception raised when a required resource is not found."""
    default_message = "Resource not found: %(path)s"


class ResourceInvalidError(ResourceError):
    """Exception raised when a resource is the wrong type."""
    default_message = "Resource is invalid: %(path)s"


class DestinationExistsError(ResourceError):
    """Exception raised when a target destination already exists."""
    default_message = "Destination exists: %(path)s"


class DirectoryNotEmptyError(ResourceError):
    """Exception raised when a directory to be removed is not empty."""
    default_message = "Directory is not empty: %(path)s"


class ParentDirectoryMissingError(ResourceError):
    """Exception raised when a parent directory is missing."""
    default_message = "Parent directory is missing: %(path)s"


class ResourceLockedError(ResourceError):
    """Exception raised when a resource can't be used because it is locked."""
    default_message = "Resource is locked: %(path)s"


class NoMMapError(ResourceError):
    """Exception raise when getmmap fails to create a mmap"""
    default_message = "Can't get mmap for %(path)s"


class BackReferenceError(ValueError):
    """Exception raised when too many backrefs exist in a path (ex: '/..', '/docs/../..')."""


def convert_fs_errors(func):
    """Function wrapper to convert FSError instances into OSError."""
    @wraps(func)
    def wrapper(*args,**kwds):
        try:
            return func(*args,**kwds)
        except ResourceNotFoundError, e:
            raise OSError(errno.ENOENT,str(e))
        except ParentDirectoryMissingError, e:
            if sys.platform == "win32":
                raise OSError(errno.ESRCH,str(e))
            else:
                raise OSError(errno.ENOENT,str(e))
        except ResourceInvalidError, e:
            raise OSError(errno.EINVAL,str(e))
        except PermissionDeniedError, e:
            raise OSError(errno.EACCES,str(e))
        except ResourceLockedError, e:
            if sys.platform == "win32":
                raise WindowsError(32,str(e))
            else:
                raise OSError(errno.EACCES,str(e))
        except DirectoryNotEmptyError, e:
            raise OSError(errno.ENOTEMPTY,str(e))
        except DestinationExistsError, e:
            raise OSError(errno.EEXIST,str(e))
        except StorageSpaceError, e:
            raise OSError(errno.ENOSPC,str(e))
        except RemoteConnectionError, e:
            raise OSError(errno.ENETDOWN,str(e))
        except UnsupportedError, e:
            raise OSError(errno.ENOSYS,str(e))
        except FSError, e:
            raise OSError(errno.EFAULT,str(e))
    return wrapper


def convert_os_errors(func):
    """Function wrapper to convert OSError/IOError instances into FSError."""
    opname = func.__name__
    @wraps(func)
    def wrapper(self,*args,**kwds):
        try:
            return func(self,*args,**kwds)
        except (OSError,IOError), e:
            (exc_type,exc_inst,tb) = sys.exc_info()
            path = getattr(e,"filename",None)
            if path and path[0] == "/" and hasattr(self,"root_path"):
                path = normpath(path)
                if isprefix(self.root_path,path):
                    path = path[len(self.root_path):]
            if not hasattr(e,"errno") or not e.errno:
                raise OperationFailedError(opname,details=e),None,tb
            if e.errno == errno.ENOENT:
                raise ResourceNotFoundError(path,opname=opname,details=e),None,tb
            if e.errno == errno.ESRCH:
                raise ResourceNotFoundError(path,opname=opname,details=e),None,tb
            if e.errno == errno.ENOTEMPTY:
                raise DirectoryNotEmptyError(path,opname=opname,details=e),None,tb
            if e.errno == errno.EEXIST:
                raise DestinationExistsError(path,opname=opname,details=e),None,tb
            if e.errno == 183: # some sort of win32 equivalent to EEXIST
                raise DestinationExistsError(path,opname=opname,details=e),None,tb
            if e.errno == errno.ENOTDIR:
                raise ResourceInvalidError(path,opname=opname,details=e),None,tb
            if e.errno == errno.EISDIR:
                raise ResourceInvalidError(path,opname=opname,details=e),None,tb
            if e.errno == errno.EINVAL:
                raise ResourceInvalidError(path,opname=opname,details=e),None,tb
            if e.errno == errno.ENOSPC:
                raise StorageSpaceError(opname,path=path,details=e),None,tb
            if e.errno == errno.EPERM:
                raise PermissionDeniedError(opname,path=path,details=e),None,tb
            if hasattr(errno,"ENONET") and e.errno == errno.ENONET:
                raise RemoteConnectionError(opname,path=path,details=e),None,tb
            if e.errno == errno.ENETDOWN:
                raise RemoteConnectionError(opname,path=path,details=e),None,tb
            if e.errno == errno.ECONNRESET:
                raise RemoteConnectionError(opname,path=path,details=e),None,tb
            if e.errno == errno.EACCES:
                if sys.platform == "win32":
                    if e.args[0] and e.args[0] == 32:
                        raise ResourceLockedError(path,opname=opname,details=e),None,tb
                raise PermissionDeniedError(opname,details=e),None,tb
            # Sometimes windows gives some random errors...
            if sys.platform == "win32":
                if e.errno in (13,):
                    raise ResourceInvalidError(path,opname=opname,details=e),None,tb
            if e.errno == errno.ENAMETOOLONG:
                raise PathError(path,details=e),None,tb
            if e.errno == errno.EOPNOTSUPP:
                raise UnsupportedError(opname,details=e),None,tb
            if e.errno == errno.ENOSYS:
                raise UnsupportedError(opname,details=e),None,tb
            raise OperationFailedError(opname,details=e),None,tb
    return wrapper