From 98fd06eac359a20cc1a00d68a4681e38754f7a2b Mon Sep 17 00:00:00 2001 From: atollk Date: Tue, 27 Apr 2021 13:14:22 +0200 Subject: [PATCH 1/4] Fixes issue #477. Unit tests for `FS.setinfo` now function correctly, whereas they basically did nothing before. Also, a bug in OSFS was fixed which caused timestamps to lose precision with `setinfo`. --- fs/info.py | 3 +++ fs/osfs.py | 8 ++++---- fs/test.py | 26 ++++++++++++++++---------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/fs/info.py b/fs/info.py index 03bf27cd..a2208cc5 100644 --- a/fs/info.py +++ b/fs/info.py @@ -135,6 +135,9 @@ def is_writeable(self, namespace, key): When creating an `Info` object, you can add a ``_write`` key to each raw namespace that lists which keys are writable or not. + In general, this means they are compatible with the `setinfo` + function of filesystem objects. + Arguments: namespace (str): A namespace identifier. key (str): A key within the namespace. diff --git a/fs/osfs.py b/fs/osfs.py index ac43471a..3e6292ef 100644 --- a/fs/osfs.py +++ b/fs/osfs.py @@ -665,10 +665,10 @@ def setinfo(self, path, info): if "details" in info: details = info["details"] if "accessed" in details or "modified" in details: - _accessed = typing.cast(int, details.get("accessed")) - _modified = typing.cast(int, details.get("modified", _accessed)) - accessed = int(_modified if _accessed is None else _accessed) - modified = int(_modified) + _accessed = typing.cast(float, details.get("accessed")) + _modified = typing.cast(float, details.get("modified", _accessed)) + accessed = float(_modified if _accessed is None else _accessed) + modified = float(_modified) if accessed is not None or modified is not None: with convert_os_errors("setinfo", path): os.utime(sys_path, (accessed, modified)) diff --git a/fs/test.py b/fs/test.py index e40e43cf..3e08f4ff 100644 --- a/fs/test.py +++ b/fs/test.py @@ -12,7 +12,6 @@ import io import itertools import json -import math import os import time import unittest @@ -1171,15 +1170,21 @@ def test_removetree_root(self): def test_setinfo(self): self.fs.create("birthday.txt") - now = math.floor(time.time()) + now = time.time() change_info = {"details": {"accessed": now + 60, "modified": now + 60 * 60}} self.fs.setinfo("birthday.txt", change_info) - new_info = self.fs.getinfo("birthday.txt", namespaces=["details"]).raw - if "accessed" in new_info.get("_write", []): - self.assertEqual(new_info["details"]["accessed"], now + 60) - if "modified" in new_info.get("_write", []): - self.assertEqual(new_info["details"]["modified"], now + 60 * 60) + new_info = self.fs.getinfo("birthday.txt", namespaces=["details"]) + can_write_acccess = new_info.is_writeable("details", "accessed") + can_write_modified = new_info.is_writeable("details", "modified") + if can_write_acccess: + self.assertAlmostEqual( + new_info.get("details", "accessed"), now + 60, places=4 + ) + if can_write_modified: + self.assertAlmostEqual( + new_info.get("details", "modified"), now + 60 * 60, places=4 + ) with self.assertRaises(errors.ResourceNotFound): self.fs.setinfo("nothing", {}) @@ -1188,10 +1193,11 @@ def test_settimes(self): self.fs.create("birthday.txt") self.fs.settimes("birthday.txt", accessed=datetime(2016, 7, 5)) info = self.fs.getinfo("birthday.txt", namespaces=["details"]) - writeable = info.get("details", "_write", []) - if "accessed" in writeable: + can_write_acccess = info.is_writeable("details", "accessed") + can_write_modified = info.is_writeable("details", "modified") + if can_write_acccess: self.assertEqual(info.accessed, datetime(2016, 7, 5, tzinfo=pytz.UTC)) - if "modified" in writeable: + if can_write_modified: self.assertEqual(info.modified, datetime(2016, 7, 5, tzinfo=pytz.UTC)) def test_touch(self): From 234851843b4a2f532f5696cffc1d7df04e58da63 Mon Sep 17 00:00:00 2001 From: atollk Date: Tue, 27 Apr 2021 13:15:46 +0200 Subject: [PATCH 2/4] Updated CHANGELOG.md. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c24b0e66..f9abcd53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Fixed - Fixed performance bugs in `fs.copy.copy_dir_if_newer`. Test cases were adapted to catch those bugs in the future. +- Fixed precision bug for timestamps in `fs.OSFS.setinfo`. ## [2.4.13] - 2021-03-27 From 762ee067c71b4bacf89e5056684775aaa26945fe Mon Sep 17 00:00:00 2001 From: atollk Date: Tue, 27 Apr 2021 14:11:48 +0200 Subject: [PATCH 3/4] Fixed py27 test. --- tests/test_osfs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_osfs.py b/tests/test_osfs.py index ff3af9e5..c296b4d5 100644 --- a/tests/test_osfs.py +++ b/tests/test_osfs.py @@ -111,7 +111,7 @@ def test_copy_preserve_time(self): self.assertTrue(self.fs.exists("bar/file.txt")) dst_info = self.fs.getinfo("bar/file.txt", namespaces) - self.assertEqual(dst_info.modified, src_info.modified) + self.assertAlmostEqual(dst_info.modified, src_info.modified, places=2) @unittest.skipUnless(osfs.sendfile, "sendfile not supported") @unittest.skipIf( From b1511d270d099921f0eaa45b8ce65fc787c4797f Mon Sep 17 00:00:00 2001 From: atollk Date: Tue, 27 Apr 2021 14:34:35 +0200 Subject: [PATCH 4/4] Fixed py27-scandir test ? --- tests/test_osfs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_osfs.py b/tests/test_osfs.py index c296b4d5..7fdc5590 100644 --- a/tests/test_osfs.py +++ b/tests/test_osfs.py @@ -111,7 +111,8 @@ def test_copy_preserve_time(self): self.assertTrue(self.fs.exists("bar/file.txt")) dst_info = self.fs.getinfo("bar/file.txt", namespaces) - self.assertAlmostEqual(dst_info.modified, src_info.modified, places=2) + delta = dst_info.modified - src_info.modified + self.assertAlmostEqual(delta.total_seconds(), 0, places=2) @unittest.skipUnless(osfs.sendfile, "sendfile not supported") @unittest.skipIf(