GeezerVison is showing github.com — ZipFile.open() should not ignore compression, new file should default to today rather than 1980 · Issue #121402 · python/cpython
ZipFile.open() should not ignore compression, new file should default to today rather than 1980 #121402DescriptionBug report
Bug description:
When streaming, writing to a zip file, it internally creates a ZipInfo object without explicitly setting date_time, causing a timestamp of 1980 to be used. I believe in almost all cases, using the current timestamp makes more sense, I rarely zip files from 1980. To work around that issue, I create the ZipInfo object myself and pass it to the open() method, which causes another bug where the specified compression is ignored and the file is simply stored.
After some minor investigation, I've found issue #113971 which at least hinted towards what I now use as workaround but otherwise didn't fix this:
Today people pass ZipInfo instances into APIs that accept them such as ZipFile.writestr() in order to control how individual items are put into a zip archive as such ...
zip_info.compress_type
So you'd create a new ZipFile specifying a compression but after open(), you're just storing uncompressed:
zip_info = zipfile.ZipInfo("file")
# zip_info.compress_type, ._compresslevel are wrong
zip_file.open(zip_info, mode="w", force_zip64=True)
In other words, I expect that:
a) when using open("file"...), it should use the current time as mtime by default
b) when creating a ZipInfo object to specify another date or another setting for the new file, it should inherit the previously configured compression
Both assumptions are wrong, which is what I'm suggesting to fix.
Here's a reproducer and I'll add a PR as suggested fix...
https://gist.github.com/c0xc/b54c005b296cdf6378ce65dd4aff3fe7
CPython versions tested on:
3.11, CPython main branch
Operating systems tested on:
Linux
Linked PRs
Activityserhiy-storchaka commented on Jul 10, 2024 Why do you use ZipInfo instead of a string if you do not set its attributes? c0xc commented on Jul 10, 2024 serhiy-storchaka commented on Jul 10, 2024 zip uses the current timestamp when add a file from a streamed input. So I think that it is reasonable to do the same in zipfile.
As for using the archive default compression by default, this is more complex question. c0xc commented on Jul 10, 2024
zip uses the current timestamp when add a file from a streamed input. So I think that it is reasonable to do the same in zipfile.
Exactly and that's what my suggested change does, see:
https://github.com/python/cpython/pull/121405/files#diff-7629293618f2b3cf8ae7daf98526226fa12f047d71645a05497e6687aae10c76R413
As for using the archive default compression by default, this is more complex question.
Please note that the second bug I'm trying to fix is not merely using a default compression (I don't want to change the default behavior) but what happens is that the programmer explicitly specifies the compression (line 8) which is then ignored when adding files (lines 20 and 26). obfusk commented on Aug 28, 2024 Reacted by David Buchanan thatch commented on May 20, 2025 I'm a fan of the logic in writestr, would using that when a str filename is passed would satisfy this request I think.
|
if isinstance(zinfo_or_arcname, ZipInfo): |
|
zinfo = zinfo_or_arcname |
|
else: |
|
zinfo = ZipInfo(zinfo_or_arcname)._for_archive(self) |
- Defaults to current date unless SOURCE_DATE_EPOCH is set
- Inherits compression setting from archive
danny0838 commented on Jun 23, 2025 @thatch
I'm a fan of the logic in writestr, would using that when a str filename is passed would satisfy this request I think.
+1 to this approach. This will make the patch as simple as:
--- a/Lib/zipfile/__init__.py
+++ b/Lib/zipfile/__init__.py
@@ -1710,9 +1710,7 @@ def open(self, name, mode="r", pwd=None, *, force_zip64=False):
# 'name' is already an info object
zinfo = name
elif mode == 'w':
- zinfo = ZipInfo(name)
- zinfo.compress_type = self.compression
- zinfo.compress_level = self.compresslevel
+ zinfo = ZipInfo(name)._for_archive(self)
else:
# Get info object for name
zinfo = self.getinfo(name)
However the problem is that it breaks backward compatibility and I don't know if it worth. If it's not to be implemented due to backward compatibility, the behavior differences when passing a name should at least be documented.
MetadataMetadataStatusShow more project fields
DevelopmentNo branches or pull requestsIssue actions
|