Commit 754fcc98 by willmcgugan

First stab at a Python3 port

parent 87a736d5
...@@ -35,6 +35,10 @@ from fs.path import * ...@@ -35,6 +35,10 @@ from fs.path import *
from fs.errors import * from fs.errors import *
from fs.local_functools import wraps from fs.local_functools import wraps
import compatibility
import six
from six import PY3
class DummyLock(object): class DummyLock(object):
"""A dummy lock object that doesn't do anything. """A dummy lock object that doesn't do anything.
...@@ -703,7 +707,7 @@ class FS(object): ...@@ -703,7 +707,7 @@ class FS(object):
return sys_path return sys_path
def getcontents(self, path): def getcontents(self, path, mode="rb"):
"""Returns the contents of a file as a string. """Returns the contents of a file as a string.
:param path: A path of file to read :param path: A path of file to read
...@@ -712,7 +716,7 @@ class FS(object): ...@@ -712,7 +716,7 @@ class FS(object):
""" """
f = None f = None
try: try:
f = self.open(path, "rb") f = self.open(path, mode)
contents = f.read() contents = f.read()
return contents return contents
finally: finally:
...@@ -731,23 +735,7 @@ class FS(object): ...@@ -731,23 +735,7 @@ class FS(object):
if not data: if not data:
self.createfile(path) self.createfile(path)
else: else:
f = None compatibility.copy_file_to_fs(data, self, path, chunk_size=chunk_size)
try:
f = self.open(path, 'wb')
if hasattr(data, "read"):
read = data.read
write = f.write
chunk = read(chunk_size)
while chunk:
write(chunk)
chunk = read(chunk_size)
else:
f.write(data)
if hasattr(f, 'flush'):
f.flush()
finally:
if f is not None:
f.close()
def setcontents_async(self, def setcontents_async(self,
path, path,
...@@ -777,7 +765,53 @@ class FS(object): ...@@ -777,7 +765,53 @@ class FS(object):
if progress_callback is None: if progress_callback is None:
progress_callback = lambda bytes_written:None progress_callback = lambda bytes_written:None
finished_event = threading.Event()
def do_setcontents(): def do_setcontents():
if PY3:
try:
f = None
try:
progress_callback(0)
if hasattr(data, "read"):
bytes_written = 0
read = data.read
chunk = read(chunk_size)
if isinstance(chunk, six.text_type):
f = self.open(path, 'w')
else:
f = self.open(path, 'wb')
write = f.write
while chunk:
write(chunk)
bytes_written += len(chunk)
progress_callback(bytes_written)
chunk = read(chunk_size)
else:
if isinstance(data, six.text_type):
f = self.open(path, 'w')
else:
f = self.open(path, 'wb')
f.write(data)
progress_callback(len(data))
if finished_callback is not None:
finished_callback()
finally:
if f is not None:
f.close()
except Exception, e:
if error_callback is not None:
error_callback(e)
raise
finally:
finished_event.set()
else:
try: try:
f = None f = None
try: try:
...@@ -812,7 +846,6 @@ class FS(object): ...@@ -812,7 +846,6 @@ class FS(object):
finally: finally:
finished_event.set() finished_event.set()
finished_event = threading.Event()
threading.Thread(target=do_setcontents).start() threading.Thread(target=do_setcontents).start()
return finished_event return finished_event
......
"""
Some functions for Python3 compatibility.
Not for general usage, the functionality in this file is exposed elsewhere
"""
import six
from six import PY3
if PY3:
def copy_file_to_fs(data, dst_fs, dst_path, chunk_size=64 * 1024):
"""Copy data from a string or a file-like object to a given fs/path"""
if hasattr(data, "read"):
read = data.read
chunk = read(chunk_size)
f = None
try:
if isinstance(chunk, six.text_type):
f = dst_fs.open(dst_path, 'w')
else:
f = dst_fs.open(dst_path, 'wb')
write = f.write
while chunk:
write(chunk)
chunk = read(chunk_size)
finally:
if f is not None:
f.close()
else:
f = None
try:
if isinstance(data, six.text_type):
f = dst_fs.open(dst_path, 'w')
else:
f = dst_fs.open(dst_path, 'wb')
f.write(data)
finally:
if f is not None:
f.close()
else:
def copy_file_to_fs(data, dst_fs, dst_path, chunk_size=64 * 1024):
"""Copy data from a string or a file-like object to a given fs/path"""
f = None
try:
f = dst_fs.open(dst_path, 'wb')
if hasattr(data, "read"):
read = data.read
write = f.write
chunk = read(chunk_size)
while chunk:
write(chunk)
chunk = read(chunk_size)
else:
f.write(data)
if hasattr(f, 'flush'):
f.flush()
finally:
if f is not None:
f.close()
...@@ -544,7 +544,7 @@ class MountProcess(subprocess.Popen): ...@@ -544,7 +544,7 @@ class MountProcess(subprocess.Popen):
os.close(w) os.close(w)
if os.read(r,1) != "S": if os.read(r,1) != "S":
self.terminate() self.terminate()
raise RuntimeError("FUSE error: " + os.read(r,20)) raise RuntimeError("FUSE error: " + os.read(r,20)).decode(NATIVE_ENCODING)
def unmount(self): def unmount(self):
"""Cleanly unmount the FUSE filesystem, terminating this subprocess.""" """Cleanly unmount the FUSE filesystem, terminating this subprocess."""
......
...@@ -33,11 +33,6 @@ Other useful classes include: ...@@ -33,11 +33,6 @@ Other useful classes include:
import tempfile as _tempfile import tempfile as _tempfile
try:
from cStringIO import StringIO as _StringIO
except ImportError:
from StringIO import StringIO as _StringIO
import fs import fs
...@@ -50,6 +45,17 @@ class NotSeekableError(IOError): ...@@ -50,6 +45,17 @@ class NotSeekableError(IOError):
class NotTruncatableError(IOError): class NotTruncatableError(IOError):
pass pass
import six
from six import PY3, b
if PY3:
_StringIO = six.BytesIO
else:
try:
from cStringIO import StringIO as _StringIO
except ImportError:
from StringIO import StringIO as _StringIO
class FileLikeBase(object): class FileLikeBase(object):
"""Base class for implementing file-like objects. """Base class for implementing file-like objects.
...@@ -265,14 +271,14 @@ class FileLikeBase(object): ...@@ -265,14 +271,14 @@ class FileLikeBase(object):
if self.closed: if self.closed:
raise IOError("File has been closed") raise IOError("File has been closed")
if self._check_mode("w-") and self._wbuffer is not None: if self._check_mode("w-") and self._wbuffer is not None:
buffered = "" buffered = b("")
if self._sbuffer: if self._sbuffer:
buffered = buffered + self._sbuffer buffered = buffered + self._sbuffer
self._sbuffer = None self._sbuffer = None
buffered = buffered + self._wbuffer buffered = buffered + self._wbuffer
self._wbuffer = None self._wbuffer = None
leftover = self._write(buffered,flushing=True) leftover = self._write(buffered,flushing=True)
if leftover: if leftover and not isinstance(leftover, int):
raise IOError("Could not flush write buffer.") raise IOError("Could not flush write buffer.")
def close(self): def close(self):
...@@ -306,7 +312,7 @@ class FileLikeBase(object): ...@@ -306,7 +312,7 @@ class FileLikeBase(object):
next() returning subsequent lines from the file. next() returning subsequent lines from the file.
""" """
ln = self.readline() ln = self.readline()
if ln == "": if ln == b(""):
raise StopIteration() raise StopIteration()
return ln return ln
...@@ -442,19 +448,19 @@ class FileLikeBase(object): ...@@ -442,19 +448,19 @@ class FileLikeBase(object):
data = [self._rbuffer] data = [self._rbuffer]
else: else:
data = [] data = []
self._rbuffer = "" self._rbuffer = b("")
newData = self._read() newData = self._read()
while newData is not None: while newData is not None:
data.append(newData) data.append(newData)
newData = self._read() newData = self._read()
output = "".join(data) output = b("").join(data)
# Otherwise, we need to return a specific amount of data # Otherwise, we need to return a specific amount of data
else: else:
if self._rbuffer: if self._rbuffer:
newData = self._rbuffer newData = self._rbuffer
data = [newData] data = [newData]
else: else:
newData = "" newData = b("")
data = [] data = []
sizeSoFar = len(newData) sizeSoFar = len(newData)
while sizeSoFar < size: while sizeSoFar < size:
...@@ -463,20 +469,20 @@ class FileLikeBase(object): ...@@ -463,20 +469,20 @@ class FileLikeBase(object):
break break
data.append(newData) data.append(newData)
sizeSoFar += len(newData) sizeSoFar += len(newData)
data = "".join(data) data = b("").join(data)
if sizeSoFar > size: if sizeSoFar > size:
# read too many bytes, store in the buffer # read too many bytes, store in the buffer
self._rbuffer = data[size:] self._rbuffer = data[size:]
data = data[:size] data = data[:size]
else: else:
self._rbuffer = "" self._rbuffer = b("")
output = data output = data
return output return output
def _do_read_rest(self): def _do_read_rest(self):
"""Private method to read the file through to EOF.""" """Private method to read the file through to EOF."""
data = self._do_read(self._bufsize) data = self._do_read(self._bufsize)
while data != "": while data != b(""):
data = self._do_read(self._bufsize) data = self._do_read(self._bufsize)
def readline(self,size=-1): def readline(self,size=-1):
...@@ -488,11 +494,11 @@ class FileLikeBase(object): ...@@ -488,11 +494,11 @@ class FileLikeBase(object):
nextBit = self.read(self._bufsize) nextBit = self.read(self._bufsize)
bits.append(nextBit) bits.append(nextBit)
sizeSoFar += len(nextBit) sizeSoFar += len(nextBit)
if nextBit == "": if nextBit == b(""):
break break
if size > 0 and sizeSoFar >= size: if size > 0 and sizeSoFar >= size:
break break
indx = nextBit.find("\n") indx = nextBit.find(b("\n"))
# If not found, return whole string up to <size> length # If not found, return whole string up to <size> length
# Any leftovers are pushed onto front of buffer # Any leftovers are pushed onto front of buffer
if indx == -1: if indx == -1:
...@@ -508,7 +514,7 @@ class FileLikeBase(object): ...@@ -508,7 +514,7 @@ class FileLikeBase(object):
extra = bits[-1][indx:] extra = bits[-1][indx:]
bits[-1] = bits[-1][:indx] bits[-1] = bits[-1][:indx]
self._rbuffer = extra + self._rbuffer self._rbuffer = extra + self._rbuffer
return "".join(bits) return b("").join(bits)
def readlines(self,sizehint=-1): def readlines(self,sizehint=-1):
"""Return a list of all lines in the file.""" """Return a list of all lines in the file."""
...@@ -542,8 +548,8 @@ class FileLikeBase(object): ...@@ -542,8 +548,8 @@ class FileLikeBase(object):
if self._wbuffer: if self._wbuffer:
string = self._wbuffer + string string = self._wbuffer + string
leftover = self._write(string) leftover = self._write(string)
if leftover is None: if leftover is None or isinstance(leftover, int):
self._wbuffer = "" self._wbuffer = b("")
else: else:
self._wbuffer = leftover self._wbuffer = leftover
...@@ -649,7 +655,7 @@ class FileWrapper(FileLikeBase): ...@@ -649,7 +655,7 @@ class FileWrapper(FileLikeBase):
def _read(self,sizehint=-1): def _read(self,sizehint=-1):
data = self.wrapped_file.read(sizehint) data = self.wrapped_file.read(sizehint)
if data == "": if data == b(""):
return None return None
return data return data
...@@ -694,7 +700,7 @@ class StringIO(FileWrapper): ...@@ -694,7 +700,7 @@ class StringIO(FileWrapper):
if size > curlen: if size > curlen:
self.wrapped_file.seek(curlen) self.wrapped_file.seek(curlen)
try: try:
self.wrapped_file.write("\x00"*(size-curlen)) self.wrapped_file.write(b("\x00")*(size-curlen))
finally: finally:
self.wrapped_file.seek(pos) self.wrapped_file.seek(pos)
...@@ -715,7 +721,8 @@ class SpooledTemporaryFile(FileWrapper): ...@@ -715,7 +721,8 @@ class SpooledTemporaryFile(FileWrapper):
try: try:
stf_args = (max_size,mode,bufsize) + args stf_args = (max_size,mode,bufsize) + args
wrapped_file = _tempfile.SpooledTemporaryFile(*stf_args,**kwds) wrapped_file = _tempfile.SpooledTemporaryFile(*stf_args,**kwds)
wrapped_file._file = StringIO() #wrapped_file._file = StringIO()
wrapped_file._file = six.BytesIO()
self.__is_spooled = True self.__is_spooled = True
except AttributeError: except AttributeError:
ntf_args = (mode,bufsize) + args ntf_args = (mode,bufsize) + args
......
...@@ -1170,7 +1170,7 @@ class FTPFS(FS): ...@@ -1170,7 +1170,7 @@ class FTPFS(FS):
self.ftp.storbinary('STOR %s' % _encode(path), data, blocksize=chunk_size) self.ftp.storbinary('STOR %s' % _encode(path), data, blocksize=chunk_size)
@ftperrors @ftperrors
def getcontents(self, path): def getcontents(self, path, mode="rb"):
path = normpath(path) path = normpath(path)
contents = StringIO() contents = StringIO()
self.ftp.retrbinary('RETR %s' % _encode(path), contents.write, blocksize=1024*64) self.ftp.retrbinary('RETR %s' % _encode(path), contents.write, blocksize=1024*64)
......
...@@ -20,6 +20,9 @@ from fs.filelike import StringIO ...@@ -20,6 +20,9 @@ from fs.filelike import StringIO
from os import SEEK_END from os import SEEK_END
import threading import threading
import six
def _check_mode(mode, mode_chars): def _check_mode(mode, mode_chars):
for c in mode_chars: for c in mode_chars:
...@@ -264,10 +267,11 @@ class MemoryFS(FS): ...@@ -264,10 +267,11 @@ class MemoryFS(FS):
def __str__(self): def __str__(self):
return "<MemoryFS>" return "<MemoryFS>"
__repr__ = __str__ def __repr__(self):
return "MemoryFS()"
def __unicode__(self): def __unicode__(self):
return unicode(self.__str__()) return "<MemoryFS>"
@synchronize @synchronize
def _get_dir_entry(self, dirpath): def _get_dir_entry(self, dirpath):
...@@ -600,7 +604,7 @@ class MemoryFS(FS): ...@@ -600,7 +604,7 @@ class MemoryFS(FS):
dst_dir_entry.xattrs.update(src_xattrs) dst_dir_entry.xattrs.update(src_xattrs)
@synchronize @synchronize
def getcontents(self, path): def getcontents(self, path, mode="rb"):
dir_entry = self._get_dir_entry(path) dir_entry = self._get_dir_entry(path)
if dir_entry is None: if dir_entry is None:
raise ResourceNotFoundError(path) raise ResourceNotFoundError(path)
......
...@@ -91,7 +91,7 @@ class MountFS(FS): ...@@ -91,7 +91,7 @@ class MountFS(FS):
__repr__ = __str__ __repr__ = __str__
def __unicode__(self): def __unicode__(self):
return unicode(self.__str__()) return u"<%s [%s]>" % (self.__class__.__name__,self.mount_tree.items(),)
def _delegate(self, path): def _delegate(self, path):
path = abspath(normpath(path)) path = abspath(normpath(path))
......
...@@ -270,7 +270,7 @@ class OpenerRegistry(object): ...@@ -270,7 +270,7 @@ class OpenerRegistry(object):
file_object.fs = fs file_object.fs = fs
return file_object return file_object
def getcontents(self, fs_url): def getcontents(self, fs_url, mode="rb"):
"""Gets the contents from a given FS url (if it references a file) """Gets the contents from a given FS url (if it references a file)
:param fs_url: a FS URL e.g. ftp://ftp.mozilla.org/README :param fs_url: a FS URL e.g. ftp://ftp.mozilla.org/README
...@@ -278,7 +278,7 @@ class OpenerRegistry(object): ...@@ -278,7 +278,7 @@ class OpenerRegistry(object):
""" """
fs, path = self.parse(fs_url) fs, path = self.parse(fs_url)
return fs.getcontents(path) return fs.getcontents(path, mode)
def opendir(self, fs_url, writeable=True, create_dir=False): def opendir(self, fs_url, writeable=True, create_dir=False):
"""Opens an FS object from an FS URL """Opens an FS object from an FS URL
......
...@@ -205,7 +205,8 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS): ...@@ -205,7 +205,8 @@ class OSFS(OSFSXAttrMixin, OSFSWatchMixin, FS):
@convert_os_errors @convert_os_errors
def open(self, path, mode="r", **kwargs): def open(self, path, mode="r", **kwargs):
mode = filter(lambda c: c in "rwabt+",mode) #mode = filter(lambda c: c in "rwabt+",mode)
mode = ''.join(c for c in mode if c in 'rwabt+')
sys_path = self.getsyspath(path) sys_path = self.getsyspath(path)
try: try:
return open(sys_path, mode, kwargs.get("buffering", -1)) return open(sys_path, mode, kwargs.get("buffering", -1))
......
...@@ -39,6 +39,7 @@ from fs import SEEK_SET, SEEK_CUR, SEEK_END ...@@ -39,6 +39,7 @@ from fs import SEEK_SET, SEEK_CUR, SEEK_END
_SENTINAL = object() _SENTINAL = object()
from six import PY3, b
class RemoteFileBuffer(FileWrapper): class RemoteFileBuffer(FileWrapper):
"""File-like object providing buffer for local file operations. """File-like object providing buffer for local file operations.
...@@ -186,7 +187,7 @@ class RemoteFileBuffer(FileWrapper): ...@@ -186,7 +187,7 @@ class RemoteFileBuffer(FileWrapper):
self.wrapped_file.seek(curpos) self.wrapped_file.seek(curpos)
def _read(self, length=None): def _read(self, length=None):
if length < 0: if length is not None and length < 0:
length = None length = None
with self._lock: with self._lock:
self._fillbuffer(length) self._fillbuffer(length)
...@@ -668,7 +669,7 @@ class CacheFSMixin(FS): ...@@ -668,7 +669,7 @@ class CacheFSMixin(FS):
def getsize(self,path): def getsize(self,path):
return self.getinfo(path)["size"] return self.getinfo(path)["size"]
def setcontents(self, path, contents="", chunk_size=64*1024): def setcontents(self, path, contents=b(""), chunk_size=64*1024):
supsc = super(CacheFSMixin,self).setcontents supsc = super(CacheFSMixin,self).setcontents
res = supsc(path, contents, chunk_size=chunk_size) res = supsc(path, contents, chunk_size=chunk_size)
with self.__cache_lock: with self.__cache_lock:
......
...@@ -155,11 +155,11 @@ class S3FS(FS): ...@@ -155,11 +155,11 @@ class S3FS(FS):
super(S3FS,self).__setstate__(state) super(S3FS,self).__setstate__(state)
self._tlocal = thread_local() self._tlocal = thread_local()
def __str__(self): def __repr__(self):
args = (self.__class__.__name__,self._bucket_name,self._prefix) args = (self.__class__.__name__,self._bucket_name,self._prefix)
return '<%s: %s:%s>' % args return '<%s: %s:%s>' % args
__repr__ = __str__ __str__ = __repr__
def _s3path(self,path): def _s3path(self,path):
"""Get the absolute path to a file stored in S3.""" """Get the absolute path to a file stored in S3."""
......
...@@ -177,7 +177,7 @@ class SFTPFS(FS): ...@@ -177,7 +177,7 @@ class SFTPFS(FS):
self._transport = connection self._transport = connection
def __unicode__(self): def __unicode__(self):
return '<SFTPFS: %s>' % self.desc('/') return u'<SFTPFS: %s>' % self.desc('/')
@classmethod @classmethod
def _agent_auth(cls, transport, username): def _agent_auth(cls, transport, username):
......
...@@ -49,10 +49,10 @@ class TempFS(OSFS): ...@@ -49,10 +49,10 @@ class TempFS(OSFS):
self._cleaned = False self._cleaned = False
super(TempFS, self).__init__(self._temp_dir, dir_mode=dir_mode, thread_synchronize=thread_synchronize) super(TempFS, self).__init__(self._temp_dir, dir_mode=dir_mode, thread_synchronize=thread_synchronize)
def __str__(self): def __repr__(self):
return '<TempFS: %s>' % self._temp_dir return '<TempFS: %s>' % self._temp_dir
__repr__ = __str__ __str__ = __repr__
def __unicode__(self): def __unicode__(self):
return u'<TempFS: %s>' % self._temp_dir return u'<TempFS: %s>' % self._temp_dir
......
...@@ -11,7 +11,7 @@ from __future__ import with_statement ...@@ -11,7 +11,7 @@ from __future__ import with_statement
# be captured by nose and reported appropriately # be captured by nose and reported appropriately
import sys import sys
import logging import logging
logging.basicConfig(level=logging.ERROR, stream=sys.stdout) #logging.basicConfig(level=logging.ERROR, stream=sys.stdout)
from fs.base import * from fs.base import *
from fs.path import * from fs.path import *
...@@ -31,6 +31,8 @@ try: ...@@ -31,6 +31,8 @@ try:
except ImportError: except ImportError:
import dummy_threading as threading import dummy_threading as threading
import six
from six import PY3, b
class FSTestCases(object): class FSTestCases(object):
"""Base suite of testcases for filesystem implementations. """Base suite of testcases for filesystem implementations.
...@@ -109,54 +111,54 @@ class FSTestCases(object): ...@@ -109,54 +111,54 @@ class FSTestCases(object):
def test_writefile(self): def test_writefile(self):
self.assertRaises(ResourceNotFoundError,self.fs.open,"test1.txt") self.assertRaises(ResourceNotFoundError,self.fs.open,"test1.txt")
f = self.fs.open("test1.txt","w") f = self.fs.open("test1.txt","wb")
f.write("testing") f.write(b("testing"))
f.close() f.close()
self.assertTrue(self.check("test1.txt")) self.assertTrue(self.check("test1.txt"))
f = self.fs.open("test1.txt","r") f = self.fs.open("test1.txt","rb")
self.assertEquals(f.read(),"testing") self.assertEquals(f.read(),b("testing"))
f.close() f.close()
f = self.fs.open("test1.txt","w") f = self.fs.open("test1.txt","wb")
f.write("test file overwrite") f.write(b("test file overwrite"))
f.close() f.close()
self.assertTrue(self.check("test1.txt")) self.assertTrue(self.check("test1.txt"))
f = self.fs.open("test1.txt","r") f = self.fs.open("test1.txt","rb")
self.assertEquals(f.read(),"test file overwrite") self.assertEquals(f.read(),b("test file overwrite"))
f.close() f.close()
def test_setcontents(self): def test_setcontents(self):
# setcontents() should accept both a string... # setcontents() should accept both a string...
self.fs.setcontents("hello","world") self.fs.setcontents("hello",b("world"))
self.assertEquals(self.fs.getcontents("hello"),"world") self.assertEquals(self.fs.getcontents("hello", "rb"),b("world"))
# ...and a file-like object # ...and a file-like object
self.fs.setcontents("hello",StringIO("to you, good sir!")) self.fs.setcontents("hello",StringIO(b("to you, good sir!")))
self.assertEquals(self.fs.getcontents("hello"),"to you, good sir!") self.assertEquals(self.fs.getcontents("hello", "rb"),b("to you, good sir!"))
# setcontents() should accept both a string... # setcontents() should accept both a string...
self.fs.setcontents("hello","world", chunk_size=2) self.fs.setcontents("hello",b("world"), chunk_size=2)
self.assertEquals(self.fs.getcontents("hello"),"world") self.assertEquals(self.fs.getcontents("hello", "rb"),b("world"))
# ...and a file-like object # ...and a file-like object
self.fs.setcontents("hello",StringIO("to you, good sir!"), chunk_size=2) self.fs.setcontents("hello",StringIO(b("to you, good sir!")), chunk_size=2)
self.assertEquals(self.fs.getcontents("hello"),"to you, good sir!") self.assertEquals(self.fs.getcontents("hello", "rb"),b("to you, good sir!"))
def test_setcontents_async(self): def test_setcontents_async(self):
# setcontents() should accept both a string... # setcontents() should accept both a string...
self.fs.setcontents_async("hello", "world").wait() self.fs.setcontents_async("hello", b("world")).wait()
self.assertEquals(self.fs.getcontents("hello"), "world") self.assertEquals(self.fs.getcontents("hello", "rb"), b("world"))
# ...and a file-like object # ...and a file-like object
self.fs.setcontents_async("hello",StringIO("to you, good sir!")).wait() self.fs.setcontents_async("hello",StringIO(b("to you, good sir!"))).wait()
self.assertEquals(self.fs.getcontents("hello"), "to you, good sir!") self.assertEquals(self.fs.getcontents("hello"), b("to you, good sir!"))
self.fs.setcontents_async("hello", "world", chunk_size=2).wait() self.fs.setcontents_async("hello", b("world"), chunk_size=2).wait()
self.assertEquals(self.fs.getcontents("hello"), "world") self.assertEquals(self.fs.getcontents("hello", "rb"), b("world"))
# ...and a file-like object # ...and a file-like object
self.fs.setcontents_async("hello", StringIO("to you, good sir!"), chunk_size=2).wait() self.fs.setcontents_async("hello", StringIO(b("to you, good sir!")), chunk_size=2).wait()
self.assertEquals(self.fs.getcontents("hello"), "to you, good sir!") self.assertEquals(self.fs.getcontents("hello", "rb"), b("to you, good sir!"))
def test_isdir_isfile(self): def test_isdir_isfile(self):
self.assertFalse(self.fs.exists("dir1")) self.assertFalse(self.fs.exists("dir1"))
self.assertFalse(self.fs.isdir("dir1")) self.assertFalse(self.fs.isdir("dir1"))
self.assertFalse(self.fs.isfile("a.txt")) self.assertFalse(self.fs.isfile("a.txt"))
self.fs.setcontents("a.txt", '') self.fs.setcontents("a.txt", b(''))
self.assertFalse(self.fs.isdir("dir1")) self.assertFalse(self.fs.isdir("dir1"))
self.assertTrue(self.fs.exists("a.txt")) self.assertTrue(self.fs.exists("a.txt"))
self.assertTrue(self.fs.isfile("a.txt")) self.assertTrue(self.fs.isfile("a.txt"))
...@@ -172,10 +174,10 @@ class FSTestCases(object): ...@@ -172,10 +174,10 @@ class FSTestCases(object):
def check_unicode(items): def check_unicode(items):
for item in items: for item in items:
self.assertTrue(isinstance(item,unicode)) self.assertTrue(isinstance(item,unicode))
self.fs.setcontents(u"a", '') self.fs.setcontents(u"a", b(''))
self.fs.setcontents("b", '') self.fs.setcontents("b", b(''))
self.fs.setcontents("foo", '') self.fs.setcontents("foo", b(''))
self.fs.setcontents("bar", '') self.fs.setcontents("bar", b(''))
# Test listing of the root directory # Test listing of the root directory
d1 = self.fs.listdir() d1 = self.fs.listdir()
self.assertEqual(len(d1), 4) self.assertEqual(len(d1), 4)
...@@ -196,10 +198,10 @@ class FSTestCases(object): ...@@ -196,10 +198,10 @@ class FSTestCases(object):
# Create some deeper subdirectories, to make sure their # Create some deeper subdirectories, to make sure their
# contents are not inadvertantly included # contents are not inadvertantly included
self.fs.makedir("p/1/2/3",recursive=True) self.fs.makedir("p/1/2/3",recursive=True)
self.fs.setcontents("p/1/2/3/a", '') self.fs.setcontents("p/1/2/3/a", b(''))
self.fs.setcontents("p/1/2/3/b", '') self.fs.setcontents("p/1/2/3/b", b(''))
self.fs.setcontents("p/1/2/3/foo", '') self.fs.setcontents("p/1/2/3/foo", b(''))
self.fs.setcontents("p/1/2/3/bar", '') self.fs.setcontents("p/1/2/3/bar", b(''))
self.fs.makedir("q") self.fs.makedir("q")
# Test listing just files, just dirs, and wildcards # Test listing just files, just dirs, and wildcards
dirs_only = self.fs.listdir(dirs_only=True) dirs_only = self.fs.listdir(dirs_only=True)
...@@ -236,10 +238,10 @@ class FSTestCases(object): ...@@ -236,10 +238,10 @@ class FSTestCases(object):
def check_equal(items,target): def check_equal(items,target):
names = [nm for (nm,info) in items] names = [nm for (nm,info) in items]
self.assertEqual(sorted(names),sorted(target)) self.assertEqual(sorted(names),sorted(target))
self.fs.setcontents(u"a", '') self.fs.setcontents(u"a", b(''))
self.fs.setcontents("b", '') self.fs.setcontents("b", b(''))
self.fs.setcontents("foo", '') self.fs.setcontents("foo", b(''))
self.fs.setcontents("bar", '') self.fs.setcontents("bar", b(''))
# Test listing of the root directory # Test listing of the root directory
d1 = self.fs.listdirinfo() d1 = self.fs.listdirinfo()
self.assertEqual(len(d1), 4) self.assertEqual(len(d1), 4)
...@@ -261,10 +263,10 @@ class FSTestCases(object): ...@@ -261,10 +263,10 @@ class FSTestCases(object):
# Create some deeper subdirectories, to make sure their # Create some deeper subdirectories, to make sure their
# contents are not inadvertantly included # contents are not inadvertantly included
self.fs.makedir("p/1/2/3",recursive=True) self.fs.makedir("p/1/2/3",recursive=True)
self.fs.setcontents("p/1/2/3/a", '') self.fs.setcontents("p/1/2/3/a", b(''))
self.fs.setcontents("p/1/2/3/b", '') self.fs.setcontents("p/1/2/3/b", b(''))
self.fs.setcontents("p/1/2/3/foo", '') self.fs.setcontents("p/1/2/3/foo", b(''))
self.fs.setcontents("p/1/2/3/bar", '') self.fs.setcontents("p/1/2/3/bar", b(''))
self.fs.makedir("q") self.fs.makedir("q")
# Test listing just files, just dirs, and wildcards # Test listing just files, just dirs, and wildcards
dirs_only = self.fs.listdirinfo(dirs_only=True) dirs_only = self.fs.listdirinfo(dirs_only=True)
...@@ -297,7 +299,7 @@ class FSTestCases(object): ...@@ -297,7 +299,7 @@ class FSTestCases(object):
def test_walk(self): def test_walk(self):
self.fs.setcontents('a.txt', 'hello') self.fs.setcontents('a.txt', 'hello')
self.fs.setcontents('b.txt', 'world') self.fs.setcontents('b.txt', 'world')
self.fs.makeopendir('foo').setcontents('c', '123') self.fs.makeopendir('foo').setcontents('c', b('123'))
sorted_walk = sorted([(d,sorted(fs)) for (d,fs) in self.fs.walk()]) sorted_walk = sorted([(d,sorted(fs)) for (d,fs) in self.fs.walk()])
self.assertEquals(sorted_walk, self.assertEquals(sorted_walk,
[("/",["a.txt","b.txt"]), [("/",["a.txt","b.txt"]),
...@@ -320,10 +322,10 @@ class FSTestCases(object): ...@@ -320,10 +322,10 @@ class FSTestCases(object):
assert found_c, "depth search order was wrong: " + str(list(self.fs.walk(search="depth"))) assert found_c, "depth search order was wrong: " + str(list(self.fs.walk(search="depth")))
def test_walk_wildcard(self): def test_walk_wildcard(self):
self.fs.setcontents('a.txt', 'hello') self.fs.setcontents('a.txt', b('hello'))
self.fs.setcontents('b.txt', 'world') self.fs.setcontents('b.txt', b('world'))
self.fs.makeopendir('foo').setcontents('c', '123') self.fs.makeopendir('foo').setcontents('c', b('123'))
self.fs.makeopendir('.svn').setcontents('ignored', '') self.fs.makeopendir('.svn').setcontents('ignored', b(''))
for dir_path, paths in self.fs.walk(wildcard='*.txt'): for dir_path, paths in self.fs.walk(wildcard='*.txt'):
for path in paths: for path in paths:
self.assert_(path.endswith('.txt')) self.assert_(path.endswith('.txt'))
...@@ -332,24 +334,24 @@ class FSTestCases(object): ...@@ -332,24 +334,24 @@ class FSTestCases(object):
self.assert_(path.endswith('.txt')) self.assert_(path.endswith('.txt'))
def test_walk_dir_wildcard(self): def test_walk_dir_wildcard(self):
self.fs.setcontents('a.txt', 'hello') self.fs.setcontents('a.txt', b('hello'))
self.fs.setcontents('b.txt', 'world') self.fs.setcontents('b.txt', b('world'))
self.fs.makeopendir('foo').setcontents('c', '123') self.fs.makeopendir('foo').setcontents('c', b('123'))
self.fs.makeopendir('.svn').setcontents('ignored', '') self.fs.makeopendir('.svn').setcontents('ignored', b(''))
for dir_path, paths in self.fs.walk(dir_wildcard=lambda fn:not fn.endswith('.svn')): for dir_path, paths in self.fs.walk(dir_wildcard=lambda fn:not fn.endswith('.svn')):
for path in paths: for path in paths:
self.assert_('.svn' not in path) self.assert_('.svn' not in path)
def test_walkfiles(self): def test_walkfiles(self):
self.fs.makeopendir('bar').setcontents('a.txt', '123') self.fs.makeopendir('bar').setcontents('a.txt', b('123'))
self.fs.makeopendir('foo').setcontents('b', '123') self.fs.makeopendir('foo').setcontents('b', b('123'))
self.assertEquals(sorted(self.fs.walkfiles()),["/bar/a.txt","/foo/b"]) self.assertEquals(sorted(self.fs.walkfiles()),["/bar/a.txt","/foo/b"])
self.assertEquals(sorted(self.fs.walkfiles(dir_wildcard="*foo*")),["/foo/b"]) self.assertEquals(sorted(self.fs.walkfiles(dir_wildcard="*foo*")),["/foo/b"])
self.assertEquals(sorted(self.fs.walkfiles(wildcard="*.txt")),["/bar/a.txt"]) self.assertEquals(sorted(self.fs.walkfiles(wildcard="*.txt")),["/bar/a.txt"])
def test_walkdirs(self): def test_walkdirs(self):
self.fs.makeopendir('bar').setcontents('a.txt', '123') self.fs.makeopendir('bar').setcontents('a.txt', b('123'))
self.fs.makeopendir('foo').makeopendir("baz").setcontents('b', '123') self.fs.makeopendir('foo').makeopendir("baz").setcontents('b', b('123'))
self.assertEquals(sorted(self.fs.walkdirs()),["/","/bar","/foo","/foo/baz"]) self.assertEquals(sorted(self.fs.walkdirs()),["/","/bar","/foo","/foo/baz"])
self.assertEquals(sorted(self.fs.walkdirs(wildcard="*foo*")),["/","/foo","/foo/baz"]) self.assertEquals(sorted(self.fs.walkdirs(wildcard="*foo*")),["/","/foo","/foo/baz"])
...@@ -357,8 +359,8 @@ class FSTestCases(object): ...@@ -357,8 +359,8 @@ class FSTestCases(object):
alpha = u"\N{GREEK SMALL LETTER ALPHA}" alpha = u"\N{GREEK SMALL LETTER ALPHA}"
beta = u"\N{GREEK SMALL LETTER BETA}" beta = u"\N{GREEK SMALL LETTER BETA}"
self.fs.makedir(alpha) self.fs.makedir(alpha)
self.fs.setcontents(alpha+"/a", '') self.fs.setcontents(alpha+"/a", b(''))
self.fs.setcontents(alpha+"/"+beta, '') self.fs.setcontents(alpha+"/"+beta, b(''))
self.assertTrue(self.check(alpha)) self.assertTrue(self.check(alpha))
self.assertEquals(sorted(self.fs.listdir(alpha)),["a",beta]) self.assertEquals(sorted(self.fs.listdir(alpha)),["a",beta])
...@@ -375,18 +377,18 @@ class FSTestCases(object): ...@@ -375,18 +377,18 @@ class FSTestCases(object):
self.assert_(check("a/b/child")) self.assert_(check("a/b/child"))
self.assertRaises(DestinationExistsError,self.fs.makedir,"/a/b") self.assertRaises(DestinationExistsError,self.fs.makedir,"/a/b")
self.fs.makedir("/a/b",allow_recreate=True) self.fs.makedir("/a/b",allow_recreate=True)
self.fs.setcontents("/a/file", '') self.fs.setcontents("/a/file", b(''))
self.assertRaises(ResourceInvalidError,self.fs.makedir,"a/file") self.assertRaises(ResourceInvalidError,self.fs.makedir,"a/file")
def test_remove(self): def test_remove(self):
self.fs.setcontents("a.txt", '') self.fs.setcontents("a.txt", b(''))
self.assertTrue(self.check("a.txt")) self.assertTrue(self.check("a.txt"))
self.fs.remove("a.txt") self.fs.remove("a.txt")
self.assertFalse(self.check("a.txt")) self.assertFalse(self.check("a.txt"))
self.assertRaises(ResourceNotFoundError,self.fs.remove,"a.txt") self.assertRaises(ResourceNotFoundError,self.fs.remove,"a.txt")
self.fs.makedir("dir1") self.fs.makedir("dir1")
self.assertRaises(ResourceInvalidError,self.fs.remove,"dir1") self.assertRaises(ResourceInvalidError,self.fs.remove,"dir1")
self.fs.setcontents("/dir1/a.txt", '') self.fs.setcontents("/dir1/a.txt", b(''))
self.assertTrue(self.check("dir1/a.txt")) self.assertTrue(self.check("dir1/a.txt"))
self.fs.remove("dir1/a.txt") self.fs.remove("dir1/a.txt")
self.assertFalse(self.check("/dir1/a.txt")) self.assertFalse(self.check("/dir1/a.txt"))
...@@ -413,13 +415,13 @@ class FSTestCases(object): ...@@ -413,13 +415,13 @@ class FSTestCases(object):
self.assert_(not check("foo/bar")) self.assert_(not check("foo/bar"))
self.assert_(not check("foo")) self.assert_(not check("foo"))
self.fs.makedir("foo/bar/baz", recursive=True) self.fs.makedir("foo/bar/baz", recursive=True)
self.fs.setcontents("foo/file.txt", "please don't delete me") self.fs.setcontents("foo/file.txt", b("please don't delete me"))
self.fs.removedir("foo/bar/baz", recursive=True) self.fs.removedir("foo/bar/baz", recursive=True)
self.assert_(not check("foo/bar/baz")) self.assert_(not check("foo/bar/baz"))
self.assert_(not check("foo/bar")) self.assert_(not check("foo/bar"))
# Ensure that force=True works as expected # Ensure that force=True works as expected
self.fs.makedir("frollic/waggle", recursive=True) self.fs.makedir("frollic/waggle", recursive=True)
self.fs.setcontents("frollic/waddle.txt","waddlewaddlewaddle") self.fs.setcontents("frollic/waddle.txt",b("waddlewaddlewaddle"))
self.assertRaises(DirectoryNotEmptyError,self.fs.removedir,"frollic") self.assertRaises(DirectoryNotEmptyError,self.fs.removedir,"frollic")
self.assertRaises(ResourceInvalidError,self.fs.removedir,"frollic/waddle.txt") self.assertRaises(ResourceInvalidError,self.fs.removedir,"frollic/waddle.txt")
self.fs.removedir("frollic",force=True) self.fs.removedir("frollic",force=True)
...@@ -439,14 +441,14 @@ class FSTestCases(object): ...@@ -439,14 +441,14 @@ class FSTestCases(object):
def test_rename(self): def test_rename(self):
check = self.check check = self.check
# test renaming a file in the same directory # test renaming a file in the same directory
self.fs.setcontents("foo.txt","Hello, World!") self.fs.setcontents("foo.txt",b("Hello, World!"))
self.assert_(check("foo.txt")) self.assert_(check("foo.txt"))
self.fs.rename("foo.txt", "bar.txt") self.fs.rename("foo.txt", "bar.txt")
self.assert_(check("bar.txt")) self.assert_(check("bar.txt"))
self.assert_(not check("foo.txt")) self.assert_(not check("foo.txt"))
# test renaming a directory in the same directory # test renaming a directory in the same directory
self.fs.makedir("dir_a") self.fs.makedir("dir_a")
self.fs.setcontents("dir_a/test.txt","testerific") self.fs.setcontents("dir_a/test.txt",b("testerific"))
self.assert_(check("dir_a")) self.assert_(check("dir_a"))
self.fs.rename("dir_a","dir_b") self.fs.rename("dir_a","dir_b")
self.assert_(check("dir_b")) self.assert_(check("dir_b"))
...@@ -462,7 +464,7 @@ class FSTestCases(object): ...@@ -462,7 +464,7 @@ class FSTestCases(object):
self.assertRaises(ParentDirectoryMissingError,self.fs.rename,"dir_a/test.txt","nonexistent/test.txt") self.assertRaises(ParentDirectoryMissingError,self.fs.rename,"dir_a/test.txt","nonexistent/test.txt")
def test_info(self): def test_info(self):
test_str = "Hello, World!" test_str = b("Hello, World!")
self.fs.setcontents("info.txt",test_str) self.fs.setcontents("info.txt",test_str)
info = self.fs.getinfo("info.txt") info = self.fs.getinfo("info.txt")
self.assertEqual(info['size'], len(test_str)) self.assertEqual(info['size'], len(test_str))
...@@ -471,18 +473,18 @@ class FSTestCases(object): ...@@ -471,18 +473,18 @@ class FSTestCases(object):
self.assertRaises(ResourceNotFoundError,self.fs.getinfo,"info.txt/inval") self.assertRaises(ResourceNotFoundError,self.fs.getinfo,"info.txt/inval")
def test_getsize(self): def test_getsize(self):
test_str = "*"*23 test_str = b("*")*23
self.fs.setcontents("info.txt",test_str) self.fs.setcontents("info.txt",test_str)
size = self.fs.getsize("info.txt") size = self.fs.getsize("info.txt")
self.assertEqual(size, len(test_str)) self.assertEqual(size, len(test_str))
def test_movefile(self): def test_movefile(self):
check = self.check check = self.check
contents = "If the implementation is hard to explain, it's a bad idea." contents = b("If the implementation is hard to explain, it's a bad idea.")
def makefile(path): def makefile(path):
self.fs.setcontents(path,contents) self.fs.setcontents(path,contents)
def checkcontents(path): def checkcontents(path):
check_contents = self.fs.getcontents(path) check_contents = self.fs.getcontents(path, "rb")
self.assertEqual(check_contents,contents) self.assertEqual(check_contents,contents)
return contents == check_contents return contents == check_contents
...@@ -511,7 +513,7 @@ class FSTestCases(object): ...@@ -511,7 +513,7 @@ class FSTestCases(object):
def test_movedir(self): def test_movedir(self):
check = self.check check = self.check
contents = "If the implementation is hard to explain, it's a bad idea." contents = b("If the implementation is hard to explain, it's a bad idea.")
def makefile(path): def makefile(path):
self.fs.setcontents(path, contents) self.fs.setcontents(path, contents)
...@@ -556,11 +558,11 @@ class FSTestCases(object): ...@@ -556,11 +558,11 @@ class FSTestCases(object):
def test_copyfile(self): def test_copyfile(self):
check = self.check check = self.check
contents = "If the implementation is hard to explain, it's a bad idea." contents = b("If the implementation is hard to explain, it's a bad idea.")
def makefile(path,contents=contents): def makefile(path,contents=contents):
self.fs.setcontents(path,contents) self.fs.setcontents(path,contents)
def checkcontents(path,contents=contents): def checkcontents(path,contents=contents):
check_contents = self.fs.getcontents(path) check_contents = self.fs.getcontents(path, "rb")
self.assertEqual(check_contents,contents) self.assertEqual(check_contents,contents)
return contents == check_contents return contents == check_contents
...@@ -580,18 +582,18 @@ class FSTestCases(object): ...@@ -580,18 +582,18 @@ class FSTestCases(object):
self.assert_(check("/c.txt")) self.assert_(check("/c.txt"))
self.assert_(checkcontents("/c.txt")) self.assert_(checkcontents("/c.txt"))
makefile("foo/bar/a.txt","different contents") makefile("foo/bar/a.txt",b("different contents"))
self.assert_(checkcontents("foo/bar/a.txt","different contents")) self.assert_(checkcontents("foo/bar/a.txt",b("different contents")))
self.assertRaises(DestinationExistsError,self.fs.copy,"foo/bar/a.txt","/c.txt") self.assertRaises(DestinationExistsError,self.fs.copy,"foo/bar/a.txt","/c.txt")
self.assert_(checkcontents("/c.txt")) self.assert_(checkcontents("/c.txt"))
self.fs.copy("foo/bar/a.txt","/c.txt",overwrite=True) self.fs.copy("foo/bar/a.txt","/c.txt",overwrite=True)
self.assert_(checkcontents("foo/bar/a.txt","different contents")) self.assert_(checkcontents("foo/bar/a.txt",b("different contents")))
self.assert_(checkcontents("/c.txt","different contents")) self.assert_(checkcontents("/c.txt",b("different contents")))
def test_copydir(self): def test_copydir(self):
check = self.check check = self.check
contents = "If the implementation is hard to explain, it's a bad idea." contents = b("If the implementation is hard to explain, it's a bad idea.")
def makefile(path): def makefile(path):
self.fs.setcontents(path,contents) self.fs.setcontents(path,contents)
def checkcontents(path): def checkcontents(path):
...@@ -630,7 +632,7 @@ class FSTestCases(object): ...@@ -630,7 +632,7 @@ class FSTestCases(object):
def test_copydir_with_dotfile(self): def test_copydir_with_dotfile(self):
check = self.check check = self.check
contents = "If the implementation is hard to explain, it's a bad idea." contents = b("If the implementation is hard to explain, it's a bad idea.")
def makefile(path): def makefile(path):
self.fs.setcontents(path,contents) self.fs.setcontents(path,contents)
...@@ -650,13 +652,13 @@ class FSTestCases(object): ...@@ -650,13 +652,13 @@ class FSTestCases(object):
def test_readwriteappendseek(self): def test_readwriteappendseek(self):
def checkcontents(path, check_contents): def checkcontents(path, check_contents):
read_contents = self.fs.getcontents(path) read_contents = self.fs.getcontents(path, "rb")
self.assertEqual(read_contents,check_contents) self.assertEqual(read_contents,check_contents)
return read_contents == check_contents return read_contents == check_contents
test_strings = ["Beautiful is better than ugly.", test_strings = [b("Beautiful is better than ugly."),
"Explicit is better than implicit.", b("Explicit is better than implicit."),
"Simple is better than complex."] b("Simple is better than complex.")]
all_strings = "".join(test_strings) all_strings = b("").join(test_strings)
self.assertRaises(ResourceNotFoundError, self.fs.open, "a.txt", "r") self.assertRaises(ResourceNotFoundError, self.fs.open, "a.txt", "r")
self.assert_(not self.fs.exists("a.txt")) self.assert_(not self.fs.exists("a.txt"))
...@@ -689,67 +691,67 @@ class FSTestCases(object): ...@@ -689,67 +691,67 @@ class FSTestCases(object):
self.assert_(checkcontents("b.txt", test_strings[2])) self.assert_(checkcontents("b.txt", test_strings[2]))
f5 = self.fs.open("c.txt", "wb") f5 = self.fs.open("c.txt", "wb")
for s in test_strings: for s in test_strings:
f5.write(s+"\n") f5.write(s+b("\n"))
f5.close() f5.close()
f6 = self.fs.open("c.txt", "rb") f6 = self.fs.open("c.txt", "rb")
for s, t in zip(f6, test_strings): for s, t in zip(f6, test_strings):
self.assertEqual(s, t+"\n") self.assertEqual(s, t+b("\n"))
f6.close() f6.close()
f7 = self.fs.open("c.txt", "rb") f7 = self.fs.open("c.txt", "rb")
f7.seek(13) f7.seek(13)
word = f7.read(6) word = f7.read(6)
self.assertEqual(word, "better") self.assertEqual(word, b("better"))
f7.seek(1, os.SEEK_CUR) f7.seek(1, os.SEEK_CUR)
word = f7.read(4) word = f7.read(4)
self.assertEqual(word, "than") self.assertEqual(word, b("than"))
f7.seek(-9, os.SEEK_END) f7.seek(-9, os.SEEK_END)
word = f7.read(7) word = f7.read(7)
self.assertEqual(word, "complex") self.assertEqual(word, b("complex"))
f7.close() f7.close()
self.assertEqual(self.fs.getcontents("a.txt"), all_strings) self.assertEqual(self.fs.getcontents("a.txt", "rb"), all_strings)
def test_truncate(self): def test_truncate(self):
def checkcontents(path, check_contents): def checkcontents(path, check_contents):
read_contents = self.fs.getcontents(path) read_contents = self.fs.getcontents(path, "rb")
self.assertEqual(read_contents,check_contents) self.assertEqual(read_contents,check_contents)
return read_contents == check_contents return read_contents == check_contents
self.fs.setcontents("hello","world") self.fs.setcontents("hello",b("world"))
checkcontents("hello","world") checkcontents("hello",b("world"))
self.fs.setcontents("hello","hi") self.fs.setcontents("hello",b("hi"))
checkcontents("hello","hi") checkcontents("hello",b("hi"))
self.fs.setcontents("hello","1234567890") self.fs.setcontents("hello",b("1234567890"))
checkcontents("hello","1234567890") checkcontents("hello",b("1234567890"))
with self.fs.open("hello","r+") as f: with self.fs.open("hello","r+") as f:
f.truncate(7) f.truncate(7)
checkcontents("hello","1234567") checkcontents("hello",b("1234567"))
with self.fs.open("hello","r+") as f: with self.fs.open("hello","r+") as f:
f.seek(5) f.seek(5)
f.truncate() f.truncate()
checkcontents("hello","12345") checkcontents("hello",b("12345"))
def test_truncate_to_larger_size(self): def test_truncate_to_larger_size(self):
with self.fs.open("hello","w") as f: with self.fs.open("hello","wb") as f:
f.truncate(30) f.truncate(30)
self.assertEquals(self.fs.getsize("hello"), 30) self.assertEquals(self.fs.getsize("hello"), 30)
# Some file systems (FTPFS) don't support both reading and writing # Some file systems (FTPFS) don't support both reading and writing
if self.fs.getmeta('file.read_and_write', True): if self.fs.getmeta('file.read_and_write', True):
with self.fs.open("hello","r+") as f: with self.fs.open("hello","rb+") as f:
f.seek(25) f.seek(25)
f.write("123456") f.write(b("123456"))
with self.fs.open("hello","r") as f: with self.fs.open("hello","rb") as f:
f.seek(25) f.seek(25)
self.assertEquals(f.read(),"123456") self.assertEquals(f.read(),b("123456"))
def test_write_past_end_of_file(self): def test_write_past_end_of_file(self):
if self.fs.getmeta('file.read_and_write', True): if self.fs.getmeta('file.read_and_write', True):
with self.fs.open("write_at_end","w") as f: with self.fs.open("write_at_end","wb") as f:
f.seek(25) f.seek(25)
f.write("EOF") f.write(b("EOF"))
with self.fs.open("write_at_end","r") as f: with self.fs.open("write_at_end","rb") as f:
self.assertEquals(f.read(),"\x00"*25 + "EOF") self.assertEquals(f.read(),b("\x00")*25 + b("EOF"))
def test_with_statement(self): def test_with_statement(self):
...@@ -760,24 +762,24 @@ class FSTestCases(object): ...@@ -760,24 +762,24 @@ class FSTestCases(object):
# A successful 'with' statement # A successful 'with' statement
contents = "testing the with statement" contents = "testing the with statement"
code = "from __future__ import with_statement\n" code = "from __future__ import with_statement\n"
code += "with self.fs.open('f.txt','w-') as testfile:\n" code += "with self.fs.open('f.txt','wb-') as testfile:\n"
code += " testfile.write(contents)\n" code += " testfile.write(contents)\n"
code += "self.assertEquals(self.fs.getcontents('f.txt'),contents)" code += "self.assertEquals(self.fs.getcontents('f.txt', 'rb'),contents)"
code = compile(code,"<string>",'exec') code = compile(code,"<string>",'exec')
eval(code) eval(code)
# A 'with' statement raising an error # A 'with' statement raising an error
contents = "testing the with statement" contents = "testing the with statement"
code = "from __future__ import with_statement\n" code = "from __future__ import with_statement\n"
code += "with self.fs.open('f.txt','w-') as testfile:\n" code += "with self.fs.open('f.txt','wb-') as testfile:\n"
code += " testfile.write(contents)\n" code += " testfile.write(contents)\n"
code += " raise ValueError\n" code += " raise ValueError\n"
code = compile(code,"<string>",'exec') code = compile(code,"<string>",'exec')
self.assertRaises(ValueError,eval,code,globals(),locals()) self.assertRaises(ValueError,eval,code,globals(),locals())
self.assertEquals(self.fs.getcontents('f.txt'),contents) self.assertEquals(self.fs.getcontents('f.txt', 'rb'),contents)
def test_pickling(self): def test_pickling(self):
if self.fs.getmeta('pickle_contents', True): if self.fs.getmeta('pickle_contents', True):
self.fs.setcontents("test1","hello world") self.fs.setcontents("test1",b("hello world"))
fs2 = pickle.loads(pickle.dumps(self.fs)) fs2 = pickle.loads(pickle.dumps(self.fs))
self.assert_(fs2.isfile("test1")) self.assert_(fs2.isfile("test1"))
fs3 = pickle.loads(pickle.dumps(self.fs,-1)) fs3 = pickle.loads(pickle.dumps(self.fs,-1))
...@@ -789,14 +791,15 @@ class FSTestCases(object): ...@@ -789,14 +791,15 @@ class FSTestCases(object):
def test_big_file(self): def test_big_file(self):
"""Test handling of a big file (1MB)""" """Test handling of a big file (1MB)"""
chunk_size = 1024 * 256 chunk_size = 1024 * 256
num_chunks = 4 num_chunks = 4
def chunk_stream(): def chunk_stream():
"""Generate predictable-but-randomy binary content.""" """Generate predictable-but-randomy binary content."""
r = random.Random(0) r = random.Random(0)
randint = r.randint randint = r.randint
for i in xrange(num_chunks): int2byte = six.int2byte
c = "".join(chr(randint(0,255)) for j in xrange(chunk_size/8)) for _i in xrange(num_chunks):
c = b("").join(int2byte(randint(0,255)) for _j in xrange(chunk_size//8))
yield c * 8 yield c * 8
f = self.fs.open("bigfile","wb") f = self.fs.open("bigfile","wb")
try: try:
...@@ -812,7 +815,7 @@ class FSTestCases(object): ...@@ -812,7 +815,7 @@ class FSTestCases(object):
if chunks.next() != f.read(chunk_size): if chunks.next() != f.read(chunk_size):
assert False, "bigfile was corrupted" assert False, "bigfile was corrupted"
except StopIteration: except StopIteration:
if f.read() != "": if f.read() != b(""):
assert False, "bigfile was corrupted" assert False, "bigfile was corrupted"
finally: finally:
f.close() f.close()
...@@ -825,7 +828,7 @@ class FSTestCases(object): ...@@ -825,7 +828,7 @@ class FSTestCases(object):
return int(dts1) == int(dts2) return int(dts1) == int(dts2)
d1 = datetime.datetime(2010, 6, 20, 11, 0, 9, 987699) d1 = datetime.datetime(2010, 6, 20, 11, 0, 9, 987699)
d2 = datetime.datetime(2010, 7, 5, 11, 0, 9, 500000) d2 = datetime.datetime(2010, 7, 5, 11, 0, 9, 500000)
self.fs.setcontents('/dates.txt', 'check dates') self.fs.setcontents('/dates.txt', b('check dates'))
# If the implementation supports settimes, check that the times # If the implementation supports settimes, check that the times
# can be set and then retrieved # can be set and then retrieved
try: try:
...@@ -838,9 +841,12 @@ class FSTestCases(object): ...@@ -838,9 +841,12 @@ class FSTestCases(object):
self.assertTrue(cmp_datetimes(d2, info['modified_time'])) self.assertTrue(cmp_datetimes(d2, info['modified_time']))
# Disabled - see below
class ThreadingTestCases(object): class ThreadingTestCases(object):
"""Testcases for thread-safety of FS implementations.""" """Testcases for thread-safety of FS implementations."""
# These are either too slow to be worth repeating, # These are either too slow to be worth repeating,
# or cannot possibly break cross-thread. # or cannot possibly break cross-thread.
_dont_retest = ("test_pickling","test_multiple_overwrite",) _dont_retest = ("test_pickling","test_multiple_overwrite",)
...@@ -885,7 +891,7 @@ class ThreadingTestCases(object): ...@@ -885,7 +891,7 @@ class ThreadingTestCases(object):
def test_setcontents_threaded(self): def test_setcontents_threaded(self):
def setcontents(name,contents): def setcontents(name,contents):
f = self.fs.open(name,"w") f = self.fs.open(name,"wb")
self._yield() self._yield()
try: try:
f.write(contents) f.write(contents)
...@@ -893,13 +899,13 @@ class ThreadingTestCases(object): ...@@ -893,13 +899,13 @@ class ThreadingTestCases(object):
finally: finally:
f.close() f.close()
def thread1(): def thread1():
c = "thread1 was 'ere" c = b("thread1 was 'ere")
setcontents("thread1.txt",c) setcontents("thread1.txt",c)
self.assertEquals(self.fs.getcontents("thread1.txt"),c) self.assertEquals(self.fs.getcontents("thread1.txt", 'rb'),c)
def thread2(): def thread2():
c = "thread2 was 'ere" c = b("thread2 was 'ere")
setcontents("thread2.txt",c) setcontents("thread2.txt",c)
self.assertEquals(self.fs.getcontents("thread2.txt"),c) self.assertEquals(self.fs.getcontents("thread2.txt", 'rb'),c)
self._runThreads(thread1,thread2) self._runThreads(thread1,thread2)
def test_setcontents_threaded_samefile(self): def test_setcontents_threaded_samefile(self):
...@@ -912,17 +918,17 @@ class ThreadingTestCases(object): ...@@ -912,17 +918,17 @@ class ThreadingTestCases(object):
finally: finally:
f.close() f.close()
def thread1(): def thread1():
c = "thread1 was 'ere" c = b("thread1 was 'ere")
setcontents("threads.txt",c) setcontents("threads.txt",c)
self._yield() self._yield()
self.assertEquals(self.fs.listdir("/"),["threads.txt"]) self.assertEquals(self.fs.listdir("/"),["threads.txt"])
def thread2(): def thread2():
c = "thread2 was 'ere" c = b("thread2 was 'ere")
setcontents("threads.txt",c) setcontents("threads.txt",c)
self._yield() self._yield()
self.assertEquals(self.fs.listdir("/"),["threads.txt"]) self.assertEquals(self.fs.listdir("/"),["threads.txt"])
def thread3(): def thread3():
c = "thread3 was 'ere" c = b("thread3 was 'ere")
setcontents("threads.txt",c) setcontents("threads.txt",c)
self._yield() self._yield()
self.assertEquals(self.fs.listdir("/"),["threads.txt"]) self.assertEquals(self.fs.listdir("/"),["threads.txt"])
...@@ -1011,9 +1017,9 @@ class ThreadingTestCases(object): ...@@ -1011,9 +1017,9 @@ class ThreadingTestCases(object):
def test_concurrent_copydir(self): def test_concurrent_copydir(self):
self.fs.makedir("a") self.fs.makedir("a")
self.fs.makedir("a/b") self.fs.makedir("a/b")
self.fs.setcontents("a/hello.txt","hello world") self.fs.setcontents("a/hello.txt",b("hello world"))
self.fs.setcontents("a/guido.txt","is a space alien") self.fs.setcontents("a/guido.txt",b("is a space alien"))
self.fs.setcontents("a/b/parrot.txt","pining for the fiords") self.fs.setcontents("a/b/parrot.txt",b("pining for the fiords"))
def copydir(): def copydir():
self._yield() self._yield()
self.fs.copydir("a","copy of a") self.fs.copydir("a","copy of a")
...@@ -1030,22 +1036,26 @@ class ThreadingTestCases(object): ...@@ -1030,22 +1036,26 @@ class ThreadingTestCases(object):
pass pass
self.assertTrue(self.fs.isdir("copy of a")) self.assertTrue(self.fs.isdir("copy of a"))
self.assertTrue(self.fs.isdir("copy of a/b")) self.assertTrue(self.fs.isdir("copy of a/b"))
self.assertEqual(self.fs.getcontents("copy of a/b/parrot.txt"),"pining for the fiords") self.assertEqual(self.fs.getcontents("copy of a/b/parrot.txt", 'rb'),b("pining for the fiords"))
self.assertEqual(self.fs.getcontents("copy of a/hello.txt"),"hello world") self.assertEqual(self.fs.getcontents("copy of a/hello.txt", 'rb'),b("hello world"))
self.assertEqual(self.fs.getcontents("copy of a/guido.txt"),"is a space alien") self.assertEqual(self.fs.getcontents("copy of a/guido.txt", 'rb'),b("is a space alien"))
def test_multiple_overwrite(self): def test_multiple_overwrite(self):
contents = ["contents one","contents the second","number three"] contents = [b("contents one"),b("contents the second"),b("number three")]
def thread1(): def thread1():
for i in xrange(30): for i in xrange(30):
for c in contents: for c in contents:
self.fs.setcontents("thread1.txt",c) self.fs.setcontents("thread1.txt",c)
self.assertEquals(self.fs.getsize("thread1.txt"),len(c)) self.assertEquals(self.fs.getsize("thread1.txt"),len(c))
self.assertEquals(self.fs.getcontents("thread1.txt"),c) self.assertEquals(self.fs.getcontents("thread1.txt", 'rb'),c)
def thread2(): def thread2():
for i in xrange(30): for i in xrange(30):
for c in contents: for c in contents:
self.fs.setcontents("thread2.txt",c) self.fs.setcontents("thread2.txt",c)
self.assertEquals(self.fs.getsize("thread2.txt"),len(c)) self.assertEquals(self.fs.getsize("thread2.txt"),len(c))
self.assertEquals(self.fs.getcontents("thread2.txt"),c) self.assertEquals(self.fs.getcontents("thread2.txt", 'rb'),c)
self._runThreads(thread1,thread2) self._runThreads(thread1,thread2)
class ThreadingTestCases(object):
_dont_retest = ()
...@@ -20,6 +20,10 @@ from fs.errors import * ...@@ -20,6 +20,10 @@ from fs.errors import *
from fs import rpcfs from fs import rpcfs
from fs.expose.xmlrpc import RPCFSServer from fs.expose.xmlrpc import RPCFSServer
import six
from six import PY3, b
class TestRPCFS(unittest.TestCase, FSTestCases, ThreadingTestCases): class TestRPCFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
def makeServer(self,fs,addr): def makeServer(self,fs,addr):
...@@ -106,7 +110,7 @@ class TestRPCFS(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -106,7 +110,7 @@ class TestRPCFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
sock = socket.socket(af, socktype, proto) sock = socket.socket(af, socktype, proto)
sock.settimeout(.1) sock.settimeout(.1)
sock.connect(sa) sock.connect(sa)
sock.send("\n") sock.send(b("\n"))
except socket.error, e: except socket.error, e:
pass pass
finally: finally:
...@@ -114,10 +118,16 @@ class TestRPCFS(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -114,10 +118,16 @@ class TestRPCFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
sock.close() sock.close()
from fs import sftpfs try:
from fs.expose.sftp import BaseSFTPServer from fs import sftpfs
from fs.expose.sftp import BaseSFTPServer
except ImportError:
if not PY3:
raise
class TestSFTPFS(TestRPCFS): class TestSFTPFS(TestRPCFS):
__test__ = not PY3
def makeServer(self,fs,addr): def makeServer(self,fs,addr):
return BaseSFTPServer(addr,fs) return BaseSFTPServer(addr,fs)
...@@ -209,7 +219,7 @@ if dokan.is_available: ...@@ -209,7 +219,7 @@ if dokan.is_available:
def test_safety_wrapper(self): def test_safety_wrapper(self):
rawfs = MemoryFS() rawfs = MemoryFS()
safefs = dokan.Win32SafetyFS(rawfs) safefs = dokan.Win32SafetyFS(rawfs)
rawfs.setcontents("autoRun.inf","evilcodeevilcode") rawfs.setcontents("autoRun.inf", b("evilcodeevilcode"))
self.assertTrue(safefs.exists("_autoRun.inf")) self.assertTrue(safefs.exists("_autoRun.inf"))
self.assertTrue("autoRun.inf" not in safefs.listdir("/")) self.assertTrue("autoRun.inf" not in safefs.listdir("/"))
safefs.setcontents("file:stream","test") safefs.setcontents("file:stream","test")
......
...@@ -12,9 +12,13 @@ import time ...@@ -12,9 +12,13 @@ import time
from os.path import abspath from os.path import abspath
import urllib import urllib
from six import PY3
try: try:
from pyftpdlib import ftpserver from pyftpdlib import ftpserver
except ImportError: except ImportError:
if not PY3:
raise ImportError("Requires pyftpdlib <http://code.google.com/p/pyftpdlib/>") raise ImportError("Requires pyftpdlib <http://code.google.com/p/pyftpdlib/>")
from fs.path import * from fs.path import *
...@@ -24,6 +28,8 @@ from fs import ftpfs ...@@ -24,6 +28,8 @@ from fs import ftpfs
ftp_port = 30000 ftp_port = 30000
class TestFTPFS(unittest.TestCase, FSTestCases, ThreadingTestCases): class TestFTPFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
__test__ = not PY3
def setUp(self): def setUp(self):
global ftp_port global ftp_port
ftp_port += 1 ftp_port += 1
......
...@@ -22,6 +22,7 @@ from fs.tempfs import TempFS ...@@ -22,6 +22,7 @@ from fs.tempfs import TempFS
from fs.path import * from fs.path import *
from fs.local_functools import wraps from fs.local_functools import wraps
from six import PY3, b
class RemoteTempFS(TempFS): class RemoteTempFS(TempFS):
""" """
...@@ -79,7 +80,7 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -79,7 +80,7 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases):
self.fs.close() self.fs.close()
self.fakeOff() self.fakeOff()
def fake_setcontents(self, path, content='', chunk_size=16*1024): def fake_setcontents(self, path, content=b(''), chunk_size=16*1024):
''' Fake replacement for RemoteTempFS setcontents() ''' ''' Fake replacement for RemoteTempFS setcontents() '''
raise self.FakeException("setcontents should not be called here!") raise self.FakeException("setcontents should not be called here!")
...@@ -99,8 +100,8 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -99,8 +100,8 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases):
''' '''
Tests on-demand loading of remote content in RemoteFileBuffer Tests on-demand loading of remote content in RemoteFileBuffer
''' '''
contents = "Tristatricettri stribrnych strikacek strikalo" + \ contents = b("Tristatricettri stribrnych strikacek strikalo") + \
"pres tristatricettri stribrnych strech." b("pres tristatricettri stribrnych strech.")
f = self.fs.open('test.txt', 'wb') f = self.fs.open('test.txt', 'wb')
f.write(contents) f.write(contents)
f.close() f.close()
...@@ -136,10 +137,10 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -136,10 +137,10 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases):
# Rollback position 5 characters before eof # Rollback position 5 characters before eof
f._rfile.seek(len(contents[:-5])) f._rfile.seek(len(contents[:-5]))
# Write 10 new characters (will make contents longer for 5 chars) # Write 10 new characters (will make contents longer for 5 chars)
f.write(u'1234567890') f.write(b('1234567890'))
f.flush() f.flush()
# We are on the end of file (and buffer not serve anything anymore) # We are on the end of file (and buffer not serve anything anymore)
self.assertEquals(f.read(), '') self.assertEquals(f.read(), b(''))
f.close() f.close()
self.fakeOn() self.fakeOn()
...@@ -147,7 +148,7 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -147,7 +148,7 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases):
# Check if we wrote everything OK from # Check if we wrote everything OK from
# previous writing over the remote buffer edge # previous writing over the remote buffer edge
f = self.fs.open('test.txt', 'rb') f = self.fs.open('test.txt', 'rb')
self.assertEquals(f.read(), contents[:-5] + u'1234567890') self.assertEquals(f.read(), contents[:-5] + b('1234567890'))
f.close() f.close()
self.fakeOff() self.fakeOff()
...@@ -161,19 +162,19 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -161,19 +162,19 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases):
''' '''
self.fakeOn() self.fakeOn()
f = self.fs.open('test.txt', 'wb', write_on_flush=True) f = self.fs.open('test.txt', 'wb', write_on_flush=True)
f.write('Sample text') f.write(b('Sample text'))
self.assertRaises(self.FakeException, f.flush) self.assertRaises(self.FakeException, f.flush)
f.write('Second sample text') f.write(b('Second sample text'))
self.assertRaises(self.FakeException, f.close) self.assertRaises(self.FakeException, f.close)
self.fakeOff() self.fakeOff()
f.close() f.close()
self.fakeOn() self.fakeOn()
f = self.fs.open('test.txt', 'wb', write_on_flush=False) f = self.fs.open('test.txt', 'wb', write_on_flush=False)
f.write('Sample text') f.write(b('Sample text'))
# FakeException is not raised, because setcontents is not called # FakeException is not raised, because setcontents is not called
f.flush() f.flush()
f.write('Second sample text') f.write(b('Second sample text'))
self.assertRaises(self.FakeException, f.close) self.assertRaises(self.FakeException, f.close)
self.fakeOff() self.fakeOff()
...@@ -183,8 +184,8 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -183,8 +184,8 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases):
back to remote destination and opened file is still back to remote destination and opened file is still
in good condition. in good condition.
''' '''
contents = "Zlutoucky kun upel dabelske ody." contents = b("Zlutoucky kun upel dabelske ody.")
contents2 = 'Ententyky dva spaliky cert vyletel z elektriky' contents2 = b('Ententyky dva spaliky cert vyletel z elektriky')
f = self.fs.open('test.txt', 'wb') f = self.fs.open('test.txt', 'wb')
f.write(contents) f.write(contents)
...@@ -195,17 +196,17 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases): ...@@ -195,17 +196,17 @@ class TestRemoteFileBuffer(unittest.TestCase, FSTestCases, ThreadingTestCases):
self.assertEquals(f.read(10), contents[:10]) self.assertEquals(f.read(10), contents[:10])
self.assertEquals(f._rfile.tell(), 10) self.assertEquals(f._rfile.tell(), 10)
# Write garbage to file to mark it as _changed # Write garbage to file to mark it as _changed
f.write('x') f.write(b('x'))
# This should read the rest of file and store file back to again. # This should read the rest of file and store file back to again.
f.flush() f.flush()
f.seek(0) f.seek(0)
# Try if we have unocrrupted file locally... # Try if we have unocrrupted file locally...
self.assertEquals(f.read(), contents[:10] + 'x' + contents[11:]) self.assertEquals(f.read(), contents[:10] + b('x') + contents[11:])
f.close() f.close()
# And if we have uncorrupted file also on storage # And if we have uncorrupted file also on storage
f = self.fs.open('test.txt', 'rb') f = self.fs.open('test.txt', 'rb')
self.assertEquals(f.read(), contents[:10] + 'x' + contents[11:]) self.assertEquals(f.read(), contents[:10] + b('x') + contents[11:])
f.close() f.close()
# Now try it again, but write garbage behind edge of remote file # Now try it again, but write garbage behind edge of remote file
...@@ -243,7 +244,7 @@ class TestCacheFS(unittest.TestCase,FSTestCases,ThreadingTestCases): ...@@ -243,7 +244,7 @@ class TestCacheFS(unittest.TestCase,FSTestCases,ThreadingTestCases):
self.fs.cache_timeout = None self.fs.cache_timeout = None
try: try:
self.assertFalse(self.fs.isfile("hello")) self.assertFalse(self.fs.isfile("hello"))
self.wrapped_fs.setcontents("hello","world") self.wrapped_fs.setcontents("hello",b("world"))
self.assertTrue(self.fs.isfile("hello")) self.assertTrue(self.fs.isfile("hello"))
self.wrapped_fs.remove("hello") self.wrapped_fs.remove("hello")
self.assertTrue(self.fs.isfile("hello")) self.assertTrue(self.fs.isfile("hello"))
...@@ -257,11 +258,11 @@ class TestCacheFS(unittest.TestCase,FSTestCases,ThreadingTestCases): ...@@ -257,11 +258,11 @@ class TestCacheFS(unittest.TestCase,FSTestCases,ThreadingTestCases):
self.fs.cache_timeout = None self.fs.cache_timeout = None
try: try:
self.assertFalse(self.fs.isfile("hello")) self.assertFalse(self.fs.isfile("hello"))
self.wrapped_fs.setcontents("hello","world") self.wrapped_fs.setcontents("hello",b("world"))
self.assertTrue(self.fs.isfile("hello")) self.assertTrue(self.fs.isfile("hello"))
self.wrapped_fs.remove("hello") self.wrapped_fs.remove("hello")
self.assertTrue(self.fs.isfile("hello")) self.assertTrue(self.fs.isfile("hello"))
self.wrapped_fs.setcontents("hello","world") self.wrapped_fs.setcontents("hello",b("world"))
self.assertTrue(self.fs.isfile("hello")) self.assertTrue(self.fs.isfile("hello"))
self.fs.remove("hello") self.fs.remove("hello")
self.assertFalse(self.fs.isfile("hello")) self.assertFalse(self.fs.isfile("hello"))
...@@ -315,7 +316,7 @@ class DisconnectingFS(WrapFS): ...@@ -315,7 +316,7 @@ class DisconnectingFS(WrapFS):
time.sleep(random.random()*0.1) time.sleep(random.random()*0.1)
self._connected = not self._connected self._connected = not self._connected
def setcontents(self, path, contents='', chunk_size=64*1024): def setcontents(self, path, contents=b(''), chunk_size=64*1024):
return self.wrapped_fs.setcontents(path, contents) return self.wrapped_fs.setcontents(path, contents)
def close(self): def close(self):
......
...@@ -13,7 +13,13 @@ import unittest ...@@ -13,7 +13,13 @@ import unittest
from fs.tests import FSTestCases, ThreadingTestCases from fs.tests import FSTestCases, ThreadingTestCases
from fs.path import * from fs.path import *
from fs import s3fs from six import PY3
try:
from fs import s3fs
except ImportError:
if not PY3:
raise
class TestS3FS(unittest.TestCase,FSTestCases,ThreadingTestCases): class TestS3FS(unittest.TestCase,FSTestCases,ThreadingTestCases):
# Disable the tests by default # Disable the tests by default
......
...@@ -29,6 +29,8 @@ if sys.platform == "win32": ...@@ -29,6 +29,8 @@ if sys.platform == "win32":
else: else:
watch_win32 = None watch_win32 = None
import six
from six import PY3, b
class WatcherTestCases: class WatcherTestCases:
"""Testcases for filesystems providing change watcher support. """Testcases for filesystems providing change watcher support.
...@@ -88,11 +90,11 @@ class WatcherTestCases: ...@@ -88,11 +90,11 @@ class WatcherTestCases:
def test_watch_readfile(self): def test_watch_readfile(self):
self.setupWatchers() self.setupWatchers()
self.fs.setcontents("hello","hello world") self.fs.setcontents("hello", b("hello world"))
self.assertEventOccurred(CREATED,"/hello") self.assertEventOccurred(CREATED,"/hello")
self.clearCapturedEvents() self.clearCapturedEvents()
old_atime = self.fs.getinfo("hello").get("accessed_time") old_atime = self.fs.getinfo("hello").get("accessed_time")
self.assertEquals(self.fs.getcontents("hello"),"hello world") self.assertEquals(self.fs.getcontents("hello"), b("hello world"))
if not isinstance(self.watchfs,PollingWatchableFS): if not isinstance(self.watchfs,PollingWatchableFS):
# Help it along by updting the atime. # Help it along by updting the atime.
# TODO: why is this necessary? # TODO: why is this necessary?
...@@ -121,17 +123,17 @@ class WatcherTestCases: ...@@ -121,17 +123,17 @@ class WatcherTestCases:
def test_watch_writefile(self): def test_watch_writefile(self):
self.setupWatchers() self.setupWatchers()
self.fs.setcontents("hello","hello world") self.fs.setcontents("hello", b("hello world"))
self.assertEventOccurred(CREATED,"/hello") self.assertEventOccurred(CREATED,"/hello")
self.clearCapturedEvents() self.clearCapturedEvents()
self.fs.setcontents("hello","hello again world") self.fs.setcontents("hello", b("hello again world"))
self.assertEventOccurred(MODIFIED,"/hello") self.assertEventOccurred(MODIFIED,"/hello")
def test_watch_single_file(self): def test_watch_single_file(self):
self.fs.setcontents("hello","hello world") self.fs.setcontents("hello", b("hello world"))
events = [] events = []
self.watchfs.add_watcher(events.append,"/hello",(MODIFIED,)) self.watchfs.add_watcher(events.append,"/hello",(MODIFIED,))
self.fs.setcontents("hello","hello again world") self.fs.setcontents("hello", b("hello again world"))
self.fs.remove("hello") self.fs.remove("hello")
self.waitForEvents() self.waitForEvents()
for evt in events: for evt in events:
...@@ -140,10 +142,10 @@ class WatcherTestCases: ...@@ -140,10 +142,10 @@ class WatcherTestCases:
def test_watch_single_file_remove(self): def test_watch_single_file_remove(self):
self.fs.makedir("testing") self.fs.makedir("testing")
self.fs.setcontents("testing/hello","hello world") self.fs.setcontents("testing/hello", b("hello world"))
events = [] events = []
self.watchfs.add_watcher(events.append,"/testing/hello",(REMOVED,)) self.watchfs.add_watcher(events.append,"/testing/hello",(REMOVED,))
self.fs.setcontents("testing/hello","hello again world") self.fs.setcontents("testing/hello", b("hello again world"))
self.waitForEvents() self.waitForEvents()
self.fs.remove("testing/hello") self.fs.remove("testing/hello")
self.waitForEvents() self.waitForEvents()
...@@ -154,7 +156,7 @@ class WatcherTestCases: ...@@ -154,7 +156,7 @@ class WatcherTestCases:
def test_watch_iter_changes(self): def test_watch_iter_changes(self):
changes = iter_changes(self.watchfs) changes = iter_changes(self.watchfs)
self.fs.makedir("test1") self.fs.makedir("test1")
self.fs.setcontents("test1/hello","hello world") self.fs.setcontents("test1/hello", b("hello world"))
self.waitForEvents() self.waitForEvents()
self.fs.removedir("test1",force=True) self.fs.removedir("test1",force=True)
self.waitForEvents() self.waitForEvents()
......
...@@ -15,11 +15,15 @@ import tempfile ...@@ -15,11 +15,15 @@ import tempfile
from fs import osfs from fs import osfs
from fs.errors import * from fs.errors import *
from fs.path import * from fs.path import *
from fs import wrapfs
import six
from six import PY3, b
from fs import wrapfs
class TestWrapFS(unittest.TestCase, FSTestCases, ThreadingTestCases): class TestWrapFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
__test__ = False
def setUp(self): def setUp(self):
self.temp_dir = tempfile.mkdtemp(u"fstest") self.temp_dir = tempfile.mkdtemp(u"fstest")
self.fs = wrapfs.WrapFS(osfs.OSFS(self.temp_dir)) self.fs = wrapfs.WrapFS(osfs.OSFS(self.temp_dir))
...@@ -67,7 +71,7 @@ class TestLimitSizeFS(TestWrapFS): ...@@ -67,7 +71,7 @@ class TestLimitSizeFS(TestWrapFS):
for i in xrange(1024*2): for i in xrange(1024*2):
try: try:
total_written += 1030 total_written += 1030
self.fs.setcontents("file"+str(i),"C"*1030) self.fs.setcontents("file %i" % i, b("C")*1030)
except StorageSpaceError: except StorageSpaceError:
self.assertTrue(total_written > 1024*1024*2) self.assertTrue(total_written > 1024*1024*2)
self.assertTrue(total_written < 1024*1024*2 + 1030) self.assertTrue(total_written < 1024*1024*2 + 1030)
......
...@@ -120,8 +120,8 @@ class WrapFS(FS): ...@@ -120,8 +120,8 @@ class WrapFS(FS):
def __unicode__(self): def __unicode__(self):
return u"<%s: %s>" % (self.__class__.__name__,self.wrapped_fs,) return u"<%s: %s>" % (self.__class__.__name__,self.wrapped_fs,)
def __str__(self): #def __str__(self):
return unicode(self).encode(sys.getdefaultencoding(),"replace") # return unicode(self).encode(sys.getdefaultencoding(),"replace")
@rewrite_errors @rewrite_errors
...@@ -155,10 +155,11 @@ class WrapFS(FS): ...@@ -155,10 +155,11 @@ class WrapFS(FS):
# We can't pass setcontents() through to the wrapped FS if the # We can't pass setcontents() through to the wrapped FS if the
# wrapper has defined a _file_wrap method, as it would bypass # wrapper has defined a _file_wrap method, as it would bypass
# the file contents wrapping. # the file contents wrapping.
if self._file_wrap.im_func is WrapFS._file_wrap.im_func: #if self._file_wrap.im_func is WrapFS._file_wrap.im_func:
if getattr(self.__class__, '_file_wrap', None) is getattr(WrapFS, '_file_wrap', None):
return self.wrapped_fs.setcontents(self._encode(path), data, chunk_size=chunk_size) return self.wrapped_fs.setcontents(self._encode(path), data, chunk_size=chunk_size)
else: else:
return super(WrapFS,self).setcontents(path, data, chunk_size) return super(WrapFS,self).setcontents(path, data, chunk_size=chunk_size)
@rewrite_errors @rewrite_errors
def createfile(self, path): def createfile(self, path):
......
...@@ -95,7 +95,7 @@ class LimitSizeFS(WrapFS): ...@@ -95,7 +95,7 @@ class LimitSizeFS(WrapFS):
def setcontents(self, path, data, chunk_size=64*1024): def setcontents(self, path, data, chunk_size=64*1024):
f = None f = None
try: try:
f = self.open(path, 'w') f = self.open(path, 'wb')
if hasattr(data, 'read'): if hasattr(data, 'read'):
chunk = data.read(chunk_size) chunk = data.read(chunk_size)
while chunk: while chunk:
......
...@@ -37,7 +37,7 @@ class SubFS(WrapFS): ...@@ -37,7 +37,7 @@ class SubFS(WrapFS):
return u'<SubFS: %s/%s>' % (self.wrapped_fs, self.sub_dir.lstrip('/')) return u'<SubFS: %s/%s>' % (self.wrapped_fs, self.sub_dir.lstrip('/'))
def __repr__(self): def __repr__(self):
return str(self) return "SubFS(%r, %r)" % (self.wrapped_fs, self.sub_dir)
def desc(self, path): def desc(self, path):
if path in ('', '/'): if path in ('', '/'):
......
...@@ -142,7 +142,7 @@ class ZipFS(FS): ...@@ -142,7 +142,7 @@ class ZipFS(FS):
return "<ZipFS: %s>" % self.zip_path return "<ZipFS: %s>" % self.zip_path
def __unicode__(self): def __unicode__(self):
return unicode(self.__str__()) return u"<ZipFS: %s>" % self.zip_path
def _parse_resource_list(self): def _parse_resource_list(self):
for path in self.zf.namelist(): for path in self.zf.namelist():
...@@ -209,7 +209,7 @@ class ZipFS(FS): ...@@ -209,7 +209,7 @@ class ZipFS(FS):
raise ValueError("Mode must contain be 'r' or 'w'") raise ValueError("Mode must contain be 'r' or 'w'")
@synchronize @synchronize
def getcontents(self, path): def getcontents(self, path, mode="rb"):
if not self.exists(path): if not self.exists(path):
raise ResourceNotFoundError(path) raise ResourceNotFoundError(path)
path = normpath(relpath(path)) path = normpath(relpath(path))
......
#!/usr/bin/env python #!/usr/bin/env python
from distutils.core import setup #from distribute_setup import use_setuptools
from fs import __version__ as VERSION #use_setuptools()
from setuptools import setup
import sys
PY3 = sys.version_info >= (3,)
VERSION = "0.4.1"
COMMANDS = ['fscat', COMMANDS = ['fscat',
'fscp', 'fscp',
...@@ -17,7 +23,7 @@ COMMANDS = ['fscat', ...@@ -17,7 +23,7 @@ COMMANDS = ['fscat',
classifiers = [ classifiers = [
'Development Status :: 3 - Alpha', "Development Status :: 5 - Production/Stable",
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License', 'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent', 'Operating System :: OS Independent',
...@@ -30,7 +36,12 @@ long_desc = """Pyfilesystem is a module that provides a simplified common interf ...@@ -30,7 +36,12 @@ long_desc = """Pyfilesystem is a module that provides a simplified common interf
Even if you only need to work with file and directories on the local hard-drive, Pyfilesystem can simplify your code and make it more robust -- with the added advantage that you can change where the files are located by changing a single line of code. Even if you only need to work with file and directories on the local hard-drive, Pyfilesystem can simplify your code and make it more robust -- with the added advantage that you can change where the files are located by changing a single line of code.
""" """
setup(name='fs', extra = {}
if PY3:
extra["use_2to3"] = True
setup(install_requires=['distribute'],
name='fs',
version=VERSION, version=VERSION,
description="Filesystem abstraction", description="Filesystem abstraction",
long_description=long_desc, long_description=long_desc,
...@@ -55,5 +66,6 @@ setup(name='fs', ...@@ -55,5 +66,6 @@ setup(name='fs',
'fs.commands'], 'fs.commands'],
scripts=['fs/commands/%s' % command for command in COMMANDS], scripts=['fs/commands/%s' % command for command in COMMANDS],
classifiers=classifiers, classifiers=classifiers,
**extra
) )
[tox] [tox]
envlist = py25,py26,py27 envlist = py25,py26,py27,py32
[testenv] [testenv]
deps = dexml deps = distribute
six
dexml
paramiko paramiko
boto boto
nose nose
mako mako
pyftpdlib pyftpdlib
commands = nosetests -v \ changedir=.tox
commands = nosetests fs.tests -v \
[] []
[testenv:py25] [testenv:py25]
deps = dexml deps = distribute
six
dexml
paramiko paramiko
boto boto
nose nose
mako mako
pyftpdlib pyftpdlib
simplejson simplejson
[testenv:py32]
commands = nosetests fs.tests -v \
[]
deps = distribute
six
dexml
nose
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