From 49c8f4cf36926d40951fd3a151ddf45caf06cfdb Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Mon, 15 May 2006 19:30:35 +0000 Subject: [ 1488881 ] tarfile.py: support for file-objects and bz2 (cp. #1488634) --- Lib/tarfile.py | 69 ++++++++++++++++++++++++++++++++++++++++++++++-- Lib/test/test_tarfile.py | 23 ++++++++++++++++ Misc/NEWS | 3 +++ 3 files changed, 93 insertions(+), 2 deletions(-) diff --git a/Lib/tarfile.py b/Lib/tarfile.py index 8987ca7..6c29783 100644 --- a/Lib/tarfile.py +++ b/Lib/tarfile.py @@ -556,6 +556,69 @@ class _StreamProxy(object): self.fileobj.close() # class StreamProxy +class _BZ2Proxy(object): + """Small proxy class that enables external file object + support for "r:bz2" and "w:bz2" modes. This is actually + a workaround for a limitation in bz2 module's BZ2File + class which (unlike gzip.GzipFile) has no support for + a file object argument. + """ + + blocksize = 16 * 1024 + + def __init__(self, fileobj, mode): + self.fileobj = fileobj + self.mode = mode + self.init() + + def init(self): + import bz2 + self.pos = 0 + if self.mode == "r": + self.bz2obj = bz2.BZ2Decompressor() + self.fileobj.seek(0) + self.buf = "" + else: + self.bz2obj = bz2.BZ2Compressor() + + def read(self, size): + b = [self.buf] + x = len(self.buf) + while x < size: + try: + raw = self.fileobj.read(self.blocksize) + data = self.bz2obj.decompress(raw) + b.append(data) + except EOFError: + break + x += len(data) + self.buf = "".join(b) + + buf = self.buf[:size] + self.buf = self.buf[size:] + self.pos += len(buf) + return buf + + def seek(self, pos): + if pos < self.pos: + self.init() + self.read(pos - self.pos) + + def tell(self): + return self.pos + + def write(self, data): + self.pos += len(data) + raw = self.bz2obj.compress(data) + self.fileobj.write(raw) + + def close(self): + if self.mode == "w": + raw = self.bz2obj.flush() + self.fileobj.write(raw) + self.fileobj.close() +# class _BZ2Proxy + #------------------------ # Extraction file object #------------------------ @@ -1057,10 +1120,12 @@ class TarFile(object): tarname = pre + ext if fileobj is not None: - raise ValueError, "no support for external file objects" + fileobj = _BZ2Proxy(fileobj, mode) + else: + fileobj = bz2.BZ2File(name, mode, compresslevel=compresslevel) try: - t = cls.taropen(tarname, mode, bz2.BZ2File(name, mode, compresslevel=compresslevel)) + t = cls.taropen(tarname, mode, fileobj) except IOError: raise ReadError, "not a bzip2 file" t._extfileobj = False diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py index 03fb55f..cd58c9a 100644 --- a/Lib/test/test_tarfile.py +++ b/Lib/test/test_tarfile.py @@ -212,6 +212,17 @@ class ReadStreamTest(ReadTest): stream.close() +class ReadDetectTest(ReadTest): + + def setUp(self): + self.tar = tarfile.open(tarname(self.comp), self.mode) + +class ReadDetectFileobjTest(ReadTest): + + def setUp(self): + name = tarname(self.comp) + self.tar = tarfile.open(name, mode=self.mode, fileobj=file(name)) + class ReadAsteriskTest(ReadTest): def setUp(self): @@ -503,6 +514,10 @@ class WriteTestGzip(WriteTest): comp = "gz" class WriteStreamTestGzip(WriteStreamTest): comp = "gz" +class ReadDetectTestGzip(ReadDetectTest): + comp = "gz" +class ReadDetectFileobjTestGzip(ReadDetectFileobjTest): + comp = "gz" class ReadAsteriskTestGzip(ReadAsteriskTest): comp = "gz" class ReadStreamAsteriskTestGzip(ReadStreamAsteriskTest): @@ -526,6 +541,10 @@ if bz2: comp = "bz2" class WriteStreamTestBzip2(WriteStreamTestGzip): comp = "bz2" + class ReadDetectTestBzip2(ReadDetectTest): + comp = "bz2" + class ReadDetectFileobjTestBzip2(ReadDetectFileobjTest): + comp = "bz2" class ReadAsteriskTestBzip2(ReadAsteriskTest): comp = "bz2" class ReadStreamAsteriskTestBzip2(ReadStreamAsteriskTest): @@ -550,6 +569,8 @@ def test_main(): FileModeTest, ReadTest, ReadStreamTest, + ReadDetectTest, + ReadDetectFileobjTest, ReadAsteriskTest, ReadStreamAsteriskTest, WriteTest, @@ -567,6 +588,7 @@ def test_main(): tests.extend([ ReadTestGzip, ReadStreamTestGzip, WriteTestGzip, WriteStreamTestGzip, + ReadDetectTestGzip, ReadDetectFileobjTestGzip, ReadAsteriskTestGzip, ReadStreamAsteriskTestGzip ]) @@ -574,6 +596,7 @@ def test_main(): tests.extend([ ReadTestBzip2, ReadStreamTestBzip2, WriteTestBzip2, WriteStreamTestBzip2, + ReadDetectTestBzip2, ReadDetectFileobjTestBzip2, ReadAsteriskTestBzip2, ReadStreamAsteriskTestBzip2 ]) try: diff --git a/Misc/NEWS b/Misc/NEWS index d2aefb1..6551b99 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -40,6 +40,9 @@ Extension Modules Library ------- +- Patch #1488881: add support for external file objects in bz2 compressed + tarfiles. + - Patch #721464: pdb.Pdb instances can now be given explicit stdin and stdout arguments, making it possible to redirect input and output for remote debugging. -- cgit v0.12