diff --git a/Doc/library/gzip.rst b/Doc/library/gzip.rst index b99ac740fd5..0401cd82116 100644 --- a/Doc/library/gzip.rst +++ b/Doc/library/gzip.rst @@ -82,6 +82,17 @@ The module defines the following items: The *filename* argument is required; *mode* defaults to ``'rb'`` and *compresslevel* defaults to ``9``. +.. function:: compress(data, compresslevel=9) + + Compress the *data*, returning a :class:`bytes` object containing + the compressed data. *compresslevel* has the same meaning as in + the :class:`GzipFile` constructor above. + +.. function:: decompress(data) + + Decompress the *data*, returning a :class:`bytes` object containing the + uncompressed data. + .. _gzip-usage-examples: @@ -112,6 +123,11 @@ Example of how to GZIP compress an existing file:: f_out.close() f_in.close() +Example of how to GZIP compress a binary string:: + + import gzip + s_in = b"Lots of content here" + s_out = gzip.compress(s_in) .. seealso:: diff --git a/Lib/gzip.py b/Lib/gzip.py index fab55a3015d..83311cc0deb 100644 --- a/Lib/gzip.py +++ b/Lib/gzip.py @@ -10,7 +10,7 @@ import zlib import builtins import io -__all__ = ["GzipFile","open"] +__all__ = ["GzipFile", "open", "compress", "decompress"] FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16 @@ -476,6 +476,23 @@ class GzipFile(io.BufferedIOBase): return b''.join(bufs) # Return resulting line +def compress(data, compresslevel=9): + """Compress data in one shot and return the compressed string. + Optional argument is the compression level, in range of 1-9. + """ + buf = io.BytesIO() + with GzipFile(fileobj=buf, mode='wb', compresslevel=compresslevel) as f: + f.write(data) + return buf.getvalue() + +def decompress(data): + """Decompress a gzip compressed string in one shot. + Return the decompressed string. + """ + with GzipFile(fileobj=io.BytesIO(data)) as f: + return f.read() + + def _test(): # Act like gzip; with -d, act like gunzip. # The input file is not deleted, however, nor are any other gzip diff --git a/Lib/test/test_gzip.py b/Lib/test/test_gzip.py index 7eade6f60a6..a95af058a39 100644 --- a/Lib/test/test_gzip.py +++ b/Lib/test/test_gzip.py @@ -265,6 +265,26 @@ class TestGzip(unittest.TestCase): d = f.read() self.assertEqual(d, data1 * 50, "Incorrect data in file") + # Testing compress/decompress shortcut functions + + def test_compress(self): + for data in [data1, data2]: + for args in [(), (1,), (6,), (9,)]: + datac = gzip.compress(data, *args) + self.assertEqual(type(datac), bytes) + with gzip.GzipFile(fileobj=io.BytesIO(datac), mode="rb") as f: + self.assertEqual(f.read(), data) + + def test_decompress(self): + for data in (data1, data2): + buf = io.BytesIO() + with gzip.GzipFile(fileobj=buf, mode="wb") as f: + f.write(data) + self.assertEqual(gzip.decompress(buf.getvalue()), data) + # Roundtrip with compress + datac = gzip.compress(data) + self.assertEqual(gzip.decompress(datac), data) + def test_main(verbose=None): support.run_unittest(TestGzip) diff --git a/Misc/ACKS b/Misc/ACKS index 26feba34e9d..45cd1b70cdb 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -634,6 +634,7 @@ Neale Pickett Jim St. Pierre Dan Pierson Martijn Pieters +Anand B. Pillai François Pinard Zach Pincus Michael Piotrowski diff --git a/Misc/NEWS b/Misc/NEWS index 42a04f7c59f..4e119228678 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -95,6 +95,9 @@ Extensions Library ------- +- Issue #3488: Provide convenient shorthand functions ``gzip.compress`` + and ``gzip.decompress``. Original patch by Anand B. Pillai. + - Issue #8807: poplib.POP3_SSL class now accepts a context parameter, which is a ssl.SSLContext object allowing bundling SSL configuration options, certificates and private keys into a single (potentially long-lived)