diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -72,16 +72,19 @@ Some facts and figures: +------------------+---------------------------------------------+ | ``'w:xz'`` | Open for lzma compressed writing. | +------------------+---------------------------------------------+ Note that ``'a:gz'``, ``'a:bz2'`` or ``'a:xz'`` is not possible. If *mode* is not suitable to open a certain (compressed) file for reading, :exc:`ReadError` is raised. Use *mode* ``'r'`` to avoid this. If a compression method is not supported, :exc:`CompressionError` is raised. + As a :term:`LBYL` alternative to catching this exception, a program can + inspect :data:`.compression_formats` to find what formats are + supported. If *fileobj* is specified, it is used as an alternative to a :term:`file object` opened in binary mode for *name*. It is supposed to be at position 0. For modes ``'w:gz'``, ``'r:gz'``, ``'w:bz2'``, ``'r:bz2'``, :func:`tarfile.open` accepts the keyword argument *compresslevel* to specify the compression level of the file. @@ -206,16 +209,24 @@ The following variables are available on .. data:: ENCODING The default character encoding: ``'utf-8'`` on Windows, :func:`sys.getfilesystemencoding` otherwise. +.. data:: compression_formats + + A list containing all supported compression formats, sorted by strength; it + may contain ``'xz'``, ``'bz2'`` and ``'gz'``, in this order. + + .. versionadded:: 3.5 + + .. seealso:: Module :mod:`zipfile` Documentation of the :mod:`zipfile` standard module. `GNU tar manual, Basic Tar Format `_ Documentation for tar archive files, including GNU tar extensions. diff --git a/Lib/tarfile.py b/Lib/tarfile.py --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -48,27 +48,43 @@ import struct import copy import re try: import grp, pwd except ImportError: grp = pwd = None +try: + import zlib +except ImportError: + zlib = None + +try: + import lzma +except ImportError: + lzma = None + +try: + import bz2 +except ImportError: + bz2 = None + # os.symlink on Windows prior to 6.0 raises NotImplementedError symlink_exception = (AttributeError, NotImplementedError) try: # OSError (winerror=1314) will be raised if the caller does not hold the # SeCreateSymbolicLinkPrivilege privilege symlink_exception += (OSError,) except NameError: pass # from tarfile import * -__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError"] +__all__ = ["TarFile", "TarInfo", "is_tarfile", "TarError", + "compression_formats"] #--------------------------------------------------------- # tar constants #--------------------------------------------------------- NUL = b"\0" # the null character BLOCKSIZE = 512 # length of processing blocks RECORDSIZE = BLOCKSIZE * 20 # length of records GNU_MAGIC = b"ustar \0" # magic gnu tar string @@ -354,44 +370,37 @@ class _Stream: self.fileobj = fileobj self.bufsize = bufsize self.buf = b"" self.pos = 0 self.closed = False try: if comptype == "gz": - try: - import zlib - except ImportError: + if zlib is None: raise CompressionError("zlib module is not available") - self.zlib = zlib self.crc = zlib.crc32(b"") if mode == "r": self._init_read_gz() self.exception = zlib.error else: self._init_write_gz() elif comptype == "bz2": - try: - import bz2 - except ImportError: + if bz2 is None: raise CompressionError("bz2 module is not available") if mode == "r": self.dbuf = b"" self.cmp = bz2.BZ2Decompressor() self.exception = OSError else: self.cmp = bz2.BZ2Compressor() elif comptype == "xz": - try: - import lzma - except ImportError: + if lzma is None: raise CompressionError("lzma module is not available") if mode == "r": self.dbuf = b"" self.cmp = lzma.LZMADecompressor() self.exception = lzma.LZMAError else: self.cmp = lzma.LZMACompressor() @@ -406,32 +415,32 @@ class _Stream: def __del__(self): if hasattr(self, "closed") and not self.closed: self.close() def _init_write_gz(self): """Initialize for writing with gzip compression. """ - self.cmp = self.zlib.compressobj(9, self.zlib.DEFLATED, - -self.zlib.MAX_WBITS, - self.zlib.DEF_MEM_LEVEL, - 0) + self.cmp = zlib.compressobj(9, zlib.DEFLATED, + -zlib.MAX_WBITS, + zlib.DEF_MEM_LEVEL, + 0) timestamp = struct.pack("