Index: Doc/library/tarfile.rst =================================================================== --- Doc/library/tarfile.rst (Revision 85808) +++ Doc/library/tarfile.rst (Arbeitskopie) @@ -335,12 +335,13 @@ dots ``".."``. -.. method:: TarFile.extract(member, path="") +.. method:: TarFile.extract(member, path="", touch=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 + *touch* is False. .. note:: @@ -351,6 +352,8 @@ See the warning for :meth:`extractall`. + .. versionchanged:: 3.2 + Added the *touch* parameter. .. method:: TarFile.extractfile(member) Index: Lib/tarfile.py =================================================================== --- Lib/tarfile.py (Revision 85808) +++ Lib/tarfile.py (Arbeitskopie) @@ -2104,7 +2104,8 @@ directories.append(tarinfo) tarinfo = copy.copy(tarinfo) tarinfo.mode = 0o700 - self.extract(tarinfo, path) + # Do not touch directories, as we will do that further down + self.extract(tarinfo, path, touch=not tarinfo.isdir()) # Reverse sort directories. directories.sort(key=lambda a: a.name) @@ -2123,11 +2124,12 @@ else: self._dbg(1, "tarfile: %s" % e) - def extract(self, member, path=""): + def extract(self, member, path="", touch=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 `touch' is False. """ self._check("r") @@ -2141,7 +2143,8 @@ 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), + touch=touch) except EnvironmentError as e: if self.errorlevel > 0: raise @@ -2194,7 +2197,7 @@ # blkdev, etc.), return None instead of a file object. return None - def _extract_member(self, tarinfo, targetpath): + def _extract_member(self, tarinfo, targetpath, touch=True): """Extract the TarInfo object tarinfo to a physical file called targetpath. """ @@ -2231,10 +2234,11 @@ else: self.makefile(tarinfo, targetpath) - self.chown(tarinfo, targetpath) - if not tarinfo.issym(): - self.chmod(tarinfo, targetpath) - self.utime(tarinfo, targetpath) + if touch: + 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 Index: Lib/test/test_tarfile.py =================================================================== --- Lib/test/test_tarfile.py (Revision 85809) +++ Lib/test/test_tarfile.py (Arbeitskopie) @@ -349,6 +349,14 @@ 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) + 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