@@ -625,7 +625,7 @@ def describe(self):
625625
626626
627627class _LocalPipeline (object ):
628- """Placeholder docstring """
628+ """Class representing a local SageMaker Pipeline """
629629
630630 _executions = {}
631631
@@ -645,7 +645,7 @@ def __init__(
645645 self .last_modified_time = now_time
646646
647647 def describe (self ):
648- """Placeholder docstring """
648+ """Describe Pipeline """
649649 response = {
650650 "PipelineArn" : self .pipeline .name ,
651651 "PipelineDefinition" : self .pipeline .definition (),
@@ -659,7 +659,7 @@ def describe(self):
659659 return response
660660
661661 def start (self , ** kwargs ):
662- """Placeholder docstring """
662+ """Start a pipeline execution. Returns a _LocalPipelineExecution object. """
663663 from sagemaker .local .pipeline import LocalPipelineExecutor
664664
665665 execution_id = str (uuid4 ())
@@ -670,7 +670,7 @@ def start(self, **kwargs):
670670
671671
672672class _LocalPipelineExecution (object ):
673- """Placeholder docstring """
673+ """Class representing a local SageMaker pipeline execution. """
674674
675675 def __init__ (
676676 self ,
@@ -693,7 +693,7 @@ def __init__(
693693 self .blockout_steps = {}
694694
695695 def describe (self ):
696- """Placeholder docstring """
696+ """Describe Pipeline Execution. """
697697 response = {
698698 "CreationTime" : self .creation_time ,
699699 "LastModifiedTime" : self .creation_time ,
@@ -708,8 +708,14 @@ def describe(self):
708708 return filtered_response
709709
710710 def list_steps (self ):
711- """Placeholder docstring"""
712- # TODO
711+ """List pipeline execution steps."""
712+ return {
713+ "PipelineExecutionSteps" : [
714+ step .to_list_steps_response ()
715+ for step in self .step_execution .values ()
716+ if step .status is not None
717+ ]
718+ }
713719
714720 def update_execution_success (self ):
715721 """Mark execution as succeeded."""
@@ -730,8 +736,8 @@ def update_step_failure(self, step_name, failure_message):
730736 self .step_execution .get (step_name ).update_step_failure (failure_message )
731737
732738 def mark_step_executing (self , step_name ):
733- """Update step's status to EXECUTING"""
734- self .step_execution .get (step_name ).status = _LocalExecutionStatus . EXECUTING . value
739+ """Update pipelines step's status to EXECUTING and start_time to now. """
740+ self .step_execution .get (step_name ).mark_step_executing ()
735741
736742 def _initialize_step_execution (self , steps ):
737743 """Initialize step_execution dict."""
@@ -751,7 +757,9 @@ def _initialize_step_execution(self, steps):
751757 "Step type {} is not supported in local mode." .format (step .step_type .value )
752758 )
753759 raise ClientError (error_msg , "start_pipeline_execution" )
754- self .step_execution [step .name ] = _LocalPipelineStepExecution (step .name , step .step_type )
760+ self .step_execution [step .name ] = _LocalPipelineExecutionStep (
761+ step .name , step .step_type , step .description , step .display_name
762+ )
755763 if step .step_type == StepTypeEnum .CONDITION :
756764 self ._initialize_step_execution (step .if_steps + step .else_steps )
757765
@@ -790,44 +798,105 @@ def _construct_validation_exception_message(exception_msg):
790798 return {"Error" : {"Code" : "ValidationException" , "Message" : exception_msg }}
791799
792800
793- class _LocalPipelineStepExecution (object ):
794- """Placeholder docstring """
801+ class _LocalPipelineExecutionStep (object ):
802+ """Class representing a local pipeline execution step. """
795803
796804 def __init__ (
797805 self ,
798- step_name ,
806+ name ,
799807 step_type ,
800- last_modified_time = None ,
808+ description ,
809+ display_name = None ,
810+ start_time = None ,
811+ end_time = None ,
801812 status = None ,
802813 properties = None ,
803814 failure_reason = None ,
804815 ):
805- self .step_name = step_name
806- self .step_type = step_type
807- self .status = status or _LocalExecutionStatus .STARTING .value
816+ from sagemaker .workflow .steps import StepTypeEnum
817+
818+ self .name = name
819+ self .type = step_type
820+ self .description = description
821+ self .display_name = display_name
822+ self .status = status
808823 self .failure_reason = failure_reason
809824 self .properties = properties or {}
810- self .creation_time = datetime .datetime .now ()
811- self .last_modified_time = last_modified_time or self .creation_time
825+ self .start_time = start_time
826+ self .end_time = end_time
827+ self ._step_type_to_output_format_map = {
828+ StepTypeEnum .TRAINING : self ._construct_training_metadata ,
829+ StepTypeEnum .PROCESSING : self ._construct_processing_metadata ,
830+ StepTypeEnum .TRANSFORM : self ._construct_transform_metadata ,
831+ StepTypeEnum .CONDITION : self ._construct_condition_metadata ,
832+ StepTypeEnum .FAIL : self ._construct_fail_metadata ,
833+ }
812834
813835 def update_step_properties (self , properties ):
814836 """Update pipeline step execution output properties."""
815- logger .info ("Successfully completed step %s." , self .step_name )
837+ logger .info ("Successfully completed step %s." , self .name )
816838 self .properties = deepcopy (properties )
817839 self .status = _LocalExecutionStatus .SUCCEEDED .value
840+ self .end_time = datetime .datetime .now ()
818841
819842 def update_step_failure (self , failure_message ):
820843 """Update pipeline step execution failure status and message."""
821844 logger .error (failure_message )
822845 self .failure_reason = failure_message
823846 self .status = _LocalExecutionStatus .FAILED .value
824- raise StepExecutionException (self .step_name , failure_message )
847+ self .end_time = datetime .datetime .now ()
848+ raise StepExecutionException (self .name , failure_message )
849+
850+ def mark_step_executing (self ):
851+ """Update pipelines step's status to EXECUTING and start_time to now"""
852+ self .status = _LocalExecutionStatus .EXECUTING .value
853+ self .start_time = datetime .datetime .now ()
854+
855+ def to_list_steps_response (self ):
856+ """Convert to response dict for list_steps calls."""
857+ response = {
858+ "EndTime" : self .end_time ,
859+ "FailureReason" : self .failure_reason ,
860+ "Metadata" : self ._construct_metadata (),
861+ "StartTime" : self .start_time ,
862+ "StepDescription" : self .description ,
863+ "StepDisplayName" : self .display_name ,
864+ "StepName" : self .name ,
865+ "StepStatus" : self .status ,
866+ }
867+ filtered_response = {k : v for k , v in response .items () if v is not None }
868+ return filtered_response
869+
870+ def _construct_metadata (self ):
871+ """Constructs the metadata shape for the list_steps_response."""
872+ if self .properties :
873+ return self ._step_type_to_output_format_map [self .type ]()
874+ return None
875+
876+ def _construct_training_metadata (self ):
877+ """Construct training job metadata response."""
878+ return {"TrainingJob" : {"Arn" : self .properties .TrainingJobArn }}
879+
880+ def _construct_processing_metadata (self ):
881+ """Construct processing job metadata response."""
882+ return {"ProcessingJob" : {"Arn" : self .properties .ProcessingJobArn }}
883+
884+ def _construct_transform_metadata (self ):
885+ """Construct transform job metadata response."""
886+ return {"TransformJob" : {"Arn" : self .properties .TransformJobArn }}
887+
888+ def _construct_condition_metadata (self ):
889+ """Construct condition step metadata response."""
890+ return {"Condition" : {"Outcome" : self .properties .Outcome }}
891+
892+ def _construct_fail_metadata (self ):
893+ """Construct fail step metadata response."""
894+ return {"Fail" : {"ErrorMessage" : self .properties .ErrorMessage }}
825895
826896
827897class _LocalExecutionStatus (enum .Enum ):
828- """Placeholder docstring """
898+ """Pipeline execution status. """
829899
830- STARTING = "Starting"
831900 EXECUTING = "Executing"
832901 SUCCEEDED = "Succeeded"
833902 FAILED = "Failed"
0 commit comments