1818import attrs
1919from filelock import SoftFileLock
2020from pydra .engine .specs import TaskDef , WorkflowDef , TaskOutputs , WorkflowOutputs
21- from pydra .engine .graph import DiGraph
21+ from pydra .engine .graph import DiGraph , INPUTS_NODE_NAME , OUTPUTS_NODE_NAME
2222from pydra .engine import state
2323from .lazy import LazyInField , LazyOutField
2424from pydra .utils .hash import hash_function , Cache
@@ -598,16 +598,32 @@ def clear_cache(
598598
599599 @classmethod
600600 def construct (
601- cls , definition : WorkflowDef [WorkflowOutputsType ], dont_cache : bool = False
601+ cls ,
602+ definition : WorkflowDef [WorkflowOutputsType ],
603+ dont_cache : bool = False ,
604+ lazy : ty .Sequence [str ] = (),
602605 ) -> Self :
603- """Construct a workflow from a definition, caching the constructed worklow"""
606+ """Construct a workflow from a definition, caching the constructed worklow
607+
608+ Parameters
609+ ----------
610+ definition : WorkflowDef
611+ The definition of the workflow to construct
612+ dont_cache : bool, optional
613+ Whether to cache the constructed workflow, by default False
614+ lazy : Sequence[str], optional
615+ The names of the inputs to the workflow to be considered lazy even if they
616+ have values in the given definition, by default ()
617+ """
604618
605619 # Check the previously constructed workflows to see if a workflow has been
606620 # constructed for the given set of inputs, or a less-specific set (i.e. with a
607621 # super-set of lazy inputs), and use that if it exists
608622
609623 non_lazy_vals = {
610- n : v for n , v in attrs_values (definition ).items () if not is_lazy (v )
624+ n : v
625+ for n , v in attrs_values (definition ).items ()
626+ if not is_lazy (v ) and n not in lazy
611627 }
612628 non_lazy_keys = frozenset (non_lazy_vals )
613629 hash_cache = Cache () # share the hash cache to avoid recalculations
@@ -821,56 +837,55 @@ def _create_graph(
821837 DiGraph
822838 The graph of the workflow
823839 """
824- graph : DiGraph = DiGraph ()
840+ graph : DiGraph = DiGraph (name = self . name )
825841 for node in nodes :
826842 graph .add_nodes (node )
827843 # TODO: create connection is run twice
828844 for node in nodes :
829845 other_states = {}
830- for field in attrs_fields (node .inputs ):
846+ for field in list_fields (node ._definition ):
831847 lf = node ._definition [field .name ]
832848 if isinstance (lf , LazyOutField ):
833849 # adding an edge to the graph if task id expecting output from a different task
834- if lf ._node .name != self .name :
835- # checking if the connection is already in the graph
836- if (graph .node (lf ._node .name ), node ) not in graph .edges :
837- graph .add_edges ((graph .node (lf ._node .name ), node ))
838- if detailed :
839- graph .add_edges_description (
840- (node .name , field .name , lf ._node .name , lf ._field )
841- )
842- logger .debug ("Connecting %s to %s" , lf ._node .name , node .name )
843- # adding a state from the previous task to other_states
850+
851+ # checking if the connection is already in the graph
852+ if (graph .node (lf ._node .name ), node ) not in graph .edges :
853+ graph .add_edges ((graph .node (lf ._node .name ), node ))
854+ if detailed :
855+ graph .add_edges_description (
856+ (node .name , field .name , lf ._node .name , lf ._field )
857+ )
858+ logger .debug ("Connecting %s to %s" , lf ._node .name , node .name )
859+ # adding a state from the previous task to other_states
860+ if (
861+ graph .node (lf ._node .name ).state
862+ and graph .node (lf ._node .name ).state .splitter_rpn_final
863+ ):
864+ # variables that are part of inner splitters should be
865+ # treated as a containers
844866 if (
845- graph .node (lf ._node .name ).state
846- and graph .node (lf ._node .name ).state .splitter_rpn_final
867+ node .state
868+ and f"{ node .name } .{ field .name } "
869+ in node .state ._current_splitter_rpn
847870 ):
848- # variables that are part of inner splitters should be
849- # treated as a containers
850- if (
851- node .state
852- and f"{ node .name } .{ field .name } "
853- in node .state ._current_splitter_rpn
854- ):
855- node .state ._inner_cont_dim [
856- f"{ node .name } .{ field .name } "
857- ] = 1
858- # adding task_name: (task.state, [a field from the connection]
859- if lf ._node .name not in other_states :
860- other_states [lf ._node .name ] = (
861- graph .node (lf ._node .name ).state ,
862- [field .name ],
863- )
864- else :
865- # if the task already exist in other_state,
866- # additional field name should be added to the list of fields
867- other_states [lf ._node .name ][1 ].append (field .name )
868- else : # LazyField with the wf input
869- # connections with wf input should be added to the detailed graph description
870- if detailed :
871- graph .add_edges_description (
872- (node .name , field .name , lf ._node .name , lf ._field )
871+ node .state ._inner_cont_dim [f"{ node .name } .{ field .name } " ] = 1
872+ # adding task_name: (task.state, [a field from the connection]
873+ if lf ._node .name not in other_states :
874+ other_states [lf ._node .name ] = (
875+ graph .node (lf ._node .name ).state ,
876+ [field .name ],
873877 )
878+ else :
879+ # if the task already exist in other_state,
880+ # additional field name should be added to the list of fields
881+ other_states [lf ._node .name ][1 ].append (field .name )
882+ elif (
883+ isinstance (lf , LazyInField ) and detailed
884+ ): # LazyField with the wf input
885+ # connections with wf input should be added to the detailed graph description
886+ graph .add_edges_description (
887+ (node .name , field .name , INPUTS_NODE_NAME , lf ._field )
888+ )
874889
875890 # if task has connections state has to be recalculated
876891 if other_states :
@@ -890,6 +905,12 @@ def _create_graph(
890905 other_states = other_states ,
891906 combiner = combiner ,
892907 )
908+ if detailed :
909+ lf : LazyOutField
910+ for outpt_name , lf in attrs_values (self .outputs ).items ():
911+ graph .add_edges_description (
912+ (OUTPUTS_NODE_NAME , outpt_name , lf ._node .name , lf ._field )
913+ )
893914 return graph
894915
895916
0 commit comments