From 16f344df36a8a7d7c609eb7036bb52b8a9a47a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20v=2E=20L=C3=B6wis?= Date: Mon, 1 Nov 2010 21:39:13 +0000 Subject: [PATCH] Issue #10184: Touch directories only once when extracting a tarfile. --- Doc/library/tarfile.rst | 7 +++++-- Lib/tarfile.py | 22 +++++++++++++--------- Lib/test/test_tarfile.py | 9 +++++++++ Misc/NEWS | 2 ++ 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst index 0dfb065252a..d7fbf39ee7d 100644 --- a/Doc/library/tarfile.rst +++ b/Doc/library/tarfile.rst @@ -336,12 +336,13 @@ be finalized; only the internally used file object will be closed. See the dots ``".."``. -.. method:: TarFile.extract(member, path="") +.. method:: TarFile.extract(member, path="", set_attrs=True) 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 :class:`TarInfo` object. You can specify a different - directory using *path*. + directory using *path*. File attributes (owner, mtime, mode) are set unless + *set_attrs* is False. .. note:: @@ -352,6 +353,8 @@ be finalized; only the internally used file object will be closed. See the See the warning for :meth:`extractall`. + .. versionchanged:: 3.2 + Added the *set_attrs* parameter. .. method:: TarFile.extractfile(member) diff --git a/Lib/tarfile.py b/Lib/tarfile.py index d49e82ff4a8..fd898c74a1b 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -2131,7 +2131,8 @@ class TarFile(object): directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 0o700 - self.extract(tarinfo, path) + # Do not set_attrs directories, as we will do that further down + self.extract(tarinfo, path, set_attrs=not tarinfo.isdir()) # Reverse sort directories. directories.sort(key=lambda a: a.name) @@ -2150,11 +2151,12 @@ class TarFile(object): else: self._dbg(1, "tarfile: %s" % e) - def extract(self, member, path=""): + def extract(self, member, path="", set_attrs=True): """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'. + specify a different directory using `path'. File attributes (owner, + mtime, mode) are set unless `set_attrs' is False. """ self._check("r") @@ -2168,7 +2170,8 @@ class TarFile(object): tarinfo._link_target = os.path.join(path, tarinfo.linkname) try: - self._extract_member(tarinfo, os.path.join(path, tarinfo.name)) + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs) except EnvironmentError as e: if self.errorlevel > 0: raise @@ -2221,7 +2224,7 @@ class TarFile(object): # blkdev, etc.), return None instead of a file object. return None - def _extract_member(self, tarinfo, targetpath): + def _extract_member(self, tarinfo, targetpath, set_attrs=True): """Extract the TarInfo object tarinfo to a physical file called targetpath. """ @@ -2258,10 +2261,11 @@ class TarFile(object): else: self.makefile(tarinfo, targetpath) - self.chown(tarinfo, targetpath) - if not tarinfo.issym(): - self.chmod(tarinfo, targetpath) - self.utime(tarinfo, targetpath) + if set_attrs: + self.chown(tarinfo, targetpath) + if not tarinfo.issym(): + self.chmod(tarinfo, targetpath) + self.utime(tarinfo, targetpath) #-------------------------------------------------------------------------- # Below are the different file methods. They are called via diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 302ee8554e5..daf46f7bfff 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -377,6 +377,15 @@ class MiscReadTest(CommonReadTest): finally: tar.close() + def test_extract_directory(self): + dirtype = "ustar/dirtype" + with tarfile.open(tarname, encoding="iso8859-1") as tar: + tarinfo = tar.getmember(dirtype) + tar.extract(tarinfo) + self.assertEqual(os.path.getmtime(dirtype), tarinfo.mtime) + if sys.platform != "win32": + self.assertEqual(os.stat(dirtype).st_mode & 0o777, 0o755) + def test_init_close_fobj(self): # Issue #7341: Close the internal file object in the TarFile # constructor in case of an error. For the test we rely on diff --git a/Misc/NEWS b/Misc/NEWS index 9cb25896fe6..9e6c1136092 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -59,6 +59,8 @@ Core and Builtins Library ------- +- Issue #10184: Touch directories only once when extracting a tarfile. + - Issue #10199: New package, ``turtledemo`` now contains selected demo scripts that were formerly found under Demo/turtle.