Browse Source

Merge pull request #9 from smartfile/python3

Python 3
test_fixup
Jessica Hair 4 years ago
committed by GitHub
parent
commit
59030cb6bf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 121 additions and 81 deletions
  1. +1
    -0
      .gitignore
  2. +8
    -6
      .travis.yml
  3. +6
    -0
      Makefile
  4. +6
    -6
      docs/conf.py
  5. +2
    -6
      libarchive/Makefile
  6. +39
    -24
      libarchive/__init__.py
  7. +1
    -1
      libarchive/_libarchive.i
  8. +9
    -2
      libarchive/_libarchive_wrap.c
  9. +2
    -2
      libarchive/tar.py
  10. +1
    -1
      libarchive/zip.py
  11. +1
    -1
      setup.py
  12. +45
    -32
      tests.py

+ 1
- 0
.gitignore View File

@@ -4,4 +4,5 @@ venv

build
libarchive/__libarchive.so
libarchive/_libarchive_wrap.o
python_libarchive.egg-info

+ 8
- 6
.travis.yml View File

@@ -1,14 +1,16 @@
language: python
python:
- "2.7"
env:
- DJANGO=1.3
- DJANGO=1.4
jobs:
include:
- python: "2.7"
env: PYVER=2.7
- python: "3.6"
env: PYVER=3.6
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq libarchive-dev
- pyenv shell $(python -c 'import platform; print(platform.python_version())')
install:
- pip install .
- make build
script:
- make test
notifications:


+ 6
- 0
Makefile View File

@@ -1,3 +1,6 @@
build:
make -C libarchive

test:
python tests.py

@@ -11,3 +14,6 @@ install:
publish:
python setup.py register
python setup.py sdist upload

clean:
make -C libarchive clean

+ 6
- 6
docs/conf.py View File

@@ -40,8 +40,8 @@ source_suffix = '.rst'
master_doc = 'index'

# General information about the project.
project = u'python-libarchive'
copyright = u'2012, Ben Timby'
project = 'python-libarchive'
copyright = '2012, Ben Timby'

# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
@@ -178,8 +178,8 @@ htmlhelp_basename = 'python-libarchivedoc'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'python-libarchive.tex', u'python-libarchive Documentation',
u'Ben Timby', 'manual'),
('index', 'python-libarchive.tex', 'python-libarchive Documentation',
'Ben Timby', 'manual'),
]

# The name of an image file (relative to this directory) to place at the top of
@@ -211,6 +211,6 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'python-libarchive', u'python-libarchive Documentation',
[u'Ben Timby'], 1)
('index', 'python-libarchive', 'python-libarchive Documentation',
['Ben Timby'], 1)
]

+ 2
- 6
libarchive/Makefile View File

@@ -1,12 +1,8 @@
CFLAGS = -g
INCLUDE = -I/usr/include -I.
LIBS = -L/usr/local/lib -l:libarchive.so.13.1.2
LIBS = -larchive

#if PYTHON_VERSION
PYVER = $(PYTHON_VERSION)
#else
PYVER = 2.7
#endif
PYVER ?= 2.7

all: __libarchive.so



+ 39
- 24
libarchive/__init__.py View File

@@ -30,10 +30,9 @@ import time
import warnings

from libarchive import _libarchive
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
from io import StringIO

PY3 = sys.version_info[0] == 3

# Suggested block size for libarchive. Libarchive may adjust it.
BLOCK_SIZE = 10240
@@ -134,7 +133,7 @@ def is_archive_name(filename, formats=None):
This function will return the name of the most likely archive format, None if the file is
unlikely to be an archive.'''
if formats is None:
formats = FORMAT_EXTENSIONS.values()
formats = list(FORMAT_EXTENSIONS.values())
format, filter = guess_format(filename)
if format in formats:
return format
@@ -153,8 +152,8 @@ def is_archive(f, formats=(None, ), filters=(None, )):

This function will return True if the file can be opened as an archive using the given
format(s)/filter(s).'''
if isinstance(f, basestring):
f = file(f, 'r')
if isinstance(f, str):
f = open(f, 'r')
a = _libarchive.archive_read_new()
for format in formats:
format = get_func(format, FORMATS, 0)
@@ -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,
@@ -330,7 +335,7 @@ class Entry(object):
if entry is None:
entry = cls(encoding=encoding)
if entry.pathname is None:
if isinstance(f, basestring):
if isinstance(f, str):
st = os.stat(f)
entry.pathname = f
entry.size = st.st_size
@@ -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)
@@ -390,9 +398,9 @@ class Archive(object):
self._stream = None
self.encoding = encoding
self.blocksize = blocksize
if isinstance(f, basestring):
if isinstance(f, str):
self.filename = f
f = file(f, mode)
f = open(f, mode)
# Only close it if we opened it...
self._defer_close = True
elif hasattr(f, 'fileno'):
@@ -520,11 +528,11 @@ class Archive(object):
def readpath(self, f):
'''Write current archive entry contents to file. f can be a file-like object or
a path.'''
if isinstance(f, basestring):
if isinstance(f, str):
basedir = os.path.basename(f)
if not os.path.exists(basedir):
os.makedirs(basedir)
f = file(f, 'w')
f = open(f, 'w')
return _libarchive.archive_read_data_into_fd(self._a, f.fileno())

def readstream(self, size):
@@ -534,23 +542,26 @@ class Archive(object):

def write(self, member, data=None):
'''Writes a string buffer to the archive as the given entry.'''
if isinstance(member, basestring):
if isinstance(member, str):
member = self.entry_class(pathname=member, encoding=self.encoding)
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):
'''Writes a file to the archive. f can be a file-like object or a path. Uses
write() to do the actual writing.'''
member = self.entry_class.from_file(f, encoding=self.encoding)
if isinstance(f, basestring):
if isinstance(f, str):
if os.path.isfile(f):
f = file(f, 'r')
f = open(f, 'r')
if pathname:
member.pathname = pathname
if folder and not member.isdir():
@@ -587,8 +598,8 @@ 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, basestring):
f = file(f, mode)
if isinstance(f, str):
f = open(f, mode)
super(SeekableArchive, self).__init__(f, **kwargs)
self.entries = []
self.eof = 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)



+ 1
- 1
libarchive/_libarchive.i View File

@@ -360,7 +360,7 @@ extern const char *archive_error_string(struct archive *);
%inline %{
PyObject *archive_read_data_into_str(struct archive *archive, int len) {
PyObject *str = NULL;
if (!(str = PyString_FromStringAndSize(NULL, len))) {
if (!(str = PyUnicode_FromStringAndSize(NULL, len))) {
PyErr_SetString(PyExc_MemoryError, "could not allocate string.");
return NULL;
}


+ 9
- 2
libarchive/_libarchive_wrap.c View File

@@ -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_STRING(x)
#define PyString_AS_STRING(x) PyBytes_AsString(x)
#define _PyLong_FromSsize_t(x) PyLong_FromSsize_t(x)

#endif
@@ -3342,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 = PyString_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;


+ 2
- 2
libarchive/tar.py View File

@@ -76,14 +76,14 @@ class TarFile(SeekableArchive):
def getnames(self):
return list(self.iterpaths)

def next(self):
def __next__(self):
raise NotImplementedError
pass # TODO: how to do this?

def extract(self, member, path=None):
if path is None:
path = os.getcwd()
if isinstance(member, basestring):
if isinstance(member, str):
f = os.path.join(path, member)
else:
f = os.path.join(path, member.pathname)


+ 1
- 1
libarchive/zip.py View File

@@ -23,7 +23,7 @@ class ZipEntry(Entry):
return self.size

def set_file_size(self, value):
assert isinstance(value, (int, long)), 'Please provide size as int or long.'
assert isinstance(value, int), 'Please provide size as int or long.'
self.size = value

file_size = property(get_file_size, set_file_size)


+ 1
- 1
setup.py View File

@@ -42,7 +42,7 @@ versrel = version + '-' + release
readme = 'README.rst'
download_url = "http://" + name + ".googlecode.com/files/" + name + "-" + \
versrel + ".tar.gz"
long_description = file(readme).read()
long_description = open(readme).read()
class build_ext_extra(build_ext, object):
"""


+ 45
- 32
tests.py View File

@@ -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:
file(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 = file(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 = file(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 = file(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 = file(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 = file(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,25 +168,27 @@ 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 = file(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 = file(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 = file(ZIPPATH, mode='r')
f = open(ZIPPATH, mode='r')
z = ZipFile(f, 'r')

entries = z.infolist()
@@ -192,46 +199,52 @@ class TestZipWrite(unittest.TestCase):
f.close()

def test_writestream(self):
f = file(ZIPPATH, mode='w')
z = ZipFile(f, 'w')
z = ZipFile(self.f, 'w')
for fname in FILENAMES:
full_path = os.path.join(TMPDIR, fname)
i = file(full_path)
i = open(full_path)
o = z.writestream(fname)
while True:
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 = file(ZIPPATH, mode='w')
z = ZipFile(f, 'w')
z = ZipFile(self.f, 'w')
for fname in FILENAMES:
full_path = os.path.join(TMPDIR, fname)
i = file(full_path)
i = open(full_path)
o = z.writestream(fname, os.path.getsize(full_path))
while True:
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 = file(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)


Loading…
Cancel
Save