Commit bfa33be4 authored by Norbert Preining's avatar Norbert Preining

New upstream version 0.1.3

parents
os: Visual Studio 2015
platform:
- x64
- x86
environment:
global:
UNRAR_INCLUDE: sw/include
UNRAR_LIBDIRS: sw/lib
build_script:
- ps: |
If ($env:Platform -Match "x86") {
$env:VCVARS_PLATFORM="x86"
$env:PATH="C:/Python36;$env:PATH"
} Else {
$env:VCVARS_PLATFORM="amd64"
$env:PATH="C:/Python36-x64;$env:PATH"
}
- call "%VS140COMNTOOLS%\..\..\VC\vcvarsall.bat" %VCVARS_PLATFORM%
- python.exe -c "import sys; print(sys.version_info); import platform; print(platform.architecture())"
- python.exe ci.py
test_script:
- python.exe setup.py test
build
.eggs
*.egg-info
dist
docs/_build
dist: trusty
sudo: false
env:
global:
- PYTHONHASHSEED=random
- UNRAR_INCLUDE=sw/include
- UNRAR_LIBDIRS=sw/lib
- LD_LIBRARY_PATH=sw/lib
- DYLD_LIBRARY_PATH=sw/lib
matrix:
include:
- os: linux
language: python
python: 2.7
- os: linux
language: python
python: 3.4
- os: osx
python:
language: generic
install:
- pip install psutil
- python ci.py
script:
- python setup.py test
Copyright (c) 2017-, Kovid Goyal <kovid@kovidgoyal.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
include LICENSE
unrardll
==========
|pypi| |unix_build| |windows_build|
Python wrapper for the `UNRAR DLL <http://www.rarlab.com/rar_add.htm>`_.
Usage
-------
.. code-block:: python
from unrardll import extract
extract(archive_path, destination_directory) # extract everything
from unrardll import names
print(list(names(archive_path))) # get list of filenames in archive
from unrardll import headers
from pprint import pprint
pprint(list(headers(archive_path))) # get list of file headers in archive
from unrardll import extract_member
# Extract a single file using a predicate function to select the file
filename, data = extract_member(archive_path, lambda h: h['filename'] == 'myfile.txt')
from unrardll import comment
print(comment(archive_path)) # get the comment from the archive
Installation
--------------
Assuming that the RAR dll is installed and the RAR headers available in the
include path.
.. code-block:: bash
pip install unrardll
You can set the environment variables ``UNRAR_INCLUDE`` and ``UNRAR_LIBDIRS``
to point to the location of the unrar headers and library file.
See the ``ci.py`` file for a script to install the unrar dll from source, if
needed. This is used on the continuous integration servers.
.. |pypi| image:: https://img.shields.io/pypi/v/unrardll.svg?label=version
:target: https://pypi.python.org/pypi/unrardll
:alt: Latest version released on PyPi
.. |unix_build| image:: https://api.travis-ci.org/kovidgoyal/unrardll.svg
:target: http://travis-ci.org/kovidgoyal/unrardll
:alt: Build status of the master branch on Unix
.. |windows_build| image:: https://ci.appveyor.com/api/projects/status/github/kovidgoyal/unrardll?svg=true
:target: https://ci.appveyor.com/project/kovidgoyal/unrardll
:alt: Build status of the master branch on Windows
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: BSD Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import absolute_import, division, print_function, unicode_literals
import glob
import os
import re
import shutil
import subprocess
import sys
import tarfile
import time
from io import BytesIO
try:
from urllib.request import urlopen
except ImportError:
from urllib import urlopen
isosx = 'darwin' in sys.platform.lower()
iswindows = hasattr(sys, 'getwindowsversion')
is64bit = sys.maxsize > (1 << 32)
def download(url):
i = 5
while i > 0:
i -= 1
try:
return urlopen(url).read()
except Exception:
if i <= 0:
raise
print('Download failed, retrying...')
sys.stdout.flush()
time.sleep(1)
def download_unrar():
html = download('http://www.rarlab.com/rar_add.htm').decode('utf-8', 'replace')
href = re.search(r'<a\s+.*?href="([^"]+)".*?>UnRAR source</a>', html).group(1)
print('Downloading unrar', href)
sys.stdout.flush()
return download(href)
def download_and_extract():
raw = download_unrar()
with tarfile.open(fileobj=BytesIO(raw), mode='r:*') as tf:
tf.extractall()
def replace_in_file(path, old, new, missing_ok=False):
if isinstance(old, type('')):
old = old.encode('utf-8')
if isinstance(new, type('')):
new = new.encode('utf-8')
with open(path, 'r+b') as f:
raw = f.read()
if isinstance(old, bytes):
nraw = raw.replace(old, new)
else:
nraw = old.sub(new, raw)
if raw == nraw and not missing_ok:
raise ValueError('Failed (pattern not found) to patch: ' + path)
f.seek(0), f.truncate()
f.write(nraw)
def build_unix():
if isosx:
with open('makefile', 'r+b') as m:
raw = m.read().decode('utf-8')
raw = raw.replace('libunrar.so', 'libunrar.dylib')
m.seek(0), m.truncate()
m.write(raw.encode('utf-8'))
flags = '-fPIC ' + os.environ.get('CXXFLAGS', '')
subprocess.check_call(['make', '-j4', 'lib', 'CXXFLAGS="%s"' % flags.strip()])
lib = 'libunrar.' + ('dylib' if isosx else 'so')
shutil.copy2(lib, '../../lib')
def build_windows():
PL = 'x64' if is64bit else 'Win32'
subprocess.check_call([
'msbuild.exe', 'UnRARDll.vcxproj', '/t:Build', '/p:Platform=' + PL, '/p:Configuration=Release'])
lib = glob.glob('./build/*/Release/UnRAR.lib')[0]
dll = glob.glob('./build/*/Release/unrar.dll')[0]
shutil.copy2(lib, '../../lib')
shutil.copy2(dll, '../../..')
def build_unrar():
os.makedirs('sw/build'), os.makedirs('sw/include/unrar'), os.mkdir('sw/lib')
os.chdir('sw/build')
download_and_extract()
os.chdir('unrar')
replace_in_file('dll.cpp', 'WideToChar', 'WideToUtf')
(build_windows if iswindows else build_unix)()
for f in glob.glob('*.hpp'):
shutil.copy2(f, '../../include/unrar')
if __name__ == '__main__':
build_unrar()
#!/usr/bin/env python2
# vim:fileencoding=utf-8
# License: Apache 2.0 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import absolute_import, division, print_function, unicode_literals
import glob
import os
import re
import shlex
import shutil
import subprocess
import sys
os.chdir(os.path.dirname(os.path.abspath(__file__)))
raw = open('src/unrardll/__init__.py', 'rb').read().decode('utf-8')
version = map(
int, re.search(r'^version = V\((\d+), (\d+), (\d+)', raw, flags=re.M).groups())
VERSION = '{}.{}.{}'.format(*version)
def red(text):
return '\033[91;1m' + text + '\033[39;22m'
def green(text):
return '\033[92;1m' + text + '\033[39;22m'
def run(*cmd):
if len(cmd) == 1:
cmd = shlex.split(cmd[0])
print(green(' '.join(cmd)))
ret = subprocess.Popen(cmd).wait()
if ret != 0:
raise SystemExit(ret)
def build_release():
for rem in 'dist build'.split():
os.path.exists(rem) and shutil.rmtree(rem)
run(sys.executable, 'setup.py', '-q', 'sdist')
def sign_release():
for installer in glob.glob('dist/*'):
run(os.environ['PENV'] + '/gpg-as-kovid', '--armor', '--detach-sig', installer)
def tag_release():
run('git push')
run('git tag -s "v{0}" -m "version-{0}"'.format(VERSION))
run('git push origin "v{0}"'.format(VERSION))
def upload_release():
files = list(glob.glob('dist/*'))
run('twine', 'upload', '--config-file', os.path.join(os.environ['PENV'], 'pypi'), *files)
try:
raw_input
except NameError:
raw_input = input
def main():
if raw_input('Publish version {} [y/n]? '.format(red(VERSION))) != 'y':
raise SystemExit(1)
build_release()
sign_release()
if raw_input(red('Upload') + ' release [y/n]? ') != 'y':
raise SystemExit(1)
tag_release()
upload_release()
if __name__ == '__main__':
main()
let g:ycm_python_binary_path = 'python3'
let g:syntastic_python_flake8_exec = 'flake8'
[flake8]
max-line-length = 160
[yapf]
based_on_style = pep8
column_limit = 85
split_penalty_import_names = 100
dedent_closing_brackets = True
coalesce_brackets = True
blank_line_before_nested_class_or_def = True
[isort]
combine_as_imports = True
line_length = 85
multi_line_output = 5
#!/usr/bin/env python2
# vim:fileencoding=utf-8
# License: BSD Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import absolute_import, division, print_function, unicode_literals
import importlib
import os
import re
import sys
from distutils.command.build import build as Build
from setuptools import Extension, setup
self_path = os.path.abspath(__file__)
base = os.path.dirname(self_path)
iswindows = hasattr(sys, 'getwindowsversion')
raw = open(os.path.join(base, 'src/unrardll/__init__.py'),
'rb').read().decode('utf-8')
version = map(
int, re.search(r'^version = V\((\d+), (\d+), (\d+)', raw, flags=re.M).groups())
def include_dirs():
ans = []
if 'UNRAR_INCLUDE' in os.environ:
ans.extend(os.environ['UNRAR_INCLUDE'].split(os.pathsep))
return ans
def libraries():
return ['unrar']
def library_dirs():
ans = []
if 'UNRAR_LIBDIRS' in os.environ:
ans.extend(os.environ['UNRAR_LIBDIRS'].split(os.pathsep))
return ans
def macros():
ans = [
('SILENT', 1),
('RARDLL', 1),
('UNRAR', 1), ]
if not iswindows:
ans.append(('_UNIX', 1))
return ans
def find_tests():
import unittest
suites = []
for f in os.listdir(os.path.join(base, 'test')):
n, ext = os.path.splitext(f)
if ext == '.py' and n not in ('__init__',):
m = importlib.import_module('test.' + n)
suite = unittest.defaultTestLoader.loadTestsFromModule(m)
suites.append(suite)
return unittest.TestSuite(suites)
class Test(Build):
description = "run unit tests after in-place build"
def run(self):
import unittest
Build.run(self)
if self.dry_run:
self.announce('skipping "test" (dry run)')
return
sys.path.insert(0, self.build_lib)
tests = find_tests()
r = unittest.TextTestRunner
result = r(verbosity=2).run(tests)
if not result.wasSuccessful():
raise SystemExit(1)
CLASSIFIERS = """\
Development Status :: 5 - Production/Stable
Intended Audience :: Developers
License :: OSI Approved :: BSD License
Natural Language :: English
Operating System :: OS Independent
Programming Language :: Python
Topic :: Software Development :: Libraries :: Python Modules
Topic :: System :: Archiving :: Compression
"""
setup(
name=str('unrardll'),
version='{}.{}.{}'.format(*version),
author='Kovid Goyal',
author_email='redacted@acme.com',
description='Wrap the Unrar DLL to enable unraring of files in python',
license='BSD',
url='https://github.com/kovidgoyal/unrardll',
classifiers=[c for c in CLASSIFIERS.split("\n") if c],
platforms=['any'],
packages=[str('unrardll')],
package_dir={'': str('src')},
cmdclass={'test': Test},
ext_modules=[
Extension(
str('unrardll.unrar'),
include_dirs=include_dirs(),
libraries=libraries(),
library_dirs=library_dirs(),
define_macros=macros(),
sources=[str('src/unrardll/wrapper.cpp')]
)
]
)
This diff is collapsed.
This diff is collapsed.
#!/usr/bin/env python
# vim:fileencoding=utf-8
# License: BSD Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import absolute_import, division, print_function, unicode_literals
import os
import shutil
import sys
import tempfile
import unittest
base = os.path.dirname(os.path.abspath(__file__))
iswindows = hasattr(sys, 'getwindowsversion')
class TestCase(unittest.TestCase):
ae = unittest.TestCase.assertEqual
longMessage = True
tb_locals = True
maxDiff = None
class TempDir(object):
def __enter__(self):
self.tdir = tempfile.mkdtemp()
if isinstance(self.tdir, bytes):
self.tdir = self.tdir.decode(sys.getfilesystemencoding())
return self.tdir
def __exit__(self, *a):
shutil.rmtree(self.tdir)
del self.tdir
#!/usr/bin/env python2
# vim:fileencoding=utf-8
# License: BSD Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import absolute_import, division, print_function, unicode_literals
import hashlib
import os
import unicodedata
import unittest
from binascii import crc32
from unrardll import (
BadPassword, PasswordRequired, comment, extract, extract_member, headers, names,
open_archive, unrar
)
from . import TempDir, TestCase, base
multipart_rar = os.path.join(base, 'example_split_archive.part1.rar')
password_rar = os.path.join(base, 'example_password_protected.rar')
simple_rar = os.path.join(base, 'simple.rar')
sr_data = {
'1': b'',
'1/sub-one': b'sub-one\n',
'2': b'',
'2/sub-two.txt': b'sub-two\n',
'Füße.txt': b'unicode\n',
'max-compressed': b'max\n',
'one.txt': b'one\n',
'symlink': b'sub-two',
'uncompressed': b'uncompressed\n',
'诶比屁.txt': b'chinese unicode\n'}
def normalize(x):
return unicodedata.normalize('NFC', x)
def get_memory():
'Return memory usage in bytes'
# See https://pythonhosted.org/psutil/#psutil.Process.memory_info
try:
import psutil
except ImportError:
raise unittest.SkipTest('psutil is not available')
return psutil.Process(os.getpid()).memory_info().rss
def memory(since=0.0):
'Return memory used in MB. The value of since is subtracted from the used memory'
ans = get_memory()
ans /= float(1024**2)
return ans - since
class BasicTests(TestCase):
def test_names(self):
all_names = [
'1/sub-one', 'one.txt', '诶比屁.txt', 'Füße.txt', '2/sub-two.txt',
'symlink', '1', '2', 'uncompressed', 'max-compressed']
self.ae(all_names, list(names(simple_rar)))
all_names.remove('symlink'), all_names.remove('1'), all_names.remove('2')
self.ae(all_names, list(names(simple_rar, only_useful=True)))
def test_comment(self):
self.ae(comment(simple_rar), 'some comment\n')
self.ae(comment(password_rar), '')
def test_share_open(self):
with open(simple_rar, 'rb') as f:
self.ae(comment(simple_rar), 'some comment\n')
f.close()
def test_extract(self):
for v in (True, False):
with TempDir() as tdir:
extract(simple_rar, tdir, verify_data=v)
h = {
normalize(os.path.abspath(os.path.join(tdir, h['filename']))): h
for h in headers(simple_rar)}
data = {}
for dirpath, dirnames, filenames in os.walk(tdir):
for f in filenames:
path = normalize(os.path.join(dirpath, f))
data[os.path.relpath(path, tdir).replace(os.sep, '/')] = d = open(path, 'rb').read()
if f == 'one.txt':
self.ae(os.path.getmtime(path), 1098472879)
self.ae(h[path]['unpack_size'], len(d))
self.ae(h[path]['file_crc'] & 0xffffffff, crc32(d) & 0xffffffff)
q = {k: v for k, v in sr_data.items() if v}
del q['symlink']
self.ae(data, q)
def test_password(self):
with TempDir() as tdir:
self.assertRaises(PasswordRequired, extract, password_rar, tdir)
self.assertRaises(BadPassword, extract, password_rar, tdir, password='sfasgsfdg')
extract(password_rar, tdir, password='example')
def test_invalid_callback(self):
class Callback(object):
pass
with open_archive(simple_rar, None, mode=unrar.RAR_OM_EXTRACT) as f:
unrar.read_next_header(f)
self.assertRaisesRegexp(unrar.UNRARError, "An exception occurred in the password callback handler", unrar.process_file, f)
c = Callback()
c._process_data = lambda x: None
with open_archive(simple_rar, c, mode=unrar.RAR_OM_EXTRACT) as f:
unrar.read_next_header(f)
self.assertRaisesRegexp(unrar.UNRARError, "Processing canceled by the callback", unrar.process_file, f)
def test_multipart(self):
self.ae(list(names(multipart_rar)), ['Fifteen_Feet_of_Time.pdf'])
for v in (True, False):
with TempDir() as tdir:
extract(multipart_rar, tdir, verify_data=v)
h = next(headers(multipart_rar))
raw = open(os.path.join(tdir, h['filename']), 'rb').read()
self.ae(len(raw), h['unpack_size'])
self.ae(hashlib.sha1(raw).hexdigest(), 'a9fc6a11d000044f17fcdf65816348ce0be3b145')
def test_extract_member(self):
self.ae(extract_member(simple_rar, lambda h: h['filename'] == 'one.txt', verify_data=True), ('one.txt', sr_data['one.txt']))
self.ae(extract_member(simple_rar, lambda h: False), (None, None))
def test_open_failure(self):
self.assertRaises(OSError, extract, 'sdfgsfgsggsdfg.rar', '.')
def test_memory_leaks(self):
import gc
def collect():
for i in range(6):
gc.collect()
gc.collect()
with TempDir() as tdir:
def get_mem_use(num):
collect()
start = memory()
for i in range(num):
extract(simple_rar, tdir)
collect()
return max(0, memory(start))
get_mem_use(5) # ensure no memory used by get_mem_use itself is counted
a, b = get_mem_use(10), get_mem_use(100)
self.assertTrue(a == 0 or b/a < 3, '10 times usage: {} 100 times usage: {}'.format(a, b))
File added
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment