diff --git a/pydeface/utils.py b/pydeface/utils.py index 647c027..b3eff84 100644 --- a/pydeface/utils.py +++ b/pydeface/utils.py @@ -51,12 +51,15 @@ def output_checks(infile, outfile=None, force=False): def generate_tmpfiles(verbose=True): - _, template_reg_mat = tempfile.mkstemp(suffix='.mat') - _, warped_mask = tempfile.mkstemp(suffix='.nii.gz') + paths = [] + for suffix in ('.mat', '.nii.gz', '.nii.gz', '.mat'): + fd, path = tempfile.mkstemp(suffix=suffix) + # only the names are needed; FSL opens the files itself + os.close(fd) + paths.append(path) + template_reg_mat, warped_mask, template_reg, warped_mask_mat = paths if verbose: print(f'Temporary files:\n {template_reg_mat}\n {warped_mask}') - _, template_reg = tempfile.mkstemp(suffix='.nii.gz') - _, warped_mask_mat = tempfile.mkstemp(suffix='.mat') return template_reg, template_reg_mat, warped_mask, warped_mask_mat @@ -121,6 +124,10 @@ def deface_image( flirt.inputs.out_matrix_file = warped_mask_mat flirt.run() + # this matrix is not returned to the caller, so nobody else can remove it + if os.path.exists(warped_mask_mat): + os.remove(warped_mask_mat) + # multiply mask by infile and save infile_img = load(infile) infile_data = np.asarray(infile_img.dataobj) diff --git a/tests/test_utils.py b/tests/test_utils.py index 7a5af01..cbda69a 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -23,6 +23,39 @@ def test_generate_tmpfiles(): os.remove(f) +def test_generate_tmpfiles_closes_descriptors(): + fd_dir = Path('/proc/self/fd') + if not fd_dir.exists(): + pytest.skip('needs /proc/self/fd') + before = len(os.listdir(fd_dir)) + for _ in range(5): + pdu.cleanup_files(*pdu.generate_tmpfiles(verbose=False)) + assert len(os.listdir(fd_dir)) == before + + +def test_deface_image_leaves_no_matrix_file(monkeypatch, tmp_path): + import nibabel as nib + import numpy as np + from nipype.interfaces import fsl + + def fake_run(self): + img = nib.Nifti1Image(np.ones((4, 4, 4), dtype='f4'), np.eye(4)) + nib.save(img, self.inputs.out_file) + + infile = tmp_path / 'in.nii.gz' + nib.save(nib.Nifti1Image(np.ones((4, 4, 4), dtype='f4'), np.eye(4)), infile) + tmp_dir = tmp_path / 'tmp' + tmp_dir.mkdir() + monkeypatch.setattr('tempfile.tempdir', str(tmp_dir)) + monkeypatch.setattr(pdu.shutil, 'which', lambda name: '/fsl/bin/flirt') + monkeypatch.setenv('FSLDIR', '/fsl') + monkeypatch.setattr(fsl.FLIRT, 'run', fake_run) + + pdu.deface_image(str(infile), force=True, forcecleanup=True, verbose=False) + + assert list(tmp_dir.iterdir()) == [] + + def test_get_outfile_type(): assert pdu.get_outfile_type('path.nii.gz') == 'NIFTI_GZ' assert pdu.get_outfile_type('path.nii') == 'NIFTI'