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 306 307 308 309 310 311 312 313 314 315
|
"""
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
import six
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):
keys = {}
for k,v in self.__dict__.iteritems():
if isinstance(v, six.binary_type):
v = v.decode(sys.getfilesystemencoding(), 'replace')
keys[k] = v
return unicode(self.msg, encoding=sys.getfilesystemencoding(), errors='replace') % keys
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.EFAULT:
# This can happen when listdir a directory that is deleted by another thread
# Best to interpret it as a resource not found
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
|