Skip to content

Commit ca4e4a2

Browse files
authored
Detailed doc about difference between MGmt SDK generated from swagger and typespec which is converted from swagger (#3086)
* init * init * agent update * Update sdk_from_swagger_and_typespec.md * update for review * review * review
1 parent 36f4c9e commit ca4e4a2

File tree

1 file changed

+200
-0
lines changed

1 file changed

+200
-0
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Overview
2+
3+
This document compares Python SDKs generated from Swagger (OpenAPI) specifications versus TypeSpec. For clarity, we'll refer to these as "Swagger SDKs" and "TypeSpec SDKs" respectively.
4+
5+
## Model
6+
7+
### Model Structure
8+
9+
#### Msrest Model
10+
11+
Swagger SDKs are generated from [Swagger specifications](https://github.com/Azure/azure-rest-api-specs/tree/main/specification) using [@autorest/python](https://www.npmjs.com/package/@autorest/python), and implement the Msrest model pattern. The following example illustrates the fundamental structure of an Msrest model:
12+
13+
```python
14+
class Person(_serialization.Model):
15+
_attribute_map = {
16+
"name": {"key": "name", "type": "str"},
17+
"parent_name": {"key": "parentName", "type": "str"},
18+
}
19+
20+
def __init__(self, *, name: Optional[str] = None, parent_name: Optional[str] = None) -> None:
21+
...
22+
```
23+
24+
#### DPG Model
25+
26+
TypeSpec SDKs are generated from [TypeSpec](https://github.com/microsoft/typespec/) using [@azure-tools/typespec-python](https://www.npmjs.com/package/@azure-tools/typespec-python), and implement the DPG model pattern. The following example demonstrates the fundamental structure of a DPG model:
27+
28+
```python
29+
class Person(_Model):
30+
name: Optional[str] = rest_field()
31+
parent_name: Optional[str] = rest_field(name="parentName")
32+
33+
@overload
34+
def __init__(
35+
self,
36+
*,
37+
name: Optional[str] = None,
38+
parent_name: Optional[str] = None,
39+
) -> None: ...
40+
41+
@overload
42+
def __init__(self, mapping: Mapping[str, Any]) -> None: ...
43+
44+
45+
def __init__(self, *args: Any, **kwargs: Any) -> None:
46+
super().__init__(*args, **kwargs)
47+
```
48+
49+
### Model Usage
50+
51+
#### Msrest Model Usage
52+
53+
```python
54+
msrest_model = Person(name="xxx", parent_name="xxx")
55+
print(msrest_model.name)
56+
print(msrest_model.parent_name)
57+
58+
# Access model as a dictionary
59+
json_model = msrest_model.as_dict()
60+
print(json_model["name"])
61+
print(json_model["parentName"])
62+
```
63+
64+
#### DPG Model Usage
65+
66+
```python
67+
dpg_model = Person(name="xxx", parent_name="xxx")
68+
print(dpg_model.name)
69+
print(dpg_model.parent_name)
70+
71+
# Access model directly as a dictionary
72+
print(dpg_model["name"])
73+
print(dpg_model["parentName"])
74+
```
75+
76+
By comparing these usage patterns, we can see that DPG models can be accessed directly as dictionaries without calling `.as_dict()`, providing a more convenient experience.
77+
78+
#### Usage Note
79+
80+
For backward compatibility, DPG models continue to support the `.as_dict()` method for existing SDK users.
81+
82+
### Model Flattening
83+
84+
When a property is marked with `"x-ms-flatten": "true"` (as described [here](https://azure.github.io/autorest/extensions/#x-ms-client-flatten)), nested properties can be accessed directly in Msrest models as follows:
85+
86+
#### Simple Flattening Example
87+
88+
```python
89+
class Person(_serialization.Model):
90+
_attribute_map = {
91+
"name": {"key": "properties.name", "type": "str"},
92+
}
93+
94+
def __init__(self, *, name: Optional[str] = None) -> None:
95+
...
96+
97+
msrest_model = Person(name="xxx")
98+
print(msrest_model.name) # equivalent to `msrest_model.serialize()["properties"]["name"]`
99+
```
100+
101+
When an inner property name matches an outer property name, a prefix is added to avoid name collisions:
102+
103+
#### Complex Flattening Example
104+
105+
```python
106+
class Person(_serialization.Model):
107+
_attribute_map = {
108+
"name": {"key": "name", "type": "str"},
109+
"properties_name": {"key": "properties.name", "type": "str"},
110+
}
111+
112+
def __init__(self, *, name: Optional[str] = None) -> None:
113+
...
114+
115+
msrest_model = Person(name="xxx", properties_name="properties_name")
116+
print(msrest_model.name)
117+
print(msrest_model.properties_name) # equivalent to `msrest_model.serialize()["properties"]["name"]`
118+
```
119+
120+
Due to inconsistent usage of flattening in some Swagger specifications, property names can become unwieldy and user-unfriendly. For this reason, **DPG models do not support flattening**.
121+
122+
#### Flattening Compatibility Note
123+
124+
For legacy SDKs generated from Swagger that are migrated to TypeSpec, we've designed a [compatibility mechanism](https://azure.github.io/typespec-azure/docs/howtos/generate-client-libraries/07types/#flattening) to minimize breaking changes. However, for deeply nested flattened properties, code updates may be required:
125+
126+
```python
127+
# Msrest model
128+
msrest_model = Model(...)
129+
130+
print(msrest_model.properties_name) # A, equivalent to `msrest_model.serialize()["properties"]["name"]`
131+
print(msrest_model.properties_properties_name) # B, equivalent to `msrest_model.serialize()["properties"]["properties"]["name"]`
132+
133+
# After migration to TypeSpec
134+
dpg_model = Model(...)
135+
print(dpg_model.properties_name) # A, backwards compatible but not recommended
136+
print(dpg_model.properties.name) # equivalent to A
137+
print.dpg_model.properties_properties_name) # no longer works
138+
print(dpg_model.properties.properties.name) # recommended approach after migration
139+
```
140+
141+
### Additional Properties
142+
143+
#### Additional Properties in Msrest Models
144+
145+
To support [additional properties](https://www.apimatic.io/openapi/additionalproperties), Msrest models include an `additional_properties` parameter:
146+
147+
```python
148+
msrest_model = Model(additional_properties={"hello": "world"})
149+
print(msrest_model.as_dict()) # output is `{"hello": "world"}`
150+
```
151+
152+
#### Additional Properties in DPG Models
153+
154+
DPG models inherently support additional properties through dictionary-like behavior:
155+
156+
```python
157+
dpg_model = Model({"hello": "world"})
158+
# or
159+
dpg_model = Model()
160+
dpg_model.update({"hello": "world"})
161+
# or
162+
dpg_model = Model()
163+
dpg_model["hello"] = "world"
164+
165+
print(dpg_model) # output is `{"hello": "world"}`
166+
```
167+
168+
#### Additional Properties Note
169+
170+
DPG models don't have property named `additional_properties` anymore.
171+
172+
## Operations
173+
174+
### Query/Header Parameters in Operations
175+
176+
Query and header parameters in Swagger-generated SDKs are positional, while in TypeSpec-generated SDKs they are keyword-only:
177+
178+
```python
179+
# Swagger SDK
180+
client.operation("header", "query") # A
181+
client.operation(header_parameter="header", query_parameter="query") # equivalent to A
182+
183+
# After migration to TypeSpec
184+
client.operation("header", "query") # no longer works
185+
client.operation(header_parameter="header", query_parameter="query") # correct approach
186+
```
187+
188+
## File Name Changes
189+
190+
After migration, some internal file names change, but these changes do not affect SDK users:
191+
192+
```
193+
_xxx_client.py => _client.py
194+
_xxx_enums.py => _enum.py
195+
_models_py3.py => _models.py
196+
```
197+
198+
### File Name Note
199+
200+
Files that name starts with `_` are internal files.

0 commit comments

Comments
 (0)