bpo-34097: Add support for zipping files older than 1980-01-01 (GH-8270)
ZipFile can zip files older than 1980-01-01 and newer than 2107-12-31 using a new strict_timestamps parameter at the cost of setting the timestamp to the limit.
This commit is contained in:
parent
fc512e3e06
commit
a2fe1e52eb
|
@ -368,7 +368,7 @@ ZipFile Objects
|
|||
|
||||
|
||||
.. method:: ZipFile.write(filename, arcname=None, compress_type=None, \
|
||||
compresslevel=None)
|
||||
compresslevel=None, *, strict_timestamps=True)
|
||||
|
||||
Write the file named *filename* to the archive, giving it the archive name
|
||||
*arcname* (by default, this will be the same as *filename*, but without a drive
|
||||
|
@ -377,6 +377,11 @@ ZipFile Objects
|
|||
the new entry. Similarly, *compresslevel* will override the constructor if
|
||||
given.
|
||||
The archive must be open with mode ``'w'``, ``'x'`` or ``'a'``.
|
||||
The *strict_timestamps* argument, when set to ``False``, allows to
|
||||
zip files older than 1980-01-01 at the cost of setting the
|
||||
timestamp to 1980-01-01.
|
||||
Similar behavior occurs with files newer than 2107-12-31,
|
||||
the timestamp is also set to the limit.
|
||||
|
||||
.. note::
|
||||
|
||||
|
@ -400,6 +405,9 @@ ZipFile Objects
|
|||
a closed ZipFile will raise a :exc:`ValueError`. Previously,
|
||||
a :exc:`RuntimeError` was raised.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
The *strict_timestamps* keyword-only argument
|
||||
|
||||
|
||||
.. method:: ZipFile.writestr(zinfo_or_arcname, data, compress_type=None, \
|
||||
compresslevel=None)
|
||||
|
@ -540,7 +548,8 @@ information about a single member of the ZIP archive.
|
|||
There is one classmethod to make a :class:`ZipInfo` instance for a filesystem
|
||||
file:
|
||||
|
||||
.. classmethod:: ZipInfo.from_file(filename, arcname=None)
|
||||
.. classmethod:: ZipInfo.from_file(filename, arcname=None, *, \
|
||||
strict_timestamps=True)
|
||||
|
||||
Construct a :class:`ZipInfo` instance for a file on the filesystem, in
|
||||
preparation for adding it to a zip file.
|
||||
|
@ -551,11 +560,20 @@ file:
|
|||
If *arcname* is not specified, the name will be the same as *filename*, but
|
||||
with any drive letter and leading path separators removed.
|
||||
|
||||
The *strict_timestamps* argument, when set to ``False``, allows to
|
||||
zip files older than 1980-01-01 at the cost of setting the
|
||||
timestamp to 1980-01-01.
|
||||
Similar behavior occurs with files newer than 2107-12-31,
|
||||
the timestamp is also set to the limit.
|
||||
|
||||
.. versionadded:: 3.6
|
||||
|
||||
.. versionchanged:: 3.6.2
|
||||
The *filename* parameter accepts a :term:`path-like object`.
|
||||
|
||||
.. versionadded:: 3.8
|
||||
The *strict_timestamps* keyword-only argument
|
||||
|
||||
|
||||
Instances have the following methods and attributes:
|
||||
|
||||
|
|
|
@ -549,6 +549,22 @@ class StoredTestsWithSourceFile(AbstractTestsWithSourceFile,
|
|||
with zipfile.ZipFile(TESTFN2, "w") as zipfp:
|
||||
self.assertRaises(ValueError, zipfp.write, TESTFN)
|
||||
|
||||
with zipfile.ZipFile(TESTFN2, "w") as zipfp:
|
||||
zipfp.write(TESTFN, strict_timestamps=False)
|
||||
zinfo = zipfp.getinfo(TESTFN)
|
||||
self.assertEqual(zinfo.date_time, (1980, 1, 1, 0, 0, 0))
|
||||
|
||||
def test_add_file_after_2107(self):
|
||||
# Set atime and mtime to 2108-12-30
|
||||
os.utime(TESTFN, (4386268800, 4386268800))
|
||||
with zipfile.ZipFile(TESTFN2, "w") as zipfp:
|
||||
self.assertRaises(struct.error, zipfp.write, TESTFN)
|
||||
|
||||
with zipfile.ZipFile(TESTFN2, "w") as zipfp:
|
||||
zipfp.write(TESTFN, strict_timestamps=False)
|
||||
zinfo = zipfp.getinfo(TESTFN)
|
||||
self.assertEqual(zinfo.date_time, (2107, 12, 31, 23, 59, 59))
|
||||
|
||||
|
||||
@requires_zlib
|
||||
class DeflateTestsWithSourceFile(AbstractTestsWithSourceFile,
|
||||
|
|
|
@ -469,7 +469,7 @@ class ZipInfo (object):
|
|||
extra = extra[ln+4:]
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, filename, arcname=None):
|
||||
def from_file(cls, filename, arcname=None, *, strict_timestamps=True):
|
||||
"""Construct an appropriate ZipInfo for a file on the filesystem.
|
||||
|
||||
filename should be the path to a file or directory on the filesystem.
|
||||
|
@ -484,6 +484,10 @@ class ZipInfo (object):
|
|||
isdir = stat.S_ISDIR(st.st_mode)
|
||||
mtime = time.localtime(st.st_mtime)
|
||||
date_time = mtime[0:6]
|
||||
if not strict_timestamps and date_time[0] < 1980:
|
||||
date_time = (1980, 1, 1, 0, 0, 0)
|
||||
elif not strict_timestamps and date_time[0] > 2107:
|
||||
date_time = (2107, 12, 31, 23, 59, 59)
|
||||
# Create ZipInfo instance to store file information
|
||||
if arcname is None:
|
||||
arcname = filename
|
||||
|
@ -1673,7 +1677,8 @@ class ZipFile:
|
|||
" would require ZIP64 extensions")
|
||||
|
||||
def write(self, filename, arcname=None,
|
||||
compress_type=None, compresslevel=None):
|
||||
compress_type=None, compresslevel=None, *,
|
||||
strict_timestamps=True):
|
||||
"""Put the bytes from filename into the archive under the name
|
||||
arcname."""
|
||||
if not self.fp:
|
||||
|
@ -1684,7 +1689,8 @@ class ZipFile:
|
|||
"Can't write to ZIP archive while an open writing handle exists"
|
||||
)
|
||||
|
||||
zinfo = ZipInfo.from_file(filename, arcname)
|
||||
zinfo = ZipInfo.from_file(filename, arcname,
|
||||
strict_timestamps=strict_timestamps)
|
||||
|
||||
if zinfo.is_dir():
|
||||
zinfo.compress_size = 0
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
ZipFile can zip files older than 1980-01-01 and newer than 2107-12-31 using
|
||||
a new ``strict_timestamps`` parameter at the cost of setting the timestamp
|
||||
to the limit.
|
Loading…
Reference in New Issue