Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/ml/azure-ai-ml/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- Fixed `deployment_templates.list(name=...)` raising `AttributeError: 'str' object has no attribute 'request_timeout'`. In the list response, `requestSettings` / `livenessProbe` / `readinessProbe` arrive as stringified dicts nested under `properties`; these are now parsed before conversion, giving `list()` parity with `models.list()` / `environments.list()`.
- Fixed `deployment_templates.get(name)` failing with a 404 (`DeploymentTemplate {name}:latest not found`) when no version was supplied, because the literal string `"latest"` was sent as the version. The latest version is now resolved client-side (the service exposes no `latest` label and no server-side ordering), and `get()` accepts a `label` keyword (`label="latest"` resolves to the latest version) mirroring `models.get()`. `delete(name)` resolves the latest version the same way.
- Fixed `models.get(name, label="latest")` returning a `Model` whose `default_deployment_template` / `allowed_deployment_templates` references had `asset_id=None`. Label resolution goes through the version list endpoint (`top=1`), whose items omit the deployment-template references; for registry models the resolved version is now re-fetched through the get endpoint, so the label path hydrates these references identically to the explicit `version=` path.
- Fixed loading a registered MLTable data asset via `mltable.load("azureml://.../data/<name>/versions/<version>")` failing with `AttributeError: 'DataVersionEntity' object has no attribute 'additional_properties'`. When the `dataset_dataplane` client was migrated to the TypeSpec (hybrid) model, `DataVersionEntity` stopped exposing the msrest `additional_properties` attribute that the `mltable` package reads (`isV2` / `legacyDataflow`) on the local resolution path. The attribute is now restored as a compatibility shim returning the un-modeled wire keys, so the on-the-wire contract is unchanged.

### Other Changes
- Migrated SDK entities and their consumers off the per-version msrest REST clients onto the shared `arm_ml_service` hybrid client. This is an internal change; the on-the-wire request/response contract is unchanged.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,45 @@
Follow our quickstart for examples: https://aka.ms/azsdk/python/dpcodegen/python/customize
"""

from typing import Any, Dict

from ._models import DataVersionEntity

__all__: list[str] = [] # Add all objects you want publicly available to users at this package level


def _additional_properties(self: DataVersionEntity) -> Dict[str, Any]:
"""Backwards-compatibility shim for the removed msrest ``additional_properties`` attribute.

When this client was migrated to TypeSpec, ``DataVersionEntity`` became a hybrid
(``MutableMapping``) model, which does not expose the msrest ``additional_properties``
attribute. External consumers still depend on it -- notably the ``mltable`` package's local
data-asset resolution path (``MLClient.jobs._dataset_dataplane_operations._operation.get``),
which reads ``data_version.additional_properties['isV2' | 'legacyDataflow']``. Losing the
attribute raised ``AttributeError`` when loading an ``azureml://`` MLTable data asset.

This restores the original contract by returning the un-modeled wire keys, i.e. the keys
present on the wire payload that are not backed by a declared ``rest_field`` on the model.

:return: The wire properties not declared as fields on the model.
:rtype: dict[str, typing.Any]
"""
# Wire names of the declared fields (e.g. ``dataVersion``, ``entityMetadata``).
modeled = {
getattr(rf, "_rest_name", None) # pylint: disable=protected-access
for rf in getattr(self, "_attr_to_rest_field", {}).values()
}
raw = getattr(self, "_data", None) # pylint: disable=protected-access
items = raw.items() if isinstance(raw, dict) else self.items()
return {key: value for key, value in items if key not in modeled}


# Attach only when the generated model does not already provide it, so a future TypeSpec
# regeneration that restores ``additional_properties`` natively takes precedence.
if not hasattr(DataVersionEntity, "additional_properties"):
DataVersionEntity.additional_properties = property(_additional_properties) # type: ignore[attr-defined]


def patch_sdk():
"""Do not remove from this file.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import pytest

from azure.ai.ml._restclient.dataset_dataplane.models import DataVersionEntity


@pytest.mark.unittest
@pytest.mark.data_experiences_test
class TestDataVersionEntityAdditionalProperties:
"""Regression guard for the ``mltable`` local data-asset resolution path.

``mltable.load("azureml://.../data/<name>/versions/<version>")`` fetches a ``DataVersionEntity``
via ``MLClient.jobs._dataset_dataplane_operations._operation.get`` and reads
``data_version.additional_properties['isV2' | 'legacyDataflow']``. The TypeSpec (hybrid) model
dropped the msrest ``additional_properties`` attribute; the ``models/_patch.py`` shim restores it.
"""

def _wire(self):
return {
"dataVersion": {"dataUri": "azureml://datastores/x/paths/y", "dataType": "mltable"},
"entityMetadata": {"etag": "abc"},
"isV2": True,
"legacyDataflow": "some-legacy-dataflow-yaml",
}

def test_additional_properties_exposes_unmodeled_wire_keys(self):
entity = DataVersionEntity._deserialize(self._wire(), [])

# mltable reads these exact keys off ``additional_properties``.
assert entity.additional_properties["isV2"] is True
assert entity.additional_properties["legacyDataflow"] == "some-legacy-dataflow-yaml"

def test_additional_properties_excludes_modeled_fields(self):
entity = DataVersionEntity._deserialize(self._wire(), [])

# Declared fields must not leak into ``additional_properties`` (msrest parity).
assert "dataVersion" not in entity.additional_properties
assert "entityMetadata" not in entity.additional_properties

# Declared fields remain accessible via their model attributes.
assert entity.data_version.data_uri == "azureml://datastores/x/paths/y"
assert entity.data_version.data_type == "mltable"

def test_additional_properties_empty_when_no_extra_keys(self):
entity = DataVersionEntity._deserialize(
{"dataVersion": {"dataUri": "azureml://x", "dataType": "uri_folder"}}, []
)

assert entity.additional_properties == {}

def test_missing_key_raises_key_error(self):
# mltable relies on ``KeyError`` (caught) when ``legacyDataflow`` is absent.
entity = DataVersionEntity._deserialize(
{"dataVersion": {"dataUri": "azureml://x", "dataType": "mltable"}, "isV2": False}, []
)

with pytest.raises(KeyError):
_ = entity.additional_properties["legacyDataflow"]
Loading