diff --git a/PKG-INFO b/PKG-INFO index c3dc99b..521757b 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,91 +1,91 @@ Metadata-Version: 2.1 Name: swh.core -Version: 0.0.83 +Version: 0.0.84 Summary: Software Heritage core utilities Home-page: https://forge.softwareheritage.org/diffusion/DCORE/ Author: Software Heritage developers Author-email: swh-devel@inria.fr License: UNKNOWN Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest Project-URL: Funding, https://www.softwareheritage.org/donate Project-URL: Source, https://forge.softwareheritage.org/source/swh-core Description: swh-core ======== core library for swh's modules: - config parser - hash computations - serialization - logging mechanism - database connection - http-based RPC client/server Development ----------- We strongly recommend you to use a [virtualenv][1] if you want to run tests or hack the code. To set up your development environment: ``` (swh) user@host:~/swh-environment/swh-core$ pip install -e .[testing] ``` This will install every Python package needed to run this package's tests. Unit tests can be executed using [pytest][2] or [tox][3]. ``` (swh) user@host:~/swh-environment/swh-core$ pytest ============================== test session starts ============================== platform linux -- Python 3.7.3, pytest-3.10.1, py-1.8.0, pluggy-0.12.0 hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ddouard/src/swh-environment/swh-core/.hypothesis/examples') rootdir: /home/ddouard/src/swh-environment/swh-core, inifile: pytest.ini plugins: requests-mock-1.6.0, hypothesis-4.26.4, celery-4.3.0, postgresql-1.4.1 collected 89 items swh/core/api/tests/test_api.py .. [ 2%] swh/core/api/tests/test_async.py .... [ 6%] swh/core/api/tests/test_serializers.py ..... [ 12%] swh/core/db/tests/test_db.py .... [ 16%] swh/core/tests/test_cli.py ...... [ 23%] swh/core/tests/test_config.py .............. [ 39%] swh/core/tests/test_statsd.py ........................................... [ 87%] .... [ 92%] swh/core/tests/test_utils.py ....... [100%] ===================== 89 passed, 9 warnings in 6.94 seconds ===================== ``` Note: this git repository uses [pre-commit][4] hooks to ensure better and more consistent code. It should already be installed in your virtualenv (if not, just type `pip install pre-commit`). Make sure to activate it in your local copy of the git repository: ``` (swh) user@host:~/swh-environment/swh-core$ pre-commit install pre-commit installed at .git/hooks/pre-commit ``` Please read the [developer setup manual][5] for more information on how to hack on Software Heritage. [1]: https://virtualenv.pypa.io [2]: https://docs.pytest.org [3]: https://tox.readthedocs.io [4]: https://pre-commit.com [5]: https://docs.softwareheritage.org/devel/developer-setup.html Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) Classifier: Operating System :: OS Independent Classifier: Development Status :: 5 - Production/Stable Description-Content-Type: text/markdown Provides-Extra: testing-core Provides-Extra: logging Provides-Extra: db Provides-Extra: testing-db Provides-Extra: http Provides-Extra: testing diff --git a/swh.core.egg-info/PKG-INFO b/swh.core.egg-info/PKG-INFO index c3dc99b..521757b 100644 --- a/swh.core.egg-info/PKG-INFO +++ b/swh.core.egg-info/PKG-INFO @@ -1,91 +1,91 @@ Metadata-Version: 2.1 Name: swh.core -Version: 0.0.83 +Version: 0.0.84 Summary: Software Heritage core utilities Home-page: https://forge.softwareheritage.org/diffusion/DCORE/ Author: Software Heritage developers Author-email: swh-devel@inria.fr License: UNKNOWN Project-URL: Bug Reports, https://forge.softwareheritage.org/maniphest Project-URL: Funding, https://www.softwareheritage.org/donate Project-URL: Source, https://forge.softwareheritage.org/source/swh-core Description: swh-core ======== core library for swh's modules: - config parser - hash computations - serialization - logging mechanism - database connection - http-based RPC client/server Development ----------- We strongly recommend you to use a [virtualenv][1] if you want to run tests or hack the code. To set up your development environment: ``` (swh) user@host:~/swh-environment/swh-core$ pip install -e .[testing] ``` This will install every Python package needed to run this package's tests. Unit tests can be executed using [pytest][2] or [tox][3]. ``` (swh) user@host:~/swh-environment/swh-core$ pytest ============================== test session starts ============================== platform linux -- Python 3.7.3, pytest-3.10.1, py-1.8.0, pluggy-0.12.0 hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/home/ddouard/src/swh-environment/swh-core/.hypothesis/examples') rootdir: /home/ddouard/src/swh-environment/swh-core, inifile: pytest.ini plugins: requests-mock-1.6.0, hypothesis-4.26.4, celery-4.3.0, postgresql-1.4.1 collected 89 items swh/core/api/tests/test_api.py .. [ 2%] swh/core/api/tests/test_async.py .... [ 6%] swh/core/api/tests/test_serializers.py ..... [ 12%] swh/core/db/tests/test_db.py .... [ 16%] swh/core/tests/test_cli.py ...... [ 23%] swh/core/tests/test_config.py .............. [ 39%] swh/core/tests/test_statsd.py ........................................... [ 87%] .... [ 92%] swh/core/tests/test_utils.py ....... [100%] ===================== 89 passed, 9 warnings in 6.94 seconds ===================== ``` Note: this git repository uses [pre-commit][4] hooks to ensure better and more consistent code. It should already be installed in your virtualenv (if not, just type `pip install pre-commit`). Make sure to activate it in your local copy of the git repository: ``` (swh) user@host:~/swh-environment/swh-core$ pre-commit install pre-commit installed at .git/hooks/pre-commit ``` Please read the [developer setup manual][5] for more information on how to hack on Software Heritage. [1]: https://virtualenv.pypa.io [2]: https://docs.pytest.org [3]: https://tox.readthedocs.io [4]: https://pre-commit.com [5]: https://docs.softwareheritage.org/devel/developer-setup.html Platform: UNKNOWN Classifier: Programming Language :: Python :: 3 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) Classifier: Operating System :: OS Independent Classifier: Development Status :: 5 - Production/Stable Description-Content-Type: text/markdown Provides-Extra: testing-core Provides-Extra: logging Provides-Extra: db Provides-Extra: testing-db Provides-Extra: http Provides-Extra: testing diff --git a/swh.core.egg-info/SOURCES.txt b/swh.core.egg-info/SOURCES.txt index c6cd455..bd230b7 100644 --- a/swh.core.egg-info/SOURCES.txt +++ b/swh.core.egg-info/SOURCES.txt @@ -1,72 +1,79 @@ MANIFEST.in Makefile README.md conftest.py requirements-db.txt requirements-http.txt requirements-logging.txt requirements-swh.txt requirements-test-db.txt requirements-test.txt requirements.txt setup.py version.txt swh/__init__.py swh.core.egg-info/PKG-INFO swh.core.egg-info/SOURCES.txt swh.core.egg-info/dependency_links.txt swh.core.egg-info/entry_points.txt swh.core.egg-info/requires.txt swh.core.egg-info/top_level.txt swh/core/__init__.py swh/core/api_async.py swh/core/config.py swh/core/logger.py swh/core/py.typed swh/core/pytest_plugin.py swh/core/statsd.py swh/core/tarball.py swh/core/utils.py swh/core/api/__init__.py swh/core/api/asynchronous.py swh/core/api/gunicorn_config.py swh/core/api/negotiation.py swh/core/api/serializers.py swh/core/api/tests/__init__.py swh/core/api/tests/server_testing.py swh/core/api/tests/test_async.py swh/core/api/tests/test_gunicorn.py swh/core/api/tests/test_rpc_client.py swh/core/api/tests/test_rpc_client_server.py swh/core/api/tests/test_rpc_server.py swh/core/api/tests/test_serializers.py swh/core/cli/__init__.py swh/core/cli/db.py swh/core/db/__init__.py swh/core/db/common.py swh/core/db/db_utils.py swh/core/db/tests/__init__.py swh/core/db/tests/conftest.py swh/core/db/tests/db_testing.py swh/core/db/tests/test_cli.py swh/core/db/tests/test_db.py swh/core/sql/log-schema.sql swh/core/tests/__init__.py swh/core/tests/test_cli.py swh/core/tests/test_config.py swh/core/tests/test_logger.py swh/core/tests/test_pytest_plugin.py swh/core/tests/test_statsd.py swh/core/tests/test_tarball.py swh/core/tests/test_utils.py +swh/core/tests/data/archives/groff-1.02.tar.Z +swh/core/tests/data/archives/hello.tar +swh/core/tests/data/archives/hello.tar.bz2 +swh/core/tests/data/archives/hello.tar.gz +swh/core/tests/data/archives/hello.tar.lz +swh/core/tests/data/archives/hello.tar.x +swh/core/tests/data/archives/hello.zip swh/core/tests/data/http_example.com/something.json swh/core/tests/data/https_example.com/file.json swh/core/tests/data/https_example.com/file.json,name=doe,firstname=jane swh/core/tests/data/https_example.com/file.json_visit1 swh/core/tests/data/https_example.com/other.json swh/core/tests/data/https_forge.s.o/api_diffusion,attachments[uris]=1 swh/core/tests/data/https_www.reference.com/web,q=What+Is+an+Example+of+a+URL?,qo=contentPageRelatedSearch,o=600605,l=dir,sga=1 swh/core/tests/fixture/__init__.py swh/core/tests/fixture/conftest.py swh/core/tests/fixture/test_pytest_plugin.py swh/core/tests/fixture/data/https_example.com/file.json \ No newline at end of file diff --git a/swh/core/tarball.py b/swh/core/tarball.py index 261bfe7..1544432 100644 --- a/swh/core/tarball.py +++ b/swh/core/tarball.py @@ -1,228 +1,147 @@ -# Copyright (C) 2015-2017 The Software Heritage developers +# Copyright (C) 2015-2019 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information import os +import shutil import stat import tarfile import zipfile -from os.path import abspath, realpath, join, dirname -from . import utils - - -def _canonical_abspath(path): - """Resolve all paths to an absolute and real one. - - Args: - path: to resolve - - Returns: - canonical absolute path to path - - """ - return realpath(abspath(path)) - - -def _badpath(path, basepath): - """Determine if a path is outside basepath. - - Args: - path: a relative or absolute path of a file or directory - basepath: the basepath path must be in - - Returns: - True if path is outside basepath, false otherwise. - - """ - return not _canonical_abspath(join(basepath, path)).startswith(basepath) - +from subprocess import run -def _badlink(info, basepath): - """Determine if the tarinfo member is outside basepath. - - Args: - info: TarInfo member representing a symlink or hardlink of tar archive - basepath: the basepath the info member must be in - - Returns: - True if info is outside basepath, false otherwise. - - """ - tippath = _canonical_abspath(join(basepath, dirname(info.name))) - return _badpath(info.linkname, basepath=tippath) - - -def is_tarball(filepath): - """Given a filepath, determine if it represents an archive. +from . import utils - Args: - filepath: file to test for tarball property - Returns: - Bool, True if it's a tarball, False otherwise +def _unpack_tar(tarpath: str, extract_dir: str) -> str: + """Unpack tarballs unsupported by the standard python library. Examples + include tar.Z, tar.lz, tar.x, etc.... - """ - return tarfile.is_tarfile(filepath) or zipfile.is_zipfile(filepath) + As this implementation relies on the `tar` command, this function supports + the same compression the tar command supports. + This expects the `extract_dir` to exist. -def _uncompress_zip(tarpath, dirpath): - """Uncompress zip archive safely. + Raises - As per zipfile is concerned - (cf. note on https://docs.python.org/3.5/library/zipfile.html#zipfile.ZipFile.extract) # noqa + shutil.ReadError in case of issue uncompressing the archive (tarpath + does not exist, extract_dir does not exist, etc...) - Args: - tarpath: path to the archive - dirpath: directory to uncompress the archive to + Returns + full path to the uncompressed directory. """ - with zipfile.ZipFile(tarpath) as z: - z.extractall(path=dirpath) - - -def _safemembers(tarpath, members, basepath): - """Given a list of archive members, yield the members (directory, - file, hard-link) that stays in bounds with basepath. Note - that symbolic link are authorized to point outside the - basepath though. + try: + run(['tar', 'xf', tarpath, '-C', extract_dir], check=True) + return extract_dir + except Exception as e: + raise shutil.ReadError( + f'Unable to uncompress {tarpath} to {extract_dir}. Reason: {e}') - Args: - tarpath: Name of the tarball - members: Archive members for such tarball - basepath: the basepath sandbox - - Yields: - Safe TarInfo member - Raises: - ValueError when a member would be extracted outside basepath +def register_new_archive_formats(): + """Register new archive formats to uncompress """ - errormsg = 'Archive {} blocked. Illegal path to %s %s'.format(tarpath) - - for finfo in members: - if finfo.isdir() and _badpath(finfo.name, basepath): - raise ValueError(errormsg % ('directory', finfo.name)) - elif finfo.isfile() and _badpath(finfo.name, basepath): - raise ValueError(errormsg % ('file', finfo.name)) - elif finfo.islnk() and _badlink(finfo, basepath): - raise ValueError(errormsg % ('hard-link', finfo.linkname)) - # Authorize symlinks to point outside basepath - # elif finfo.issym() and _badlink(finfo, basepath): - # raise ValueError(errormsg % ('symlink', finfo.linkname)) - else: - yield finfo - - -def _uncompress_tar(tarpath, dirpath): - """Uncompress tarpath if the tarpath is safe. - Safe means, no file will be uncompressed outside of dirpath. - - Args: - tarpath: path to the archive - dirpath: directory to uncompress the archive to - - Raises: - ValueError when a member would be extracted outside dirpath. - - """ - with tarfile.open(tarpath) as t: - members = t.getmembers() - t.extractall(path=dirpath, - members=_safemembers(tarpath, members, dirpath)) + registered_formats = [f[0] for f in shutil.get_unpack_formats()] + for name, extensions, function in ADDITIONAL_ARCHIVE_FORMATS: + if name in registered_formats: + continue + shutil.register_unpack_format(name, extensions, function) -def uncompress(tarpath, dest): - """Uncompress tarpath to dest folder if tarball is supported and safe. - Safe means, no file will be uncompressed outside of dirpath. +def uncompress(tarpath: str, dest: str): + """Uncompress tarpath to dest folder if tarball is supported. Note that this fixes permissions after successfully uncompressing the archive. Args: tarpath: path to tarball to uncompress dest: the destination folder where to uncompress the tarball Returns: The nature of the tarball, zip or tar. Raises: - ValueError when: - - an archive member would be extracted outside basepath - - the archive is not supported + ValueError when a problem occurs during unpacking """ - if tarfile.is_tarfile(tarpath): - _uncompress_tar(tarpath, dest) - nature = 'tar' - elif zipfile.is_zipfile(tarpath): - _uncompress_zip(tarpath, dest) - nature = 'zip' - else: - raise ValueError('File %s is not a supported archive.' % tarpath) + try: + shutil.unpack_archive(tarpath, extract_dir=dest) + except shutil.ReadError as e: + raise ValueError(f'Problem during unpacking {tarpath}. Reason: {e}') # Fix permissions for dirpath, _, fnames in os.walk(dest): os.chmod(dirpath, 0o755) for fname in fnames: fpath = os.path.join(dirpath, fname) if not os.path.islink(fpath): fpath_exec = os.stat(fpath).st_mode & stat.S_IXUSR if not fpath_exec: os.chmod(fpath, 0o644) - return nature - def _ls(rootdir): """Generator of filepath, filename from rootdir. """ for dirpath, dirnames, fnames in os.walk(rootdir): for fname in (dirnames+fnames): fpath = os.path.join(dirpath, fname) fname = utils.commonname(rootdir, fpath) yield fpath, fname def _compress_zip(tarpath, files): """Compress dirpath's content as tarpath. """ with zipfile.ZipFile(tarpath, 'w') as z: for fpath, fname in files: z.write(fpath, arcname=fname) def _compress_tar(tarpath, files): """Compress dirpath's content as tarpath. """ with tarfile.open(tarpath, 'w:bz2') as t: for fpath, fname in files: t.add(fpath, arcname=fname, recursive=False) def compress(tarpath, nature, dirpath_or_files): """Create a tarball tarpath with nature nature. The content of the tarball is either dirpath's content (if representing a directory path) or dirpath's iterable contents. Compress the directory dirpath's content to a tarball. The tarball being dumped at tarpath. The nature of the tarball is determined by the nature argument. """ if isinstance(dirpath_or_files, str): files = _ls(dirpath_or_files) else: # iterable of 'filepath, filename' files = dirpath_or_files if nature == 'zip': _compress_zip(tarpath, files) else: _compress_tar(tarpath, files) return tarpath + + +# Additional uncompression archive format support +ADDITIONAL_ARCHIVE_FORMATS = [ + # name , extensions, function + ('tar.Z|x', ['.tar.Z', '.tar.x'], _unpack_tar), + # FIXME: make this optional depending on the runtime lzip package install + ('tar.lz', ['.tar.lz'], _unpack_tar), +] + +register_new_archive_formats() diff --git a/swh/core/tests/data/archives/groff-1.02.tar.Z b/swh/core/tests/data/archives/groff-1.02.tar.Z new file mode 100644 index 0000000..973ffb6 Binary files /dev/null and b/swh/core/tests/data/archives/groff-1.02.tar.Z differ diff --git a/swh/core/tests/data/archives/hello.tar b/swh/core/tests/data/archives/hello.tar new file mode 100644 index 0000000..73fafe8 Binary files /dev/null and b/swh/core/tests/data/archives/hello.tar differ diff --git a/swh/core/tests/data/archives/hello.tar.bz2 b/swh/core/tests/data/archives/hello.tar.bz2 new file mode 100644 index 0000000..730b09f Binary files /dev/null and b/swh/core/tests/data/archives/hello.tar.bz2 differ diff --git a/swh/core/tests/data/archives/hello.tar.gz b/swh/core/tests/data/archives/hello.tar.gz new file mode 100644 index 0000000..87727db Binary files /dev/null and b/swh/core/tests/data/archives/hello.tar.gz differ diff --git a/swh/core/tests/data/archives/hello.tar.lz b/swh/core/tests/data/archives/hello.tar.lz new file mode 100644 index 0000000..8195e33 Binary files /dev/null and b/swh/core/tests/data/archives/hello.tar.lz differ diff --git a/swh/core/tests/data/archives/hello.tar.x b/swh/core/tests/data/archives/hello.tar.x new file mode 100644 index 0000000..09298d3 Binary files /dev/null and b/swh/core/tests/data/archives/hello.tar.x differ diff --git a/swh/core/tests/data/archives/hello.zip b/swh/core/tests/data/archives/hello.zip new file mode 100644 index 0000000..e9dde2a Binary files /dev/null and b/swh/core/tests/data/archives/hello.zip differ diff --git a/swh/core/tests/test_tarball.py b/swh/core/tests/test_tarball.py index 92e4f5e..7c7f189 100644 --- a/swh/core/tests/test_tarball.py +++ b/swh/core/tests/test_tarball.py @@ -1,67 +1,169 @@ # Copyright (C) 2019 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information -from zipfile import ZipFile +import os +import pytest +import shutil from swh.core import tarball -def test_is_tarball(tmp_path): +@pytest.fixture +def prepare_shutil_state(): + """Reset any shutil modification in its current state - nozip = tmp_path / 'nozip.zip' - nozip.write_text('Im no zip') + """ + import shutil - assert tarball.is_tarball(str(nozip)) is False + registered_formats = [f[0] for f in shutil.get_unpack_formats()] + for format_id in tarball.ADDITIONAL_ARCHIVE_FORMATS: + name = format_id[0] + if name in registered_formats: + shutil.unregister_unpack_format(name) - notar = tmp_path / 'notar.tar' - notar.write_text('Im no tar') - - assert tarball.is_tarball(str(notar)) is False - - zipfile = tmp_path / 'truezip.zip' - with ZipFile(str(zipfile), 'w') as myzip: - myzip.writestr('file1.txt', 'some content') - - assert tarball.is_tarball(str(zipfile)) is True + return shutil def test_compress_uncompress_zip(tmp_path): tocompress = tmp_path / 'compressme' tocompress.mkdir() for i in range(10): fpath = tocompress / ('file%s.txt' % i) fpath.write_text('content of file %s' % i) zipfile = tmp_path / 'archive.zip' tarball.compress(str(zipfile), 'zip', str(tocompress)) - assert tarball.is_tarball(str(zipfile)) - destdir = tmp_path / 'destdir' tarball.uncompress(str(zipfile), str(destdir)) lsdir = sorted(x.name for x in destdir.iterdir()) assert ['file%s.txt' % i for i in range(10)] == lsdir def test_compress_uncompress_tar(tmp_path): tocompress = tmp_path / 'compressme' tocompress.mkdir() for i in range(10): fpath = tocompress / ('file%s.txt' % i) fpath.write_text('content of file %s' % i) tarfile = tmp_path / 'archive.tar' tarball.compress(str(tarfile), 'tar', str(tocompress)) - assert tarball.is_tarball(str(tarfile)) - destdir = tmp_path / 'destdir' tarball.uncompress(str(tarfile), str(destdir)) lsdir = sorted(x.name for x in destdir.iterdir()) assert ['file%s.txt' % i for i in range(10)] == lsdir + + +def test__unpack_tar_failure(tmp_path, datadir): + """Unpack inexistent tarball should fail + + """ + tarpath = os.path.join(datadir, 'archives', 'inexistent-archive.tar.Z') + + assert not os.path.exists(tarpath) + + with pytest.raises(shutil.ReadError, + match=f'Unable to uncompress {tarpath} to {tmp_path}'): + tarball._unpack_tar(tarpath, tmp_path) + + +def test__unpack_tar_failure2(tmp_path, datadir): + """Unpack Existent tarball into an inexistent folder should fail + + """ + filename = 'groff-1.02.tar.Z' + tarpath = os.path.join(datadir, 'archives', filename) + + assert os.path.exists(tarpath) + + extract_dir = os.path.join(tmp_path, 'dir', 'inexistent') + + with pytest.raises(shutil.ReadError, + match=f'Unable to uncompress {tarpath} to {tmp_path}'): + tarball._unpack_tar(tarpath, extract_dir) + + +def test__unpack_tar_failure3(tmp_path, datadir): + """Unpack unsupported tarball should fail + + """ + filename = 'hello.zip' + tarpath = os.path.join(datadir, 'archives', filename) + + assert os.path.exists(tarpath) + + with pytest.raises(shutil.ReadError, + match=f'Unable to uncompress {tarpath} to {tmp_path}'): + tarball._unpack_tar(tarpath, tmp_path) + + +def test__unpack_tar(tmp_path, datadir): + """Unpack supported tarball into an existent folder should be ok + + """ + filename = 'groff-1.02.tar.Z' + tarpath = os.path.join(datadir, 'archives', filename) + + assert os.path.exists(tarpath) + + extract_dir = os.path.join(tmp_path, filename) + os.makedirs(extract_dir, exist_ok=True) + + output_directory = tarball._unpack_tar(tarpath, extract_dir) + + assert extract_dir == output_directory + assert len(os.listdir(extract_dir)) > 0 + + +def test_register_new_archive_formats(prepare_shutil_state): + """Registering new archive formats should be fine + + """ + unpack_formats_v1 = [f[0] for f in shutil.get_unpack_formats()] + for format_id in tarball.ADDITIONAL_ARCHIVE_FORMATS: + assert format_id[0] not in unpack_formats_v1 + + # when + tarball.register_new_archive_formats() + + # then + unpack_formats_v2 = [f[0] for f in shutil.get_unpack_formats()] + for format_id in tarball.ADDITIONAL_ARCHIVE_FORMATS: + assert format_id[0] in unpack_formats_v2 + + +def test_uncompress_tarpaths(tmp_path, datadir, prepare_shutil_state): + """High level call uncompression on un/supported tarballs + + """ + archive_dir = os.path.join(datadir, 'archives') + tarfiles = os.listdir(archive_dir) + tarpaths = [os.path.join(archive_dir, tarfile) for tarfile in tarfiles] + + unsupported_tarpaths = [] + for t in tarpaths: + if t.endswith('.Z') or t.endswith('.x') or t.endswith('.lz'): + unsupported_tarpaths.append(t) + + # not supported yet + for tarpath in unsupported_tarpaths: + with pytest.raises(ValueError, + match=f'Problem during unpacking {tarpath}.'): + tarball.uncompress(tarpath, dest=tmp_path) + + # register those unsupported formats + tarball.register_new_archive_formats() + + # unsupported formats are now supported + for n, tarpath in enumerate(tarpaths, start=1): + tarball.uncompress(tarpath, dest=tmp_path) + + assert n == len(tarpaths) diff --git a/version.txt b/version.txt index 98d761c..f4b36fa 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v0.0.83-0-g2ba1714 \ No newline at end of file +v0.0.84-0-ga8dd002 \ No newline at end of file