Commit fb95d7be by willmcgugan

Added better zip exceptions, and added __all__ to core classes

parent 8a504419
......@@ -10,6 +10,13 @@ start by sublcassing the base FS class.
"""
__all__ = ['DummyLock',
'silence_fserrors',
'NullFile',
'synchronize',
'FS',
'flags_to_mode']
import os, os.path
import sys
import shutil
......@@ -23,9 +30,10 @@ except ImportError:
from fs.path import *
from fs.errors import *
from fs.functools import wraps
class DummyLock:
class DummyLock(object):
"""A dummy lock object that doesn't do anything.
This is used as a placeholder when locking is disabled. We can't
......@@ -102,11 +110,6 @@ class NullFile(object):
def writelines(self, *args, **kwargs):
pass
try:
from functools import wraps
except ImportError:
wraps = lambda f: lambda f: f
def synchronize(func):
"""Decorator to synchronize a method on self._lock."""
......@@ -119,6 +122,7 @@ def synchronize(func):
self._lock.release()
return acquire_lock
class FS(object):
"""The base class for Filesystem abstraction objects.
......@@ -489,7 +493,7 @@ class FS(object):
f = None
try:
f = self.open(path, 'wb')
if hasattr(data,"read"):
if hasattr(data, "read"):
chunk = data.read(1024*512)
while chunk:
f.write(chunk)
......
......@@ -5,15 +5,33 @@ All Exception classes are derived from `FSError` which can be used as a catch-al
"""
__all__ = ['FSError',
'CreateFailedError',
'PathError',
'OperationFailedError',
'UnsupportedError',
'RemoteConnectionError',
'StorageSpaceError',
'PermissionDeniedError',
'FSClosedError',
'OperationTimeoutError',
'ResourceError',
'NoSysPathError',
'ResourceNotFoundError',
'ResourceInvalidError',
'DestinationExistsError',
'DirectoryNotEmptyError',
'ParentDirectoryMissingError',
'ResourceLockedError',
'convert_fs_errors',
'convert_os_errors'
]
import sys
import errno
from fs.path import *
try:
from functools import wraps
except ImportError:
wraps = lambda f: lambda f: f
from fs.functools import wraps
class FSError(Exception):
......@@ -41,6 +59,11 @@ class FSError(Exception):
return 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.
"""
......
......@@ -58,6 +58,7 @@ import pickle
from fs.base import flags_to_mode, threading
from fs.errors import *
from fs.path import *
from fs.functools import wraps
try:
import fuse_ctypes as fuse
......
......@@ -36,9 +36,7 @@ import paramiko
from fs.base import flags_to_mode
from fs.path import *
from fs.errors import *
from fs.errors import wraps
from fs.functools import wraps
# Default host key used by BaseSFTPServer
......
......@@ -10,7 +10,8 @@ __all__ = ['FTPFS']
import fs
from fs.base import *
from fs.path import pathsplit
from fs.errors import *
from fs.path import pathsplit, abspath, dirname, recursepath, normpath
from ftplib import FTP, error_perm, error_temp, error_proto, error_reply
......@@ -26,10 +27,7 @@ from time import sleep
import datetime
import re
from socket import error as socket_error
try:
from functools import wraps
except ImportError:
wraps = lambda f: lambda f: f
from fs.functools import wraps
try:
from cStringIO import StringIO
......
......@@ -11,8 +11,9 @@ If you open a file from a `memoryfs` you will get back a StringIO object from th
"""
import datetime
from fs.path import iteratepath
from fs.path import iteratepath, pathsplit, normpath
from fs.base import *
from fs.errors import *
from fs import _thread_synchronize_default
try:
......@@ -131,7 +132,6 @@ class MemoryFile(object):
return False
class DirEntry(object):
def __init__(self, type, name, contents=None):
......@@ -509,7 +509,6 @@ class MemoryFS(FS):
return info
@synchronize
def copydir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384):
src_dir_entry = self._get_dir_entry(src)
......
......@@ -43,6 +43,8 @@ Now both filesystems can be accessed with the same path structure::
"""
from fs.base import *
from fs.errors import *
from fs.path import *
from fs.objecttree import ObjectTree
from fs import _thread_synchronize_default
......
......@@ -54,6 +54,7 @@ directories::
from fs.base import FS, FSError, synchronize
from fs.path import *
from fs.errors import *
from fs import _thread_synchronize_default
from fs.errors import ResourceNotFoundError
......
......@@ -16,8 +16,10 @@ For example, to print all the files and directories in the OS root::
import os
import sys
import errno
import datetime
from fs.base import *
from fs.errors import *
from fs.path import *
from fs import _thread_synchronize_default
......
......@@ -269,6 +269,7 @@ def forcedir(path):
return path + '/'
return path
def frombase(path1, path2):
if not isprefix(path1, path2):
raise ValueError("path1 must be a prefix of path2")
......
......@@ -29,6 +29,7 @@ from fs.wrapfs import WrapFS, wrap_fs_methods
from fs.wrapfs.lazyfs import LazyFS
from fs.path import *
from fs.errors import *
from fs.functools import wraps
try:
from tempfile import SpooledTemporaryFile
......
......@@ -11,6 +11,8 @@ class from the :mod:`fs.expose.xmlrpc` module.
import xmlrpclib
from fs.base import *
from fs.errors import *
from fs.path import *
from StringIO import StringIO
if hasattr(StringIO,"__exit__"):
......
......@@ -19,6 +19,8 @@ from boto.s3.prefix import Prefix
from boto.exception import S3ResponseError
from fs.base import *
from fs.path import *
from fs.errors import *
from fs.remote import *
......
......@@ -8,13 +8,16 @@ Filesystem accessing an SFTP server (via paramiko)
import datetime
import stat as statinfo
import threading
import paramiko
from fs.base import *
from fs.path import *
from fs.errors import *
# SFTPClient appears to not be thread-safe, so we use an instance per thread
if hasattr(threading,"local"):
if hasattr(threading, "local"):
thread_local = threading.local
else:
class thread_local(object):
......
......@@ -12,6 +12,7 @@ import tempfile
from fs.osfs import OSFS
from fs.errors import *
from fs import _thread_synchronize_default
class TempFS(OSFS):
......
......@@ -12,6 +12,8 @@ import logging
logging.basicConfig(level=logging.ERROR, stream=sys.stdout)
from fs.base import *
from fs.path import *
from fs.errors import *
import datetime
import unittest
......
......@@ -46,12 +46,12 @@ class TestPathFunctions(unittest.TestCase):
for testpaths in tests:
paths = testpaths[:-1]
result = testpaths[-1]
self.assertEqual(fs.pathjoin(*paths), result)
self.assertEqual(pathjoin(*paths), result)
self.assertRaises(ValueError, fs.pathjoin, "../")
self.assertRaises(ValueError, fs.pathjoin, "./../")
self.assertRaises(ValueError, fs.pathjoin, "a/b", "../../..")
self.assertRaises(ValueError, fs.pathjoin, "a/b/../../../d")
self.assertRaises(ValueError, pathjoin, "../")
self.assertRaises(ValueError, pathjoin, "./../")
self.assertRaises(ValueError, pathjoin, "a/b", "../../..")
self.assertRaises(ValueError, pathjoin, "a/b/../../../d")
def test_relpath(self):
tests = [ ("/a/b", "a/b"),
......@@ -59,7 +59,7 @@ class TestPathFunctions(unittest.TestCase):
("/", "") ]
for path, result in tests:
self.assertEqual(fs.relpath(path), result)
self.assertEqual(relpath(path), result)
def test_abspath(self):
tests = [ ("/a/b", "/a/b"),
......@@ -67,7 +67,7 @@ class TestPathFunctions(unittest.TestCase):
("/", "/") ]
for path, result in tests:
self.assertEqual(fs.abspath(path), result)
self.assertEqual(abspath(path), result)
def test_iteratepath(self):
tests = [ ("a/b", ["a", "b"]),
......@@ -92,7 +92,7 @@ class TestPathFunctions(unittest.TestCase):
("foo/bar/baz", ("foo/bar", "baz")),
]
for path, result in tests:
self.assertEqual(fs.pathsplit(path), result)
self.assertEqual(pathsplit(path), result)
def test_recursepath(self):
self.assertEquals(recursepath("/"),["/"])
......
......@@ -10,12 +10,14 @@ import unittest
import threading
import random
import time
import sys
from fs.remote import *
from fs.wrapfs import WrapFS, wrap_fs_methods
from fs.tempfs import TempFS
from fs.path import *
from fs.functools import wraps
class TestCacheFS(unittest.TestCase,FSTestCases,ThreadingTestCases):
......
......@@ -4,6 +4,14 @@ The `utils` module provides a number of utility functions that don't belong in t
"""
__all__ = ['copyfile',
'movefile',
'movedir',
'copydir',
'countbytes',
'find_duplicates',
'print_fs']
import shutil
import os
import sys
......@@ -12,6 +20,7 @@ from fs.path import pathjoin, pathsplit
from fs.errors import DestinationExistsError
def copyfile(src_fs, src_path, dst_fs, dst_path, overwrite=True, chunk_size=16384):
"""Copy a file from one filesystem to another. Will use system copyfile, if both files have a syspath.
Otherwise file will be copied a chunk at a time.
......
......@@ -20,6 +20,8 @@ from fnmatch import fnmatch
from fs.base import FS, threading, synchronize
from fs.errors import *
from fs.functools import wraps
def rewrite_errors(func):
"""Re-write paths in errors raised by wrapped FS objects."""
......
......@@ -7,6 +7,7 @@ An FS wrapper class for hiding dot-files in directory listings.
"""
from fs.wrapfs import WrapFS
from fs.path import *
class HideDotFilesFS(WrapFS):
......
......@@ -16,6 +16,7 @@ except ImportError:
from fs.base import FS
from fs.wrapfs import WrapFS
from fs.path import *
class LazyFS(WrapFS):
......
......@@ -11,6 +11,7 @@ total size of files stored in the wrapped FS.
# for Python2.5 compatibility
from __future__ import with_statement
from fs.errors import *
from fs.path import *
from fs.base import FS, threading, synchronize
from fs.wrapfs import WrapFS
......
......@@ -7,8 +7,10 @@ A FS object that represents the contents of a Zip file
"""
from fs.base import *
from fs.path import *
from fs.errors import *
from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED
from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED, BadZipfile, LargeZipFile
from memoryfs import MemoryFS
try:
......@@ -18,6 +20,17 @@ except ImportError:
import tempfs
class ZipOpenError(CreateFailedError):
"""Thrown when the zip file could not be opened"""
pass
class ZipMissingError(CreateFailedError):
"""Thrown when the requested zip file does not exist"""
pass
class _TempWriteFile(object):
"""Proxies a file object and calls a callback when the file is closed."""
......@@ -38,6 +51,7 @@ class _TempWriteFile(object):
self._file.close()
self.close_callback(self.filename)
class _ExceptionProxy(object):
"""A placeholder for an object that may no longer be used."""
......@@ -51,6 +65,7 @@ class _ExceptionProxy(object):
def __nonzero__(self):
return False
class ZipFS(FS):
"""A FileSystem that represents a zip file."""
......@@ -64,6 +79,8 @@ class ZipFS(FS):
:param allow_zip_64: -- Set to True to use zip files greater than 2 GB, default is False
:param encoding: -- The encoding to use for unicode filenames
:param thread_synchronize: -- Set to True (default) to enable thread-safety
:raises ZipOpenError: Thrown when the zip file could not be opened
:raises ZipMissingError: Thrown when the zip file does not exist (derived from ZipOpenError)
"""
super(ZipFS, self).__init__(thread_synchronize=thread_synchronize)
......@@ -81,8 +98,16 @@ class ZipFS(FS):
self.encoding = encoding
try:
self.zf = ZipFile(zip_file, mode, compression_type, allow_zip_64)
except IOError:
raise ResourceNotFoundError(str(zip_file), msg="Zip file does not exist: %(path)s")
except BadZipfile, bzf:
raise ZipOpenError("Not a zip file or corrupt (%s)" % str(zip_file),
details=bzf)
except IOError, ioe:
if str(ioe).startswith('[Errno 22] Invalid argument'):
raise ZipOpenError("Not a zip file or corrupt (%s)" % str(zip_file),
details=bzf)
raise ZipMissingError("Zip file not found (%s)" % str(zip_file),
details=ioe)
self.zip_path = str(zip_file)
self.temp_fs = None
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment