From 8d7538e7ddeab109bfefd857d9d906f8e12d41f7 Mon Sep 17 00:00:00 2001 From: James Emerton Date: Wed, 26 Oct 2022 17:05:56 -0700 Subject: [PATCH 1/7] #556 Fallback to alternate mtime depending correctly This will check the `stat` and `details` namespaces before defaulting to the current time. --- fs/compress.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/fs/compress.py b/fs/compress.py index a1b2e346..2cb181a7 100644 --- a/fs/compress.py +++ b/fs/compress.py @@ -65,17 +65,15 @@ def write_zip( # Python2 expects bytes filenames zip_name = zip_name.encode(encoding, "replace") - if info.has_namespace("stat"): - # If the file has a stat namespace, get the - # zip time directory from the stat structure - st_mtime = info.get("stat", "st_mtime", None) - _mtime = time.localtime(st_mtime) - zip_time = _mtime[0:6] # type: ZipTime - else: - # Otherwise, use the modified time from details - # namespace. - mt = info.modified or datetime.utcnow() - zip_time = (mt.year, mt.month, mt.day, mt.hour, mt.minute, mt.second) + # If the file has a stat namespace, get the + # zip time directory from the stat structure + st_mtime = info.get("stat", "st_mtime") + # Otherwise, use the modified time from details namespace. + if st_mtime is None: + st_mtime = info.get("details", "modified") + + # If st_mtime is still None this will default to current time + zip_time = time.localtime(st_mtime)[:6] # type: ZipTime # NOTE(@althonos): typeshed's `zipfile.py` on declares # ZipInfo.__init__ for Python < 3 ?! From 206cbda7a8c48241b13cfcea2ddf9daca049fd98 Mon Sep 17 00:00:00 2001 From: James Emerton Date: Wed, 26 Oct 2022 17:30:47 -0700 Subject: [PATCH 2/7] Update changelog/contributors --- CHANGELOG.md | 5 +++++ CONTRIBUTORS.md | 1 + 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 267c942b..5f0d76a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +- Fix an issue with `fs.compress.write_zip` that would cause an error + interacting with `S3FS` directory entries. + ([#557](https://github.com/PyFilesystem/pyfilesystem2/pull/557)) + Closes [#556](https://github.com/PyFilesystem/pyfilesystem2/issues/556). + ## [2.4.16] - 2022-05-02 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index d23ba105..188f4576 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -20,6 +20,7 @@ Many thanks to the following developers for contributing to this project: - [George Macon](https://github.com/gmacon) - [Giampaolo Cimino](https://github.com/gpcimino) - [@Hoboneer](https://github.com/Hoboneer) +- [James Emerton](https://github.com/james-emerton) - [Jon Hagg](https://github.com/jon-hagg) - [Joseph Atkins-Turkish](https://github.com/Spacerat) - [Joshua Tauberer](https://github.com/JoshData) From 0771b29f515d561fd88b10d43ccf2cfe8a934bd0 Mon Sep 17 00:00:00 2001 From: James Emerton Date: Mon, 5 Dec 2022 10:27:46 -0800 Subject: [PATCH 3/7] Fix CONTRIBUTORS --- CONTRIBUTORS.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b960deec..93340e1d 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -22,7 +22,6 @@ Many thanks to the following developers for contributing to this project: - [@Hoboneer](https://github.com/Hoboneer) - [James Emerton](https://github.com/james-emerton) - [Jen Hagg](https://github.com/jenhagg) -- [Jon Hagg](https://github.com/jon-hagg) - [Joseph Atkins-Turkish](https://github.com/Spacerat) - [Joshua Tauberer](https://github.com/JoshData) - [Justin Charlong](https://github.com/jcharlong) From e36dd76fbc7b08dc35585926392713f2d8c597c5 Mon Sep 17 00:00:00 2001 From: James Emerton Date: Mon, 5 Dec 2022 12:34:45 -0800 Subject: [PATCH 4/7] Add fallback mtime test --- tests/test_zipfs.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/test_zipfs.py b/tests/test_zipfs.py index 7390649c..49efdc3c 100644 --- a/tests/test_zipfs.py +++ b/tests/test_zipfs.py @@ -8,11 +8,13 @@ import tempfile import unittest import zipfile +from datetime import datetime, timedelta from fs import zipfs from fs.compress import write_zip from fs.enums import Seek from fs.errors import NoURL +from fs.memoryfs import MemoryFS from fs.opener import open_fs from fs.opener.errors import NotWriteable from fs.test import FSTestCases @@ -224,3 +226,37 @@ class TestOpener(unittest.TestCase): def test_not_writeable(self): with self.assertRaises(NotWriteable): open_fs("zip://foo.zip", writeable=True) + + +class FSWithoutMtime(MemoryFS): + '''MemoryFS subclass that doesn't return details namespace + ''' + def getinfo(self, path, namespaces): + if namespaces is not None: + namespaces = set(namespaces) - {'details'} + return super().getinfo(path, namespaces) + + +class TestZipFSMtimeFallback(unittest.TestCase): + def setUp(self): + fh, self._temp_path = tempfile.mkstemp() + os.close(fh) + + def tearDown(self): + os.remove(self._temp_path) + + def test_no_mtime(self): + '''Fallback to current time when creating an archive of an fs that + ''' + src_fs = FSWithoutMtime() + with src_fs.open('test.txt', 'w') as f: + f.write('Hello World') + + now = datetime.now() + write_zip(src_fs, self._temp_path) + + zf = zipfile.ZipFile(self._temp_path) + info = zf.getinfo('test.txt') + zf.close() + + self.assertLessEqual(now - datetime(*info.date_time), timedelta(seconds=2)) From 7c73b6af3e1bbb65cb375997dc2372d554f5c457 Mon Sep 17 00:00:00 2001 From: James Emerton Date: Mon, 5 Dec 2022 12:36:59 -0800 Subject: [PATCH 5/7] Fix docstring --- tests/test_zipfs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_zipfs.py b/tests/test_zipfs.py index 49efdc3c..ba2334c2 100644 --- a/tests/test_zipfs.py +++ b/tests/test_zipfs.py @@ -247,6 +247,7 @@ def tearDown(self): def test_no_mtime(self): '''Fallback to current time when creating an archive of an fs that + doesn't support stat or details namespaces. ''' src_fs = FSWithoutMtime() with src_fs.open('test.txt', 'w') as f: From 94f6f8f7ad8904719dd46068b196a73af4d6aadd Mon Sep 17 00:00:00 2001 From: James Emerton Date: Fri, 9 Dec 2022 12:32:55 -0800 Subject: [PATCH 6/7] Rename test class for clarity --- tests/test_zipfs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_zipfs.py b/tests/test_zipfs.py index ba2334c2..8c4afd44 100644 --- a/tests/test_zipfs.py +++ b/tests/test_zipfs.py @@ -228,7 +228,7 @@ def test_not_writeable(self): open_fs("zip://foo.zip", writeable=True) -class FSWithoutMtime(MemoryFS): +class FSWithoutDetailsNamespace(MemoryFS): '''MemoryFS subclass that doesn't return details namespace ''' def getinfo(self, path, namespaces): @@ -249,7 +249,7 @@ def test_no_mtime(self): '''Fallback to current time when creating an archive of an fs that doesn't support stat or details namespaces. ''' - src_fs = FSWithoutMtime() + src_fs = FSWithoutDetailsNamespace() with src_fs.open('test.txt', 'w') as f: f.write('Hello World') From 67a26adf215b9dca25d984a12a8785126ab048fe Mon Sep 17 00:00:00 2001 From: James Emerton Date: Fri, 9 Dec 2022 12:35:58 -0800 Subject: [PATCH 7/7] Rename variable for clarity --- tests/test_zipfs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_zipfs.py b/tests/test_zipfs.py index 8c4afd44..91911f14 100644 --- a/tests/test_zipfs.py +++ b/tests/test_zipfs.py @@ -253,11 +253,11 @@ def test_no_mtime(self): with src_fs.open('test.txt', 'w') as f: f.write('Hello World') - now = datetime.now() + expected_timestamp = datetime.now() write_zip(src_fs, self._temp_path) zf = zipfile.ZipFile(self._temp_path) info = zf.getinfo('test.txt') zf.close() - self.assertLessEqual(now - datetime(*info.date_time), timedelta(seconds=2)) + self.assertLessEqual(expected_timestamp - datetime(*info.date_time), timedelta(seconds=2))