diff --git a/CHANGES.md b/CHANGES.md index 4bee865e..d0c0be6d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,13 +5,15 @@ The release versions are PyPi releases. #### New Features * Added fake `os.path.samefile` implementation ([#193](../../issues/193)) + * Added support for `ns` argument in `os.utime()` (Python >= 3.3) ([#192](../../issues/192)). + * Added nanosecond time members in `os.stat_result` (Python >= 3.3) ([#196](../../issues/196)). #### Infrastructure #### Fixes -## Version 3.2 +## [Version 3.2](https://pypi.python.org/pypi/pyfakefs/3.2) #### New Features * The `errors` argument is supported for `io.open()` and `os.open()` diff --git a/fake_filesystem_test.py b/fake_filesystem_test.py index c9362b6d..e82cd90f 100755 --- a/fake_filesystem_test.py +++ b/fake_filesystem_test.py @@ -32,13 +32,21 @@ from pyfakefs import fake_filesystem -def _GetDummyTime(start_time, increment): - def _DummyTime(): - _DummyTime._curr_time += increment - return _DummyTime._curr_time +class _DummyTime(object): + """Mock replacement for time.time. Increases returned time on access.""" - _DummyTime._curr_time = start_time - increment # pylint: disable-msg=W0612 - return _DummyTime + def __init__(self, curr_time, increment): + self.curr_time = curr_time + self.increment = increment + self.started = False + + def start(self): + self.started = True + + def __call__(self, *args, **kwargs): + if self.started: + self.curr_time += self.increment + return self.curr_time class TestCase(unittest.TestCase): @@ -67,7 +75,7 @@ def assertRaisesOSError(self, subtype, expression, *args, **kwargs): class FakeDirectoryUnitTest(TestCase): def setUp(self): self.orig_time = time.time - time.time = _GetDummyTime(10, 1) + time.time = _DummyTime(10, 1) self.fake_file = fake_filesystem.FakeFile('foobar', contents='dummy_file') self.fake_dir = fake_filesystem.FakeDirectory('somedir') @@ -676,7 +684,7 @@ def setUp(self): self.rwx = self.os.R_OK | self.os.W_OK | self.os.X_OK self.rw = self.os.R_OK | self.os.W_OK self.orig_time = time.time - time.time = _GetDummyTime(200, 20) + time.time = _DummyTime(200, 20) def tearDown(self): time.time = self.orig_time @@ -1717,6 +1725,8 @@ def testChmodStCtime(self): file_path = 'some_file' self.filesystem.CreateFile(file_path) self.assertTrue(self.filesystem.Exists(file_path)) + time.time.start() + st = self.os.stat(file_path) self.assertEqual(200, st.st_ctime) # tests @@ -1728,6 +1738,8 @@ def testUtimeSetsCurrentTimeIfArgsIsNone(self): # set up path = '/some_file' self._CreateTestFile(path) + time.time.start() + st = self.os.stat(path) # 200 is the current time established in setUp(). self.assertEqual(200, st.st_atime) @@ -1736,39 +1748,51 @@ def testUtimeSetsCurrentTimeIfArgsIsNone(self): self.os.utime(path, None) st = self.os.stat(path) self.assertEqual(220, st.st_atime) - self.assertEqual(240, st.st_mtime) + self.assertEqual(220, st.st_mtime) def testUtimeSetsCurrentTimeIfArgsIsNoneWithFloats(self): # set up # we set os.stat_float_times() to False, so atime/ctime/mtime # are converted as ints (seconds since epoch) - time.time = _GetDummyTime(200.9123, 20) + time.time = _DummyTime(200.9123, 20) path = '/some_file' fake_filesystem.FakeOsModule.stat_float_times(False) self._CreateTestFile(path) + time.time.start() + st = self.os.stat(path) # 200 is the current time established above (if converted to int). self.assertEqual(200, st.st_atime) self.assertTrue(isinstance(st.st_atime, int)) self.assertEqual(200, st.st_mtime) self.assertTrue(isinstance(st.st_mtime, int)) + + if sys.version_info >= (3, 3): + self.assertEqual(200912300000, st.st_atime_ns) + self.assertEqual(200912300000, st.st_mtime_ns) + + self.assertEqual(200, st.st_mtime) # actual tests self.os.utime(path, None) st = self.os.stat(path) self.assertEqual(220, st.st_atime) self.assertTrue(isinstance(st.st_atime, int)) - self.assertEqual(240, st.st_mtime) + self.assertEqual(220, st.st_mtime) self.assertTrue(isinstance(st.st_mtime, int)) + if sys.version_info >= (3, 3): + self.assertEqual(220912300000, st.st_atime_ns) + self.assertEqual(220912300000, st.st_mtime_ns) def testUtimeSetsCurrentTimeIfArgsIsNoneWithFloatsNSec(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.os = fake_filesystem.FakeOsModule(self.filesystem) - self.assertTrue(not self.os.stat_float_times()) + fake_filesystem.FakeOsModule.stat_float_times(False) - time.time = _GetDummyTime(200.9123, 20) + time.time = _DummyTime(200.9123, 20) path = '/some_file' test_file = self._CreateTestFile(path) + time.time.start() st = self.os.stat(path) self.assertEqual(200, st.st_ctime) self.assertEqual(200, test_file.st_ctime) @@ -1796,7 +1820,7 @@ def testUtimeSetsCurrentTimeIfArgsIsNoneWithFloatsNSec(self): self.os.utime(path, None) st = self.os.stat(path) self.assertEqual(220.9123, st.st_atime) - self.assertEqual(240.9123, st.st_mtime) + self.assertEqual(220.9123, st.st_mtime) def testUtimeSetsSpecifiedTime(self): # set up @@ -1865,6 +1889,8 @@ def testUtimeSetsSpecifiedTimeInNs(self): # set up path = '/some_file' self._CreateTestFile(path) + time.time.start() + st = self.os.stat(path) # actual tests self.os.utime(path, ns=(200000000, 400000000)) @@ -2358,6 +2384,12 @@ def testStat(self): self.assertEqual(self.filesystem.ResolveObject('/linked/plugh/dir').st_mtime, self.dir_entries[2].stat().st_mtime) + def testIndexAccessToStatTimesReturnsInt(self): + self.assertEqual(self.os.stat('/xyzzy/plugh/dir')[stat.ST_CTIME], + int(self.dir_entries[0].stat().st_ctime)) + self.assertEqual(self.os.stat('/linked/plugh/dir')[stat.ST_MTIME], + int(self.dir_entries[2].stat().st_mtime)) + def testStatInoDevPosix(self): self.filesystem.is_windows_fs = False file_obj = self.filesystem.ResolveObject('/linked/plugh/file') @@ -2522,7 +2554,7 @@ def testCreateTopLevelDirectory(self): class FakePathModuleTest(TestCase): def setUp(self): self.orig_time = time.time - time.time = _GetDummyTime(10, 1) + time.time = _DummyTime(10, 1) self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!') self.os = fake_filesystem.FakeOsModule(self.filesystem) self.path = self.os.path @@ -2737,9 +2769,8 @@ def testIsfile(self): def testGetMtime(self): test_file = self.filesystem.CreateFile('foo!bar1.txt') - # The root directory ('', effectively '!') is created at time 10, - # the parent directory ('foo') at time 11, and the file at time 12. - self.assertEqual(12, test_file.st_mtime) + time.time.start() + self.assertEqual(10, test_file.st_mtime) test_file.SetMTime(24) self.assertEqual(24, self.path.getmtime('foo!bar1.txt')) @@ -2908,7 +2939,7 @@ def setUp(self): self.open = self.file self.os = fake_filesystem.FakeOsModule(self.filesystem) self.orig_time = time.time - time.time = _GetDummyTime(100, 10) + time.time = _DummyTime(100, 10) def tearDown(self): time.time = self.orig_time @@ -3287,41 +3318,52 @@ def testOpenStCtime(self): self.assertFalse(self.filesystem.Exists(file_path)) # tests fake_file = self.file(file_path, 'w') + time.time.start() st = self.os.stat(file_path) - self.assertEqual(100, st.st_ctime, st.st_mtime) + self.assertEqual(100, st.st_ctime) + self.assertEqual(100, st.st_mtime) fake_file.close() st = self.os.stat(file_path) - self.assertEqual(110, st.st_ctime, st.st_mtime) + self.assertEqual(110, st.st_ctime) + self.assertEqual(110, st.st_mtime) fake_file = self.file(file_path, 'w') st = self.os.stat(file_path) # truncating the file cause an additional stat update - self.assertEqual(120, st.st_ctime, st.st_mtime) + self.assertEqual(120, st.st_ctime) + self.assertEqual(120, st.st_mtime) fake_file.close() st = self.os.stat(file_path) - self.assertEqual(130, st.st_ctime, st.st_mtime) + self.assertEqual(130, st.st_ctime) + self.assertEqual(130, st.st_mtime) fake_file = self.file(file_path, 'w+') st = self.os.stat(file_path) - self.assertEqual(140, st.st_ctime, st.st_mtime) + self.assertEqual(140, st.st_ctime) + self.assertEqual(140, st.st_mtime) fake_file.close() st = self.os.stat(file_path) - self.assertEqual(150, st.st_ctime, st.st_mtime) + self.assertEqual(150, st.st_ctime) + self.assertEqual(150, st.st_mtime) fake_file = self.file(file_path, 'a') st = self.os.stat(file_path) # not updating m_time or c_time here, since no truncating. - self.assertEqual(150, st.st_ctime, st.st_mtime) + self.assertEqual(150, st.st_ctime) + self.assertEqual(150, st.st_mtime) fake_file.close() st = self.os.stat(file_path) - self.assertEqual(160, st.st_ctime, st.st_mtime) + self.assertEqual(160, st.st_ctime) + self.assertEqual(160, st.st_mtime) fake_file = self.file(file_path, 'r') st = self.os.stat(file_path) - self.assertEqual(160, st.st_ctime, st.st_mtime) + self.assertEqual(160, st.st_ctime) + self.assertEqual(160, st.st_mtime) fake_file.close() st = self.os.stat(file_path) - self.assertEqual(160, st.st_ctime, st.st_mtime) + self.assertEqual(160, st.st_ctime) + self.assertEqual(160, st.st_mtime) def _CreateWithPermission(self, file_path, perm_bits): self.filesystem.CreateFile(file_path) @@ -4600,9 +4642,9 @@ def checkFakeFileStat(self, fake_file, real_file_path): real_stat = os.stat(real_file_path) self.assertIsNone(fake_file._byte_contents) self.assertEqual(fake_file.st_size, real_stat.st_size) - self.assertEqual(fake_file.st_ctime, real_stat.st_ctime) - self.assertEqual(fake_file.st_atime, real_stat.st_atime) - self.assertEqual(fake_file.st_mtime, real_stat.st_mtime) + self.assertAlmostEqual(fake_file.st_ctime, real_stat.st_ctime, places=5) + self.assertAlmostEqual(fake_file.st_atime, real_stat.st_atime, places=5) + self.assertAlmostEqual(fake_file.st_mtime, real_stat.st_mtime, places=5) self.assertEqual(fake_file.st_uid, real_stat.st_uid) self.assertEqual(fake_file.st_gid, real_stat.st_gid) diff --git a/fake_filesystem_unittest_test.py b/fake_filesystem_unittest_test.py index 59d1eada..7df4d809 100755 --- a/fake_filesystem_unittest_test.py +++ b/fake_filesystem_unittest_test.py @@ -216,10 +216,10 @@ def testCopyRealFile(self): self.assertEqual(oct(fake_file.st_mode), oct(self.real_stat.st_mode)) self.assertEqual(fake_file.st_size, self.real_stat.st_size) - self.assertEqual(fake_file.st_ctime, self.real_stat.st_ctime) - self.assertGreaterEqual(fake_file.st_atime, self.real_stat.st_atime) + self.assertAlmostEqual(fake_file.st_ctime, self.real_stat.st_ctime, places=5) + self.assertAlmostEqual(fake_file.st_atime, self.real_stat.st_atime, places=5) self.assertLess(fake_file.st_atime, self.real_stat.st_atime + 10) - self.assertEqual(fake_file.st_mtime, self.real_stat.st_mtime) + self.assertAlmostEqual(fake_file.st_mtime, self.real_stat.st_mtime, places=5) self.assertEqual(fake_file.st_uid, self.real_stat.st_uid) self.assertEqual(fake_file.st_gid, self.real_stat.st_gid) diff --git a/fake_pathlib_test.py b/fake_pathlib_test.py index 860fb933..329d7e37 100644 --- a/fake_pathlib_test.py +++ b/fake_pathlib_test.py @@ -235,21 +235,22 @@ def test_stat(self): file_object = self.filesystem.ResolveObject('/home/jane/test.py') stat_result = self.path('/test.py').stat() - self.assertFalse(stat_result[stat.ST_MODE] & stat.S_IFDIR) - self.assertTrue(stat_result[stat.ST_MODE] & stat.S_IFREG) - self.assertEqual(stat_result[stat.ST_INO], file_object.st_ino) - self.assertEqual(stat_result[stat.ST_SIZE], 100) - self.assertEqual(stat_result[stat.ST_MTIME], file_object.st_mtime) + self.assertFalse(stat_result.st_mode & stat.S_IFDIR) + self.assertTrue(stat_result.st_mode & stat.S_IFREG) + self.assertEqual(stat_result.st_ino, file_object.st_ino) + self.assertEqual(stat_result.st_size, 100) + self.assertEqual(stat_result.st_mtime, file_object.st_mtime) + self.assertEqual(stat_result[stat.ST_MTIME], int(file_object.st_mtime)) def test_lstat(self): link_object = self.filesystem.LResolveObject('/test.py') stat_result = self.path('/test.py').lstat() - self.assertTrue(stat_result[stat.ST_MODE] & stat.S_IFREG) - self.assertTrue(stat_result[stat.ST_MODE] & stat.S_IFLNK) - self.assertEqual(stat_result[stat.ST_INO], link_object.st_ino) - self.assertEqual(stat_result[stat.ST_SIZE], len('/home/jane/test.py')) - self.assertEqual(stat_result[stat.ST_MTIME], link_object.st_mtime) + self.assertTrue(stat_result.st_mode & stat.S_IFREG) + self.assertTrue(stat_result.st_mode & stat.S_IFLNK) + self.assertEqual(stat_result.st_ino, link_object.st_ino) + self.assertEqual(stat_result.st_size, len('/home/jane/test.py')) + self.assertEqual(stat_result.st_mtime, link_object.st_mtime) def test_chmod(self): file_object = self.filesystem.ResolveObject('/home/jane/test.py') diff --git a/pyfakefs/fake_filesystem.py b/pyfakefs/fake_filesystem.py index 1c077002..2e15b1c4 100644 --- a/pyfakefs/fake_filesystem.py +++ b/pyfakefs/fake_filesystem.py @@ -102,6 +102,7 @@ from collections import namedtuple import stat +from copy import copy if sys.version_info < (3, 0): import cStringIO # pylint: disable=import-error @@ -160,6 +161,160 @@ def CopyModule(old): return new +class _FakeStatResult(object): + """Mimics os.stat_result for use as return type of `stat()` and similar. + This is needed as `os.stat_result` has no possibility to set + nanosecond times directly. + """ + long_type = long if sys.version_info < (3,) else int + + def __init__(self, initial_time=None): + self.use_float = FakeOsModule.stat_float_times + self.st_mode = None + self.st_ino = None + self.st_dev = None + self.st_nlink = 0 + self.st_uid = None + self.st_gid = None + self.st_size = None + if initial_time is not None: + self._st_atime_ns = self.long_type(initial_time * 1e9) + else: + self._st_atime_ns = None + self._st_mtime_ns = self._st_atime_ns + self._st_ctime_ns = self._st_atime_ns + + def __eq__(self, other): + return ( + self._st_atime_ns == other._st_atime_ns and + self._st_ctime_ns == other._st_ctime_ns and + self._st_mtime_ns == other._st_mtime_ns and + self.st_size == other.st_size and + self.st_gid == other.st_gid and + self.st_uid == other.st_uid and + self.st_nlink == other.st_nlink and + self.st_dev == other.st_dev and + self.st_ino == other.st_ino and + self.st_mode == other.st_mode + ) + + def copy(self): + """Return a copy where the float usage is hard-coded to mimic the behavior + of the real os.stat_result. + """ + use_float = self.use_float() + stat_result = copy(self) + stat_result.use_float = lambda: use_float + return stat_result + + def set_from_stat_result(self, stat_result): + """Set values from a real os.stat_result. + Note: values that are controlled by the fake filesystem are not set. + This includes st_ino, st_dev and st_nlink. + """ + self.st_mode = stat_result.st_mode + self.st_uid = stat_result.st_uid + self.st_gid = stat_result.st_gid + self.st_size = stat_result.st_size + if sys.version_info < (3, 3): + self._st_atime_ns = self.long_type(stat_result.st_atime * 1e9) + self._st_mtime_ns = self.long_type(stat_result.st_mtime * 1e9) + self._st_ctime_ns = self.long_type(stat_result.st_ctime * 1e9) + else: + self._st_atime_ns = stat_result.st_atime_ns + self._st_mtime_ns = stat_result.st_mtime_ns + self._st_ctime_ns = stat_result.st_ctime_ns + + @property + def st_ctime(self): + """Return the creation time in seconds.""" + ctime = self._st_ctime_ns / 1e9 + return ctime if self.use_float() else int(ctime) + + @property + def st_atime(self): + """Return the access time in seconds.""" + atime = self._st_atime_ns / 1e9 + return atime if self.use_float() else int(atime) + + @property + def st_mtime(self): + """Return the modification time in seconds.""" + mtime = self._st_mtime_ns / 1e9 + return mtime if self.use_float() else int(mtime) + + @st_ctime.setter + def st_ctime(self, val): + """Set the creation time in seconds.""" + self._st_ctime_ns = self.long_type(val * 1e9) + + @st_atime.setter + def st_atime(self, val): + """Set the access time in seconds.""" + self._st_atime_ns = self.long_type(val * 1e9) + + @st_mtime.setter + def st_mtime(self, val): + """Set the modification time in seconds.""" + self._st_mtime_ns = self.long_type(val * 1e9) + + def __getitem__(self, item): + """Implement item access to mimic `os.stat_result` behavior.""" + if item == stat.ST_MODE: + return self.st_mode + if item == stat.ST_INO: + return self.st_ino + if item == stat.ST_DEV: + return self.st_dev + if item == stat.ST_NLINK: + return self.st_nlink + if item == stat.ST_UID: + return self.st_uid + if item == stat.ST_GID: + return self.st_gid + if item == stat.ST_SIZE: + return self.st_size + if item == stat.ST_ATIME: + # item access always returns int for backward compatibility + return int(self.st_atime) + if item == stat.ST_MTIME: + return int(self.st_mtime) + if item == stat.ST_CTIME: + return int(self.st_ctime) + + if sys.version_info >= (3, 3): + + @property + def st_atime_ns(self): + """Return the access time in nanoseconds.""" + return self._st_atime_ns + + @property + def st_mtime_ns(self): + """Return the modification time in nanoseconds.""" + return self._st_mtime_ns + + @property + def st_ctime_ns(self): + """Return the creation time in nanoseconds.""" + return self._st_ctime_ns + + @st_atime_ns.setter + def st_atime_ns(self, val): + """Set the access time in nanoseconds.""" + self._st_atime_ns = val + + @st_mtime_ns.setter + def st_mtime_ns(self, val): + """Set the modification time of the fake file in nanoseconds.""" + self._st_mtime_ns = val + + @st_ctime_ns.setter + def st_ctime_ns(self, val): + """Set the creation time of the fake file in nanoseconds.""" + self._st_ctime_ns = val + + class FakeFile(object): """Provides the appearance of a real file. @@ -196,23 +351,15 @@ def __init__(self, name, st_mode=stat.S_IFREG | PERM_DEF_FILE, New in pyfakefs 3.2. """ self.name = name - self.st_mode = st_mode + self.stat_result = _FakeStatResult(time.time()) + self.stat_result.st_mode = st_mode self.encoding = encoding self.errors = errors or 'strict' self._byte_contents = self._encode_contents(contents) - self.st_size = len(self._byte_contents) if self._byte_contents is not None else 0 + self.stat_result.st_size = ( + len(self._byte_contents) if self._byte_contents is not None else 0) self.filesystem = filesystem self.epoch = 0 - self._st_ctime = time.time() # times are accessed through properties - self._st_atime = self._st_ctime - self._st_mtime = self._st_ctime - self.st_nlink = 0 - self.st_ino = None - self.st_dev = None - - # Non faked features, write setter methods for faking them - self.st_uid = None - self.st_gid = None @property def byte_contents(self): @@ -227,39 +374,6 @@ def contents(self): errors=self.errors) return self.byte_contents - @property - def st_ctime(self): - """Return the creation time of the fake file.""" - return (self._st_ctime if FakeOsModule.stat_float_times() - else int(self._st_ctime)) - - @property - def st_atime(self): - """Return the access time of the fake file.""" - return (self._st_atime if FakeOsModule.stat_float_times() - else int(self._st_atime)) - - @property - def st_mtime(self): - """Return the modification time of the fake file.""" - return (self._st_mtime if FakeOsModule.stat_float_times() - else int(self._st_mtime)) - - @st_ctime.setter - def st_ctime(self, val): - """Set the creation time of the fake file.""" - self._st_ctime = val - - @st_atime.setter - def st_atime(self, val): - """Set the access time of the fake file.""" - self._st_atime = val - - @st_mtime.setter - def st_mtime(self, val): - """Set the modification time of the fake file.""" - self._st_mtime = val - def SetLargeFileSize(self, st_size): """Sets the self.st_size attribute and replaces self.content with None. @@ -338,8 +452,9 @@ def SetContents(self, contents, encoding=None): """ self.encoding = encoding self._set_initial_contents(contents) - self.st_ctime = time.time() - self.st_mtime = self._st_ctime + current_time = time.time() + self.st_ctime = current_time + self.st_mtime = current_time def GetSize(self): """Returns the size in bytes of the file contents. @@ -404,6 +519,18 @@ def SetCTime(self, st_ctime): """ self.st_ctime = st_ctime + def __getattr__(self, item): + """Forward some properties to stat_result.""" + return getattr(self.stat_result, item) + + def __setattr__(self, key, value): + """Forward some properties to stat_result.""" + if key in ('st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid', + 'st_size', 'st_atime', 'st_mtime', 'st_ctime', + 'st_atime_ns', 'st_mtime_ns', 'st_ctime_ns'): + return setattr(self.stat_result, key, value) + return super(FakeFile, self).__setattr__(key, value) + def __str__(self): return '%s(%o)' % (self.name, self.st_mode) @@ -439,16 +566,11 @@ def __init__(self, file_path, filesystem, read_only=True): """ real_stat = os.stat(file_path) # for read-only mode, remove the write/executable permission bits - mode = real_stat.st_mode & 0o777444 if read_only else real_stat.st_mode super(FakeFileFromRealFile, self).__init__(name=os.path.basename(file_path), - st_mode=mode, filesystem=filesystem) - self.st_ctime = real_stat.st_ctime - self.st_atime = real_stat.st_atime - self.st_mtime = real_stat.st_mtime - self.st_gid = real_stat.st_gid - self.st_uid = real_stat.st_uid - self.st_size = real_stat.st_size + self.stat_result.set_from_stat_result(real_stat) + if read_only: + self.st_mode &= 0o777444 self.file_path = file_path self.contents_read = False @@ -833,19 +955,15 @@ def GetStat(self, entry_path, follow_symlinks=True): instead of the linked object. Returns: - the os.stat_result object corresponding to entry_path. + the FakeStatResult object corresponding to entry_path. Raises: OSError: if the filesystem object doesn't exist. """ # stat should return the tuple representing return value of os.stat try: - stats = self.ResolveObject(entry_path, follow_symlinks) - st_obj = os.stat_result((stats.st_mode, stats.st_ino, stats.st_dev, - stats.st_nlink, stats.st_uid, stats.st_gid, - stats.st_size, stats.st_atime, - stats.st_mtime, stats.st_ctime)) - return st_obj + file_object = self.ResolveObject(entry_path, follow_symlinks) + return file_object.stat_result.copy() except IOError as io_error: raise OSError(io_error.errno, io_error.strerror, entry_path) @@ -921,11 +1039,12 @@ def UpdateTime(self, path, times=None, ns=None, follow_symlinks=True): 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 + file_object.st_atime_ns = ns[0] + file_object.st_mtime_ns = ns[1] else: - file_object.st_atime = time.time() - file_object.st_mtime = time.time() + current_time = time.time() + file_object.st_atime = current_time + file_object.st_mtime = current_time def SetIno(self, path, st_ino): """Set the self.st_ino attribute of file at 'path'. @@ -2356,32 +2475,24 @@ def stat(self, follow_symlinks=True): """ if follow_symlinks: if self._statresult_symlink is None: - stats = self._filesystem.ResolveObject(self.path) + file_object = self._filesystem.ResolveObject(self.path) if self._filesystem.is_windows_fs: # under Windows, some properties are 0 # probably due to performance reasons - stats.st_ino = 0 - stats.st_dev = 0 - stats.st_nlink = 0 - self._statresult_symlink = os.stat_result( - (stats.st_mode, stats.st_ino, stats.st_dev, - stats.st_nlink, stats.st_uid, stats.st_gid, - stats.st_size, stats.st_atime, - stats.st_mtime, stats.st_ctime)) + file_object.st_ino = 0 + file_object.st_dev = 0 + file_object.st_nlink = 0 + self._statresult_symlink = file_object.stat_result.copy() return self._statresult_symlink if self._statresult is None: - stats = self._filesystem.LResolveObject(self.path) - self._inode = stats.st_ino + file_object = self._filesystem.LResolveObject(self.path) + self._inode = file_object.st_ino if self._filesystem.is_windows_fs: - stats.st_ino = 0 - stats.st_dev = 0 - stats.st_nlink = 0 - self._statresult = os.stat_result( - (stats.st_mode, stats.st_ino, stats.st_dev, - stats.st_nlink, stats.st_uid, stats.st_gid, - stats.st_size, stats.st_atime, - stats.st_mtime, stats.st_ctime)) + file_object.st_ino = 0 + file_object.st_dev = 0 + file_object.st_nlink = 0 + self._statresult = file_object.stat_result.copy() return self._statresult class ScanDirIter: @@ -3005,18 +3116,14 @@ def fstat(self, file_des): file_des: file descriptor of filesystem object to retrieve. Returns: - the os.stat_result object corresponding to entry_path. + the FakeStatResult object corresponding to entry_path. Raises: OSError: if the filesystem object doesn't exist. """ # stat should return the tuple representing return value of os.stat - stats = self.filesystem.GetOpenFile(file_des).GetObject() - st_obj = os.stat_result((stats.st_mode, stats.st_ino, stats.st_dev, - stats.st_nlink, stats.st_uid, stats.st_gid, - stats.st_size, stats.st_atime, - stats.st_mtime, stats.st_ctime)) - return st_obj + file_object = self.filesystem.GetOpenFile(file_des).GetObject() + return file_object.stat_result.copy() def umask(self, new_mask): """Change the current umask. @@ -3192,7 +3299,7 @@ def stat(self, entry_path, follow_symlinks=None): instead of the linked object. New in Python 3.3. New in pyfakefs 3.0. Returns: - the os.stat_result object corresponding to entry_path. + the FakeStatResult object corresponding to entry_path. Raises: OSError: if the filesystem object doesn't exist. @@ -3210,7 +3317,7 @@ def lstat(self, entry_path): entry_path: path to filesystem object to retrieve. Returns: - the os.stat_result object corresponding to entry_path. + the FakeStatResult object corresponding to entry_path. Raises: OSError: if the filesystem object doesn't exist.