From 6933b8935f6155cf7c536d7deda8be47657b9f5d Mon Sep 17 00:00:00 2001 From: Yishi Wang Date: Tue, 8 Jul 2025 13:14:58 +0800 Subject: [PATCH 1/4] Adopt serialization func from azure-core for CLI's todict formatting --- src/azure-cli-core/azure/cli/core/util.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/util.py b/src/azure-cli-core/azure/cli/core/util.py index e4c319ad55f..eecd73c6ebe 100644 --- a/src/azure-cli-core/azure/cli/core/util.py +++ b/src/azure-cli-core/azure/cli/core/util.py @@ -633,6 +633,7 @@ def todict(obj, post_processor=None): """ from datetime import date, time, datetime, timedelta from enum import Enum + from azure.core.serialization import is_generated_model, attribute_list if isinstance(obj, dict): result = {k: todict(v, post_processor) for (k, v) in obj.items()} return post_processor(obj, result) if post_processor else result @@ -644,11 +645,14 @@ def todict(obj, post_processor=None): return obj.isoformat() if isinstance(obj, timedelta): return str(obj) - # This is the only difference with knack.util.todict because for typespec generated SDKs - # The base model stores data in obj.__dict__['_data'] instead of in obj.__dict__ - # We need to call obj.as_dict() to extract data for this kind of model - if hasattr(obj, 'as_dict') and not hasattr(obj, '_attribute_map'): - result = {to_camel_case(k): todict(v, post_processor) for k, v in obj.as_dict().items()} + # This is the only difference with knack.util.todict + # azure-core has provided + # - `is_generated_model` to identify auto generated SDK models + # - `attribute_list` to list all attribute names + # no matter it's track1/track2/swagger generated/typespec generated SDKs + # no matter what transformation (renaming/formatting/...) the attribute has during auto generation + if is_generated_model(obj): + result = {to_camel_case(attr): todict(getattr(obj, attr), post_processor) for attr in attribute_list(obj)} return post_processor(obj, result) if post_processor else result if hasattr(obj, '_asdict'): return todict(obj._asdict(), post_processor) From 4cfbdfa7ba4aa3c2484eccb85bafb637c4e2ee76 Mon Sep 17 00:00:00 2001 From: Yishi Wang Date: Tue, 8 Jul 2025 15:01:58 +0800 Subject: [PATCH 2/4] Fix attr not exist --- src/azure-cli-core/azure/cli/core/util.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/azure-cli-core/azure/cli/core/util.py b/src/azure-cli-core/azure/cli/core/util.py index eecd73c6ebe..7608ec21c12 100644 --- a/src/azure-cli-core/azure/cli/core/util.py +++ b/src/azure-cli-core/azure/cli/core/util.py @@ -652,7 +652,10 @@ def todict(obj, post_processor=None): # no matter it's track1/track2/swagger generated/typespec generated SDKs # no matter what transformation (renaming/formatting/...) the attribute has during auto generation if is_generated_model(obj): - result = {to_camel_case(attr): todict(getattr(obj, attr), post_processor) for attr in attribute_list(obj)} + result = {} + for attr in attribute_list(obj): + if hasattr(obj, attr): + result[to_camel_case(attr)] = todict(getattr(obj, attr), post_processor) return post_processor(obj, result) if post_processor else result if hasattr(obj, '_asdict'): return todict(obj._asdict(), post_processor) From 71491cf9909040095de64495614ad859bd0b9aa8 Mon Sep 17 00:00:00 2001 From: Yishi Wang Date: Wed, 9 Jul 2025 11:26:33 +0800 Subject: [PATCH 3/4] restrict the change to typespec generated SDK models only --- src/azure-cli-core/azure/cli/core/util.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/util.py b/src/azure-cli-core/azure/cli/core/util.py index 7608ec21c12..5490908b977 100644 --- a/src/azure-cli-core/azure/cli/core/util.py +++ b/src/azure-cli-core/azure/cli/core/util.py @@ -645,17 +645,12 @@ def todict(obj, post_processor=None): return obj.isoformat() if isinstance(obj, timedelta): return str(obj) - # This is the only difference with knack.util.todict - # azure-core has provided - # - `is_generated_model` to identify auto generated SDK models - # - `attribute_list` to list all attribute names - # no matter it's track1/track2/swagger generated/typespec generated SDKs - # no matter what transformation (renaming/formatting/...) the attribute has during auto generation - if is_generated_model(obj): - result = {} - for attr in attribute_list(obj): - if hasattr(obj, attr): - result[to_camel_case(attr)] = todict(getattr(obj, attr), post_processor) + # This is the only difference with knack.util.todict because for typespec generated SDKs + # The base model stores data in obj.__dict__['_data'] instead of in obj.__dict__ + # azure-core provided new function `attribute_list` to list all attribute names + # so that we don't need to use raw __dict__ directly + if hasattr(obj, 'as_dict') and not hasattr(obj, '_attribute_map'): + result = {to_camel_case(attr): todict(getattr(obj, attr), post_processor) for attr in attribute_list(obj) if hasattr(obj, attr)} return post_processor(obj, result) if post_processor else result if hasattr(obj, '_asdict'): return todict(obj._asdict(), post_processor) From feac6bf6af2e8bddffbb20085aca733647116557 Mon Sep 17 00:00:00 2001 From: Yishi Wang Date: Wed, 9 Jul 2025 13:17:54 +0800 Subject: [PATCH 4/4] fix style --- src/azure-cli-core/azure/cli/core/util.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/azure-cli-core/azure/cli/core/util.py b/src/azure-cli-core/azure/cli/core/util.py index 5490908b977..1a691d417a5 100644 --- a/src/azure-cli-core/azure/cli/core/util.py +++ b/src/azure-cli-core/azure/cli/core/util.py @@ -633,7 +633,7 @@ def todict(obj, post_processor=None): """ from datetime import date, time, datetime, timedelta from enum import Enum - from azure.core.serialization import is_generated_model, attribute_list + from azure.core.serialization import attribute_list if isinstance(obj, dict): result = {k: todict(v, post_processor) for (k, v) in obj.items()} return post_processor(obj, result) if post_processor else result @@ -647,10 +647,12 @@ def todict(obj, post_processor=None): return str(obj) # This is the only difference with knack.util.todict because for typespec generated SDKs # The base model stores data in obj.__dict__['_data'] instead of in obj.__dict__ + # The way to detect if it's a typespec generated model is to check the private `_is_model` attribute # azure-core provided new function `attribute_list` to list all attribute names # so that we don't need to use raw __dict__ directly - if hasattr(obj, 'as_dict') and not hasattr(obj, '_attribute_map'): - result = {to_camel_case(attr): todict(getattr(obj, attr), post_processor) for attr in attribute_list(obj) if hasattr(obj, attr)} + if getattr(obj, "_is_model", False): + result = {to_camel_case(attr): todict(getattr(obj, attr), post_processor) + for attr in attribute_list(obj) if hasattr(obj, attr)} return post_processor(obj, result) if post_processor else result if hasattr(obj, '_asdict'): return todict(obj._asdict(), post_processor)