From ca58ad04fd70c4064bb5e0df646755bbf8967b03 Mon Sep 17 00:00:00 2001 From: Alexander Dietrich Date: Wed, 2 Mar 2022 23:14:04 +0100 Subject: [PATCH] Add gz compression and use it by default --- README.md | 2 +- src/icepack/__init__.py | 33 ++++++++++++++------- src/icepack/cli.py | 4 +-- src/icepack/model.py | 1 + tests/test_cli.py | 63 ++++++++++++++++++++++++++++------------- 5 files changed, 70 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 147c7fa..e30f355 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ Enter passphrase: ***** | Option | Description | | --- | --- | | `--comment` | Archive comment. | -| `--compression`, `-c` | Compression for all files: `bz2` or `none` (Default: `bz2`) | +| `--compression`, `-c` | Compression for all files: `bz2`, `gz` or `none` (Default: `gz`) | | `--mode` | Store file/directory modes. | | `--mtime` | Store file/directory modification times. | | `--recipient`, `-r` | Allow another public key/alias to extract. | diff --git a/src/icepack/__init__.py b/src/icepack/__init__.py index 20d9688..4ca6db5 100644 --- a/src/icepack/__init__.py +++ b/src/icepack/__init__.py @@ -1,4 +1,5 @@ import bz2 +import gzip import json import os from pathlib import Path @@ -95,7 +96,7 @@ def extract_entry(self, entry, base_path): tmp_path = self._mktemp() self._decrypt_path(age_path, tmp_path) age_path.unlink() - self._uncompress_path(tmp_path, dst_path) + self._uncompress_path(tmp_path, dst_path, entry.compression) tmp_path.unlink() if self._mode and entry.mode is not None: dst_path.chmod(entry.mode) @@ -136,12 +137,18 @@ def _load_metadata(self): meta_path.unlink() sig_path.unlink() - def _uncompress_path(self, src_path, dst_path): + def _uncompress_path(self, src_path, dst_path, compression): """Uncompressed src_path to dst_path.""" - with open(src_path, 'rb') as src_file: - with bz2.open(src_file, 'rb') as src: - with open(dst_path, 'wb') as dst: - copyfileobj(src, dst, _BUFFER_SIZE) + with open(dst_path, 'wb') as dst: + with open(src_path, 'rb') as src_file: + if compression == Compression.BZ2: + with bz2.open(src_file, 'rb') as src: + copyfileobj(src, dst, _BUFFER_SIZE) + elif compression == Compression.GZ: + with gzip.open(src_file, 'rb') as src: + copyfileobj(src, dst, _BUFFER_SIZE) + else: + raise Exception('Unsupported compression.') class IcepackWriter(IcepackBase): @@ -152,7 +159,7 @@ def __init__( archive_path, key_path, comment=None, - compression=Compression.BZ2, + compression=Compression.GZ, mode=False, mtime=False, recipients=None): @@ -236,8 +243,14 @@ def _compress_path(self, src_path): """Return the temporary Path of the compressed src_path.""" tmp_path = self._mktemp() with open(src_path, 'rb') as src: - with bz2.open(tmp_path, 'wb') as dst: - copyfileobj(src, dst, _BUFFER_SIZE) + if self._compression == Compression.BZ2: + with bz2.open(tmp_path, 'wb') as dst: + copyfileobj(src, dst, _BUFFER_SIZE) + elif self._compression == Compression.GZ: + with gzip.open(tmp_path, 'wb') as dst: + copyfileobj(src, dst, _BUFFER_SIZE) + else: + raise Exception('Unsupported compression.') return tmp_path def _encrypt_path(self, src_path): @@ -255,7 +268,7 @@ def create_archive( dst_path, key_path, comment=None, - compression=Compression.BZ2, + compression=Compression.GZ, mode=False, mtime=False, recipients=None, diff --git a/src/icepack/cli.py b/src/icepack/cli.py index 49c9087..1dbd6a0 100644 --- a/src/icepack/cli.py +++ b/src/icepack/cli.py @@ -47,9 +47,9 @@ def init(ctx): @click.option('--comment', help='Archive comment.') @click.option( '--compression', '-c', - help='Compression for all files.', + help=f'Compression for all files. (Default: {Compression.GZ})', type=click.Choice([c.value for c in Compression]), - default=Compression.BZ2) + default=Compression.GZ) @click.option( '--mode', help='Store file/directory modes.', diff --git a/src/icepack/model.py b/src/icepack/model.py index 7ae5a5d..59e42ce 100644 --- a/src/icepack/model.py +++ b/src/icepack/model.py @@ -18,6 +18,7 @@ class Checksum(str, Enum): class Compression(str, Enum): """Supported compression types.""" BZ2 = 'bz2' + GZ = 'gz' NONE = 'none' diff --git a/tests/test_cli.py b/tests/test_cli.py index 16d55c0..cd3f8e8 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -27,10 +27,50 @@ def test_init_existing_keys(key_path): assert secret_path.read_text() == secret_key -def test_round_trip_directory(src_path, dst_path, zip_path, key_path): - """Test creation and extraction of a directory.""" +def test_bz2_compression(src_path, dst_path, zip_path, key_path): + """Test round-trip with "bz2" compression.""" # Create archive - args = ['-c', str(key_path), 'create', str(src_path), str(zip_path)] + args = [ + '-c', str(key_path), + 'create', + '--compression', 'bz2', + str(src_path), str(zip_path)] + run_cli(args) + # Extract archive + args = ['-c', str(key_path), 'extract', str(zip_path), str(dst_path)] + run_cli(args) + # Compare directories + for src in File.children(src_path): + dst = dst_path / src.relative_to(src_path.parent) + compare_paths(src, dst) + + +def test_gz_compression(src_path, dst_path, zip_path, key_path): + """Test round-trip with "gz" compression.""" + # Create archive + args = [ + '-c', str(key_path), + 'create', + '--compression', 'gz', + str(src_path), str(zip_path)] + run_cli(args) + # Extract archive + args = ['-c', str(key_path), 'extract', str(zip_path), str(dst_path)] + run_cli(args) + # Compare directories + for src in File.children(src_path): + dst = dst_path / src.relative_to(src_path.parent) + compare_paths(src, dst) + + +def test_none_compression(src_path, dst_path, zip_path, key_path): + """Test round-trip with "none" compression.""" + # Create archive + args = [ + '-c', str(key_path), + 'create', + '--compression', 'none', + str(src_path), str(zip_path)] run_cli(args) # Extract archive args = ['-c', str(key_path), 'extract', str(zip_path), str(dst_path)] @@ -77,23 +117,6 @@ def test_comment(src_path, dst_path, zip_path, key_path): assert 'Hello, World!' in result.stdout -def test_none_compression(src_path, dst_path, zip_path, key_path): - """Test round-trip with "none" compression.""" - # Create archive - args = [ - '-c', str(key_path), - 'create', '--compression', 'none', - str(src_path), str(zip_path)] - run_cli(args) - # Extract archive - args = ['-c', str(key_path), 'extract', str(zip_path), str(dst_path)] - run_cli(args) - # Compare directories - for src in File.children(src_path): - dst = dst_path / src.relative_to(src_path.parent) - compare_paths(src, dst) - - def test_mode_flag(src_path, dst_path, zip_path, key_path): """Test creation and extraction with --mode.""" (src_path / 'foo').chmod(0o755)