1""" 2Defines the Exception classes thrown by PyFilesystem objects. Exceptions relating 3to the underlying filesystem are translated in to one of the following Exceptions. 4Exceptions that relate to a path store that path in `self.path`. 5 6All Exception classes are derived from `FSError` which can be used as a 7catch-all exception. 8 9""" 10 11__all__ = ['FSError', 12 'CreateFailedError', 13 'PathError', 14 'InvalidPathError', 15 'InvalidCharsInPathError', 16 'OperationFailedError', 17 'UnsupportedError', 18 'RemoteConnectionError', 19 'StorageSpaceError', 20 'PermissionDeniedError', 21 'FSClosedError', 22 'OperationTimeoutError', 23 'RemoveRootError', 24 'ResourceError', 25 'NoSysPathError', 26 'NoMetaError', 27 'NoPathURLError', 28 'ResourceNotFoundError', 29 'ResourceInvalidError', 30 'DestinationExistsError', 31 'DirectoryNotEmptyError', 32 'ParentDirectoryMissingError', 33 'ResourceLockedError', 34 'NoMMapError', 35 'BackReferenceError', 36 'convert_fs_errors', 37 'convert_os_errors', 38 ] 39 40import sys 41import errno 42import six 43 44from fs.path import * 45from fs.local_functools import wraps 46 47 48class FSError(Exception): 49 """Base exception class for the FS module.""" 50 default_message = "Unspecified error" 51 52 def __init__(self,msg=None,details=None): 53 if msg is None: 54 msg = self.default_message 55 self.msg = msg 56 self.details = details 57 58 def __str__(self): 59 keys = {} 60 for k,v in self.__dict__.iteritems(): 61 if isinstance(v,unicode): 62 v = v.encode(sys.getfilesystemencoding()) 63 keys[k] = v 64 return str(self.msg % keys) 65 66 def __unicode__(self): 67 keys = {} 68 for k,v in self.__dict__.iteritems(): 69 if isinstance(v, six.binary_type): 70 v = v.decode(sys.getfilesystemencoding(), 'replace') 71 keys[k] = v 72 return unicode(self.msg, encoding=sys.getfilesystemencoding(), errors='replace') % keys 73 74 def __reduce__(self): 75 return (self.__class__,(),self.__dict__.copy(),) 76 77 78class CreateFailedError(FSError): 79 """An exception thrown when a FS could not be created""" 80 default_message = "Unable to create filesystem" 81 82 83class PathError(FSError): 84 """Exception for errors to do with a path string. 85 """ 86 default_message = "Path is invalid: %(path)s" 87 88 def __init__(self,path="",**kwds): 89 self.path = path 90 super(PathError,self).__init__(**kwds) 91 92 93class InvalidPathError(PathError): 94 """Base exception for fs paths that can't be mapped on to the underlaying filesystem.""" 95 default_message = "Path is invalid on this filesystem %(path)s" 96 97 98class InvalidCharsInPathError(InvalidPathError): 99 """The path contains characters that are invalid on this filesystem""" 100 default_message = "Path contains invalid characters: %(path)s" 101 102 103class OperationFailedError(FSError): 104 """Base exception class for errors associated with a specific operation.""" 105 default_message = "Unable to %(opname)s: unspecified error [%(errno)s - %(details)s]" 106 107 def __init__(self,opname="",path=None,**kwds): 108 self.opname = opname 109 self.path = path 110 self.errno = getattr(kwds.get("details",None),"errno",None) 111 super(OperationFailedError,self).__init__(**kwds) 112 113 114class UnsupportedError(OperationFailedError): 115 """Exception raised for operations that are not supported by the FS.""" 116 default_message = "Unable to %(opname)s: not supported by this filesystem" 117 118 119class RemoteConnectionError(OperationFailedError): 120 """Exception raised when operations encounter remote connection trouble.""" 121 default_message = "%(opname)s: remote connection errror" 122 123 124class StorageSpaceError(OperationFailedError): 125 """Exception raised when operations encounter storage space trouble.""" 126 default_message = "Unable to %(opname)s: insufficient storage space" 127 128 129class PermissionDeniedError(OperationFailedError): 130 default_message = "Unable to %(opname)s: permission denied" 131 132 133class FSClosedError(OperationFailedError): 134 default_message = "Unable to %(opname)s: the FS has been closed" 135 136 137class OperationTimeoutError(OperationFailedError): 138 default_message = "Unable to %(opname)s: operation timed out" 139 140 141class RemoveRootError(OperationFailedError): 142 default_message = "Can't remove root dir" 143 144 145class ResourceError(FSError): 146 """Base exception class for error associated with a specific resource.""" 147 default_message = "Unspecified resource error: %(path)s" 148 149 def __init__(self,path="",**kwds): 150 self.path = path 151 self.opname = kwds.pop("opname",None) 152 super(ResourceError,self).__init__(**kwds) 153 154 155class NoSysPathError(ResourceError): 156 """Exception raised when there is no syspath for a given path.""" 157 default_message = "No mapping to OS filesystem: %(path)s" 158 159 160class NoMetaError(FSError): 161 """Exception raised when there is no meta value available.""" 162 default_message = "No meta value named '%(meta_name)s' could be retrieved" 163 def __init__(self, meta_name, msg=None): 164 self.meta_name = meta_name 165 super(NoMetaError, self).__init__(msg) 166 def __reduce__(self): 167 return (self.__class__,(self.meta_name,),self.__dict__.copy(),) 168 169 170class NoPathURLError(ResourceError): 171 """Exception raised when there is no URL form for a given path.""" 172 default_message = "No URL form: %(path)s" 173 174 175class ResourceNotFoundError(ResourceError): 176 """Exception raised when a required resource is not found.""" 177 default_message = "Resource not found: %(path)s" 178 179 180class ResourceInvalidError(ResourceError): 181 """Exception raised when a resource is the wrong type.""" 182 default_message = "Resource is invalid: %(path)s" 183 184 185class DestinationExistsError(ResourceError): 186 """Exception raised when a target destination already exists.""" 187 default_message = "Destination exists: %(path)s" 188 189 190class DirectoryNotEmptyError(ResourceError): 191 """Exception raised when a directory to be removed is not empty.""" 192 default_message = "Directory is not empty: %(path)s" 193 194 195class ParentDirectoryMissingError(ResourceError): 196 """Exception raised when a parent directory is missing.""" 197 default_message = "Parent directory is missing: %(path)s" 198 199 200class ResourceLockedError(ResourceError): 201 """Exception raised when a resource can't be used because it is locked.""" 202 default_message = "Resource is locked: %(path)s" 203 204 205class NoMMapError(ResourceError): 206 """Exception raise when getmmap fails to create a mmap""" 207 default_message = "Can't get mmap for %(path)s" 208 209 210class BackReferenceError(ValueError): 211 """Exception raised when too many backrefs exist in a path (ex: '/..', '/docs/../..').""" 212 213 214def convert_fs_errors(func): 215 """Function wrapper to convert FSError instances into OSError.""" 216 @wraps(func) 217 def wrapper(*args,**kwds): 218 try: 219 return func(*args,**kwds) 220 except ResourceNotFoundError, e: 221 raise OSError(errno.ENOENT,str(e)) 222 except ParentDirectoryMissingError, e: 223 if sys.platform == "win32": 224 raise OSError(errno.ESRCH,str(e)) 225 else: 226 raise OSError(errno.ENOENT,str(e)) 227 except ResourceInvalidError, e: 228 raise OSError(errno.EINVAL,str(e)) 229 except PermissionDeniedError, e: 230 raise OSError(errno.EACCES,str(e)) 231 except ResourceLockedError, e: 232 if sys.platform == "win32": 233 raise WindowsError(32,str(e)) 234 else: 235 raise OSError(errno.EACCES,str(e)) 236 except DirectoryNotEmptyError, e: 237 raise OSError(errno.ENOTEMPTY,str(e)) 238 except DestinationExistsError, e: 239 raise OSError(errno.EEXIST,str(e)) 240 except StorageSpaceError, e: 241 raise OSError(errno.ENOSPC,str(e)) 242 except RemoteConnectionError, e: 243 raise OSError(errno.ENETDOWN,str(e)) 244 except UnsupportedError, e: 245 raise OSError(errno.ENOSYS,str(e)) 246 except FSError, e: 247 raise OSError(errno.EFAULT,str(e)) 248 return wrapper 249 250 251def convert_os_errors(func): 252 """Function wrapper to convert OSError/IOError instances into FSError.""" 253 opname = func.__name__ 254 @wraps(func) 255 def wrapper(self,*args,**kwds): 256 try: 257 return func(self,*args,**kwds) 258 except (OSError,IOError), e: 259 (exc_type,exc_inst,tb) = sys.exc_info() 260 path = getattr(e,"filename",None) 261 if path and path[0] == "/" and hasattr(self,"root_path"): 262 path = normpath(path) 263 if isprefix(self.root_path,path): 264 path = path[len(self.root_path):] 265 if not hasattr(e,"errno") or not e.errno: 266 raise OperationFailedError(opname,details=e),None,tb 267 if e.errno == errno.ENOENT: 268 raise ResourceNotFoundError(path,opname=opname,details=e),None,tb 269 if e.errno == errno.EFAULT: 270 # This can happen when listdir a directory that is deleted by another thread 271 # Best to interpret it as a resource not found 272 raise ResourceNotFoundError(path,opname=opname,details=e),None,tb 273 if e.errno == errno.ESRCH: 274 raise ResourceNotFoundError(path,opname=opname,details=e),None,tb 275 if e.errno == errno.ENOTEMPTY: 276 raise DirectoryNotEmptyError(path,opname=opname,details=e),None,tb 277 if e.errno == errno.EEXIST: 278 raise DestinationExistsError(path,opname=opname,details=e),None,tb 279 if e.errno == 183: # some sort of win32 equivalent to EEXIST 280 raise DestinationExistsError(path,opname=opname,details=e),None,tb 281 if e.errno == errno.ENOTDIR: 282 raise ResourceInvalidError(path,opname=opname,details=e),None,tb 283 if e.errno == errno.EISDIR: 284 raise ResourceInvalidError(path,opname=opname,details=e),None,tb 285 if e.errno == errno.EINVAL: 286 raise ResourceInvalidError(path,opname=opname,details=e),None,tb 287 if e.errno == errno.ENOSPC: 288 raise StorageSpaceError(opname,path=path,details=e),None,tb 289 if e.errno == errno.EPERM: 290 raise PermissionDeniedError(opname,path=path,details=e),None,tb 291 if hasattr(errno,"ENONET") and e.errno == errno.ENONET: 292 raise RemoteConnectionError(opname,path=path,details=e),None,tb 293 if e.errno == errno.ENETDOWN: 294 raise RemoteConnectionError(opname,path=path,details=e),None,tb 295 if e.errno == errno.ECONNRESET: 296 raise RemoteConnectionError(opname,path=path,details=e),None,tb 297 if e.errno == errno.EACCES: 298 if sys.platform == "win32": 299 if e.args[0] and e.args[0] == 32: 300 raise ResourceLockedError(path,opname=opname,details=e),None,tb 301 raise PermissionDeniedError(opname,details=e),None,tb 302 # Sometimes windows gives some random errors... 303 if sys.platform == "win32": 304 if e.errno in (13,): 305 raise ResourceInvalidError(path,opname=opname,details=e),None,tb 306 if e.errno == errno.ENAMETOOLONG: 307 raise PathError(path,details=e),None,tb 308 if e.errno == errno.EOPNOTSUPP: 309 raise UnsupportedError(opname,details=e),None,tb 310 if e.errno == errno.ENOSYS: 311 raise UnsupportedError(opname,details=e),None,tb 312 raise OperationFailedError(opname,details=e),None,tb 313 return wrapper 314 315 316