summaryrefslogtreecommitdiff
path: root/Lib/tarfile.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/tarfile.py')
-rwxr-xr-xLib/tarfile.py95
1 files changed, 59 insertions, 36 deletions
diff --git a/Lib/tarfile.py b/Lib/tarfile.py
index 5f1a979ad0..ca45126ff1 100755
--- a/Lib/tarfile.py
+++ b/Lib/tarfile.py
@@ -1414,9 +1414,9 @@ class TarFile(object):
can be determined, `mode' is overridden by `fileobj's mode.
`fileobj' is not closed, when TarFile is closed.
"""
- modes = {"r": "rb", "a": "r+b", "w": "wb"}
+ modes = {"r": "rb", "a": "r+b", "w": "wb", "x": "xb"}
if mode not in modes:
- raise ValueError("mode must be 'r', 'a' or 'w'")
+ raise ValueError("mode must be 'r', 'a', 'w' or 'x'")
self.mode = mode
self._mode = modes[mode]
@@ -1488,7 +1488,7 @@ class TarFile(object):
except HeaderError as e:
raise ReadError(str(e))
- if self.mode in "aw":
+ if self.mode in ("a", "w", "x"):
self._loaded = True
if self.pax_headers:
@@ -1529,6 +1529,15 @@ class TarFile(object):
'w:bz2' open for writing with bzip2 compression
'w:xz' open for writing with lzma compression
+ 'x' or 'x:' create a tarfile exclusively without compression, raise
+ an exception if the file is already created
+ 'x:gz' create an gzip compressed tarfile, raise an exception
+ if the file is already created
+ 'x:bz2' create an bzip2 compressed tarfile, raise an exception
+ if the file is already created
+ 'x:xz' create an lzma 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
@@ -1587,7 +1596,7 @@ class TarFile(object):
t._extfileobj = False
return t
- elif mode in ("a", "w"):
+ elif mode in ("a", "w", "x"):
return cls.taropen(name, mode, fileobj, **kwargs)
raise ValueError("undiscernible mode")
@@ -1596,8 +1605,8 @@ class TarFile(object):
def taropen(cls, name, mode="r", fileobj=None, **kwargs):
"""Open uncompressed tar archive name for reading or writing.
"""
- if mode not in ("r", "a", "w"):
- raise ValueError("mode must be 'r', 'a' or 'w'")
+ if mode not in ("r", "a", "w", "x"):
+ raise ValueError("mode must be 'r', 'a', 'w' or 'x'")
return cls(name, mode, fileobj, **kwargs)
@classmethod
@@ -1605,8 +1614,8 @@ class TarFile(object):
"""Open gzip compressed tar archive name for reading or writing.
Appending is not allowed.
"""
- if mode not in ("r", "w"):
- raise ValueError("mode must be 'r' or 'w'")
+ if mode not in ("r", "w", "x"):
+ raise ValueError("mode must be 'r', 'w' or 'x'")
try:
import gzip
@@ -1639,8 +1648,8 @@ class TarFile(object):
"""Open bzip2 compressed tar archive name for reading or writing.
Appending is not allowed.
"""
- if mode not in ("r", "w"):
- raise ValueError("mode must be 'r' or 'w'.")
+ if mode not in ("r", "w", "x"):
+ raise ValueError("mode must be 'r', 'w' or 'x'")
try:
import bz2
@@ -1668,8 +1677,8 @@ class TarFile(object):
"""Open lzma compressed tar archive name for reading or writing.
Appending is not allowed.
"""
- if mode not in ("r", "w"):
- raise ValueError("mode must be 'r' or 'w'")
+ if mode not in ("r", "w", "x"):
+ raise ValueError("mode must be 'r', 'w' or 'x'")
try:
import lzma
@@ -1711,7 +1720,7 @@ class TarFile(object):
self.closed = True
try:
- if self.mode in "aw":
+ if self.mode in ("a", "w", "x"):
self.fileobj.write(NUL * (BLOCKSIZE * 2))
self.offset += (BLOCKSIZE * 2)
# fill up the end with zero-blocks
@@ -1757,7 +1766,7 @@ class TarFile(object):
addfile(). If given, `arcname' specifies an alternative name for the
file in the archive.
"""
- self._check("aw")
+ self._check("awx")
# When fileobj is given, replace name by
# fileobj's real name.
@@ -1848,14 +1857,17 @@ class TarFile(object):
tarinfo.devminor = os.minor(statres.st_rdev)
return tarinfo
- def list(self, verbose=True):
+ def list(self, verbose=True, *, members=None):
"""Print a table of contents to sys.stdout. If `verbose' is False, only
the names of the members are printed. If it is True, an `ls -l'-like
- output is produced.
+ output is produced. `members' is optional and must be a subset of the
+ list returned by getmembers().
"""
self._check()
- for tarinfo in self:
+ if members is None:
+ members = self
+ for tarinfo in members:
if verbose:
_safe_print(stat.filemode(tarinfo.mode))
_safe_print("%s/%s" % (tarinfo.uname or tarinfo.uid,
@@ -1888,7 +1900,7 @@ class TarFile(object):
TarInfo object, if it returns None the TarInfo object will be
excluded from the archive.
"""
- self._check("aw")
+ self._check("awx")
if arcname is None:
arcname = name
@@ -1945,7 +1957,7 @@ class TarFile(object):
On Windows platforms, `fileobj' should always be opened with mode
'rb' to avoid irritation about the file size.
"""
- self._check("aw")
+ self._check("awx")
tarinfo = copy.copy(tarinfo)
@@ -1964,12 +1976,13 @@ class TarFile(object):
self.members.append(tarinfo)
- def extractall(self, path=".", members=None):
+ def extractall(self, path=".", members=None, *, numeric_owner=False):
"""Extract all members from the archive to the current working
directory and set owner, modification time and permissions on
directories afterwards. `path' specifies a different directory
to extract to. `members' is optional and must be a subset of the
- list returned by getmembers().
+ list returned by getmembers(). If `numeric_owner` is True, only
+ the numbers for user/group names are used and not the names.
"""
directories = []
@@ -1983,7 +1996,8 @@ class TarFile(object):
tarinfo = copy.copy(tarinfo)
tarinfo.mode = 0o700
# Do not set_attrs directories, as we will do that further down
- self.extract(tarinfo, path, set_attrs=not tarinfo.isdir())
+ self.extract(tarinfo, path, set_attrs=not tarinfo.isdir(),
+ numeric_owner=numeric_owner)
# Reverse sort directories.
directories.sort(key=lambda a: a.name)
@@ -1993,7 +2007,7 @@ class TarFile(object):
for tarinfo in directories:
dirpath = os.path.join(path, tarinfo.name)
try:
- self.chown(tarinfo, dirpath)
+ self.chown(tarinfo, dirpath, numeric_owner=numeric_owner)
self.utime(tarinfo, dirpath)
self.chmod(tarinfo, dirpath)
except ExtractError as e:
@@ -2002,12 +2016,14 @@ class TarFile(object):
else:
self._dbg(1, "tarfile: %s" % e)
- def extract(self, member, path="", set_attrs=True):
+ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False):
"""Extract a member from the archive to the current working directory,
using its full name. Its file information is extracted as accurately
as possible. `member' may be a filename or a TarInfo object. You can
specify a different directory using `path'. File attributes (owner,
- mtime, mode) are set unless `set_attrs' is False.
+ mtime, mode) are set unless `set_attrs' is False. If `numeric_owner`
+ is True, only the numbers for user/group names are used and not
+ the names.
"""
self._check("r")
@@ -2022,7 +2038,8 @@ class TarFile(object):
try:
self._extract_member(tarinfo, os.path.join(path, tarinfo.name),
- set_attrs=set_attrs)
+ set_attrs=set_attrs,
+ numeric_owner=numeric_owner)
except OSError as e:
if self.errorlevel > 0:
raise
@@ -2068,7 +2085,8 @@ class TarFile(object):
# blkdev, etc.), return None instead of a file object.
return None
- def _extract_member(self, tarinfo, targetpath, set_attrs=True):
+ def _extract_member(self, tarinfo, targetpath, set_attrs=True,
+ numeric_owner=False):
"""Extract the TarInfo object tarinfo to a physical
file called targetpath.
"""
@@ -2106,7 +2124,7 @@ class TarFile(object):
self.makefile(tarinfo, targetpath)
if set_attrs:
- self.chown(tarinfo, targetpath)
+ self.chown(tarinfo, targetpath, numeric_owner)
if not tarinfo.issym():
self.chmod(tarinfo, targetpath)
self.utime(tarinfo, targetpath)
@@ -2195,19 +2213,24 @@ class TarFile(object):
except KeyError:
raise ExtractError("unable to resolve link inside archive")
- def chown(self, tarinfo, targetpath):
- """Set owner of targetpath according to tarinfo.
+ def chown(self, tarinfo, targetpath, numeric_owner):
+ """Set owner of targetpath according to tarinfo. If numeric_owner
+ is True, use .gid/.uid instead of .gname/.uname.
"""
if pwd and hasattr(os, "geteuid") and os.geteuid() == 0:
# We have to be root to do so.
- try:
- g = grp.getgrnam(tarinfo.gname)[2]
- except KeyError:
+ if numeric_owner:
g = tarinfo.gid
- try:
- u = pwd.getpwnam(tarinfo.uname)[2]
- except KeyError:
u = tarinfo.uid
+ else:
+ try:
+ g = grp.getgrnam(tarinfo.gname)[2]
+ except KeyError:
+ g = tarinfo.gid
+ try:
+ u = pwd.getpwnam(tarinfo.uname)[2]
+ except KeyError:
+ u = tarinfo.uid
try:
if tarinfo.issym() and hasattr(os, "lchown"):
os.lchown(targetpath, u, g)