Skip to content

Commit 684e761

Browse files
authored
Parent job name & multiple docker argument support for command job (Azure#39432)
* testing * adding 2025_01_01_preview client * adding multiple docker argument and parent job support for command job * test case fix * api review comment for kwargs in job entity * remove unuse entry in change log * fixing test findings * command job adding key word only * fixing unit test cases * fixing end to end test * merging master * reducing the scope of job configuration 2025 only in caseof multiple docker argument * Adding 2025 job configuration in from rest object * fixing failed test * adding more unit test cases
1 parent f65d0fe commit 684e761

File tree

16 files changed

+174
-33
lines changed

16 files changed

+174
-33
lines changed

sdk/ml/azure-ai-ml/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Release History
22
## 1.26.0 (unreleased)
33

4+
### Features Added
5+
- Adding parent job support for command job.
6+
- Adding multiple docker argument support for command job.
7+
48
### Bugs Fixed
59
- #37464 - Allowing to update a component in register with anonymousEnvironment environment.
610
- #39744 - Fixing a serialization issue in RecurrencePattern schedule.

sdk/ml/azure-ai-ml/azure/ai/ml/_ml_client.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from azure.ai.ml._restclient.v2024_04_01_preview import AzureMachineLearningWorkspaces as ServiceClient042024Preview
4141
from azure.ai.ml._restclient.v2024_07_01_preview import AzureMachineLearningWorkspaces as ServiceClient072024Preview
4242
from azure.ai.ml._restclient.v2024_10_01_preview import AzureMachineLearningWorkspaces as ServiceClient102024Preview
43+
from azure.ai.ml._restclient.v2025_01_01_preview import AzureMachineLearningWorkspaces as ServiceClient012025Preview
4344
from azure.ai.ml._restclient.workspace_dataplane import (
4445
AzureMachineLearningWorkspaces as ServiceClientWorkspaceDataplane,
4546
)
@@ -396,6 +397,17 @@ def __init__(
396397
**kwargs,
397398
)
398399

400+
self._service_client_01_2025_preview = ServiceClient012025Preview(
401+
credential=self._credential,
402+
subscription_id=(
403+
self._ws_operation_scope._subscription_id
404+
if registry_reference
405+
else self._operation_scope._subscription_id
406+
),
407+
base_url=base_url,
408+
**kwargs,
409+
)
410+
399411
# A general purpose, user-configurable pipeline for making
400412
# http requests
401413
self._requests_pipeline = HttpPipeline(**kwargs)
@@ -683,6 +695,7 @@ def __init__(
683695
requests_pipeline=self._requests_pipeline,
684696
service_client_01_2024_preview=self._service_client_01_2024_preview,
685697
service_client_10_2024_preview=self._service_client_10_2024_preview,
698+
service_client_01_2025_preview=self._service_client_01_2025_preview,
686699
**ops_kwargs,
687700
)
688701
self._operation_container.add(AzureMLResourceType.JOB, self._jobs)

sdk/ml/azure-ai-ml/azure/ai/ml/_restclient/v2025_01_01_preview/models/_models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34032,4 +34032,4 @@ def __init__(
3403234032
self.service_managed_resources_settings = kwargs.get('service_managed_resources_settings', None)
3403334033
self.soft_delete_retention_in_days = kwargs.get('soft_delete_retention_in_days', None)
3403434034
self.system_datastores_auth_mode = kwargs.get('system_datastores_auth_mode', None)
34035-
self.v1_legacy_mode = kwargs.get('v1_legacy_mode', None)
34035+
self.v1_legacy_mode = kwargs.get('v1_legacy_mode', None)

sdk/ml/azure-ai-ml/azure/ai/ml/_schema/job/command_job.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@ class CommandJobSchema(ParameterizedCommandSchema, BaseJobSchema):
2020
parameters = fields.Dict(dump_only=True)
2121
inputs = InputsField()
2222
outputs = OutputsField()
23+
parent_job_name = fields.Str()

sdk/ml/azure-ai-ml/azure/ai/ml/_schema/job_resource_configuration.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from marshmallow import fields, post_load
66

7+
from azure.ai.ml._schema.core.fields import UnionField
8+
79
from .resource_configuration import ResourceConfigurationSchema
810

911

@@ -22,7 +24,12 @@ class JobResourceConfigurationSchema(ResourceConfigurationSchema):
2224
max_instance_count = fields.Int(
2325
metadata={"description": "The maximum number of instances to make available to this job."}
2426
)
25-
docker_args = fields.Str(metadata={"description": "arguments to pass to the Docker run command."})
27+
docker_args = UnionField(
28+
[
29+
fields.Str(metadata={"description": "arguments to pass to the Docker run command."}),
30+
fields.List(fields.Str()),
31+
]
32+
)
2633

2734
@post_load
2835
def make(self, data, **kwargs):

sdk/ml/azure-ai-ml/azure/ai/ml/entities/_builders/command.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212
from marshmallow import INCLUDE, Schema
1313

14-
from azure.ai.ml._restclient.v2023_04_01_preview.models import CommandJob as RestCommandJob
15-
from azure.ai.ml._restclient.v2023_04_01_preview.models import JobBase
14+
from azure.ai.ml._restclient.v2025_01_01_preview.models import CommandJob as RestCommandJob
15+
from azure.ai.ml._restclient.v2025_01_01_preview.models import JobBase
1616
from azure.ai.ml._schema.core.fields import ExperimentalField, NestedField, UnionField
1717
from azure.ai.ml._schema.job.command_job import CommandJobSchema
1818
from azure.ai.ml._schema.job.identity import AMLTokenIdentitySchema, ManagedIdentitySchema, UserIdentitySchema
@@ -127,6 +127,8 @@ class Command(BaseNode, NodeWithGroupInputMixin):
127127
~azure.ai.ml.entities.VsCodeJobService]]]
128128
:keyword queue_settings: Queue settings for the job.
129129
:paramtype queue_settings: Optional[~azure.ai.ml.entities.QueueSettings]
130+
:keyword parent_job_name: parent job id for command job
131+
:paramtype parent_job_name: Optional[str]
130132
:raises ~azure.ai.ml.exceptions.ValidationException: Raised if Command cannot be successfully validated.
131133
Details will be provided in the error message.
132134
"""
@@ -172,6 +174,7 @@ def __init__(
172174
Dict[str, Union[JobService, JupyterLabJobService, SshJobService, TensorBoardJobService, VsCodeJobService]]
173175
] = None,
174176
queue_settings: Optional[QueueSettings] = None,
177+
parent_job_name: Optional[str] = None,
175178
**kwargs: Any,
176179
) -> None:
177180
# validate init params are valid type
@@ -203,6 +206,7 @@ def __init__(
203206
self._resources = resources
204207
self._services = services
205208
self.queue_settings = queue_settings
209+
self.parent_job_name = parent_job_name
206210

207211
if isinstance(self.component, CommandComponent):
208212
self.resources = self.resources or self.component.resources # type: ignore[assignment]
@@ -473,7 +477,7 @@ def set_resources(
473477
instance_count: Optional[int] = None,
474478
locations: Optional[List[str]] = None,
475479
properties: Optional[Dict] = None,
476-
docker_args: Optional[str] = None,
480+
docker_args: Optional[Union[str, List[str]]] = None,
477481
shm_size: Optional[str] = None,
478482
# pylint: disable=unused-argument
479483
**kwargs: Any,
@@ -492,7 +496,7 @@ def set_resources(
492496
:keyword properties: The properties of the job.
493497
:paramtype properties: Optional[dict]
494498
:keyword docker_args: The Docker arguments for the job.
495-
:paramtype docker_args: Optional[str]
499+
:paramtype docker_args: Optional[Union[str,List[str]]]
496500
:keyword shm_size: The size of the docker container's shared memory block. This should be in the
497501
format of (number)(unit) where the number has to be greater than 0 and the unit can be one of
498502
b(bytes), k(kilobytes), m(megabytes), or g(gigabytes).
@@ -745,6 +749,7 @@ def _to_job(self) -> CommandJob:
745749
creation_context=self.creation_context,
746750
parameters=self.parameters,
747751
queue_settings=self.queue_settings,
752+
parent_job_name=self.parent_job_name,
748753
)
749754

750755
return CommandJob(
@@ -771,6 +776,7 @@ def _to_job(self) -> CommandJob:
771776
creation_context=self.creation_context,
772777
parameters=self.parameters,
773778
queue_settings=self.queue_settings,
779+
parent_job_name=self.parent_job_name,
774780
)
775781

776782
@classmethod

sdk/ml/azure-ai-ml/azure/ai/ml/entities/_builders/command_func.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ def command(
145145
instance_count: Optional[int] = None,
146146
instance_type: Optional[str] = None,
147147
locations: Optional[List[str]] = None,
148-
docker_args: Optional[str] = None,
148+
docker_args: Optional[Union[str, List[str]]] = None,
149149
shm_size: Optional[str] = None,
150150
timeout: Optional[int] = None,
151151
code: Optional[Union[str, os.PathLike]] = None,
@@ -156,6 +156,7 @@ def command(
156156
] = None,
157157
job_tier: Optional[str] = None,
158158
priority: Optional[str] = None,
159+
parent_job_name: Optional[str] = None,
159160
**kwargs: Any,
160161
) -> Command:
161162
"""Creates a Command object which can be used inside a dsl.pipeline function or used as a standalone Command job.
@@ -199,7 +200,7 @@ def command(
199200
:keyword docker_args: Extra arguments to pass to the Docker run command. This would override any
200201
parameters that have already been set by the system, or in this section. This parameter is only
201202
supported for Azure ML compute types. Defaults to None.
202-
:paramtype docker_args: Optional[str]
203+
:paramtype docker_args: Optional[Union[str,List[str]]]
203204
:keyword shm_size: The size of the Docker container's shared memory block. This should be in the
204205
format of (number)(unit) where the number has to be greater than 0 and the unit can be one of
205206
b(bytes), k(kilobytes), m(megabytes), or g(gigabytes).
@@ -229,6 +230,8 @@ def command(
229230
:keyword priority: The priority of the job on the compute. Accepted values are "low", "medium", and "high".
230231
Defaults to "medium".
231232
:paramtype priority: Optional[str]
233+
:keyword parent_job_name: parent job id for command job
234+
:paramtype parent_job_name: Optional[str]
232235
:return: A Command object.
233236
:rtype: ~azure.ai.ml.entities.Command
234237
@@ -283,6 +286,7 @@ def command(
283286
environment=environment,
284287
environment_variables=environment_variables,
285288
services=services,
289+
parent_job_name=parent_job_name,
286290
**kwargs,
287291
)
288292

sdk/ml/azure-ai-ml/azure/ai/ml/entities/_job/command_job.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from pathlib import Path
1010
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
1111

12-
from azure.ai.ml._restclient.v2023_04_01_preview.models import CommandJob as RestCommandJob
13-
from azure.ai.ml._restclient.v2023_04_01_preview.models import JobBase
12+
from azure.ai.ml._restclient.v2025_01_01_preview.models import CommandJob as RestCommandJob
13+
from azure.ai.ml._restclient.v2025_01_01_preview.models import JobBase
1414
from azure.ai.ml._schema.job.command_job import CommandJobSchema
1515
from azure.ai.ml._utils.utils import map_single_brackets_and_warn
1616
from azure.ai.ml.constants import JobType
@@ -77,6 +77,8 @@ class CommandJob(Job, ParameterizedCommand, JobIOMixin):
7777
~azure.ai.ml.UserIdentityConfiguration]]
7878
:keyword limits: The limits for the job.
7979
:paramtype limits: Optional[~azure.ai.ml.entities.CommandJobLimits]
80+
:keyword parent_job_name: parent job id for command job
81+
:paramtype parent_job_name: Optional[str]
8082
:keyword kwargs: A dictionary of additional configuration parameters.
8183
:paramtype kwargs: dict
8284
@@ -103,10 +105,12 @@ def __init__(
103105
services: Optional[
104106
Dict[str, Union[JobService, JupyterLabJobService, SshJobService, TensorBoardJobService, VsCodeJobService]]
105107
] = None,
108+
parent_job_name: Optional[str] = None,
106109
**kwargs: Any,
107110
) -> None:
108111
kwargs[TYPE] = JobType.COMMAND
109112
self._parameters: dict = kwargs.pop("parameters", {})
113+
self.parent_job_name = parent_job_name
110114

111115
super().__init__(**kwargs)
112116

@@ -173,6 +177,7 @@ def _to_rest_object(self) -> JobBase:
173177
limits=self.limits._to_rest_object() if self.limits else None,
174178
services=JobServiceBase._to_rest_job_services(self.services),
175179
queue_settings=self.queue_settings._to_rest_object() if self.queue_settings else None,
180+
parent_job_name=self.parent_job_name,
176181
)
177182
result = JobBase(properties=properties)
178183
result.name = self.name
@@ -215,6 +220,7 @@ def _load_from_rest(cls, obj: JobBase) -> "CommandJob":
215220
inputs=from_rest_inputs_to_dataset_literal(rest_command_job.inputs),
216221
outputs=from_rest_data_outputs(rest_command_job.outputs),
217222
queue_settings=QueueSettings._from_rest_object(rest_command_job.queue_settings),
223+
parent_job_name=rest_command_job.parent_job_name,
218224
)
219225
# Handle special case of local job
220226
if (

sdk/ml/azure-ai-ml/azure/ai/ml/entities/_job/job_resource_configuration.py

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
from typing import Any, Dict, List, Optional, Union, cast
88

99
from azure.ai.ml._restclient.v2023_04_01_preview.models import JobResourceConfiguration as RestJobResourceConfiguration
10+
from azure.ai.ml._restclient.v2025_01_01_preview.models import (
11+
JobResourceConfiguration as RestJobResourceConfiguration202501,
12+
)
1013
from azure.ai.ml.constants._job.job import JobComputePropertyFields
1114
from azure.ai.ml.entities._mixins import DictMixin, RestTranslatableMixin
1215
from azure.ai.ml.entities._util import convert_ordered_dict_to_dict
@@ -98,7 +101,7 @@ class JobResourceConfiguration(RestTranslatableMixin, DictMixin):
98101
:keyword docker_args: Extra arguments to pass to the Docker run command. This would override any
99102
parameters that have already been set by the system, or in this section. This parameter is only
100103
supported for Azure ML compute types.
101-
:paramtype docker_args: Optional[str]
104+
:paramtype docker_args: Optional[Union[str, List[str]]]
102105
:keyword shm_size: The size of the docker container's shared memory block. This should be in the
103106
format of (number)(unit) where the number has to be greater than 0 and the unit can be one of
104107
b(bytes), k(kilobytes), m(megabytes), or g(gigabytes).
@@ -125,7 +128,7 @@ def __init__(
125128
instance_count: Optional[int] = None,
126129
instance_type: Optional[Union[str, List]] = None,
127130
properties: Optional[Union[Properties, Dict]] = None,
128-
docker_args: Optional[str] = None,
131+
docker_args: Optional[Union[str, List[str]]] = None,
129132
shm_size: Optional[str] = None,
130133
max_instance_count: Optional[int] = None,
131134
**kwargs: Any
@@ -162,7 +165,16 @@ def properties(self, properties: Dict[str, Any]) -> None:
162165
else:
163166
raise TypeError("properties must be a dict.")
164167

165-
def _to_rest_object(self) -> RestJobResourceConfiguration:
168+
def _to_rest_object(self) -> Union[RestJobResourceConfiguration, RestJobResourceConfiguration202501]:
169+
if self.docker_args and isinstance(self.docker_args, list):
170+
return RestJobResourceConfiguration202501(
171+
instance_count=self.instance_count,
172+
instance_type=self.instance_type,
173+
max_instance_count=self.max_instance_count,
174+
properties=self.properties.as_dict() if isinstance(self.properties, Properties) else None,
175+
docker_args_list=self.docker_args,
176+
shm_size=self.shm_size,
177+
)
166178
return RestJobResourceConfiguration(
167179
locations=self.locations,
168180
instance_count=self.instance_count,
@@ -174,18 +186,20 @@ def _to_rest_object(self) -> RestJobResourceConfiguration:
174186
)
175187

176188
@classmethod
177-
def _from_rest_object(cls, obj: Optional[RestJobResourceConfiguration]) -> Optional["JobResourceConfiguration"]:
189+
def _from_rest_object(
190+
cls, obj: Optional[Union[RestJobResourceConfiguration, RestJobResourceConfiguration202501]]
191+
) -> Optional["JobResourceConfiguration"]:
178192
if obj is None:
179193
return None
180194
if isinstance(obj, dict):
181195
return cls(**obj)
182196
return JobResourceConfiguration(
183-
locations=obj.locations,
197+
locations=obj.locations if hasattr(obj, "locations") else None,
184198
instance_count=obj.instance_count,
185199
instance_type=obj.instance_type,
186200
max_instance_count=obj.max_instance_count if hasattr(obj, "max_instance_count") else None,
187201
properties=obj.properties,
188-
docker_args=obj.docker_args,
202+
docker_args=obj.docker_args_list if hasattr(obj, "docker_args_list") else obj.docker_args,
189203
shm_size=obj.shm_size,
190204
deserialize_properties=True,
191205
)

sdk/ml/azure-ai-ml/azure/ai/ml/entities/_job/to_rest_functions.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Any
1010

1111
from azure.ai.ml._restclient.v2023_08_01_preview.models import JobBase as JobBaseData
12+
from azure.ai.ml._restclient.v2025_01_01_preview.models import JobBase as JobBaseData202501
1213
from azure.ai.ml.constants._common import DEFAULT_EXPERIMENT_NAME
1314
from azure.ai.ml.entities._builders.command import Command
1415
from azure.ai.ml.entities._builders.pipeline import Pipeline
@@ -47,7 +48,7 @@ def _(job: Job) -> JobBaseData:
4748

4849

4950
@to_rest_job_object.register(Command)
50-
def _(command: Command) -> JobBaseData:
51+
def _(command: Command) -> JobBaseData202501:
5152
rest_job = command._to_job()._to_rest_object()
5253
generate_defaults(command, rest_job)
5354
return rest_job

0 commit comments

Comments
 (0)