https://github.com/python/cpython/commit/0e7497cb469b003a45a4abe4105b81ef6d0... commit: 0e7497cb469b003a45a4abe4105b81ef6d0c4002 branch: master author: guoci <zguoci@gmail.com> committer: Serhiy Storchaka <storchaka@gmail.com> date: 2018-11-07T11:50:23+02:00 summary: bpo-34898: Add mtime parameter to gzip.compress(). (GH-9704) 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. files: A Misc/NEWS.d/next/Library/2018-10-04-17-23-43.bpo-34898.Wo2PoJ.rst M Doc/library/gzip.rst M Doc/whatsnew/3.8.rst M Lib/gzip.py M Lib/test/test_gzip.py M Misc/ACKS diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index a57307b0e499..8850a33f4abb 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -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) diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index 2d3a116df9d3..51aee1bed835 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -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 ---------------- diff --git a/Lib/gzip.py b/Lib/gzip.py index 151ff1405b16..948fec293e23 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -520,12 +520,12 @@ def _rewind(self): 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() diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 1e8b41f07b3e..2c8f854c6436 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -499,6 +499,17 @@ def test_compress(self): 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() diff --git a/Misc/ACKS b/Misc/ACKS index 89fb0c7000b4..aba60945a1e2 100644 --- 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 index 000000000000..4c0a061daf9f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-10-04-17-23-43.bpo-34898.Wo2PoJ.rst @@ -0,0 +1,2 @@ +Add `mtime` argument to `gzip.compress` for reproducible output. +Patch by Guo Ci Teo. \ No newline at end of file