@@ -120,6 +120,7 @@ def template_update(inputs, output_dir, state_ind=None, map_copyfiles=None):
120
120
field
121
121
for field in attr_fields (inputs )
122
122
if field .metadata .get ("output_file_template" )
123
+ and getattr (inputs , field .name ) is not False
123
124
and all (
124
125
getattr (inputs , required_field ) is not attr .NOTHING
125
126
for required_field in field .metadata .get ("requires" , ())
@@ -150,25 +151,19 @@ def template_update_single(
150
151
# if input_dict_st with state specific value is not available,
151
152
# the dictionary will be created from inputs object
152
153
from ..utils .typing import TypeParser # noqa
153
- from pydra .engine .specs import LazyField
154
-
155
- VALID_TYPES = (str , ty .Union [str , bool ], Path , ty .Union [Path , bool ], LazyField )
154
+ from pydra .engine .specs import LazyField , OUTPUT_TEMPLATE_TYPES
156
155
157
156
if inputs_dict_st is None :
158
157
inputs_dict_st = attr .asdict (inputs , recurse = False )
159
158
160
159
if spec_type == "input" :
161
160
inp_val_set = inputs_dict_st [field .name ]
162
- if inp_val_set is not attr .NOTHING and not TypeParser .is_instance (
163
- inp_val_set , VALID_TYPES
164
- ):
165
- raise TypeError (
166
- f"'{ field .name } ' field has to be a Path instance or a bool, but { inp_val_set } set"
167
- )
168
161
if isinstance (inp_val_set , bool ) and field .type in (Path , str ):
169
162
raise TypeError (
170
163
f"type of '{ field .name } ' is Path, consider using Union[Path, bool]"
171
164
)
165
+ if inp_val_set is not attr .NOTHING and not isinstance (inp_val_set , LazyField ):
166
+ inp_val_set = TypeParser (ty .Union [OUTPUT_TEMPLATE_TYPES ])(inp_val_set )
172
167
elif spec_type == "output" :
173
168
if not TypeParser .contains_type (FileSet , field .type ):
174
169
raise TypeError (
@@ -178,22 +173,23 @@ def template_update_single(
178
173
else :
179
174
raise TypeError (f"spec_type can be input or output, but { spec_type } provided" )
180
175
# for inputs that the value is set (so the template is ignored)
181
- if spec_type == "input" and isinstance (inputs_dict_st [field .name ], (str , Path )):
182
- return inputs_dict_st [field .name ]
183
- elif spec_type == "input" and inputs_dict_st [field .name ] is False :
184
- # if input fld is set to False, the fld shouldn't be used (setting NOTHING)
185
- return attr .NOTHING
186
- else : # inputs_dict[field.name] is True or spec_type is output
187
- value = _template_formatting (field , inputs , inputs_dict_st )
188
- # changing path so it is in the output_dir
189
- if output_dir and value is not attr .NOTHING :
190
- # should be converted to str, it is also used for input fields that should be str
191
- if type (value ) is list :
192
- return [str (output_dir / Path (val ).name ) for val in value ]
193
- else :
194
- return str (output_dir / Path (value ).name )
195
- else :
176
+ if spec_type == "input" :
177
+ if isinstance (inp_val_set , (Path , list )):
178
+ return inp_val_set
179
+ if inp_val_set is False :
180
+ # if input fld is set to False, the fld shouldn't be used (setting NOTHING)
196
181
return attr .NOTHING
182
+ # inputs_dict[field.name] is True or spec_type is output
183
+ value = _template_formatting (field , inputs , inputs_dict_st )
184
+ # changing path so it is in the output_dir
185
+ if output_dir and value is not attr .NOTHING :
186
+ # should be converted to str, it is also used for input fields that should be str
187
+ if type (value ) is list :
188
+ return [str (output_dir / Path (val ).name ) for val in value ]
189
+ else :
190
+ return str (output_dir / Path (value ).name )
191
+ else :
192
+ return attr .NOTHING
197
193
198
194
199
195
def _template_formatting (field , inputs , inputs_dict_st ):
@@ -204,16 +200,27 @@ def _template_formatting(field, inputs, inputs_dict_st):
204
200
Allowing for multiple input values used in the template as longs as
205
201
there is no more than one file (i.e. File, PathLike or string with extensions)
206
202
"""
207
- from .specs import MultiInputObj , MultiOutputFile
208
-
209
203
# if a template is a function it has to be run first with the inputs as the only arg
210
204
template = field .metadata ["output_file_template" ]
211
205
if callable (template ):
212
206
template = template (inputs )
213
207
214
208
# as default, we assume that keep_extension is True
215
- keep_extension = field .metadata .get ("keep_extension" , True )
209
+ if isinstance (template , (tuple , list )):
210
+ formatted = [
211
+ _string_template_formatting (field , t , inputs , inputs_dict_st )
212
+ for t in template
213
+ ]
214
+ else :
215
+ assert isinstance (template , str )
216
+ formatted = _string_template_formatting (field , template , inputs , inputs_dict_st )
217
+ return formatted
218
+
216
219
220
+ def _string_template_formatting (field , template , inputs , inputs_dict_st ):
221
+ from .specs import MultiInputObj , MultiOutputFile
222
+
223
+ keep_extension = field .metadata .get ("keep_extension" , True )
217
224
inp_fields = re .findall (r"{\w+}" , template )
218
225
inp_fields_fl = re .findall (r"{\w+:[0-9.]+f}" , template )
219
226
inp_fields += [re .sub (":[0-9.]+f" , "" , el ) for el in inp_fields_fl ]
0 commit comments