@@ -279,7 +279,8 @@ class _DerivativesDataSinkInputSpec(DynamicTraitedSpec, BaseInterfaceInputSpec):
279
279
File (exists = True ), mandatory = True , desc = "the object to be saved"
280
280
)
281
281
meta_dict = traits .DictStrAny (desc = "an input dictionary containing metadata" )
282
- source_file = File (exists = False , mandatory = True , desc = "the input func file" )
282
+ source_file = InputMultiObject (
283
+ File (exists = False ), mandatory = True , desc = "the source file(s) to extract entities from" )
283
284
284
285
285
286
class _DerivativesDataSinkOutputSpec (TraitedSpec ):
@@ -348,6 +349,32 @@ class DerivativesDataSink(SimpleInterface):
348
349
['.../niworkflows/sub-01/ses-retest/anat/sub-01_ses-retest_custom1-1_custom2-b_T1w.nii',
349
350
'.../niworkflows/sub-01/ses-retest/anat/sub-01_ses-retest_custom1-2_custom2-b_T1w.nii']
350
351
352
+ When multiple source files are passed, only common entities are passed down.
353
+ For example, if two T1w images from different sessions are used to generate
354
+ a single image, the session entity is removed automatically.
355
+
356
+ >>> bids_dir = tmpdir / 'bidsroot'
357
+ >>> multi_source = [
358
+ ... bids_dir / 'sub-02/ses-A/anat/sub-02_ses-A_T1w.nii.gz',
359
+ ... bids_dir / 'sub-02/ses-B/anat/sub-02_ses-B_T1w.nii.gz']
360
+ >>> for source_file in multi_source:
361
+ ... source_file.parent.mkdir(parents=True, exist_ok=True)
362
+ ... _ = source_file.write_text("")
363
+ >>> dsink = DerivativesDataSink(base_directory=str(tmpdir), check_hdr=False)
364
+ >>> dsink.inputs.in_file = str(tmpfile)
365
+ >>> dsink.inputs.source_file = list(map(str, multi_source))
366
+ >>> dsink.inputs.desc = 'preproc'
367
+ >>> res = dsink.run()
368
+ >>> res.outputs.out_file # doctest: +ELLIPSIS
369
+ '.../niworkflows/sub-02/anat/sub-02_desc-preproc_T1w.nii'
370
+
371
+ If, on the other hand, only one is used, the session is preserved:
372
+
373
+ >>> dsink.inputs.source_file = str(multi_source[0])
374
+ >>> res = dsink.run()
375
+ >>> res.outputs.out_file # doctest: +ELLIPSIS
376
+ '.../niworkflows/sub-02/ses-A/anat/sub-02_ses-A_desc-preproc_T1w.nii'
377
+
351
378
>>> bids_dir = tmpdir / 'bidsroot' / 'sub-02' / 'ses-noanat' / 'func'
352
379
>>> bids_dir.mkdir(parents=True, exist_ok=True)
353
380
>>> tricky_source = bids_dir / 'sub-02_ses-noanat_task-rest_run-01_bold.nii.gz'
@@ -480,9 +507,12 @@ def _run_interface(self, runtime):
480
507
self ._metadata = meta
481
508
482
509
# Initialize entities with those from the source file.
483
- out_entities = parse_file_entities (
484
- str (relative_to_root (self .inputs .source_file ))
485
- )
510
+ in_entities = [
511
+ parse_file_entities (str (relative_to_root (source_file )))
512
+ for source_file in self .inputs .source_file
513
+ ]
514
+ out_entities = {k : v for k , v in in_entities [0 ].items ()
515
+ if all (ent .get (k ) == v for ent in in_entities [1 :])}
486
516
for drop_entity in listify (self .inputs .dismiss_entities or []):
487
517
out_entities .pop (drop_entity , None )
488
518
0 commit comments