1717create each one with the corresponding class from
1818:mod:`sagemaker.core.lineage` under a
1919:class:`~sagemaker.core.workflow.pipeline_context.PipelineSession` and pass
20- the captured arguments to the step.
20+ the captured arguments to the matching argument of the step -- ``actions``,
21+ ``artifacts`` or ``contexts``.
2122
2223Associations are declared rather than captured. ``Association.create()``
2324takes source and destination ARNs, but an entity created by the same step
3132
3233 step = LineageStep(
3334 name="RecordLineage",
34- step_args =[
35+ actions =[
3536 Action.create(
3637 action_name="training-run",
3738 source_uri="s3://bucket/run",
3839 source_type="S3ETag",
3940 action_type="ModelTraining",
4041 sagemaker_session=pipeline_session,
4142 ),
43+ ],
44+ artifacts=[
4245 Artifact.create(
4346 artifact_name="trained-model",
4447 source_uri="s3://bucket/model.tar.gz",
7275ENTITY_TYPE_ARTIFACT = "Artifact"
7376ENTITY_TYPE_CONTEXT = "Context"
7477
75- # Maps the captured lineage create call to the ``Arguments`` key the pipeline
78+ # One entry per kind of entity a LineageStep can create, in the order the
79+ # entities appear in the request. Each maps the step argument to the lineage
80+ # create call that must have produced it, the ``Arguments`` key the pipeline
7681# service expects, the entity type used when referencing the entity from an
7782# association, and the request field holding the entity name.
78- _ENTITY_SPECS = {
79- "create_action" : ("Actions" , ENTITY_TYPE_ACTION , "ActionName" ),
80- "create_artifact" : ("Artifacts" , ENTITY_TYPE_ARTIFACT , "ArtifactName" ),
81- "create_context" : ("Contexts" , ENTITY_TYPE_CONTEXT , "ContextName" ),
83+ _ENTITY_KINDS = (
84+ ("actions" , "create_action" , "Actions" , ENTITY_TYPE_ACTION , "ActionName" ),
85+ ("artifacts" , "create_artifact" , "Artifacts" , ENTITY_TYPE_ARTIFACT , "ArtifactName" ),
86+ ("contexts" , "create_context" , "Contexts" , ENTITY_TYPE_CONTEXT , "ContextName" ),
87+ )
88+
89+ # The producer each kind must come from, for the "wrong argument" error message.
90+ _EXPECTED_PRODUCER = {
91+ "actions" : "Action.create()" ,
92+ "artifacts" : "Artifact.create()" ,
93+ "contexts" : "Context.create()" ,
8294}
8395
8496
97+ def _as_list (value ) -> list :
98+ """Normalise a single captured argument or a sequence of them to a list."""
99+ if value is None :
100+ return []
101+ if isinstance (value , (list , tuple )):
102+ return list (value )
103+ return [value ]
104+
105+
85106class LineageEntityReference :
86107 """Reference to a lineage entity, used as an association endpoint.
87108
@@ -207,7 +228,9 @@ class LineageStep(Step):
207228 def __init__ (
208229 self ,
209230 name : str ,
210- step_args : Optional [Union [_JobStepArguments , List [_JobStepArguments ]]] = None ,
231+ actions : Optional [Union [_JobStepArguments , List [_JobStepArguments ]]] = None ,
232+ artifacts : Optional [Union [_JobStepArguments , List [_JobStepArguments ]]] = None ,
233+ contexts : Optional [Union [_JobStepArguments , List [_JobStepArguments ]]] = None ,
211234 associations : Optional [List [LineageAssociation ]] = None ,
212235 display_name : Optional [str ] = None ,
213236 description : Optional [str ] = None ,
@@ -217,11 +240,13 @@ def __init__(
217240
218241 Args:
219242 name (str): The name of the step.
220- step_args (_JobStepArguments or list): The captured arguments for
221- each entity this step creates, obtained from
222- ``Action.create()``, ``Artifact.create()`` or
223- ``Context.create()`` called with a ``PipelineSession``. A
224- single value is accepted for a step that creates one entity.
243+ actions (_JobStepArguments or list): Captured arguments from
244+ ``Action.create()`` called with a ``PipelineSession``, for each
245+ action this step creates.
246+ artifacts (_JobStepArguments or list): Captured arguments from
247+ ``Artifact.create()``, for each artifact this step creates.
248+ contexts (_JobStepArguments or list): Captured arguments from
249+ ``Context.create()``, for each context this step creates.
225250 associations (List[LineageAssociation]): Associations to add after
226251 the entities are created.
227252 display_name (str): Optional display name.
@@ -237,30 +262,30 @@ def __init__(
237262 depends_on = depends_on ,
238263 )
239264
240- if step_args is None :
241- entities = []
242- elif isinstance (step_args , (list , tuple )):
243- entities = list (step_args )
244- else :
245- entities = [step_args ]
246-
247265 self .associations = list (associations ) if associations else []
248266
249- if not entities and not self .associations :
250- raise ValueError (
251- "A LineageStep requires at least one entity in step_args, or one association."
252- )
267+ supplied = {"actions" : actions , "artifacts" : artifacts , "contexts" : contexts }
268+ self ._entities : Dict [str , List [_JobStepArguments ]] = {}
269+ for param , caller , _ , _ , _ in _ENTITY_KINDS :
270+ entities = _as_list (supplied [param ])
271+ for entity in entities :
272+ validate_step_args_input (
273+ step_args = entity ,
274+ expected_caller = {caller },
275+ error_message = (
276+ f"The { param } of LineageStep must be obtained from "
277+ f"{ _EXPECTED_PRODUCER [param ]} called with a PipelineSession. "
278+ "Associations are passed to the associations argument instead, "
279+ "because Association.create() cannot reference an entity created "
280+ "by the same step."
281+ ),
282+ )
283+ self ._entities [param ] = entities
253284
254- for entity in entities :
255- validate_step_args_input (
256- step_args = entity ,
257- expected_caller = set (_ENTITY_SPECS ),
258- error_message = (
259- "The step_args of LineageStep must be obtained from Action.create(), "
260- "Artifact.create() or Context.create() called with a PipelineSession. "
261- "Associations are passed to the associations argument instead, because "
262- "Association.create() cannot reference an entity created by the same step."
263- ),
285+ if not any (self ._entities .values ()) and not self .associations :
286+ raise ValueError (
287+ "A LineageStep requires at least one entity in actions, artifacts or "
288+ "contexts, or one association."
264289 )
265290
266291 for association in self .associations :
@@ -270,7 +295,6 @@ def __init__(
270295 f"{ type (association ).__name__ } ."
271296 )
272297
273- self .step_args = entities
274298 self ._validate_sibling_references ()
275299
276300 root = Properties (step_name = name , step = self )
@@ -279,14 +303,29 @@ def __init__(
279303 root .__dict__ ["Associations" ] = Properties (step_name = name , path = "Associations" )
280304 self ._properties = root
281305
306+ @property
307+ def actions (self ) -> List [_JobStepArguments ]:
308+ """The captured arguments for the actions this step creates."""
309+ return self ._entities ["actions" ]
310+
311+ @property
312+ def artifacts (self ) -> List [_JobStepArguments ]:
313+ """The captured arguments for the artifacts this step creates."""
314+ return self ._entities ["artifacts" ]
315+
316+ @property
317+ def contexts (self ) -> List [_JobStepArguments ]:
318+ """The captured arguments for the contexts this step creates."""
319+ return self ._entities ["contexts" ]
320+
282321 def _created_entities (self ) -> Dict [str , str ]:
283322 """Map the name of each entity created by this step to its type."""
284323 created = {}
285- for entity in self . step_args :
286- _ , entity_type , name_field = _ENTITY_SPECS [ entity . caller_name ]
287- name = entity .args .get (name_field )
288- if isinstance (name , str ):
289- created [name ] = entity_type
324+ for param , _ , _ , entity_type , name_field in _ENTITY_KINDS :
325+ for entity in self . _entities [ param ]:
326+ name = entity .args .get (name_field )
327+ if isinstance (name , str ):
328+ created [name ] = entity_type
290329 return created
291330
292331 def _validate_sibling_references (self ) -> None :
@@ -319,9 +358,10 @@ def _validate_sibling_references(self) -> None:
319358 def arguments (self ) -> RequestType :
320359 """The ``Arguments`` block: the entities and associations for this step."""
321360 request : RequestType = {}
322- for entity in self .step_args :
323- key , _ , _ = _ENTITY_SPECS [entity .caller_name ]
324- request .setdefault (key , []).append (entity .args )
361+ for param , _ , key , _ , _ in _ENTITY_KINDS :
362+ entities = self ._entities [param ]
363+ if entities :
364+ request [key ] = [entity .args for entity in entities ]
325365 if self .associations :
326366 request ["Associations" ] = [a .to_request () for a in self .associations ]
327367 return request
0 commit comments