@@ -241,19 +241,24 @@ class QCPlots(SimpleInterface):
241241 Examples
242242 --------
243243 .. testsetup::
244- >>> from tempfile import TemporaryDirectory
245- >>> tmpdir = TemporaryDirectory()
246- >>> os.chdir(tmpdir.name)
244+
245+ >>> from tempfile import TemporaryDirectory
246+ >>> tmpdir = TemporaryDirectory()
247+ >>> os.chdir(tmpdir.name)
248+
247249 .. doctest::
248- qcplots = QCPlots()
249- qcplots.inputs.cleaned_file = datafile
250- qcplots.inputs.bold_file = rawbold
251- qcplots.inputs.TR = TR
252- qcplots.inputs.temporal_mask = temporalmask
253- qcplots.inputs.mask_file = mask
254- qcplots.run()
250+
251+ qcplots = QCPlots()
252+ qcplots.inputs.cleaned_file = datafile
253+ qcplots.inputs.bold_file = rawbold
254+ qcplots.inputs.TR = TR
255+ qcplots.inputs.temporal_mask = temporalmask
256+ qcplots.inputs.mask_file = mask
257+ qcplots.run()
258+
255259 .. testcleanup::
256- >>> tmpdir.cleanup()
260+
261+ >>> tmpdir.cleanup()
257262 """
258263
259264 input_spec = _QCPlotsInputSpec
@@ -525,8 +530,9 @@ class _SlicesDirInputSpec(FSLCommandInputSpec):
525530 desc = 'output every second axial slice rather than just 9 ortho slices' ,
526531 )
527532
533+ # Copy in_files from the original location to the runtime.cwd
528534 in_files = InputMultiPath (
529- File (exists = True ),
535+ File (exists = True , copyfile = False ),
530536 argstr = '%s' ,
531537 mandatory = True ,
532538 position = - 1 ,
@@ -551,17 +557,70 @@ class SlicesDir(FSLCommand):
551557
552558 Notes
553559 -----
554- Usage: slicesdir [-o] [-p <image>] [-e <thr>] [-S] <filelist>
555- -o : filelist is pairs ( <underlying> <red-outline> ) of images
556- -p <image> : use <image> as red-outline image on top of all images in <filelist>
557- -e <thr> : use the specified threshold for edges (if >0 use this proportion of max-min,
558- if <0, use the absolute value)
559- -S : output every second axial slice rather than just 9 ortho slices
560+ Usage: ``slicesdir [-o] [-p <image>] [-e <thr>] [-S] <filelist>``
561+
562+ - ``-o`` : filelist is pairs ( <underlying> <red-outline> ) of images
563+ - ``-p <image>`` : use <image> as red-outline image on top of all images in <filelist>
564+ - ``-e <thr>`` : use the specified threshold for edges (if >0 use this proportion of
565+ max-min, if <0, use the absolute value)
566+ - ``-S`` : output every second axial slice rather than just 9 ortho slices
560567 """
561568
562569 _cmd = 'slicesdir'
563570 input_spec = _SlicesDirInputSpec
564571 output_spec = _SlicesDirOutputSpec
572+ _short_basenames = None
573+ _short_outline = None
574+
575+ def _run_interface (self , runtime ):
576+ """Create symlinks with short names before running slicesdir.
577+
578+ ``slicesdir`` names its output files by replacing path separators in the input
579+ file paths with underscores. When input paths are long (e.g., in deep working
580+ directories), the derived output filenames can exceed the OS's 255-character
581+ filename limit (see https://github.com/PennLINC/xcp_d/issues/1545).
582+
583+ We work around this by creating symlinks with short names in the working
584+ directory, and passing those short names to ``slicesdir`` instead of the
585+ original long paths.
586+ """
587+ self ._short_basenames = []
588+ for i , f in enumerate (self .inputs .in_files ):
589+ ext = '.nii.gz' if f .endswith ('.nii.gz' ) else os .path .splitext (f )[1 ]
590+ basename = f'img{ i } { ext } '
591+ symlink_path = os .path .join (runtime .cwd , basename )
592+ if os .path .lexists (symlink_path ):
593+ os .unlink (symlink_path )
594+
595+ os .symlink (os .path .abspath (f ), symlink_path )
596+ self ._short_basenames .append (basename )
597+
598+ if isdefined (self .inputs .outline_image ):
599+ f = self .inputs .outline_image
600+ ext = '.nii.gz' if f .endswith ('.nii.gz' ) else os .path .splitext (f )[1 ]
601+ self ._short_outline = f'outline{ ext } '
602+ symlink_path = os .path .join (runtime .cwd , self ._short_outline )
603+ if os .path .lexists (symlink_path ):
604+ os .unlink (symlink_path )
605+
606+ os .symlink (os .path .abspath (f ), symlink_path )
607+
608+ runtime = super ()._run_interface (runtime )
609+ return runtime
610+
611+ def _format_arg (self , name , spec , value ):
612+ """Use short symlink basenames for in_files and outline_image.
613+
614+ This ensures the ``slicesdir`` command line uses short paths, producing
615+ short output filenames that won't exceed the OS filename length limit.
616+ """
617+ if name == 'in_files' and self ._short_basenames is not None :
618+ return ' ' .join (self ._short_basenames )
619+
620+ if name == 'outline_image' and self ._short_outline is not None :
621+ return spec .argstr % self ._short_outline
622+
623+ return super ()._format_arg (name , spec , value )
565624
566625 def _list_outputs (self ):
567626 """Create a Bunch which contains all possible files generated by running the interface.
@@ -580,13 +639,21 @@ def _list_outputs(self):
580639
581640 out_dir = os .path .abspath (os .path .join (os .getcwd (), 'slicesdir' ))
582641 outputs ['out_dir' ] = out_dir
642+
643+ # Use short basenames if available (set by _run_interface),
644+ # otherwise fall back to the original path-based naming.
645+ if self ._short_basenames is not None :
646+ in_basenames = self ._short_basenames
647+ else :
648+ in_basenames = [f .replace (os .sep , '_' ) for f in self .inputs .in_files ]
649+
583650 outputs ['out_files' ] = [
584651 self ._gen_fname (
585- basename = f . replace ( os . sep , '_' ) ,
652+ basename = name ,
586653 cwd = out_dir ,
587654 ext = self .inputs .out_extension ,
588655 )
589- for f in self . inputs . in_files
656+ for name in in_basenames
590657 ]
591658 temp_files = [
592659 'grota.png' ,
@@ -633,9 +700,10 @@ class PNGAppend(FSLCommand):
633700
634701 Usage: pngappend <input 1> <+|-> [n] <input 2> [<+|-> [n] <input n>] output>
635702
636- + appends horizontally,
637- - appends vertically (i.e. works like a linebreak)
638- [n] number ofgap pixels
703+ - ``+`` appends horizontally,
704+ - ``-`` appends vertically (i.e. works like a linebreak)
705+ - ``[n]`` number of gap pixels
706+
639707 note that files with .gif extension will be input/output in GIF format
640708 """
641709
0 commit comments