bpo-34898: Add mtime parameter to gzip.compress(). (GH-9704)
authorguoci <zguoci@gmail.com>
Wed, 7 Nov 2018 09:50:23 +0000 (04:50 -0500)
committerSerhiy Storchaka <storchaka@gmail.com>
Wed, 7 Nov 2018 09:50:23 +0000 (11:50 +0200)
Without setting mtime, time.time() will be used as the timestamp which will
end up in the compressed data and each invocation of the compress() function
will vary over time.

Doc/library/gzip.rst
Doc/whatsnew/3.8.rst
Lib/gzip.py
Lib/test/test_gzip.py
Misc/ACKS
Misc/NEWS.d/next/Library/2018-10-04-17-23-43.bpo-34898.Wo2PoJ.rst [new file with mode: 0644]

index a57307b0e49921b304472862f3572bbd9d8e7be8..8850a33f4abb908a0b5e6bba39ab80dc71a6ed8d 100644 (file)
@@ -157,13 +157,15 @@ The module defines the following items:
       Accepts a :term:`path-like object`.
 
 
-.. function:: compress(data, compresslevel=9)
+.. function:: compress(data, compresslevel=9, *, mtime=None)
 
    Compress the *data*, returning a :class:`bytes` object containing
-   the compressed data.  *compresslevel* has the same meaning as in
+   the compressed data.  *compresslevel* and *mtime* have the same meaning as in
    the :class:`GzipFile` constructor above.
 
    .. versionadded:: 3.2
+   .. versionchanged:: 3.8
+      Added the *mtime* parameter for reproducible output.
 
 .. function:: decompress(data)
 
index 2d3a116df9d3bfb9d74bb25cf85f2393e973d64d..51aee1bed8355501500017915b2d7e490bfc1b1e 100644 (file)
@@ -131,6 +131,13 @@ asyncio
 On Windows, the default event loop is now :class:`~asyncio.ProactorEventLoop`.
 
 
+gzip
+----
+
+Added the *mtime* parameter to :func:`gzip.compress` for reproducible output.
+(Contributed by Guo Ci Teo in :issue:`34898`.)
+
+
 idlelib and IDLE
 ----------------
 
index 151ff1405b164c142b16015ffe6c8ee342a0ca39..948fec293e23d95a45b62e5303ef6fd023e2a707 100644 (file)
@@ -520,12 +520,12 @@ class _GzipReader(_compression.DecompressReader):
         super()._rewind()
         self._new_member = True
 
-def compress(data, compresslevel=_COMPRESS_LEVEL_BEST):
+def compress(data, compresslevel=_COMPRESS_LEVEL_BEST, *, mtime=None):
     """Compress data in one shot and return the compressed string.
     Optional argument is the compression level, in range of 0-9.
     """
     buf = io.BytesIO()
-    with GzipFile(fileobj=buf, mode='wb', compresslevel=compresslevel) as f:
+    with GzipFile(fileobj=buf, mode='wb', compresslevel=compresslevel, mtime=mtime) as f:
         f.write(data)
     return buf.getvalue()
 
index 1e8b41f07b3ecfd9f618bea4306f4976bb0e719b..2c8f854c6436708fc2c6e16dae736bcf9e2d2f5b 100644 (file)
@@ -499,6 +499,17 @@ class TestGzip(BaseTest):
                 with gzip.GzipFile(fileobj=io.BytesIO(datac), mode="rb") as f:
                     self.assertEqual(f.read(), data)
 
+    def test_compress_mtime(self):
+        mtime = 123456789
+        for data in [data1, data2]:
+            for args in [(), (1,), (6,), (9,)]:
+                with self.subTest(data=data, args=args):
+                    datac = gzip.compress(data, *args, mtime=mtime)
+                    self.assertEqual(type(datac), bytes)
+                    with gzip.GzipFile(fileobj=io.BytesIO(datac), mode="rb") as f:
+                        f.read(1) # to set mtime attribute
+                        self.assertEqual(f.mtime, mtime)
+
     def test_decompress(self):
         for data in (data1, data2):
             buf = io.BytesIO()
index 89fb0c7000b45ecdaf9e8136a6616327dde8d8d1..aba60945a1e2003f85be059240dba7aa769612be 100644 (file)
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1615,6 +1615,7 @@ Monty Taylor
 Anatoly Techtonik
 Martin Teichmann
 Gustavo Temple
+Guo Ci Teo
 Mikhail Terekhov
 Victor Terrón
 Pablo Galindo
diff --git a/Misc/NEWS.d/next/Library/2018-10-04-17-23-43.bpo-34898.Wo2PoJ.rst b/Misc/NEWS.d/next/Library/2018-10-04-17-23-43.bpo-34898.Wo2PoJ.rst
new file mode 100644 (file)
index 0000000..4c0a061
--- /dev/null
@@ -0,0 +1,2 @@
+Add `mtime` argument to `gzip.compress` for reproducible output.
+Patch by Guo Ci Teo.
\ No newline at end of file