Commit 44dcc3bf by willmcgugan

Added settimes to a few FS impementations, and added a settimes test case

parent 1a8f4d8b
...@@ -110,6 +110,7 @@ try: ...@@ -110,6 +110,7 @@ try:
except ImportError: except ImportError:
wraps = lambda f:f wraps = lambda f:f
def synchronize(func): def synchronize(func):
"""Decorator to synchronize a method on self._lock.""" """Decorator to synchronize a method on self._lock."""
@wraps(func) @wraps(func)
...@@ -121,7 +122,6 @@ def synchronize(func): ...@@ -121,7 +122,6 @@ def synchronize(func):
self._lock.release() self._lock.release()
return acquire_lock return acquire_lock
class FS(object): class FS(object):
"""The base class for Filesystem abstraction objects. """The base class for Filesystem abstraction objects.
...@@ -183,7 +183,6 @@ class FS(object): ...@@ -183,7 +183,6 @@ class FS(object):
else: else:
self._lock = DummyLock() self._lock = DummyLock()
def getsyspath(self, path, allow_none=False): def getsyspath(self, path, allow_none=False):
"""Returns the system path (a path recognised by the OS) if present. """Returns the system path (a path recognised by the OS) if present.
...@@ -417,21 +416,28 @@ class FS(object): ...@@ -417,21 +416,28 @@ class FS(object):
raise UnsupportedError("rename resource") raise UnsupportedError("rename resource")
def settimes(self, path, accessed_time=None, modified_time=None): def settimes(self, path, accessed_time=None, modified_time=None):
"""Set the access time and modifie time of a file """Set the accessed time and modified time of a file
:param path: path to a file :param path: path to a file
:param access_time: epoch time of file access :param accessed_time: a datetime object the file was accessed (defaults to current time)
:param modified_time: epock time of file modification :param modified_time: a datetime object the file was modified (defaults to current time)
"""
if self.hassyspath(path): """
sys_path = self.getsyspath(path)
os.utime(sys_path, accessed_time, modified_time) sys_path = self.getsyspath(path, allow_none=True)
if sys_path is not None:
now = datetime.datetime.now()
if accessed_time is None:
accessed_time = now
if modified_time is None:
modified_time = now
accessed_time = int(time.mktime(accessed_time.timetuple()))
modified_time = int(time.mktime(modified_time.timetuple()))
os.utime(sys_path, (accessed_time, modified_time))
return True return True
else: else:
raise UnsupportedError("settimes") raise UnsupportedError("settimes")
def getinfo(self, path): def getinfo(self, path):
"""Returns information for a path as a dictionary. The exact content of """Returns information for a path as a dictionary. The exact content of
this dictionary will vary depending on the implementation, but will this dictionary will vary depending on the implementation, but will
...@@ -442,7 +448,6 @@ class FS(object): ...@@ -442,7 +448,6 @@ class FS(object):
""" """
raise UnsupportedError("get resource info") raise UnsupportedError("get resource info")
def desc(self, path): def desc(self, path):
"""Returns short descriptive text regarding a path. Intended mainly as """Returns short descriptive text regarding a path. Intended mainly as
a debugging aid a debugging aid
...@@ -462,7 +467,6 @@ class FS(object): ...@@ -462,7 +467,6 @@ class FS(object):
else: else:
return "OS file, maps to %s" % sys_path return "OS file, maps to %s" % sys_path
def getcontents(self, path): def getcontents(self, path):
"""Returns the contents of a file as a string. """Returns the contents of a file as a string.
...@@ -512,7 +516,6 @@ class FS(object): ...@@ -512,7 +516,6 @@ class FS(object):
sub_fs = SubFS(self, path) sub_fs = SubFS(self, path)
return sub_fs return sub_fs
def walk(self, def walk(self,
path="/", path="/",
wildcard=None, wildcard=None,
...@@ -578,7 +581,6 @@ class FS(object): ...@@ -578,7 +581,6 @@ class FS(object):
else: else:
raise ValueError("Search should be 'breadth' or 'depth'") raise ValueError("Search should be 'breadth' or 'depth'")
def walkfiles(self, def walkfiles(self,
path="/", path="/",
wildcard=None, wildcard=None,
...@@ -597,7 +599,6 @@ class FS(object): ...@@ -597,7 +599,6 @@ class FS(object):
for f in files: for f in files:
yield pathjoin(path, f) yield pathjoin(path, f)
def walkdirs(self, def walkdirs(self,
path="/", path="/",
wildcard=None, wildcard=None,
...@@ -613,8 +614,6 @@ class FS(object): ...@@ -613,8 +614,6 @@ class FS(object):
for p, files in self.walk(path, wildcard=wildcard, search=search, ignore_errors=ignore_errors): for p, files in self.walk(path, wildcard=wildcard, search=search, ignore_errors=ignore_errors):
yield p yield p
def getsize(self, path): def getsize(self, path):
"""Returns the size (in bytes) of a resource. """Returns the size (in bytes) of a resource.
...@@ -714,7 +713,6 @@ class FS(object): ...@@ -714,7 +713,6 @@ class FS(object):
self.copy(src, dst, overwrite=overwrite, chunk_size=chunk_size) self.copy(src, dst, overwrite=overwrite, chunk_size=chunk_size)
self.remove(src) self.remove(src)
def movedir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384): def movedir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384):
"""moves a directory from one location to another. """moves a directory from one location to another.
...@@ -772,8 +770,6 @@ class FS(object): ...@@ -772,8 +770,6 @@ class FS(object):
self.removedir(dirname) self.removedir(dirname)
def copydir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384): def copydir(self, src, dst, overwrite=False, ignore_errors=False, chunk_size=16384):
"""copies a directory from one location to another. """copies a directory from one location to another.
...@@ -820,7 +816,6 @@ class FS(object): ...@@ -820,7 +816,6 @@ class FS(object):
dst_filename = pathjoin(dst_dirpath, filename) dst_filename = pathjoin(dst_dirpath, filename)
copyfile(src_filename, dst_filename, overwrite=overwrite, chunk_size=chunk_size) copyfile(src_filename, dst_filename, overwrite=overwrite, chunk_size=chunk_size)
def isdirempty(self, path): def isdirempty(self, path):
"""Check if a directory is empty (contains no files or sub-directories) """Check if a directory is empty (contains no files or sub-directories)
...@@ -835,7 +830,6 @@ class FS(object): ...@@ -835,7 +830,6 @@ class FS(object):
return True return True
return False return False
def makeopendir(self, path, recursive=False): def makeopendir(self, path, recursive=False):
"""makes a directory (if it doesn't exist) and returns an FS object for """makes a directory (if it doesn't exist) and returns an FS object for
the newly created directory. the newly created directory.
...@@ -858,8 +852,6 @@ class FS(object): ...@@ -858,8 +852,6 @@ class FS(object):
""" """
from fs.utils import print_fs from fs.utils import print_fs
print_fs(self, max_levels=max_levels) print_fs(self, max_levels=max_levels)
def browse(self): def browse(self):
"""Displays the FS tree in a graphical window (requires wxWidgets)""" """Displays the FS tree in a graphical window (requires wxWidgets)"""
...@@ -965,6 +957,9 @@ class SubFS(FS): ...@@ -965,6 +957,9 @@ class SubFS(FS):
except DirectoryNotEmptyError: except DirectoryNotEmptyError:
pass pass
def settimes(self, path, accessed_time=None, modified_time=None):
return self.parent.settimes(self._delegate(path), accessed_time, modified_time)
def getinfo(self, path): def getinfo(self, path):
return self.parent.getinfo(self._delegate(path)) return self.parent.getinfo(self._delegate(path))
......
...@@ -85,6 +85,10 @@ class RPCFSInterface(object): ...@@ -85,6 +85,10 @@ class RPCFSInterface(object):
dst = self.decode_path(dst) dst = self.decode_path(dst)
return self.fs.rename(src, dst) return self.fs.rename(src, dst)
def settimes(self, path, accessed_time, modified_time):
path = self.decode_path(path)
return self.fs.settimes(path, accessed_time, modified_time)
def getinfo(self, path): def getinfo(self, path):
path = self.decode_path(path) path = self.decode_path(path)
return self.fs.getinfo(path) return self.fs.getinfo(path)
......
...@@ -326,7 +326,7 @@ class MountFS(FS): ...@@ -326,7 +326,7 @@ class MountFS(FS):
@synchronize @synchronize
def unmount(self, path): def unmount(self, path):
"""Unmounds a path. """Unmounts a path.
:param path: Path to unmount :param path: Path to unmount
...@@ -334,6 +334,15 @@ class MountFS(FS): ...@@ -334,6 +334,15 @@ class MountFS(FS):
path = normpath(path) path = normpath(path)
del self.mount_tree[path] del self.mount_tree[path]
def settimes(self, path, accessed_time=None, modified_time=None):
path = normpath(path)
fs, mount_path, delegate_path = self._delegate(path)
if fs is None:
raise ResourceNotFoundError(path)
if fs is self:
raise UnsupportedError("settimes")
fs.settimes(delegate_path, accessed_time, modified_time)
@synchronize @synchronize
def getinfo(self, path): def getinfo(self, path):
path = normpath(path) path = normpath(path)
......
...@@ -78,6 +78,7 @@ class MultiFS(FS): ...@@ -78,6 +78,7 @@ class MultiFS(FS):
__repr__ = __str__ __repr__ = __str__
@synchronize
def __unicode__(self): def __unicode__(self):
return u"<MultiFS: %s>" % ", ".join(unicode(fs) for fs in self.fs_sequence) return u"<MultiFS: %s>" % ", ".join(unicode(fs) for fs in self.fs_sequence)
...@@ -218,6 +219,13 @@ class MultiFS(FS): ...@@ -218,6 +219,13 @@ class MultiFS(FS):
raise ResourceNotFoundError(path) raise ResourceNotFoundError(path)
@synchronize @synchronize
def settimes(self, path, accessed_time=None, modified_time=None):
for fs in self:
if fs.exists(path):
return fs.settimes(path, accessed_time, modified_time)
raise ResourceNotFoundError(path)
@synchronize
def getinfo(self, path): def getinfo(self, path):
for fs in self: for fs in self:
if fs.exists(path): if fs.exists(path):
......
...@@ -215,6 +215,10 @@ class RPCFS(FS): ...@@ -215,6 +215,10 @@ class RPCFS(FS):
dst = self.encode_path(dst) dst = self.encode_path(dst)
return self.proxy.rename(src,dst) return self.proxy.rename(src,dst)
def settimes(self, path, accessed_time, modified_time):
path = self.encode_path(path)
return self.proxy.settimes(path, accessed_time, modified_time)
def getinfo(self, path): def getinfo(self, path):
path = self.encode_path(path) path = self.encode_path(path)
return self.proxy.getinfo(path) return self.proxy.getinfo(path)
......
...@@ -13,6 +13,7 @@ logging.basicConfig(level=logging.ERROR, stream=sys.stdout) ...@@ -13,6 +13,7 @@ logging.basicConfig(level=logging.ERROR, stream=sys.stdout)
from fs.base import * from fs.base import *
import datetime
import unittest import unittest
import os, os.path import os, os.path
import pickle import pickle
...@@ -561,6 +562,26 @@ class FSTestCases(object): ...@@ -561,6 +562,26 @@ class FSTestCases(object):
finally: finally:
f.close() f.close()
def test_settimes(self):
def cmp_datetimes(d1, d2):
"""Test datetime objects are the same to within the timestamp accuracy"""
dts1 = time.mktime(d1.timetuple())
dts2 = time.mktime(d2.timetuple())
return dts1 == dts2
d1 = datetime.datetime(2010, 6, 20, 11, 0, 9, 987699)
d2 = datetime.datetime(2010, 7, 5, 11, 0, 9, 500000)
self.fs.createfile('/dates.txt', 'check dates')
# If the implementation supports settimes, check that the times
# can be set and then retrieved
try:
self.fs.settimes('/dates.txt', d1, d2)
except UnsupportedError:
pass
else:
info = self.fs.getinfo('/dates.txt')
self.assertTrue(cmp_datetimes(d1, info['accessed_time']))
self.assertTrue(cmp_datetimes(d2, info['modified_time']))
class ThreadingTestCases: class ThreadingTestCases:
"""Testcases for thread-safety of FS implementations.""" """Testcases for thread-safety of FS implementations."""
......
...@@ -181,7 +181,7 @@ class WrapFS(FS): ...@@ -181,7 +181,7 @@ class WrapFS(FS):
@rewrite_errors @rewrite_errors
def settimes(self, path, *args, **kwds): def settimes(self, path, *args, **kwds):
return self.wrapped_fs.settimes(*args,**kwds) return self.wrapped_fs.settimes(self._encode(path), *args,**kwds)
@rewrite_errors @rewrite_errors
def desc(self, path): def desc(self, path):
......
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