diff --git a/fixtures/testfile.tar.gz b/fixtures/testfile.tar.gz new file mode 100644 index 0000000..2ccd671 Binary files /dev/null and b/fixtures/testfile.tar.gz differ diff --git a/libarchive/__init__.py b/libarchive/__init__.py index 424e7a4..b5c0684 100644 --- a/libarchive/__init__.py +++ b/libarchive/__init__.py @@ -24,6 +24,7 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os +import pathlib import stat import sys import time @@ -200,7 +201,7 @@ class EntryReadStream(object): return self def __exit__(self, *args): - return + self.close() def __iter__(self): if self.closed: @@ -438,17 +439,20 @@ class Archive(object): self.encoding = encoding self.blocksize = blocksize self.password = password + if isinstance(f, pathlib.PurePath): + f = str(f) if isinstance(f, str): self.filename = f f = open(f, mode) # Only close it if we opened it... - self._defer_close = True + self._doclose = True elif hasattr(f, 'fileno'): self.filename = getattr(f, 'name', None) # Leave the fd alone, caller should manage it... - self._defer_close = False + self._doclose = False else: raise Exception('Provided file is not path or open file.') + self._defer_close = False self.f = f self.mode = mode # Guess the format/filter from file name (if not provided) @@ -493,7 +497,7 @@ class Archive(object): return self def __exit__(self, type, value, traceback): - self.denit() + self.close() def __del__(self): self.close() @@ -563,7 +567,7 @@ class Archive(object): if hasattr(self.f, "fileno"): os.fsync(self.f.fileno()) # and then close it, if we opened it... - if getattr(self, '_close', None): + if self._doclose and getattr(self.f, 'close', None): self.f.close() @property @@ -665,8 +669,6 @@ class SeekableArchive(Archive): self._stream = None # Convert file to open file. We need this to reopen the archive. mode = kwargs.setdefault('mode', 'r') - if isinstance(f, str): - f = open(f, mode) super(SeekableArchive, self).__init__(f, **kwargs) self.entries = [] self.eof = False diff --git a/tests.py b/tests.py index 2b15aaa..9edfb9e 100644 --- a/tests.py +++ b/tests.py @@ -27,7 +27,9 @@ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. import os, unittest, tempfile, random, string, sys +import hashlib import io +import pathlib import shutil import zipfile @@ -368,6 +370,51 @@ class TestHighLevelAPI(unittest.TestCase, MakeTempMixIn): with io.FileIO(zf.fileno(), mode='r', closefd=False) as f: self._test_listing_content(f) +_defaulthash = 'sha512' + +def _readfp(fp): + while True: + r = fp.read(64*1024) + # libarchive returns None on EOF + if r == b'' or r is None: + return + + yield r + +def _hashfp(fp): + hash = getattr(hashlib, _defaulthash)() + for r in _readfp(fp): + hash.update(r) + + return '%s:%s' % (_defaulthash, hash.hexdigest()) + + +class TestArchive(unittest.TestCase): + def setUp(self): + self.fixtures = pathlib.Path(__file__).parent / 'fixtures' + + def test_closed(self): + fname = self.fixtures / 'testfile.tar.gz' + + with Archive(fname) as arch: + origfp = arch.f + + hashes = [] + + for i in arch: + if not i.isfile(): + continue + + with arch.readstream(i.size) as fp: + hashes.append(_hashfp(fp)) + + self.assertTrue(fp.closed) + self.assertIsNone(arch._stream) + + self.assertEqual(hashes, [ 'sha512:90f8342520f0ac57fb5a779f5d331c2fa87aa40f8799940257f9ba619940951e67143a8d746535ed0284924b2b7bc1478f095198800ba96d01847d7b56ca465c', 'sha512:7d5768d47b6bc27dc4fa7e9732cfa2de506ca262a2749cb108923e5dddffde842bbfee6cb8d692fb43aca0f12946c521cce2633887914ca1f96898478d10ad3f' ]) + + self.assertTrue(arch.f.closed) + if __name__ == '__main__': unittest.main()