Kaydet (Commit) e1eca4e3 authored tarafından Antoine Pitrou's avatar Antoine Pitrou

Issue #10233: Close file objects in a timely manner in the tarfile module

and its test suite.
üst 749afa95
...@@ -1800,20 +1800,18 @@ class TarFile(object): ...@@ -1800,20 +1800,18 @@ class TarFile(object):
except (ImportError, AttributeError): except (ImportError, AttributeError):
raise CompressionError("gzip module is not available") raise CompressionError("gzip module is not available")
if fileobj is None: extfileobj = fileobj is not None
fileobj = bltn_open(name, mode + "b")
extfileobj = False
else:
extfileobj = True
try: try:
t = cls.taropen(name, mode, fileobj = gzip.GzipFile(name, mode + "b", compresslevel, fileobj)
gzip.GzipFile(name, mode, compresslevel, fileobj), t = cls.taropen(name, mode, fileobj, **kwargs)
**kwargs)
except IOError: except IOError:
if not extfileobj: if not extfileobj:
fileobj.close() fileobj.close()
raise ReadError("not a gzip file") raise ReadError("not a gzip file")
except:
if not extfileobj:
fileobj.close()
raise
t._extfileobj = extfileobj t._extfileobj = extfileobj
return t return t
......
...@@ -52,25 +52,32 @@ class UstarReadTest(ReadTest): ...@@ -52,25 +52,32 @@ class UstarReadTest(ReadTest):
def test_fileobj_regular_file(self): def test_fileobj_regular_file(self):
tarinfo = self.tar.getmember("ustar/regtype") tarinfo = self.tar.getmember("ustar/regtype")
fobj = self.tar.extractfile(tarinfo) fobj = self.tar.extractfile(tarinfo)
data = fobj.read() try:
self.assertTrue((len(data), md5sum(data)) == (tarinfo.size, md5_regtype), data = fobj.read()
"regular file extraction failed") self.assertTrue((len(data), md5sum(data)) == (tarinfo.size, md5_regtype),
"regular file extraction failed")
finally:
fobj.close()
def test_fileobj_readlines(self): def test_fileobj_readlines(self):
self.tar.extract("ustar/regtype", TEMPDIR) self.tar.extract("ustar/regtype", TEMPDIR)
tarinfo = self.tar.getmember("ustar/regtype") tarinfo = self.tar.getmember("ustar/regtype")
with open(os.path.join(TEMPDIR, "ustar/regtype"), "r") as fobj1: with open(os.path.join(TEMPDIR, "ustar/regtype"), "r") as fobj1:
lines1 = fobj1.readlines() lines1 = fobj1.readlines()
fobj2 = io.TextIOWrapper(self.tar.extractfile(tarinfo))
lines2 = fobj2.readlines() fobj = self.tar.extractfile(tarinfo)
self.assertTrue(lines1 == lines2, try:
"fileobj.readlines() failed") fobj2 = io.TextIOWrapper(fobj)
self.assertTrue(len(lines2) == 114, lines2 = fobj2.readlines()
"fileobj.readlines() failed") self.assertTrue(lines1 == lines2,
self.assertTrue(lines2[83] == "fileobj.readlines() failed")
"I will gladly admit that Python is not the fastest running scripting language.\n", self.assertTrue(len(lines2) == 114,
"fileobj.readlines() failed") "fileobj.readlines() failed")
self.assertTrue(lines2[83] ==
"I will gladly admit that Python is not the fastest running scripting language.\n",
"fileobj.readlines() failed")
finally:
fobj.close()
def test_fileobj_iter(self): def test_fileobj_iter(self):
self.tar.extract("ustar/regtype", TEMPDIR) self.tar.extract("ustar/regtype", TEMPDIR)
...@@ -78,9 +85,12 @@ class UstarReadTest(ReadTest): ...@@ -78,9 +85,12 @@ class UstarReadTest(ReadTest):
with open(os.path.join(TEMPDIR, "ustar/regtype"), "rU") as fobj1: with open(os.path.join(TEMPDIR, "ustar/regtype"), "rU") as fobj1:
lines1 = fobj1.readlines() lines1 = fobj1.readlines()
fobj2 = self.tar.extractfile(tarinfo) fobj2 = self.tar.extractfile(tarinfo)
lines2 = list(io.TextIOWrapper(fobj2)) try:
self.assertTrue(lines1 == lines2, lines2 = list(io.TextIOWrapper(fobj2))
"fileobj.__iter__() failed") self.assertTrue(lines1 == lines2,
"fileobj.__iter__() failed")
finally:
fobj2.close()
def test_fileobj_seek(self): def test_fileobj_seek(self):
self.tar.extract("ustar/regtype", TEMPDIR) self.tar.extract("ustar/regtype", TEMPDIR)
...@@ -138,7 +148,11 @@ class UstarReadTest(ReadTest): ...@@ -138,7 +148,11 @@ class UstarReadTest(ReadTest):
def _test_fileobj_link(self, lnktype, regtype): def _test_fileobj_link(self, lnktype, regtype):
a = self.tar.extractfile(lnktype) a = self.tar.extractfile(lnktype)
b = self.tar.extractfile(regtype) b = self.tar.extractfile(regtype)
self.assertEqual(a.name, b.name) try:
self.assertEqual(a.name, b.name)
finally:
a.close()
b.close()
def test_fileobj_link1(self): def test_fileobj_link1(self):
self._test_fileobj_link("ustar/lnktype", "ustar/regtype") self._test_fileobj_link("ustar/lnktype", "ustar/regtype")
...@@ -225,8 +239,8 @@ class MiscReadTest(CommonReadTest): ...@@ -225,8 +239,8 @@ class MiscReadTest(CommonReadTest):
data = fobj.read() data = fobj.read()
fobj = io.BytesIO(data) fobj = io.BytesIO(data)
fobj.name = "" fobj.name = ""
tar = tarfile.open(fileobj=fobj, mode=self.mode) with tarfile.open(fileobj=fobj, mode=self.mode) as tar:
self.assertEqual(tar.name, None) self.assertEqual(tar.name, None)
def test_fileobj_with_offset(self): def test_fileobj_with_offset(self):
# Skip the first member and store values from the second member # Skip the first member and store values from the second member
...@@ -237,7 +251,9 @@ class MiscReadTest(CommonReadTest): ...@@ -237,7 +251,9 @@ class MiscReadTest(CommonReadTest):
t = tar.next() t = tar.next()
name = t.name name = t.name
offset = t.offset offset = t.offset
data = tar.extractfile(t).read() f = tar.extractfile(t)
data = f.read()
f.close()
finally: finally:
tar.close() tar.close()
...@@ -319,7 +335,8 @@ class MiscReadTest(CommonReadTest): ...@@ -319,7 +335,8 @@ class MiscReadTest(CommonReadTest):
if e.errno == errno.ENOENT: if e.errno == errno.ENOENT:
self.fail("hardlink not extracted properly") self.fail("hardlink not extracted properly")
data = open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb").read() with open(os.path.join(TEMPDIR, "ustar/lnktype"), "rb") as f:
data = f.read()
self.assertEqual(md5sum(data), md5_regtype) self.assertEqual(md5sum(data), md5_regtype)
try: try:
...@@ -328,7 +345,8 @@ class MiscReadTest(CommonReadTest): ...@@ -328,7 +345,8 @@ class MiscReadTest(CommonReadTest):
if e.errno == errno.ENOENT: if e.errno == errno.ENOENT:
self.fail("symlink not extracted properly") self.fail("symlink not extracted properly")
data = open(os.path.join(TEMPDIR, "ustar/symtype"), "rb").read() with open(os.path.join(TEMPDIR, "ustar/symtype"), "rb") as f:
data = f.read()
self.assertEqual(md5sum(data), md5_regtype) self.assertEqual(md5sum(data), md5_regtype)
finally: finally:
tar.close() tar.close()
...@@ -604,10 +622,10 @@ class LongnameTest(ReadTest): ...@@ -604,10 +622,10 @@ class LongnameTest(ReadTest):
# the preceding extended header. # the preceding extended header.
longname = self.subdir + "/" + "123/" * 125 + "longname" longname = self.subdir + "/" + "123/" * 125 + "longname"
offset = self.tar.getmember(longname).offset offset = self.tar.getmember(longname).offset
fobj = open(tarname, "rb") with open(tarname, "rb") as fobj:
fobj.seek(offset) fobj.seek(offset)
tarinfo = tarfile.TarInfo.frombuf(fobj.read(512), "iso8859-1", "strict") tarinfo = tarfile.TarInfo.frombuf(fobj.read(512), "iso8859-1", "strict")
self.assertEqual(tarinfo.type, self.longnametype) self.assertEqual(tarinfo.type, self.longnametype)
class GNUReadTest(LongnameTest): class GNUReadTest(LongnameTest):
...@@ -1353,8 +1371,11 @@ class AppendTest(unittest.TestCase): ...@@ -1353,8 +1371,11 @@ class AppendTest(unittest.TestCase):
t = src.getmember("ustar/regtype") t = src.getmember("ustar/regtype")
t.name = "foo" t.name = "foo"
f = src.extractfile(t) f = src.extractfile(t)
with tarfile.open(self.tarname, mode) as tar: try:
tar.addfile(t, f) with tarfile.open(self.tarname, mode) as tar:
tar.addfile(t, f)
finally:
f.close()
def _test(self, names=["bar"], fileobj=None): def _test(self, names=["bar"], fileobj=None):
with tarfile.open(self.tarname, fileobj=fileobj) as tar: with tarfile.open(self.tarname, fileobj=fileobj) as tar:
......
...@@ -54,6 +54,9 @@ Core and Builtins ...@@ -54,6 +54,9 @@ Core and Builtins
Library Library
------- -------
- Issue #10233: Close file objects in a timely manner in the tarfile module
and its test suite.
- Issue #10093: ResourceWarnings are now issued when files and sockets are - Issue #10093: ResourceWarnings are now issued when files and sockets are
deallocated without explicit closing. These warnings are silenced by deallocated without explicit closing. These warnings are silenced by
default, except in pydebug mode. default, except in pydebug mode.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment