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