diff --git a/libarchive/__init__.py b/libarchive/__init__.py index 5bb0e1d..8cd8f95 100644 --- a/libarchive/__init__.py +++ b/libarchive/__init__.py @@ -30,10 +30,9 @@ import time import warnings from libarchive import _libarchive -try: - from io import StringIO -except ImportError: - from io import StringIO +from io import StringIO + +PY3 = sys.version_info[0] == 3 # Suggested block size for libarchive. Libarchive may adjust it. BLOCK_SIZE = 10240 @@ -165,7 +164,7 @@ def is_archive(f, formats=(None, ), filters=(None, )): filter = get_func(filter, FILTERS, 0) if filter is None: return False - list(filter(a)) + filter(a) try: try: call_and_check(_libarchive.archive_read_open_fd, a, a, f.fileno(), BLOCK_SIZE) @@ -175,6 +174,7 @@ def is_archive(f, formats=(None, ), filters=(None, )): finally: _libarchive.archive_read_close(a) _libarchive.archive_read_free(a) + f.close() class EntryReadStream(object): @@ -271,7 +271,7 @@ class EntryWriteStream(object): if self.buffer: self.buffer.write(data) else: - _libarchive.archive_write_data_from_str(self.archive._a, data) + _libarchive.archive_write_data_from_str(self.archive._a, data.encode('utf-8')) self.bytes += len(data) def close(self): @@ -280,7 +280,7 @@ class EntryWriteStream(object): if self.buffer: self.entry.size = self.buffer.tell() self.entry.to_archive(self.archive) - _libarchive.archive_write_data_from_str(self.archive._a, self.buffer.getvalue()) + _libarchive.archive_write_data_from_str(self.archive._a, self.buffer.getvalue().encode('utf-8')) _libarchive.archive_write_finish_entry(self.archive._a) # Call archive.close() with _defer True to let it know we have been @@ -312,8 +312,13 @@ class Entry(object): call_and_check(_libarchive.archive_read_next_header2, archive._a, archive._a, e) mode = _libarchive.archive_entry_filetype(e) mode |= _libarchive.archive_entry_perm(e) - entry = cls( + if PY3: + pathname=_libarchive.archive_entry_pathname(e) + else: pathname=_libarchive.archive_entry_pathname(e).decode(encoding), + + entry = cls( + pathname=pathname, size=_libarchive.archive_entry_size(e), mtime=_libarchive.archive_entry_mtime(e), mode=mode, @@ -353,7 +358,10 @@ class Entry(object): '''Creates an archive header and writes it to the given archive.''' e = _libarchive.archive_entry_new() try: - _libarchive.archive_entry_set_pathname(e, self.pathname.encode(self.encoding)) + if PY3: + _libarchive.archive_entry_set_pathname(e, self.pathname) + else: + _libarchive.archive_entry_set_pathname(e, self.pathname.encode(self.encoding)) _libarchive.archive_entry_set_filetype(e, stat.S_IFMT(self.mode)) _libarchive.archive_entry_set_perm(e, stat.S_IMODE(self.mode)) _libarchive.archive_entry_set_size(e, self.size) @@ -539,9 +547,12 @@ class Archive(object): if data: member.size = len(data) member.to_archive(self) - + if data: - _libarchive.archive_write_data_from_str(self._a, data) + if PY3: + result = _libarchive.archive_write_data_from_str(self._a, data.encode('utf8')) + else: + result = _libarchive.archive_write_data_from_str(self._a, data) _libarchive.archive_write_finish_entry(self._a) def writepath(self, f, pathname=None, folder=False): @@ -614,7 +625,11 @@ class SeekableArchive(Archive): def getentry(self, pathname): '''Take a name or entry object and returns an entry object.''' for entry in self: - if entry.pathname == pathname: + if PY3: + entry_pathname = entry.pathname + if not PY3: + entry_pathname = entry.pathname[0] + if entry_pathname == pathname: return entry raise KeyError(pathname) diff --git a/libarchive/_libarchive_wrap.c b/libarchive/_libarchive_wrap.c index 644f7cb..1c4c9e0 100644 --- a/libarchive/_libarchive_wrap.c +++ b/libarchive/_libarchive_wrap.c @@ -739,7 +739,7 @@ SWIG_UnpackDataName(const char *c, void *ptr, size_t sz, const char *name) { #define PyString_Size(str) PyBytes_Size(str) #define PyString_InternFromString(key) PyUnicode_InternFromString(key) #define Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_BASETYPE -#define PyString_AS_STRING(x) PyUnicode_AS_UNICODE(x) +#define PyString_AS_STRING(x) PyBytes_AsString(x) #define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x) #endif @@ -3274,7 +3274,11 @@ SWIG_FromCharPtrAndSize(const char* carray, size_t size) return pchar_descriptor ? SWIG_InternalNewPointerObj((char *)(carray), pchar_descriptor, 0) : SWIG_Py_Void(); } else { -return PyUnicode_FromStringAndSize(carray, (int)(size)); +#if PY_VERSION_HEX >= 0x03000000 + return PyUnicode_FromStringAndSize(carray, (int)(size)); +#else + return PyString_FromStringAndSize(carray, (int)(size)); +#endif } } else { return SWIG_Py_Void(); @@ -3338,10 +3342,17 @@ SWIG_AsVal_unsigned_SS_short (PyObject * obj, unsigned short *val) PyObject *archive_read_data_into_str(struct archive *archive, int len) { PyObject *str = NULL; - if (!(str = PyUnicode_FromStringAndSize(NULL, len))) { +#if PY_VERSION_HEX >= 0x03000000 + if (!(str = PyBytes_FromStringAndSize(NULL, len))) { PyErr_SetString(PyExc_MemoryError, "could not allocate string."); return NULL; } +#else + if (!(str = PyString_FromStringAndSize(NULL, len))) { + PyErr_SetString(PyExc_MemoryError, "could not allocate string."); + return NULL; + } +#endif if (len != archive_read_data(archive, PyString_AS_STRING(str), len)) { PyErr_SetString(PyExc_RuntimeError, "could not read requested data."); return NULL; diff --git a/tests.py b/tests.py index cb41d61..fa8cba8 100644 --- a/tests.py +++ b/tests.py @@ -26,11 +26,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import os, unittest, tempfile, random, string, subprocess +import os, unittest, tempfile, random, string, subprocess, sys from libarchive import is_archive_name, is_archive from libarchive.zip import is_zipfile, ZipFile, ZipEntry +PY3 = sys.version_info[0] == 3 + TMPDIR = tempfile.mkdtemp() ZIPCMD = '/usr/bin/zip' ZIPFILE = 'test.zip' @@ -47,7 +49,8 @@ FILENAMES = [ def make_temp_files(): if not os.path.exists(ZIPPATH): for name in FILENAMES: - open(os.path.join(TMPDIR, name), 'w').write(''.join(random.sample(string.printable, 10))) + with open(os.path.join(TMPDIR, name), 'w') as f: + f.write(''.join(random.sample(string.ascii_letters, 10))) def make_temp_archive(): @@ -93,14 +96,17 @@ class TestIsArchiveTar(unittest.TestCase): class TestZipRead(unittest.TestCase): def setUp(self): make_temp_archive() + self.f = open(ZIPPATH, mode='r') + + def tearDown(self): + self.f.close() def test_iszipfile(self): self.assertEqual(is_zipfile('/dev/null'), False) self.assertEqual(is_zipfile(ZIPPATH), True) def test_iterate(self): - f = open(ZIPPATH, mode='r') - z = ZipFile(f, 'r') + z = ZipFile(self.f, 'r') count = 0 for e in z: count += 1 @@ -108,8 +114,7 @@ class TestZipRead(unittest.TestCase): def test_deferred_close_by_archive(self): """ Test archive deferred close without a stream. """ - f = open(ZIPPATH, mode='r') - z = ZipFile(f, 'r') + z = ZipFile(self.f, 'r') self.assertIsNotNone(z._a) self.assertIsNone(z._stream) z.close() @@ -117,8 +122,7 @@ class TestZipRead(unittest.TestCase): def test_deferred_close_by_stream(self): """ Ensure archive closes self if stream is closed first. """ - f = open(ZIPPATH, mode='r') - z = ZipFile(f, 'r') + z = ZipFile(self.f, 'r') stream = z.readstream(FILENAMES[0]) stream.close() # Make sure archive stays open after stream is closed. @@ -131,8 +135,7 @@ class TestZipRead(unittest.TestCase): def test_close_stream_first(self): """ Ensure that archive stays open after being closed if a stream is open. Further, ensure closing the stream closes the archive. """ - f = open(ZIPPATH, mode='r') - z = ZipFile(f, 'r') + z = ZipFile(self.f, 'r') stream = z.readstream(FILENAMES[0]) z.close() try: @@ -146,11 +149,13 @@ class TestZipRead(unittest.TestCase): self.assertIsNone(z._stream) def test_filenames(self): - f = open(ZIPPATH, mode='r') - z = ZipFile(f, 'r') + z = ZipFile(self.f, 'r') names = [] for e in z: - names.append(e.filename) + if PY3: + names.append(e.filename) + else: + names.append(e.filename[0]) self.assertEqual(names, FILENAMES, 'File names differ in archive.') #~ def test_non_ascii(self): @@ -163,23 +168,25 @@ class TestZipRead(unittest.TestCase): class TestZipWrite(unittest.TestCase): def setUp(self): make_temp_files() + self.f = open(ZIPPATH, mode='w') + + def tearDown(self): + self.f.close() def test_writepath(self): - f = open(ZIPPATH, mode='w') - z = ZipFile(f, 'w') + z = ZipFile(self.f, 'w') for fname in FILENAMES: - z.writepath(file(os.path.join(TMPDIR, fname), 'r')) + with open(os.path.join(TMPDIR, fname), 'r') as f: + z.writepath(f) z.close() def test_writepath_directory(self): """ Test writing a directory. """ - - f = open(ZIPPATH, mode='w') - z = ZipFile(f, 'w') + z = ZipFile(self.f, 'w') z.writepath(None, pathname='/testdir', folder=True) z.writepath(None, pathname='/testdir/testinside', folder=True) z.close() - f.close() + self.f.close() f = open(ZIPPATH, mode='r') z = ZipFile(f, 'r') @@ -192,8 +199,7 @@ class TestZipWrite(unittest.TestCase): f.close() def test_writestream(self): - f = open(ZIPPATH, mode='w') - z = ZipFile(f, 'w') + z = ZipFile(self.f, 'w') for fname in FILENAMES: full_path = os.path.join(TMPDIR, fname) i = open(full_path) @@ -202,14 +208,16 @@ class TestZipWrite(unittest.TestCase): data = i.read(1) if not data: break - o.write(data) + if PY3: + o.write(data) + else: + o.write(unicode(data)) o.close() i.close() z.close() def test_writestream_unbuffered(self): - f = open(ZIPPATH, mode='w') - z = ZipFile(f, 'w') + z = ZipFile(self.f, 'w') for fname in FILENAMES: full_path = os.path.join(TMPDIR, fname) i = open(full_path) @@ -218,20 +226,25 @@ class TestZipWrite(unittest.TestCase): data = i.read(1) if not data: break - o.write(data) + if PY3: + o.write(data) + else: + o.write(unicode(data)) o.close() i.close() z.close() def test_deferred_close_by_archive(self): """ Test archive deferred close without a stream. """ - f = open(ZIPPATH, mode='w') - z = ZipFile(f, 'w') + z = ZipFile(self.f, 'w') o = z.writestream(FILENAMES[0]) z.close() self.assertIsNotNone(z._a) self.assertIsNotNone(z._stream) - o.write('testdata') + if PY3: + o.write('testdata') + else: + o.write(unicode('testdata')) o.close() self.assertIsNone(z._a) self.assertIsNone(z._stream)