@@ -130,34 +130,40 @@ def pipeline_id(self) -> None:
130130
131131 # Input settings
132132 @property
133- def input_settings (self ) -> dict :
133+ def input_settings (self ) -> list [ dict ] | None :
134134 """All parameters needed to run the pipeline
135135 Run show_pipeline() for more information"""
136136 # Return None if the private attribute is unset
137137 return self ._get_input_settings () if self ._is_defined ("_input_settings" ) else None
138138
139139 @input_settings .setter
140- def input_settings (self , input_settings : dict ):
140+ def input_settings (self , input_settings : dict | list [ dict ] ):
141141 # Call deleter if agument is None
142142 if input_settings is None :
143143 del self .input_settings
144144 return
145145 # Display
146146 self ._print ("Input Settings --> " , end = "" , flush = True )
147147 # Check type
148- if not isinstance (input_settings , dict ):
149- raise TypeError ("`input_settings` should be a dictionary" )
150-
148+ is_dict = isinstance (input_settings , dict )
149+ is_nonempty_dict_list = isinstance (input_settings , list ) and input_settings and all (isinstance (item , dict ) for item in input_settings )
150+ if not (is_dict or is_nonempty_dict_list ):
151+ raise TypeError ("`input_settings` should be a dictionary or a non-empty list of dictionaries" )
152+
151153 # Check if each input can be converted to a string with valid characters and no empty strings
152- self ._check_invalid_input (input_settings )
153-
154- # Parse the input settings
155- new_settings = self ._parse_input_settings (input_settings )
156- self ._print ("parsed" )
157- # Check conflicts with private attribute
158- self ._check_value ("_input_settings" , new_settings )
159- # Update
160- self ._input_settings = new_settings
154+ inputs = input_settings if is_nonempty_dict_list else [input_settings ]
155+ new_input_settings = []
156+ for input_dict in inputs :
157+ self ._check_invalid_input (input_dict )
158+ # Parse the input settings
159+ new_settings = self ._parse_input_settings (input_dict )
160+ self ._print ("parsed" )
161+ # Check conflicts with private attribute
162+ self ._check_value ("_input_settings" , new_settings )
163+ # Update
164+ new_input_settings .append (new_settings )
165+
166+ self ._input_settings = new_input_settings
161167
162168 @input_settings .deleter
163169 def input_settings (self ) -> None :
@@ -308,7 +314,7 @@ def _pipeline_def(self) -> dict:
308314
309315 def __init__ (
310316 self , output_dir = None , pipeline_id : str = None ,
311- input_settings : dict = None , session_name : str = None , verbose : bool = None
317+ input_settings : dict | list [ dict ] = None , session_name : str = None , verbose : bool = None
312318 ) -> None :
313319 """
314320 Create a VipLauncher instance and sets properties from keyword arguments.
@@ -439,7 +445,7 @@ def init(cls, api_key="VIP_API_KEY", verbose=True, vip_portal_url=None,
439445
440446 # Launch executions on VIP
441447 def launch_pipeline (
442- self , pipeline_id : str = None , input_settings : dict = None , output_dir = None , nb_runs = 1 ,
448+ self , pipeline_id : str = None , input_settings : dict | list [ dict ] = None , output_dir = None , nb_runs = 1 ,
443449 ) -> VipLauncher :
444450 """
445451 Launches pipeline executions on VIP.
@@ -556,7 +562,7 @@ def monitor_workflows(self, refresh_time=30) -> VipLauncher:
556562 self ._print ("Run launch_pipeline() to launch workflows on VIP." )
557563 return self
558564 # Update existing workflows
559- self ._print ("Updating worflow inventory ... " , end = "" , flush = True )
565+ self ._print ("Updating workflow inventory ... " , end = "" , flush = True )
560566 self ._update_workflows ()
561567 self ._print ("Done." )
562568 # Check if workflows are still running
@@ -565,7 +571,7 @@ def monitor_workflows(self, refresh_time=30) -> VipLauncher:
565571 self ._execution_report ()
566572 # Display standby
567573 self ._print ("\n -------------------------------------------------------------" )
568- self ._print ("The current proccess will wait until all executions are over." )
574+ self ._print ("The current process will wait until all executions are over." )
569575 self ._print ("Their progress can be monitored on VIP portal:" )
570576 self ._print (f"\t { self ._VIP_PORTAL } " )
571577 self ._print ("-------------------------------------------------------------" )
@@ -1538,21 +1544,21 @@ def parse_value(input):
15381544 }
15391545
15401546 # Get the input settings after files are parsed as PathLib objects
1541- def _get_input_settings (self , location = "vip" ) -> dict :
1547+ def _get_input_settings (self , location = "vip" ) -> list [ dict ] :
15421548 """
15431549 Returns the input settings with their orignal values in string format.
15441550 `location` is destined to subclasses.
15451551 """
15461552 if location != "vip" :
15471553 raise NotImplementedError (f"Unknown location: { location } " )
1548- return {
1549- key : [str (v ) for v in value ] if isinstance (value , list ) else str (value )
1550- for key , value in self ._input_settings . items ()
1551- }
1554+ return [
1555+ { key : [str (v ) for v in value ] if isinstance (value , list ) else str (value ) for key , value in input_dict . items ()}
1556+ for input_dict in self ._input_settings
1557+ ]
15521558 # ------------------------------------------------
15531559
15541560 # Check the input settings based on the pipeline descriptor
1555- def _check_input_settings (self , input_settings : dict = None , location : str = None ) -> None :
1561+ def _check_input_settings (self , input_settings : list [ dict ] = None , location : str = None ) -> None :
15561562 """
15571563 Checks `input_settings` with respect to pipeline descriptor. If not provided, checks the instance property.
15581564 Prerequisite: input_settings contains only strings or lists of strings.
@@ -1580,10 +1586,12 @@ def _check_input_settings(self, input_settings: dict=None, location: str=None) -
15801586 # Check the pipeline identifier
15811587 if not self ._is_defined ("_pipeline_id" ):
15821588 raise AttributeError ("Input settings could not be checked without a pipeline identifier." )
1583- # Parameter names
1584- self ._check_input_keys (input_settings )
1585- # Parameter values
1586- self ._check_input_values (input_settings , location = location )
1589+
1590+ for input_dict in input_settings :
1591+ # Parameter names
1592+ self ._check_input_keys (input_dict )
1593+ # Parameter values
1594+ self ._check_input_values (input_dict , location = location )
15871595 # Return True when all checks are complete
15881596 return True
15891597 # ------------------------------------------------
0 commit comments