Skip to content

Commit 76637f8

Browse files
authored
Merge pull request #83 from pycompression/outputflag
Add flags for output to the CLI
2 parents d51768d + b042f67 commit 76637f8

File tree

3 files changed

+141
-30
lines changed

3 files changed

+141
-30
lines changed

CHANGELOG.rst

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,16 @@ Changelog
1010
version 0.11.0-dev
1111
------------------
1212
In this release the ``python -m isal.igzip`` relatively slow decompression rate
13-
has been improved. Previously it was 19% slower than ``igzip`` when used with
14-
the ``-d`` flag for decompressing, now it is just 8% slower.
15-
13+
has been improved in both speed and usability. Previously it was 19% slower
14+
than ``igzip`` when used with the ``-d`` flag for decompressing, now it is
15+
just 8% slower. Also some extra flags were added to make it easier to select
16+
the output file.
17+
18+
+ Prompt when an output file is overwritten with the ``python -m isal.igzip``
19+
command line utility and provide the ``-f`` or ``--force`` flags to force
20+
overwriting.
21+
+ Added ``-o`` and ``--output`` flags to the ``python -m isal.igzip`` command
22+
line utility to allow the user to select the destination of the output file.
1623
+ Reverse a bug in the build system which caused some docstring and parameter
1724
information on ``igzip_lib`` and ``isal_zlib`` to disappear in the
1825
documentation and the REPL.

src/isal/igzip.py

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,13 @@ def _argument_parser():
432432
dest="compress",
433433
const=False,
434434
help="Decompress the file instead of compressing.")
435-
parser.add_argument("-c", "--stdout", action="store_true",
436-
help="write on standard output")
435+
output_group = parser.add_mutually_exclusive_group()
436+
output_group.add_argument("-c", "--stdout", action="store_true",
437+
help="write on standard output")
438+
output_group.add_argument("-o", "--output",
439+
help="Write to this output file")
440+
parser.add_argument("-f", "--force", action="store_true",
441+
help="Overwrite output without prompting")
437442
# -b flag not taken by either gzip or igzip. Hidden attribute. Above 32K
438443
# diminishing returns hit. _compression.BUFFER_SIZE = 8k. But 32K is about
439444
# ~6% faster.
@@ -448,31 +453,46 @@ def main():
448453

449454
compresslevel = args.compresslevel or _COMPRESS_LEVEL_TRADEOFF
450455

451-
# Determine input file
452-
if args.compress and args.file is None:
453-
in_file = sys.stdin.buffer
454-
elif args.compress and args.file is not None:
455-
in_file = io.open(args.file, mode="rb")
456-
elif not args.compress and args.file is None:
457-
in_file = IGzipFile(mode="rb", fileobj=sys.stdin.buffer)
458-
elif not args.compress and args.file is not None:
459-
base, extension = os.path.splitext(args.file)
460-
if extension != ".gz" and not args.stdout:
461-
sys.exit(f"filename doesn't end in .gz: {args.file!r}. "
462-
f"Cannot determine output filename.")
463-
in_file = open(args.file, "rb")
464-
465-
# Determine output file
466-
if args.compress and (args.file is None or args.stdout):
467-
out_file = IGzipFile(mode="wb", compresslevel=compresslevel,
468-
fileobj=sys.stdout.buffer)
469-
elif args.compress and args.file is not None:
470-
out_file = open(args.file + ".gz", mode="wb",
471-
compresslevel=compresslevel)
472-
elif not args.compress and (args.file is None or args.stdout):
473-
out_file = sys.stdout.buffer
474-
elif not args.compress and args.file is not None:
475-
out_file = io.open(base, "wb")
456+
if args.output:
457+
out_filepath = args.output
458+
elif args.stdout:
459+
out_filepath = None # to stdout
460+
elif args.file is None:
461+
out_filepath = None # to stout
462+
else:
463+
if args.compress:
464+
out_filepath = args.file + ".gz"
465+
else:
466+
out_filepath, extension = os.path.splitext(args.file)
467+
if extension != ".gz" and not args.stdout:
468+
sys.exit(f"filename doesn't end in .gz: {args.file!r}. "
469+
f"Cannot determine output filename.")
470+
if out_filepath is not None and not args.force:
471+
if os.path.exists(out_filepath):
472+
yes_or_no = input(f"{out_filepath} already exists; "
473+
f"do you wish to overwrite (y/n)?")
474+
if yes_or_no not in {"y", "Y", "yes"}:
475+
sys.exit("not overwritten")
476+
477+
if args.compress:
478+
if args.file is None:
479+
in_file = sys.stdin.buffer
480+
else:
481+
in_file = io.open(args.file, mode="rb")
482+
if out_filepath is not None:
483+
out_file = open(out_filepath, "wb", compresslevel=compresslevel)
484+
else:
485+
out_file = IGzipFile(mode="wb", fileobj=sys.stdout.buffer,
486+
compresslevel=compresslevel)
487+
else:
488+
if args.file:
489+
in_file = open(args.file, mode="rb")
490+
else:
491+
in_file = IGzipFile(mode="rb", fileobj=sys.stdin.buffer)
492+
if out_filepath is not None:
493+
out_file = io.open(out_filepath, mode="wb")
494+
else:
495+
out_file = sys.stdout.buffer
476496

477497
global READ_BUFFER_SIZE
478498
READ_BUFFER_SIZE = args.buffer_size

tests/test_igzip.py

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,90 @@ def test_compress_infile_stdout(capsysbinary, tmp_path):
150150
assert err == b''
151151

152152

153+
def test_decompress_infile_out_file(tmp_path, capsysbinary):
154+
test_gz = tmp_path / "test.gz"
155+
test_gz.write_bytes(gzip.compress(DATA))
156+
out_file = tmp_path / "out"
157+
sys.argv = ['', '-d', '-o', str(out_file), str(test_gz)]
158+
igzip.main()
159+
out, err = capsysbinary.readouterr()
160+
assert out_file.read_bytes() == DATA
161+
assert err == b''
162+
assert out == b''
163+
164+
165+
def test_compress_infile_out_file(tmp_path, capsysbinary):
166+
test = tmp_path / "test"
167+
test.write_bytes(DATA)
168+
out_file = tmp_path / "compressed.gz"
169+
sys.argv = ['', '-o', str(out_file), str(test)]
170+
igzip.main()
171+
out, err = capsysbinary.readouterr()
172+
assert gzip.decompress(out_file.read_bytes()) == DATA
173+
assert err == b''
174+
assert out == b''
175+
176+
177+
def test_compress_infile_out_file_force(tmp_path, capsysbinary):
178+
test = tmp_path / "test"
179+
test.write_bytes(DATA)
180+
out_file = tmp_path / "compressed.gz"
181+
out_file.touch()
182+
sys.argv = ['', '-f', '-o', str(out_file), str(test)]
183+
igzip.main()
184+
out, err = capsysbinary.readouterr()
185+
assert gzip.decompress(out_file.read_bytes()) == DATA
186+
assert err == b''
187+
assert out == b''
188+
189+
190+
def test_compress_infile_out_file_prompt(tmp_path, capsysbinary):
191+
test = tmp_path / "test"
192+
test.write_bytes(DATA)
193+
out_file = tmp_path / "compressed.gz"
194+
out_file.touch()
195+
sys.argv = ['', '-o', str(out_file), str(test)]
196+
with pytest.raises(EOFError):
197+
# EOFError because prompt cannot be answered non-interactively.
198+
igzip.main()
199+
out, err = capsysbinary.readouterr()
200+
assert b"compressed.gz already exists; do you wish to overwrite (y/n)?" \
201+
in out
202+
203+
204+
def test_compress_infile_out_file_inmplicit_name_prompt_refuse(
205+
tmp_path, capsysbinary):
206+
test = tmp_path / "test"
207+
test.write_bytes(DATA)
208+
out_file = tmp_path / "test.gz"
209+
out_file.touch()
210+
sys.argv = ['', str(test)]
211+
mock_stdin = io.BytesIO(b"n")
212+
sys.stdin = io.TextIOWrapper(mock_stdin)
213+
with pytest.raises(SystemExit) as error:
214+
igzip.main()
215+
error.match("not overwritten")
216+
out, err = capsysbinary.readouterr()
217+
assert b"test.gz already exists; do you wish to overwrite (y/n)?" \
218+
in out
219+
220+
221+
def test_compress_infile_out_file_inmplicit_name_prompt_accept(
222+
tmp_path, capsysbinary):
223+
test = tmp_path / "test"
224+
test.write_bytes(DATA)
225+
out_file = tmp_path / "test.gz"
226+
out_file.touch()
227+
sys.argv = ['', str(test)]
228+
mock_stdin = io.BytesIO(b"y")
229+
sys.stdin = io.TextIOWrapper(mock_stdin)
230+
igzip.main()
231+
out, err = capsysbinary.readouterr()
232+
assert gzip.decompress(out_file.read_bytes()) == DATA
233+
assert b"already exists; do you wish to overwrite" in out
234+
assert err == b""
235+
236+
153237
def test_decompress():
154238
assert igzip.decompress(COMPRESSED_DATA) == DATA
155239

0 commit comments

Comments
 (0)