From d8bd2b3591f4214aa602adde890c07209f10094e Mon Sep 17 00:00:00 2001 From: Ghislain Vaillant Date: Fri, 13 May 2022 11:50:06 +0200 Subject: [PATCH 1/8] ENH: Enable compression with Gunzip interface --- nipype/algorithms/misc.py | 22 ++++++++++++++++----- nipype/algorithms/tests/test_auto_Gunzip.py | 1 + 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index dac75d960f..9ee68a5ff1 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -273,6 +273,7 @@ def _list_outputs(self): class GunzipInputSpec(BaseInterfaceInputSpec): in_file = File(exists=True, mandatory=True) + compress = traits.Bool(default_value=False) class GunzipOutputSpec(TraitedSpec): @@ -298,17 +299,28 @@ class Gunzip(BaseInterface): def _gen_output_file_name(self): _, base, ext = split_filename(self.inputs.in_file) - if ext[-3:].lower() == ".gz": - ext = ext[:-3] + + if self.inputs.compress: + ext += ".gz" + else: + if ext[-3:].lower() == ".gz": + ext = ext[:-3] + return os.path.abspath(base + ext) def _run_interface(self, runtime): import gzip import shutil - with gzip.open(self.inputs.in_file, "rb") as in_file: - with open(self._gen_output_file_name(), "wb") as out_file: - shutil.copyfileobj(in_file, out_file) + if self.inputs.compress: + with open(self.inputs.in_file, "rb") as in_file: + with gzip.open(self._gen_output_file_name(), "wb") as out_file: + shutil.copyfileobj(in_file, out_file) + else: + with gzip.open(self.inputs.in_file, "rb") as in_file: + with open(self._gen_output_file_name(), "wb") as out_file: + shutil.copyfileobj(in_file, out_file) + return runtime def _list_outputs(self): diff --git a/nipype/algorithms/tests/test_auto_Gunzip.py b/nipype/algorithms/tests/test_auto_Gunzip.py index 7629feb820..6fe12dc016 100644 --- a/nipype/algorithms/tests/test_auto_Gunzip.py +++ b/nipype/algorithms/tests/test_auto_Gunzip.py @@ -4,6 +4,7 @@ def test_Gunzip_inputs(): input_map = dict( + compress=dict(), in_file=dict( extensions=None, mandatory=True, From 3b61513a1e6d0e68c231ce0a4d37c3b5512254de Mon Sep 17 00:00:00 2001 From: Ghislain Vaillant Date: Fri, 13 May 2022 12:11:56 +0200 Subject: [PATCH 2/8] TST: Add test for gzip compression --- nipype/algorithms/misc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 9ee68a5ff1..afa0a1e425 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -288,6 +288,10 @@ class Gunzip(BaseInterface): >>> res = gunzip.run() >>> res.outputs.out_file # doctest: +ELLIPSIS '.../tpms_msk.nii' + >>> gunzip = Gunzip(in_file='tpms_msk.nii', compress=True) + >>> res = gunzip.run() + >>> res.outputs.out_file # doctest: +ELLIPSIS + '.../tpms_msk.nii.gz' .. testcleanup:: From 7350970773455028268a940f7d6393f5a112bba0 Mon Sep 17 00:00:00 2001 From: Ghislain Vaillant Date: Mon, 16 May 2022 18:47:26 +0200 Subject: [PATCH 3/8] ENH: Move compression to separate Gzip interface Rationales: Keep the Gunzip interface consistent with the behavior of `gunzip` on the CLI, which is basically `gzip` with decompression mode set nowadays. --- nipype/algorithms/misc.py | 71 +++++++++++++++------ nipype/algorithms/tests/test_auto_Gunzip.py | 1 - nipype/algorithms/tests/test_auto_Gzip.py | 32 ++++++++++ 3 files changed, 83 insertions(+), 21 deletions(-) create mode 100644 nipype/algorithms/tests/test_auto_Gzip.py diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index afa0a1e425..789009a3d0 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -273,7 +273,6 @@ def _list_outputs(self): class GunzipInputSpec(BaseInterfaceInputSpec): in_file = File(exists=True, mandatory=True) - compress = traits.Bool(default_value=False) class GunzipOutputSpec(TraitedSpec): @@ -288,10 +287,6 @@ class Gunzip(BaseInterface): >>> res = gunzip.run() >>> res.outputs.out_file # doctest: +ELLIPSIS '.../tpms_msk.nii' - >>> gunzip = Gunzip(in_file='tpms_msk.nii', compress=True) - >>> res = gunzip.run() - >>> res.outputs.out_file # doctest: +ELLIPSIS - '.../tpms_msk.nii.gz' .. testcleanup:: @@ -303,35 +298,71 @@ class Gunzip(BaseInterface): def _gen_output_file_name(self): _, base, ext = split_filename(self.inputs.in_file) + if ext[-3:].lower() == ".gz": + ext = ext[:-3] + return os.path.abspath(base + ext) + + def _run_interface(self, runtime): + import gzip + import shutil + + with gzip.open(self.inputs.in_file, "rb") as in_file: + with open(self._gen_output_file_name(), "wb") as out_file: + shutil.copyfileobj(in_file, out_file) + return runtime + + def _list_outputs(self): + outputs = self._outputs().get() + outputs["out_file"] = self._gen_output_file_name() + return outputs + + +class GzipInputSpec(GunzipInputSpec): + mode = traits.Enum("compress", "decompress", usedefault=True) + + +class Gzip(Gunzip): + """Gzip wrapper supporting both compression and decompression. + + >>> from nipype.algorithms.misc import Gzip + >>> gzip = Gzip(in_file='tpms_msk.nii.gz', mode="decompress") + >>> res = gzip.run() + >>> res.outputs.out_file # doctest: +ELLIPSIS + '.../tpms_msk.nii' + >>> gzip = Gzip(in_file='tpms_msk.nii') + >>> res = gzip.run() + >>> res.outputs.out_file # doctest: +ELLIPSIS + '.../tpms_msk.nii.gz' - if self.inputs.compress: - ext += ".gz" + .. testcleanup:: + + >>> os.unlink('tpms_msk.nii') + """ + + input_spec = GzipInputSpec + + def _gen_output_file_name(self): + if mode == "decompress": + filename = super()._gen_output_file_name() else: - if ext[-3:].lower() == ".gz": - ext = ext[:-3] + _, base, ext = split_filename(self.inputs.in_file) + filename = os.path.abspath(base + ext + ".gz") - return os.path.abspath(base + ext) + return filename def _run_interface(self, runtime): import gzip import shutil - if self.inputs.compress: + if mode == "decompress": + runtime = super()._run_interface(runtime) + else: with open(self.inputs.in_file, "rb") as in_file: with gzip.open(self._gen_output_file_name(), "wb") as out_file: shutil.copyfileobj(in_file, out_file) - else: - with gzip.open(self.inputs.in_file, "rb") as in_file: - with open(self._gen_output_file_name(), "wb") as out_file: - shutil.copyfileobj(in_file, out_file) return runtime - def _list_outputs(self): - outputs = self._outputs().get() - outputs["out_file"] = self._gen_output_file_name() - return outputs - def replaceext(in_list, ext): out_list = list() diff --git a/nipype/algorithms/tests/test_auto_Gunzip.py b/nipype/algorithms/tests/test_auto_Gunzip.py index 6fe12dc016..7629feb820 100644 --- a/nipype/algorithms/tests/test_auto_Gunzip.py +++ b/nipype/algorithms/tests/test_auto_Gunzip.py @@ -4,7 +4,6 @@ def test_Gunzip_inputs(): input_map = dict( - compress=dict(), in_file=dict( extensions=None, mandatory=True, diff --git a/nipype/algorithms/tests/test_auto_Gzip.py b/nipype/algorithms/tests/test_auto_Gzip.py new file mode 100644 index 0000000000..1503f92790 --- /dev/null +++ b/nipype/algorithms/tests/test_auto_Gzip.py @@ -0,0 +1,32 @@ +# AUTO-GENERATED by tools/checkspecs.py - DO NOT EDIT +from ..misc import Gzip + + +def test_Gzip_inputs(): + input_map = dict( + in_file=dict( + extensions=None, + mandatory=True, + ), + mode=dict( + usedefault=True, + ), + ) + inputs = Gzip.input_spec() + + for key, metadata in list(input_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(inputs.traits()[key], metakey) == value + + +def test_Gzip_outputs(): + output_map = dict( + out_file=dict( + extensions=None, + ), + ) + outputs = Gzip.output_spec() + + for key, metadata in list(output_map.items()): + for metakey, value in list(metadata.items()): + assert getattr(outputs.traits()[key], metakey) == value From f124b63608fc83eba3a7fc05f043719b774d8fe9 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Mon, 16 May 2022 13:45:33 -0400 Subject: [PATCH 4/8] RF: Unify Gzip/Gunzip, make Gzip the base class --- nipype/algorithms/misc.py | 85 +++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 789009a3d0..1145adf0a7 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -271,43 +271,57 @@ def _list_outputs(self): return outputs -class GunzipInputSpec(BaseInterfaceInputSpec): - in_file = File(exists=True, mandatory=True) +class GzipInputSpec(TraitedSpec): + in_file = File(exists=True, mandatory=True, desc="file to (de)compress") + mode = traits.Enum("compress", "decompress", usedefault=True, + desc="compress or decompress") -class GunzipOutputSpec(TraitedSpec): - out_file = File(exists=True) +class GzipOutputSpec(TraitedSpec): + out_file = File() -class Gunzip(BaseInterface): - """Gunzip wrapper +class Gzip(BaseInterface): + """Gzip wrapper - >>> from nipype.algorithms.misc import Gunzip - >>> gunzip = Gunzip(in_file='tpms_msk.nii.gz') - >>> res = gunzip.run() + >>> from nipype.algorithms.misc import Gzip + >>> gzip = Gzip(in_file='tpms_msk.nii.gz', mode="decompress") + >>> res = gzip.run() >>> res.outputs.out_file # doctest: +ELLIPSIS '.../tpms_msk.nii' + >>> gzip = Gzip(in_file='tpms_msk.nii') + >>> res = gzip.run() + >>> res.outputs.out_file # doctest: +ELLIPSIS + '.../tpms_msk.nii.gz' + .. testcleanup:: >>> os.unlink('tpms_msk.nii') """ - input_spec = GunzipInputSpec - output_spec = GunzipOutputSpec + input_spec = GzipInputSpec + output_spec = GzipOutputSpec def _gen_output_file_name(self): _, base, ext = split_filename(self.inputs.in_file) - if ext[-3:].lower() == ".gz": + if self.inputs.mode == "decompress" and ext[-3:].lower() == ".gz": ext = ext[:-3] + elif self.inputs.mode == "compress": + ext = f"{ext}.gz" return os.path.abspath(base + ext) def _run_interface(self, runtime): import gzip import shutil - with gzip.open(self.inputs.in_file, "rb") as in_file: - with open(self._gen_output_file_name(), "wb") as out_file: + if self.inputs.mode == "compress": + open_input, open_output = open, gzip.open + else: + open_input, open_output = gzip.open, open + + with open_input(self.inputs.in_file, "rb") as in_file: + with open_output(self._gen_output_file_name(), "wb") as out_file: shutil.copyfileobj(in_file, out_file) return runtime @@ -317,51 +331,26 @@ def _list_outputs(self): return outputs -class GzipInputSpec(GunzipInputSpec): - mode = traits.Enum("compress", "decompress", usedefault=True) +class GunzipInputSpec(GzipInputSpec): + mode = traits.Enum("decompress", "compress", usedefault=True, + desc="decompress or compress") -class Gzip(Gunzip): - """Gzip wrapper supporting both compression and decompression. +class Gunzip(Gzip): + """Gzip wrapper defaulting to decompression - >>> from nipype.algorithms.misc import Gzip - >>> gzip = Gzip(in_file='tpms_msk.nii.gz', mode="decompress") - >>> res = gzip.run() + >>> from nipype.algorithms.misc import Gunzip + >>> gunzip = Gunzip(in_file='tpms_msk.nii.gz') + >>> res = gunzip.run() >>> res.outputs.out_file # doctest: +ELLIPSIS '.../tpms_msk.nii' - >>> gzip = Gzip(in_file='tpms_msk.nii') - >>> res = gzip.run() - >>> res.outputs.out_file # doctest: +ELLIPSIS - '.../tpms_msk.nii.gz' .. testcleanup:: >>> os.unlink('tpms_msk.nii') """ - input_spec = GzipInputSpec - - def _gen_output_file_name(self): - if mode == "decompress": - filename = super()._gen_output_file_name() - else: - _, base, ext = split_filename(self.inputs.in_file) - filename = os.path.abspath(base + ext + ".gz") - - return filename - - def _run_interface(self, runtime): - import gzip - import shutil - - if mode == "decompress": - runtime = super()._run_interface(runtime) - else: - with open(self.inputs.in_file, "rb") as in_file: - with gzip.open(self._gen_output_file_name(), "wb") as out_file: - shutil.copyfileobj(in_file, out_file) - - return runtime + input_spec = GunzipInputSpec def replaceext(in_list, ext): From f514c683468319771ab47601aa584c58586c8707 Mon Sep 17 00:00:00 2001 From: Ghislain Vaillant Date: Mon, 16 May 2022 23:30:32 +0200 Subject: [PATCH 5/8] Set mode to decompression for Gunzip interface Co-authored-by: Chris Markiewicz --- nipype/algorithms/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 1145adf0a7..9b7bfb5bde 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -332,7 +332,7 @@ def _list_outputs(self): class GunzipInputSpec(GzipInputSpec): - mode = traits.Enum("decompress", "compress", usedefault=True, + mode = traits.Enum("decompress", usedefault=True, desc="decompress or compress") From 87556ee9a73ef7e2558db74e2be28510579116c2 Mon Sep 17 00:00:00 2001 From: Ghislain Vaillant Date: Mon, 16 May 2022 23:31:10 +0200 Subject: [PATCH 6/8] Update Gunzip interface docstring Co-authored-by: Chris Markiewicz --- nipype/algorithms/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 9b7bfb5bde..41b6a3f0c7 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -337,7 +337,7 @@ class GunzipInputSpec(GzipInputSpec): class Gunzip(Gzip): - """Gzip wrapper defaulting to decompression + """Gunzip wrapper >>> from nipype.algorithms.misc import Gunzip >>> gunzip = Gunzip(in_file='tpms_msk.nii.gz') From 574ac72d465af18b01b8bcd52ac4bf8dbbce912f Mon Sep 17 00:00:00 2001 From: Ghislain Vaillant Date: Mon, 16 May 2022 23:41:01 +0200 Subject: [PATCH 7/8] STY: Fix black formatting --- nipype/algorithms/misc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nipype/algorithms/misc.py b/nipype/algorithms/misc.py index 41b6a3f0c7..175d7642b8 100644 --- a/nipype/algorithms/misc.py +++ b/nipype/algorithms/misc.py @@ -273,8 +273,9 @@ def _list_outputs(self): class GzipInputSpec(TraitedSpec): in_file = File(exists=True, mandatory=True, desc="file to (de)compress") - mode = traits.Enum("compress", "decompress", usedefault=True, - desc="compress or decompress") + mode = traits.Enum( + "compress", "decompress", usedefault=True, desc="compress or decompress" + ) class GzipOutputSpec(TraitedSpec): @@ -332,8 +333,7 @@ def _list_outputs(self): class GunzipInputSpec(GzipInputSpec): - mode = traits.Enum("decompress", usedefault=True, - desc="decompress or compress") + mode = traits.Enum("decompress", usedefault=True, desc="decompress or compress") class Gunzip(Gzip): From c5b1fe32026ef0a33b673d8c0660dda389c13ab5 Mon Sep 17 00:00:00 2001 From: Ghislain Vaillant Date: Mon, 16 May 2022 23:51:57 +0200 Subject: [PATCH 8/8] TST: Refresh specs for Gunzip interface --- nipype/algorithms/tests/test_auto_Gunzip.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nipype/algorithms/tests/test_auto_Gunzip.py b/nipype/algorithms/tests/test_auto_Gunzip.py index 7629feb820..2d1b4a4beb 100644 --- a/nipype/algorithms/tests/test_auto_Gunzip.py +++ b/nipype/algorithms/tests/test_auto_Gunzip.py @@ -8,6 +8,9 @@ def test_Gunzip_inputs(): extensions=None, mandatory=True, ), + mode=dict( + usedefault=True, + ), ) inputs = Gunzip.input_spec()