Skip to content

Commit

Permalink
Added support for ns argument in os.utime
Browse files Browse the repository at this point in the history
  • Loading branch information
mrbean-bremen authored Jun 8, 2017
1 parent e990e00 commit b157fd2
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 72 deletions.
68 changes: 25 additions & 43 deletions fake_filesystem_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,28 +681,6 @@ def setUp(self):
def tearDown(self):
time.time = self.orig_time

def assertRaisesWithRegexpMatch(self, expected_exception, regexp_string,
callable_obj, *args, **kwargs):
"""Asserts that the message in a raised exception matches the given regexp.
Args:
expected_exception: Exception class expected to be raised.
regexp_string: Regexp (re pattern string) expected to be
found in error message.
callable_obj: Function to be called.
*args: Extra args.
**kwargs: Extra kwargs.
"""
try:
callable_obj(*args, **kwargs)
except expected_exception as err:
expected_regexp = re.compile(regexp_string)
self.assertTrue(
expected_regexp.search(str(err)),
'"%s" does not match "%s"' % (expected_regexp.pattern, str(err)))
else:
self.fail(expected_exception.__name__ + ' not raised')

def testChdir(self):
"""chdir should work on a directory."""
directory = '/foo'
Expand Down Expand Up @@ -1869,35 +1847,39 @@ def testUtimeNoFollowSymlinks(self):
self.assertEqual(2, st.st_mtime)

def testUtimeNonExistent(self):
# set up
path = '/non/existent/file'
self.assertFalse(self.filesystem.Exists(path))
# actual tests
try:
# Use try-catch to check exception attributes.
self.os.utime(path, (1, 2))
self.fail('Exception is expected.') # COV_NF_LINE
except OSError as os_error:
self.assertEqual(errno.ENOENT, os_error.errno)
self.assertEqual(path, os_error.filename)
self.assertRaisesOSError(errno.ENOENT, self.os.utime, path, (1, 2))

def testUtimeTupleArgIsOfIncorrectLength(self):
# set up
def testUtimeInvalidTimesArgRaises(self):
path = '/some_dir'
self._CreateTestDirectory(path)
# actual tests
self.assertRaisesWithRegexpMatch(
TypeError, r'utime\(\) arg 2 must be a tuple \(atime, mtime\)',
self.os.utime, path, (1, 2, 3))

def testUtimeTupleArgContainsIncorrectType(self):
# the error message differs with different Python versions
# we don't expect the same message here
self.assertRaises(TypeError, self.os.utime, path, (1, 2, 3))
self.assertRaises(TypeError, self.os.utime, path, (1, 'str'))

@unittest.skipIf(sys.version_info < (3, 3), 'ns new in Python 3.3')
def testUtimeSetsSpecifiedTimeInNs(self):
# set up
path = '/some_dir'
self._CreateTestDirectory(path)
path = '/some_file'
self._CreateTestFile(path)
st = self.os.stat(path)
# actual tests
self.assertRaisesWithRegexpMatch(
TypeError, 'atime and mtime must be numbers',
self.os.utime, path, (1, 'str'))
self.os.utime(path, ns=(200000000, 400000000))
st = self.os.stat(path)
self.assertEqual(0.2, st.st_atime)
self.assertEqual(0.4, st.st_mtime)

@unittest.skipIf(sys.version_info < (3, 3), 'ns new in Python 3.3')
def testUtimeIncorrectNsArgumentRaises(self):
file_path = 'some_file'
self.filesystem.CreateFile(file_path)

self.assertRaises(TypeError, self.os.utime, file_path, ns=(200000000))
self.assertRaises(TypeError, self.os.utime, file_path, ns=('a', 'b'))
self.assertRaises(ValueError, self.os.utime, file_path, times=(1, 2), ns=(100, 200))

def testChownExistingFile(self):
# set up
Expand Down
87 changes: 58 additions & 29 deletions pyfakefs/fake_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -871,22 +871,36 @@ def ChangeMode(self, path, mode, follow_symlinks=True):
(mode & PERM_ALL))
file_object.st_ctime = time.time()

def UpdateTime(self, path, times, follow_symlinks=True):
def UpdateTime(self, path, times=None, ns=None, follow_symlinks=True):
"""Change the access and modified times of a file.
New in pyfakefs 3.0.
Args:
path: (str) Path to the file.
times: 2-tuple of numbers, of the form (atime, mtime) which is used to set
the access and modified times, respectively. If None, file's access
and modified times are set to the current time.
follow_symlinks: if False and entry_path points to a symlink, the link itself is queried
instead of the linked object.
path: (str) Path to the file.
times: 2-tuple of int or float numbers, of the form (atime, mtime)
which is used to set the access and modified times in seconds.
If None, both times are set to the current time.
ns: 2-tuple of int numbers, of the form (atime, mtime) which is
used to set the access and modified times in nanoseconds.
If None, both times are set to the current time.
New in Python 3.3. New in pyfakefs 3.3.
follow_symlinks: If `False` and entry_path points to a symlink,
the link itself is queried instead of the linked object.
New in Python 3.3. New in pyfakefs 3.0.
Raises:
TypeError: If anything other than the expected types is
specified in the passed `times` or `ns` tuple,
or if the tuple length is not equal to 2.
ValueError: If both times and ns are specified.
"""
if times is not None and ns is not None:
raise ValueError("utime: you may specify either 'times' or 'ns' but not both")
if times is not None and len(times) != 2:
raise TypeError("utime: 'times' must be either a tuple of two ints or None")
if ns is not None and len(ns) != 2:
raise TypeError("utime: 'ns' must be a tuple of two ints")

Raises:
TypeError: If anything other than integers is specified in passed tuple or
number of elements in the tuple is not equal to 2.
"""
try:
file_object = self.ResolveObject(path, follow_symlinks)
except IOError as io_error:
Expand All @@ -895,18 +909,23 @@ def UpdateTime(self, path, times, follow_symlinks=True):
'No such file or directory in fake filesystem',
path)
raise
if times is None:
file_object.st_atime = time.time()
file_object.st_mtime = time.time()
else:
if len(times) != 2:
raise TypeError('utime() arg 2 must be a tuple (atime, mtime)')
if times is not None:
for file_time in times:
if not isinstance(file_time, (int, float)):
raise TypeError('atime and mtime must be numbers')

file_object.st_atime = times[0]
file_object.st_mtime = times[1]
elif ns is not None:
for file_time in ns:
if not isinstance(file_time, int):
raise TypeError('atime and mtime must be ints')

file_object.st_atime = ns[0] / 1e9
file_object.st_mtime = ns[1] / 1e9
else:
file_object.st_atime = time.time()
file_object.st_mtime = time.time()

def SetIno(self, path, st_ino):
"""Set the self.st_ino attribute of file at 'path'.
Expand Down Expand Up @@ -3359,26 +3378,36 @@ def lchmod(self, path, mode):
raise (NameError, "name 'lchmod' is not defined")
self.filesystem.ChangeMode(path, mode, follow_symlinks=False)

def utime(self, path, times, follow_symlinks=None):
def utime(self, path, times=None, ns=None, follow_symlinks=None):
"""Change the access and modified times of a file.
Args:
path: (str) Path to the file.
times: 2-tuple of numbers, of the form (atime, mtime) which is used to set
the access and modified times, respectively. If None, file's access
and modified times are set to the current time.
follow_symlinks: if False and entry_path points to a symlink, the link itself is queried
instead of the linked object. New in Python 3.3. New in pyfakefs 3.0.
Raises:
TypeError: If anything other than integers is specified in passed tuple or
number of elements in the tuple is not equal to 2.
path: (str) Path to the file.
times: 2-tuple of int or float numbers, of the form (atime, mtime)
which is used to set the access and modified times in seconds.
If None, both times are set to the current time.
ns: 2-tuple of int numbers, of the form (atime, mtime) which is
used to set the access and modified times in nanoseconds.
If None, both times are set to the current time.
New in Python 3.3. New in pyfakefs 3.3.
follow_symlinks: If `False` and entry_path points to a symlink,
the link itself is queried instead of the linked object.
New in Python 3.3. New in pyfakefs 3.0.
Raises:
TypeError: If anything other than the expected types is
specified in the passed `times` or `ns` tuple,
or if the tuple length is not equal to 2.
ValueError: If both times and ns are specified.
"""
if follow_symlinks is None:
follow_symlinks = True
elif sys.version_info < (3, 3):
raise TypeError("utime() got an unexpected keyword argument 'follow_symlinks'")
self.filesystem.UpdateTime(path, times, follow_symlinks)
if ns is not None and sys.version_info < (3, 3):
raise TypeError("utime() got an unexpected keyword argument 'ns'")

self.filesystem.UpdateTime(path, times, ns, follow_symlinks)

def chown(self, path, uid, gid, follow_symlinks=None):
"""Set ownership of a faked file.
Expand Down

0 comments on commit b157fd2

Please sign in to comment.