aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/tarfile.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/tarfile.py')
-rw-r--r--Lib/tarfile.py67
1 files changed, 62 insertions, 5 deletions
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 82c5f6704cb..212b71f6509 100644
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -399,7 +399,17 @@ class _Stream:
self.exception = lzma.LZMAError
else:
self.cmp = lzma.LZMACompressor(preset=preset)
-
+ elif comptype == "zst":
+ try:
+ from compression import zstd
+ except ImportError:
+ raise CompressionError("compression.zstd module is not available") from None
+ if mode == "r":
+ self.dbuf = b""
+ self.cmp = zstd.ZstdDecompressor()
+ self.exception = zstd.ZstdError
+ else:
+ self.cmp = zstd.ZstdCompressor()
elif comptype != "tar":
raise CompressionError("unknown compression type %r" % comptype)
@@ -591,6 +601,8 @@ class _StreamProxy(object):
return "bz2"
elif self.buf.startswith((b"\x5d\x00\x00\x80", b"\xfd7zXZ")):
return "xz"
+ elif self.buf.startswith(b"\x28\xb5\x2f\xfd"):
+ return "zst"
else:
return "tar"
@@ -1817,11 +1829,13 @@ class TarFile(object):
'r:gz' open for reading with gzip compression
'r:bz2' open for reading with bzip2 compression
'r:xz' open for reading with lzma compression
+ 'r:zst' open for reading with zstd compression
'a' or 'a:' open for appending, creating the file if necessary
'w' or 'w:' open for writing without compression
'w:gz' open for writing with gzip compression
'w:bz2' open for writing with bzip2 compression
'w:xz' open for writing with lzma compression
+ 'w:zst' open for writing with zstd compression
'x' or 'x:' create a tarfile exclusively without compression, raise
an exception if the file is already created
@@ -1831,16 +1845,20 @@ class TarFile(object):
if the file is already created
'x:xz' create an lzma compressed tarfile, raise an exception
if the file is already created
+ 'x:zst' create a zstd compressed tarfile, raise an exception
+ if the file is already created
'r|*' open a stream of tar blocks with transparent compression
'r|' open an uncompressed stream of tar blocks for reading
'r|gz' open a gzip compressed stream of tar blocks
'r|bz2' open a bzip2 compressed stream of tar blocks
'r|xz' open an lzma compressed stream of tar blocks
+ 'r|zst' open a zstd compressed stream of tar blocks
'w|' open an uncompressed stream for writing
'w|gz' open a gzip compressed stream for writing
'w|bz2' open a bzip2 compressed stream for writing
'w|xz' open an lzma compressed stream for writing
+ 'w|zst' open a zstd compressed stream for writing
"""
if not name and not fileobj:
@@ -2006,12 +2024,48 @@ class TarFile(object):
t._extfileobj = False
return t
+ @classmethod
+ def zstopen(cls, name, mode="r", fileobj=None, level=None, options=None,
+ zstd_dict=None, **kwargs):
+ """Open zstd compressed tar archive name for reading or writing.
+ Appending is not allowed.
+ """
+ if mode not in ("r", "w", "x"):
+ raise ValueError("mode must be 'r', 'w' or 'x'")
+
+ try:
+ from compression.zstd import ZstdFile, ZstdError
+ except ImportError:
+ raise CompressionError("compression.zstd module is not available") from None
+
+ fileobj = ZstdFile(
+ fileobj or name,
+ mode,
+ level=level,
+ options=options,
+ zstd_dict=zstd_dict
+ )
+
+ try:
+ t = cls.taropen(name, mode, fileobj, **kwargs)
+ except (ZstdError, EOFError) as e:
+ fileobj.close()
+ if mode == 'r':
+ raise ReadError("not a zstd file") from e
+ raise
+ except Exception:
+ fileobj.close()
+ raise
+ t._extfileobj = False
+ return t
+
# All *open() methods are registered here.
OPEN_METH = {
"tar": "taropen", # uncompressed tar
"gz": "gzopen", # gzip compressed tar
"bz2": "bz2open", # bzip2 compressed tar
- "xz": "xzopen" # lzma compressed tar
+ "xz": "xzopen", # lzma compressed tar
+ "zst": "zstopen", # zstd compressed tar
}
#--------------------------------------------------------------------------
@@ -2385,7 +2439,7 @@ class TarFile(object):
unfiltered = tarinfo
try:
tarinfo = filter_function(tarinfo, path)
- except (OSError, FilterError) as e:
+ except (OSError, UnicodeEncodeError, FilterError) as e:
self._handle_fatal_error(e)
except ExtractError as e:
self._handle_nonfatal_error(e)
@@ -2406,7 +2460,7 @@ class TarFile(object):
self._extract_member(tarinfo, os.path.join(path, tarinfo.name),
set_attrs=set_attrs,
numeric_owner=numeric_owner)
- except OSError as e:
+ except (OSError, UnicodeEncodeError) as e:
self._handle_fatal_error(e)
except ExtractError as e:
self._handle_nonfatal_error(e)
@@ -2883,7 +2937,7 @@ def main():
import argparse
description = 'A simple command-line interface for tarfile module.'
- parser = argparse.ArgumentParser(description=description)
+ parser = argparse.ArgumentParser(description=description, color=True)
parser.add_argument('-v', '--verbose', action='store_true', default=False,
help='Verbose output')
parser.add_argument('--filter', metavar='<filtername>',
@@ -2963,6 +3017,9 @@ def main():
'.tbz': 'bz2',
'.tbz2': 'bz2',
'.tb2': 'bz2',
+ # zstd
+ '.zst': 'zst',
+ '.tzst': 'zst',
}
tar_mode = 'w:' + compressions[ext] if ext in compressions else 'w'
tar_files = args.create