From: <pj...@us...> - 2008-09-25 21:12:35
|
Revision: 5348 http://jython.svn.sourceforge.net/jython/?rev=5348&view=rev Author: pjenvey Date: 2008-09-25 21:12:28 +0000 (Thu, 25 Sep 2008) Log Message: ----------- bump tarfile from 2.5.1 to 2.5.2, from: http://svn.python.org/projects/python/branches/release25-maint/Lib tarfile.py@60730 test/test_tarfile.py@60730 Modified Paths: -------------- trunk/jython/Lib/tarfile.py trunk/jython/Lib/test/test_tarfile.py Removed Paths: ------------- trunk/jython/Lib/test/testtar.tar Modified: trunk/jython/Lib/tarfile.py =================================================================== --- trunk/jython/Lib/tarfile.py 2008-09-25 18:52:30 UTC (rev 5347) +++ trunk/jython/Lib/tarfile.py 2008-09-25 21:12:28 UTC (rev 5348) @@ -30,17 +30,13 @@ """Read from and write to tar format archives. """ -# From CPython 2.5.1 with @classmethod decorators replaced and -# TarFile.gzopen changed to not assume CPython reference counting GC -# (make GzipFile close the underlying file) - -__version__ = "$Revision: 53162 $" +__version__ = "$Revision: 60730 $" # $Source$ version = "0.8.0" __author__ = "Lars Gust\xE4bel (la...@gu...)" -__date__ = "$Date: 2006-12-27 21:36:58 +1100 (Wed, 27 Dec 2006) $" -__cvsid__ = "$Id: tarfile.py 53162 2006-12-27 10:36:58Z lars.gustaebel $" +__date__ = "$Date: 2008-02-11 10:36:07 -0800 (Mon, 11 Feb 2008) $" +__cvsid__ = "$Id: tarfile.py 60730 2008-02-11 18:36:07Z lars.gustaebel $" __credits__ = "Gustavo Niemeyer, Niels Gust\xE4bel, Richard Townsend." #--------- @@ -143,13 +139,22 @@ """ return s[:length] + (length - len(s)) * NUL +def nts(s): + """Convert a null-terminated string field to a python string. + """ + # Use the string up to the first null char. + p = s.find("\0") + if p == -1: + return s + return s[:p] + def nti(s): """Convert a number field to a python number. """ # There are two possible encodings for a number field, see # itn() below. if s[0] != chr(0200): - n = int(s.rstrip(NUL + " ") or "0", 8) + n = int(nts(s) or "0", 8) else: n = 0L for i in xrange(len(s) - 1): @@ -865,7 +870,7 @@ def __repr__(self): return "<%s %r at %#x>" % (self.__class__.__name__,self.name,id(self)) - #@classmethod + @classmethod def frombuf(cls, buf): """Construct a TarInfo object from a 512 byte string buffer. """ @@ -876,7 +881,7 @@ tarinfo = cls() tarinfo.buf = buf - tarinfo.name = buf[0:100].rstrip(NUL) + tarinfo.name = nts(buf[0:100]) tarinfo.mode = nti(buf[100:108]) tarinfo.uid = nti(buf[108:116]) tarinfo.gid = nti(buf[116:124]) @@ -884,12 +889,12 @@ tarinfo.mtime = nti(buf[136:148]) tarinfo.chksum = nti(buf[148:156]) tarinfo.type = buf[156:157] - tarinfo.linkname = buf[157:257].rstrip(NUL) - tarinfo.uname = buf[265:297].rstrip(NUL) - tarinfo.gname = buf[297:329].rstrip(NUL) + tarinfo.linkname = nts(buf[157:257]) + tarinfo.uname = nts(buf[265:297]) + tarinfo.gname = nts(buf[297:329]) tarinfo.devmajor = nti(buf[329:337]) tarinfo.devminor = nti(buf[337:345]) - prefix = buf[345:500].rstrip(NUL) + prefix = nts(buf[345:500]) if prefix and not tarinfo.issparse(): tarinfo.name = prefix + "/" + tarinfo.name @@ -897,7 +902,6 @@ if tarinfo.chksum not in calc_chksums(buf): raise ValueError("invalid header") return tarinfo - frombuf = classmethod(frombuf) def tobuf(self, posix=False): """Return a tar header as a string of 512 byte blocks. @@ -968,7 +972,7 @@ stn(prefix, 155) ] - buf += struct.pack("%ds" % BLOCKSIZE, "".join(parts)) + buf += "".join(parts).ljust(BLOCKSIZE, NUL) chksum = calc_chksums(buf[-BLOCKSIZE:])[0] buf = buf[:-364] + "%06o\0" % chksum + buf[-357:] self.buf = buf @@ -1049,29 +1053,29 @@ can be determined, `mode' is overridden by `fileobj's mode. `fileobj' is not closed, when TarFile is closed. """ - self.name = os.path.abspath(name) - if len(mode) > 1 or mode not in "raw": raise ValueError("mode must be 'r', 'a' or 'w'") self._mode = mode self.mode = {"r": "rb", "a": "r+b", "w": "wb"}[mode] if not fileobj: - fileobj = file(self.name, self.mode) + fileobj = file(name, self.mode) self._extfileobj = False else: - if self.name is None and hasattr(fileobj, "name"): - self.name = os.path.abspath(fileobj.name) + if name is None and hasattr(fileobj, "name"): + name = fileobj.name if hasattr(fileobj, "mode"): self.mode = fileobj.mode self._extfileobj = True + self.name = os.path.abspath(name) if name else None self.fileobj = fileobj # Init datastructures self.closed = False self.members = [] # list of members as TarInfo objects self._loaded = False # flag if all members have been read - self.offset = 0L # current position in the archive file + self.offset = self.fileobj.tell() + # current position in the archive file self.inodes = {} # dictionary caching the inodes of # archive members already added @@ -1107,7 +1111,7 @@ # the super-constructor. A sub-constructor is registered and made available # by adding it to the mapping in OPEN_METH. - #@classmethod + @classmethod def open(cls, name=None, mode="r", fileobj=None, bufsize=20*512): """Open a tar archive for reading, writing or appending. Return an appropriate TarFile class. @@ -1178,18 +1182,16 @@ return cls.taropen(name, mode, fileobj) raise ValueError("undiscernible mode") - open = classmethod(open) - #@classmethod + @classmethod def taropen(cls, name, mode="r", fileobj=None): """Open uncompressed tar archive name for reading or writing. """ if len(mode) > 1 or mode not in "raw": raise ValueError("mode must be 'r', 'a' or 'w'") return cls(name, mode, fileobj) - taropen = classmethod(taropen) - #@classmethod + @classmethod def gzopen(cls, name, mode="r", fileobj=None, compresslevel=9): """Open gzip compressed tar archive name for reading or writing. Appending is not allowed. @@ -1204,19 +1206,17 @@ raise CompressionError("gzip module is not available") if fileobj is None: - fileobj = gzip.GzipFile(name, mode, compresslevel) - else: - fileobj = gzip.GzipFile(name, mode, compresslevel, fileobj) + fileobj = file(name, mode + "b") try: - t = cls.taropen(name, mode, fileobj) + t = cls.taropen(name, mode, + gzip.GzipFile(name, mode, compresslevel, fileobj)) except IOError: raise ReadError("not a gzip file") t._extfileobj = False return t - gzopen = classmethod(gzopen) - #@classmethod + @classmethod def bz2open(cls, name, mode="r", fileobj=None, compresslevel=9): """Open bzip2 compressed tar archive name for reading or writing. Appending is not allowed. @@ -1240,7 +1240,6 @@ raise ReadError("not a bzip2 file") t._extfileobj = False return t - bz2open = classmethod(bz2open) # All *open() methods are registered here. OPEN_METH = { @@ -1519,15 +1518,11 @@ for tarinfo in members: if tarinfo.isdir(): - # Extract directory with a safe mode, so that - # all files below can be extracted as well. - try: - os.makedirs(os.path.join(path, tarinfo.name), 0777) - except EnvironmentError: - pass + # Extract directories with a safe mode. directories.append(tarinfo) - else: - self.extract(tarinfo, path) + tarinfo = copy.copy(tarinfo) + tarinfo.mode = 0700 + self.extract(tarinfo, path) # Reverse sort directories. directories.sort(lambda a, b: cmp(a.name, b.name)) @@ -1535,11 +1530,11 @@ # Set correct owner, mtime and filemode on directories. for tarinfo in directories: - path = os.path.join(path, tarinfo.name) + dirpath = os.path.join(path, tarinfo.name) try: - self.chown(tarinfo, path) - self.utime(tarinfo, path) - self.chmod(tarinfo, path) + self.chown(tarinfo, dirpath) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) except ExtractError, e: if self.errorlevel > 1: raise @@ -1632,19 +1627,9 @@ # Create all upper directories. upperdirs = os.path.dirname(targetpath) if upperdirs and not os.path.exists(upperdirs): - ti = TarInfo() - ti.name = upperdirs - ti.type = DIRTYPE - ti.mode = 0777 - ti.mtime = tarinfo.mtime - ti.uid = tarinfo.uid - ti.gid = tarinfo.gid - ti.uname = tarinfo.uname - ti.gname = tarinfo.gname - try: - self._extract_member(ti, ti.name) - except: - pass + # Create directories that are not part of the archive with + # default permissions. + os.makedirs(upperdirs) if tarinfo.islnk() or tarinfo.issym(): self._dbg(1, "%s -> %s" % (tarinfo.name, tarinfo.linkname)) @@ -1680,7 +1665,9 @@ """Make a directory called targetpath. """ try: - os.mkdir(targetpath) + # Use a safe mode for the directory, the real mode is set + # later in _extract_member(). + os.mkdir(targetpath, 0700) except EnvironmentError, e: if e.errno != errno.EEXIST: raise @@ -1852,7 +1839,7 @@ tarinfo.type = DIRTYPE # Directory names should have a '/' at the end. - if tarinfo.isdir(): + if tarinfo.isdir() and not tarinfo.name.endswith("/"): tarinfo.name += "/" self.members.append(tarinfo) @@ -1914,9 +1901,9 @@ # the longname information. next.offset = tarinfo.offset if tarinfo.type == GNUTYPE_LONGNAME: - next.name = buf.rstrip(NUL) + next.name = nts(buf) elif tarinfo.type == GNUTYPE_LONGLINK: - next.linkname = buf.rstrip(NUL) + next.linkname = nts(buf) return next Modified: trunk/jython/Lib/test/test_tarfile.py =================================================================== --- trunk/jython/Lib/test/test_tarfile.py 2008-09-25 18:52:30 UTC (rev 5347) +++ trunk/jython/Lib/test/test_tarfile.py 2008-09-25 21:12:28 UTC (rev 5348) @@ -9,9 +9,6 @@ from test import test_support -# From CPython 2.5.1, with a change to tarname() to use the correct -# tempdir when the testtar path name is relative (due to regrtest) - # Check for our compression modules. try: import gzip @@ -29,12 +26,12 @@ testtar = path("testtar.tar") tempdir = os.path.join(tempfile.gettempdir(), "testtar" + os.extsep + "dir") tempname = test_support.TESTFN -membercount = 12 +membercount = 13 def tarname(comp=""): if not comp: return testtar - return os.path.join(dirname(), "%s%s%s" % (testtar, os.extsep, comp)) + return os.path.join(tempdir, "%s%s%s" % (testtar, os.extsep, comp)) def dirname(): if not os.path.exists(tempdir): @@ -194,6 +191,47 @@ except: pass + def test_dirtype(self): + for tarinfo in self.tar: + if tarinfo.isdir(): + self.assert_(tarinfo.name.endswith("/")) + self.assert_(not tarinfo.name[:-1].endswith("/")) + + def test_extractall(self): + # Test if extractall() correctly restores directory permissions + # and times (see issue1735). + if sys.platform == "win32": + # Win32 has no support for utime() on directories or + # fine grained permissions. + return + + fobj = StringIO.StringIO() + tar = tarfile.open(fileobj=fobj, mode="w:") + for name in ("foo", "foo/bar"): + tarinfo = tarfile.TarInfo(name) + tarinfo.type = tarfile.DIRTYPE + tarinfo.mtime = 07606136617 + tarinfo.mode = 0755 + tar.addfile(tarinfo) + tar.close() + fobj.seek(0) + + TEMPDIR = os.path.join(dirname(), "extract-test") + tar = tarfile.open(fileobj=fobj) + tar.extractall(TEMPDIR) + for tarinfo in tar.getmembers(): + path = os.path.join(TEMPDIR, tarinfo.name) + self.assertEqual(tarinfo.mode, os.stat(path).st_mode & 0777) + self.assertEqual(tarinfo.mtime, os.path.getmtime(path)) + tar.close() + + def test_star(self): + try: + self.tar.getmember("7-STAR") + except KeyError: + self.fail("finding 7-STAR member failed (mangled prefix?)") + + class ReadStreamTest(ReadTest): sep = "|" @@ -241,14 +279,9 @@ def setUp(self): name = tarname(self.comp) - self.fileobj = open(name, "rb") self.tar = tarfile.open(name, mode=self.mode, - fileobj=self.fileobj) + fileobj=open(name, "rb")) - def tearDown(self): - self.tar.close() - self.fileobj.close() - class ReadAsteriskTest(ReadTest): def setUp(self): @@ -261,6 +294,38 @@ mode = self.mode + self.sep + "*" self.tar = tarfile.open(tarname(self.comp), mode) +class ReadFileobjTest(BaseTest): + + def test_fileobj_with_offset(self): + # Skip the first member and store values from the second member + # of the testtar. + self.tar.next() + t = self.tar.next() + name = t.name + offset = t.offset + data = self.tar.extractfile(t).read() + self.tar.close() + + # Open the testtar and seek to the offset of the second member. + if self.comp == "gz": + _open = gzip.GzipFile + elif self.comp == "bz2": + _open = bz2.BZ2File + else: + _open = open + fobj = _open(tarname(self.comp), "rb") + fobj.seek(offset) + + # Test if the tarfile starts with the second member. + self.tar = tarfile.open(tarname(self.comp), "r:", fileobj=fobj) + t = self.tar.next() + self.assertEqual(t.name, name) + # Read to the end of fileobj and test if seeking back to the + # beginning works. + self.tar.getmembers() + self.assertEqual(self.tar.extractfile(t).read(), data, + "seek back did not work") + class WriteTest(BaseTest): mode = 'w' @@ -464,7 +529,6 @@ self.assert_(tarinfo.name == member.name and \ tarinfo.linkname == member.linkname, \ "unable to read longname member") - tar.close() def test_longname_1023(self): self._test(("longnam/" * 127) + "longnam") @@ -626,6 +690,8 @@ comp = "gz" class ReadStreamAsteriskTestGzip(ReadStreamAsteriskTest): comp = "gz" +class ReadFileobjTestGzip(ReadFileobjTest): + comp = "gz" # Filemode test cases @@ -635,15 +701,35 @@ self.assertEqual(tarfile.filemode(07111), '---s--s--t') class OpenFileobjTest(BaseTest): - # Test for SF bug #1496501. def test_opener(self): + # Test for SF bug #1496501. fobj = StringIO.StringIO("foo\n") try: - tarfile.open("", "r", fileobj=fobj) + tarfile.open("", mode="r", fileobj=fobj) except tarfile.ReadError: self.assertEqual(fobj.tell(), 0, "fileobj's position has moved") + def test_no_name_argument(self): + fobj = open(testtar, "rb") + tar = tarfile.open(fileobj=fobj, mode="r") + self.assertEqual(tar.name, os.path.abspath(fobj.name)) + + def test_no_name_attribute(self): + data = open(testtar, "rb").read() + fobj = StringIO.StringIO(data) + self.assertRaises(AttributeError, getattr, fobj, "name") + tar = tarfile.open(fileobj=fobj, mode="r") + self.assertEqual(tar.name, None) + + def test_empty_name_attribute(self): + data = open(testtar, "rb").read() + fobj = StringIO.StringIO(data) + fobj.name = "" + tar = tarfile.open(fileobj=fobj, mode="r") + self.assertEqual(tar.name, None) + + if bz2: # Bzip2 TestCases class ReadTestBzip2(ReadTestGzip): @@ -662,6 +748,8 @@ comp = "bz2" class ReadStreamAsteriskTestBzip2(ReadStreamAsteriskTest): comp = "bz2" + class ReadFileobjTestBzip2(ReadFileobjTest): + comp = "bz2" # If importing gzip failed, discard the Gzip TestCases. if not gzip: @@ -695,6 +783,7 @@ ReadDetectFileobjTest, ReadAsteriskTest, ReadStreamAsteriskTest, + ReadFileobjTest, WriteTest, Write100Test, WriteSize0Test, @@ -712,7 +801,8 @@ ReadTestGzip, ReadStreamTestGzip, WriteTestGzip, WriteStreamTestGzip, ReadDetectTestGzip, ReadDetectFileobjTestGzip, - ReadAsteriskTestGzip, ReadStreamAsteriskTestGzip + ReadAsteriskTestGzip, ReadStreamAsteriskTestGzip, + ReadFileobjTestGzip ]) if bz2: @@ -720,7 +810,8 @@ ReadTestBzip2, ReadStreamTestBzip2, WriteTestBzip2, WriteStreamTestBzip2, ReadDetectTestBzip2, ReadDetectFileobjTestBzip2, - ReadAsteriskTestBzip2, ReadStreamAsteriskTestBzip2 + ReadAsteriskTestBzip2, ReadStreamAsteriskTestBzip2, + ReadFileobjTestBzip2 ]) try: test_support.run_unittest(*tests) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |