nipype icon indicating copy to clipboard operation
nipype copied to clipboard

traits.trait_errors.TraitError: Each element of the 'in_files' trait of a SmoothInputSpec instance must be a pathlike object or string representing an existing file, but a value of 'xxx’ <class 'str'> was specified.

Open abcvav opened this issue 1 year ago • 8 comments
trafficstars

Summary

When running a script using smooth.inputs.in_files, an error occurs indicating that the in_files trait must be a pathlike object or a string representing an existing file, despite the file path being valid and existing.

Actual behavior

The script fails with the following error traceback:

Traceback (most recent call last):
  File "/Users/ab/Documents/Code/nipype_tutorial/tests/t.py", line 4, in <module>
    smooth.inputs.in_files = '/Users/ab/Documents/Code/nipype_tutorial/tests/test.nii.gz'
  File "/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/nipype/interfaces/base/traits_extension.py", line 424, in validate
    value = super(MultiObject, self).validate(objekt, name, newvalue)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/traits/trait_types.py", line 2699, in validate
    return TraitListObject(self, object, name, value)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/traits/trait_list_object.py", line 582, in __init__
    super().__init__(
  File "/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/traits/trait_list_object.py", line 213, in __init__
    super().__init__(self.item_validator(item) for item in iterable)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/traits/trait_list_object.py", line 213, in <genexpr>
    super().__init__(self.item_validator(item) for item in iterable)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/traits/trait_list_object.py", line 865, in _item_validator
    return trait_validator(object, self.name, value)
  File "/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/nipype/interfaces/base/traits_extension.py", line 334, in validate
    self.error(objekt, name, str(value))
  File "/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/traits/base_trait_handler.py", line 74, in error
    raise TraitError(
traits.trait_errors.TraitError: Each element of the 'in_files' trait of a SmoothInputSpec instance must be a pathlike object or string representing an existing file, but a value of '/Users/ab/Documents/Code/nipype_tutorial/tests/test.nii.gz' <class 'str'> was specified.

Expected behavior

The file path '/Users/ab/Documents/Code/nipype_tutorial/tests/test.nii.gz' should be accepted as a valid path, and the smooth.inputs.in_files trait should accept it without raising an error, as the file does exist. But using an absolute path results in the same outcome.

How to replicate the behavior

run the following scripts:

from nipype.interfaces.spm import Smooth
smooth = Smooth()
smooth.inputs.in_files = '/Users/ab/Documents/Code/nipype_tutorial/tests/test.nii.gz'

Script/Workflow details

>>> from pathlib import Path
>>> Path('/Users/ab/Documents/Code/nipype_tutorial/tests/test.nii.gz').exists()
True

Platform details:

{'commit_hash': '%h',
 'commit_source': 'archive substitution',
 'networkx_version': '3.4.1',
 'nibabel_version': '5.3.1',
 'nipype_version': '1.8.6',
 'numpy_version': '2.1.2',
 'pkg_path': '/opt/homebrew/Caskroom/miniconda/base/envs/preproc/lib/python3.10/site-packages/nipype',
 'scipy_version': '1.14.1',
 'sys_executable': '/opt/homebrew/Caskroom/miniconda/base/envs/preproc/bin/python',
 'sys_platform': 'darwin',
 'sys_version': '3.10.13 | packaged by conda-forge | (main, Dec 23 2023, '
                '15:35:25) [Clang 16.0.6 ]',
 'traits_version': '6.3.2'}

Execution environment

  • My python environment outside container

I use miniconda, the conda version is conda 24.7.1. Nipype was installed using conda install --channel conda-forge nipype. Python version 3.10.13

abcvav avatar Oct 17 '24 03:10 abcvav

SmoothInputSpec and similar SPM functions only accept uncompressed nifti files. Uncompress the file and try again. It should work.

https://github.com/nipy/nipype/blob/bc456dd985b785783fb94094be685b2d6759ffaf/nipype/interfaces/spm/preprocess.py#L482-L491 https://github.com/nipy/nipype/blob/bc456dd985b785783fb94094be685b2d6759ffaf/nipype/interfaces/spm/base.py#L612-L613

Maybe changing the value of allow_compressed to true could solve this problem. https://github.com/nipy/nipype/blob/bc456dd985b785783fb94094be685b2d6759ffaf/nipype/interfaces/spm/base.py#L623

tamruta avatar Feb 12 '25 06:02 tamruta

Has SPM started accepting compressed files? If so, in what version?

effigies avatar Feb 12 '25 08:02 effigies

Not that I'm aware unfortunately. Related to issue #2420 Changing the error message would be helpful. Could there be an intermediate function to unzip the files?

tamruta avatar Feb 14 '25 19:02 tamruta

Changing the error message would be helpful.

It looks like we would need to do that in this class:

https://github.com/nipy/nipype/blob/bc456dd985b785783fb94094be685b2d6759ffaf/nipype/interfaces/base/traits_extension.py#L209

By overriding

https://github.com/nipy/nipype/blob/bc456dd985b785783fb94094be685b2d6759ffaf/nipype/interfaces/base/traits_extension.py#L104-L118

to indicate acceptable extensions.

Could there be an intermediate function to unzip the files?

Automatically? I can't immediately think of a way. But there is nipype.algorithms.misc.Gunzip.

effigies avatar Feb 15 '25 03:02 effigies

I'm assuming that would not work well with other libraries that accept compressed nifti.

tamruta avatar Feb 19 '25 15:02 tamruta

Sorry, I don't understand that comment.

effigies avatar Feb 19 '25 15:02 effigies

Ah I'm sorry. Nipype incorporates a lot of libraries. Do they all use the File class to confirm filetype? If so, it would be hard to raise an error only for SPM. Or even add a restriction for .nii filetypes

tamruta avatar Feb 19 '25 18:02 tamruta

Got it. No, we should be able to do this without changing what succeeds or fails. What I'm proposing is just to improve the text emitted by the .info_text property when we know that there are required extensions, e.g.,

class File(BasePath):
    ...
    @property
    def info_text(self):
        info_text = super().info_text
        if self._exts:
            if len(self._exts) == 1:
                info_text += f" with extension {self._exts[0]}"
            else:
                info_text += f" with one of the extensions: {self.exts}"
        return info_text

effigies avatar Feb 19 '25 18:02 effigies