@autorest/python 6.2.15 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@
6
6
  import logging
7
7
  from typing import Any, Dict, Union
8
8
  from .base import BaseModel
9
- from .base_builder import BaseBuilder
9
+ from .base_builder import BaseBuilder, ParameterListType
10
10
  from .code_model import CodeModel
11
11
  from .client import Client
12
12
  from .model_type import ModelType, JSONModelType, DPGModelType, MsrestModelType
@@ -116,6 +116,7 @@ __all__ = [
116
116
  "CredentialType",
117
117
  "ClientParameter",
118
118
  "ConfigParameter",
119
+ "ParameterListType",
119
120
  ]
120
121
 
121
122
  TYPE_TO_OBJECT = {
@@ -186,4 +186,4 @@ class BaseType(BaseModel, ABC): # pylint: disable=too-many-public-methods
186
186
 
187
187
  @property
188
188
  def type_description(self) -> str:
189
- return self.type # type: ignore
189
+ return self.type_annotation()
@@ -63,6 +63,7 @@ class BaseBuilder(
63
63
  self.is_overload: bool = yaml_data["isOverload"]
64
64
  self.api_versions: List[str] = yaml_data["apiVersions"]
65
65
  self.added_on: Optional[str] = yaml_data.get("addedOn")
66
+ self.external_docs: Optional[Dict[str, Any]] = yaml_data.get("externalDocs")
66
67
 
67
68
  if code_model.options["version_tolerant"] and yaml_data.get("abstract"):
68
69
  _LOGGER.warning(
@@ -7,6 +7,7 @@ from typing import Any, Dict, List, Optional, TYPE_CHECKING
7
7
  import re
8
8
  from autorest.codegen.models.imports import FileImport, ImportType
9
9
  from .base import BaseType
10
+ from .model_type import JSONModelType
10
11
 
11
12
  if TYPE_CHECKING:
12
13
  from .code_model import CodeModel
@@ -111,3 +112,20 @@ class CombinedType(BaseType):
111
112
  code_model,
112
113
  [build_type(t, code_model) for t in yaml_data["types"]],
113
114
  )
115
+
116
+ @staticmethod
117
+ def _get_json_model_type(t: BaseType) -> Optional[JSONModelType]:
118
+ if isinstance(t, JSONModelType):
119
+ return t
120
+ if isinstance(t, CombinedType):
121
+ try:
122
+ return next(
123
+ CombinedType._get_json_model_type(sub_t) for sub_t in t.types
124
+ )
125
+ except StopIteration:
126
+ pass
127
+ return None
128
+
129
+ @property
130
+ def json_subtype(self) -> Optional[JSONModelType]:
131
+ return CombinedType._get_json_model_type(self)
@@ -162,6 +162,10 @@ class TokenCredentialType(
162
162
  return '"AsyncTokenCredential"'
163
163
  return '"TokenCredential"'
164
164
 
165
+ @property
166
+ def type_description(self) -> str:
167
+ return "TokenCredential"
168
+
165
169
  def docstring_type(self, **kwargs: Any) -> str:
166
170
  if kwargs.get("async_mode"):
167
171
  return "~azure.core.credentials_async.AsyncTokenCredential"
@@ -129,3 +129,7 @@ class DictionaryType(BaseType):
129
129
  @property
130
130
  def instance_check_template(self) -> str:
131
131
  return "isinstance({}, dict)"
132
+
133
+ @property
134
+ def type_description(self) -> str:
135
+ return f"{{str: {self.element_type.type_description}}}"
@@ -153,3 +153,7 @@ class ListType(BaseType):
153
153
  )
154
154
  file_import.merge(self.element_type.imports(**kwargs))
155
155
  return file_import
156
+
157
+ @property
158
+ def type_description(self) -> str:
159
+ return f"[{self.element_type.type_description}]"
@@ -295,6 +295,10 @@ class GeneratedModelType(ModelType): # pylint: disable=abstract-method
295
295
  def docstring_text(self, **kwargs: Any) -> str:
296
296
  return self.name
297
297
 
298
+ @property
299
+ def type_description(self) -> str:
300
+ return self.name
301
+
298
302
  def imports(self, **kwargs: Any) -> FileImport:
299
303
  file_import = super().imports(**kwargs)
300
304
  relative_path = kwargs.pop("relative_path", None)
@@ -86,6 +86,10 @@ class _ParameterBase(
86
86
  self.check_client_input: bool = self.yaml_data.get("checkClientInput", False)
87
87
  self.added_on: Optional[str] = self.yaml_data.get("addedOn")
88
88
  self.is_api_version: bool = self.yaml_data.get("isApiVersion", False)
89
+ self.in_overload: bool = self.yaml_data.get("inOverload", False)
90
+ self.default_to_unset_sentinel: bool = self.yaml_data.get(
91
+ "defaultToUnsetSentinel", False
92
+ )
89
93
 
90
94
  @property
91
95
  def constant(self) -> bool:
@@ -168,6 +172,12 @@ class _ParameterBase(
168
172
  file_import.merge(
169
173
  self.type.imports(is_operation_file=True, async_mode=async_mode, **kwargs)
170
174
  )
175
+ if self.default_to_unset_sentinel:
176
+ file_import.add_submodule_import("typing", "Any", ImportType.STDLIB)
177
+ file_import.define_mypy_type(
178
+ "_Unset: Any",
179
+ "object()",
180
+ )
171
181
  return file_import
172
182
 
173
183
  def imports_for_multiapi(self, async_mode: bool, **kwargs: Any) -> FileImport:
@@ -208,11 +218,13 @@ class _ParameterBase(
208
218
  type_annot = self.type_annotation(async_mode=async_mode)
209
219
  if self.client_default_value is not None or self.optional:
210
220
  return f"{self.client_name}: {type_annot} = {self.client_default_value_declaration},"
221
+ if self.default_to_unset_sentinel:
222
+ return f"{self.client_name}: {type_annot} = _Unset,"
211
223
  return f"{self.client_name}: {type_annot},"
212
224
 
213
225
 
214
- class _BodyParameterBase(_ParameterBase):
215
- """Base class for body parameters"""
226
+ class BodyParameter(_ParameterBase):
227
+ """Body parameter."""
216
228
 
217
229
  @property
218
230
  def is_partial_body(self) -> bool:
@@ -231,10 +243,6 @@ class _BodyParameterBase(_ParameterBase):
231
243
  def in_method_signature(self) -> bool:
232
244
  return not (self.flattened or self.grouped_by)
233
245
 
234
-
235
- class BodyParameter(_BodyParameterBase):
236
- """Body parameter."""
237
-
238
246
  @property
239
247
  def content_types(self) -> List[str]:
240
248
  return self.yaml_data["contentTypes"]
@@ -243,19 +251,11 @@ class BodyParameter(_BodyParameterBase):
243
251
  def default_content_type(self) -> str:
244
252
  return self.yaml_data["defaultContentType"]
245
253
 
246
- @staticmethod
247
- def _has_json_model_type(t: BaseType) -> bool:
248
- if isinstance(t, JSONModelType):
249
- return True
250
- if isinstance(t, CombinedType):
251
- for sub_t in t.types:
252
- if BodyParameter._has_json_model_type(sub_t):
253
- return True
254
- return False
255
-
256
254
  @property
257
255
  def has_json_model_type(self) -> bool:
258
- return BodyParameter._has_json_model_type(self.type)
256
+ if isinstance(self.type, CombinedType):
257
+ return self.type.json_subtype is not None
258
+ return isinstance(self.type, JSONModelType)
259
259
 
260
260
  @classmethod
261
261
  def from_yaml(
@@ -327,10 +327,9 @@ class Parameter(_ParameterBase):
327
327
  self.implementation: str = yaml_data["implementation"]
328
328
  self.skip_url_encoding: bool = self.yaml_data.get("skipUrlEncoding", False)
329
329
  self.explode: bool = self.yaml_data.get("explode", False)
330
- self.in_overload: bool = self.yaml_data["inOverload"]
331
330
  self.in_overriden: bool = self.yaml_data.get("inOverriden", False)
332
331
  self.delimiter: Optional[ParameterDelimeter] = self.yaml_data.get("delimiter")
333
- self.in_flattened_body: bool = self.yaml_data.get("inFlattenedBody", False)
332
+ self._default_to_unset_sentinel: bool = False
334
333
 
335
334
  @property
336
335
  def in_method_signature(self) -> bool:
@@ -31,10 +31,11 @@ from ..models import (
31
31
  MultipartBodyParameter,
32
32
  Property,
33
33
  RequestBuilderType,
34
- JSONModelType,
35
34
  CombinedType,
35
+ ParameterListType,
36
36
  )
37
37
  from .parameter_serializer import ParameterSerializer, PopKwargType
38
+ from ..models.parameter_list import ParameterType
38
39
  from . import utils
39
40
 
40
41
  T = TypeVar("T")
@@ -148,22 +149,39 @@ def _serialize_flattened_body(body_parameter: BodyParameter) -> List[str]:
148
149
  return retval
149
150
 
150
151
 
151
- def _serialize_json_model_body(body_parameter: BodyParameter) -> List[str]:
152
+ def _serialize_json_model_body(
153
+ body_parameter: BodyParameter, parameters: List[ParameterType]
154
+ ) -> List[str]:
152
155
  retval: List[str] = []
153
156
  if not body_parameter.property_to_parameter_name:
154
157
  raise ValueError(
155
158
  "This method can't be called if the operation doesn't need parameter flattening"
156
159
  )
157
160
 
158
- retval.append(f"if {body_parameter.client_name} is None:")
161
+ retval.append(f"if {body_parameter.client_name} is _Unset:")
162
+ for p in parameters:
163
+ if (
164
+ p.client_default_value is None
165
+ and not p.optional
166
+ and p.default_to_unset_sentinel
167
+ ):
168
+ retval.append(f" if {p.client_name} is _Unset:")
169
+ retval.append(
170
+ f" raise TypeError('missing required argument: {p.client_name}')"
171
+ )
159
172
  parameter_string = ", \n".join(
160
173
  f'"{property_name}": {parameter_name}'
161
174
  for property_name, parameter_name in body_parameter.property_to_parameter_name.items()
162
175
  )
163
176
  model_type = cast(ModelType, body_parameter.type)
164
- if isinstance(model_type, CombinedType):
165
- model_type = next(t for t in model_type.types if isinstance(t, JSONModelType))
177
+ if isinstance(model_type, CombinedType) and model_type.json_subtype:
178
+ model_type = model_type.json_subtype
166
179
  retval.append(f" {body_parameter.client_name} = {{{parameter_string}}}")
180
+ retval.append(f" {body_parameter.client_name} = {{")
181
+ retval.append(
182
+ f" k: v for k, v in {body_parameter.client_name}.items() if v is not None"
183
+ )
184
+ retval.append(" }")
167
185
  return retval
168
186
 
169
187
 
@@ -209,6 +227,14 @@ def _api_version_validation(builder: OperationType) -> str:
209
227
  return ""
210
228
 
211
229
 
230
+ def is_json_model_type(parameters: ParameterListType) -> bool:
231
+ return (
232
+ parameters.has_body
233
+ and parameters.body_parameter.has_json_model_type
234
+ and any(p.in_flattened_body for p in parameters.parameters)
235
+ )
236
+
237
+
212
238
  class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-method
213
239
  def __init__(self, code_model: CodeModel, async_mode: bool) -> None:
214
240
  self.code_model = code_model
@@ -334,12 +360,11 @@ class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-
334
360
  ):
335
361
  # No input template if now body parameter
336
362
  return template
337
- if builder.overloads:
338
- # if there's overloads, we do the json input example template on the overload
339
- return template
340
363
 
341
364
  body_param = builder.parameters.body_parameter
342
- if not isinstance(body_param.type, (ListType, DictionaryType, ModelType)):
365
+ if not isinstance(
366
+ body_param.type, (ListType, DictionaryType, ModelType, CombinedType)
367
+ ):
343
368
  return template
344
369
 
345
370
  if (
@@ -351,8 +376,14 @@ class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-
351
376
  if isinstance(body_param.type, ModelType) and body_param.type.base != "json":
352
377
  return template
353
378
 
379
+ json_type = body_param.type
380
+ if isinstance(body_param.type, CombinedType):
381
+ if body_param.type.json_subtype is None:
382
+ return template
383
+ json_type = body_param.type.json_subtype
384
+
354
385
  polymorphic_subtypes: List[ModelType] = []
355
- body_param.type.get_polymorphic_subtypes(polymorphic_subtypes)
386
+ json_type.get_polymorphic_subtypes(polymorphic_subtypes)
356
387
  if polymorphic_subtypes:
357
388
  # we just assume one kind of polymorphic body for input
358
389
  discriminator_name = cast(
@@ -376,7 +407,7 @@ class _BuilderBaseSerializer(Generic[BuilderType]): # pylint: disable=abstract-
376
407
  "# JSON input template you can fill out and use as your body input."
377
408
  )
378
409
  json_template = _json_dumps_template(
379
- body_param.type.get_json_template_representation(),
410
+ json_type.get_json_template_representation(),
380
411
  )
381
412
  template.extend(
382
413
  f"{builder.parameters.body_parameter.client_name} = {json_template}".splitlines()
@@ -552,6 +583,10 @@ class _OperationSerializer(
552
583
  retval.append(".. warning::")
553
584
  retval.append(" This method is deprecated")
554
585
  retval.append("")
586
+ if builder.external_docs and builder.external_docs.get("url"):
587
+ retval.append(".. seealso::")
588
+ retval.append(f" - {builder.external_docs['url']}")
589
+ retval.append("")
555
590
  return retval
556
591
 
557
592
  @property
@@ -949,12 +984,12 @@ class _OperationSerializer(
949
984
  if builder.parameters.has_body and builder.parameters.body_parameter.flattened:
950
985
  # unflatten before passing to request builder as well
951
986
  retval.extend(_serialize_flattened_body(builder.parameters.body_parameter))
952
- if (
953
- builder.parameters.has_body
954
- and builder.parameters.body_parameter.has_json_model_type
955
- and any(p.in_flattened_body for p in builder.parameters.parameters)
956
- ):
957
- retval.extend(_serialize_json_model_body(builder.parameters.body_parameter))
987
+ if is_json_model_type(builder.parameters):
988
+ retval.extend(
989
+ _serialize_json_model_body(
990
+ builder.parameters.body_parameter, builder.parameters.parameters
991
+ )
992
+ )
958
993
  if builder.overloads:
959
994
  # we are only dealing with two overloads. If there are three, we generate an abstract operation
960
995
  retval.extend(self._initialize_overloads(builder, is_paging=is_paging))
@@ -16,7 +16,10 @@ from ..models import (
16
16
  Client,
17
17
  )
18
18
  from .import_serializer import FileImportSerializer
19
- from .builder_serializer import get_operation_serializer, RequestBuilderSerializer
19
+ from .builder_serializer import (
20
+ get_operation_serializer,
21
+ RequestBuilderSerializer,
22
+ )
20
23
 
21
24
 
22
25
  class OperationGroupsSerializer:
@@ -21,6 +21,7 @@ import isodate
21
21
  from azure.core.exceptions import DeserializationError
22
22
  from azure.core import CaseInsensitiveEnumMeta
23
23
  from azure.core.pipeline import PipelineResponse
24
+ from azure.core.serialization import NULL as AzureCoreNull
24
25
 
25
26
  _LOGGER = logging.getLogger(__name__)
26
27
 
@@ -157,6 +158,8 @@ class AzureJSONEncoder(JSONEncoder):
157
158
  return {k: v for k, v in o.items() if k not in readonly_props}
158
159
  if isinstance(o, (bytes, bytearray)):
159
160
  return base64.b64encode(o).decode()
161
+ if o is AzureCoreNull:
162
+ return None
160
163
  try:
161
164
  return super(AzureJSONEncoder, self).default(o)
162
165
  except TypeError:
@@ -263,7 +266,8 @@ def _get_model(module_name: str, model_name: str):
263
266
  module_end = module_name.rsplit(".", 1)[0]
264
267
  module = sys.modules[module_end]
265
268
  models.update({k: v for k, v in module.__dict__.items() if isinstance(v, type)})
266
- model_name = model_name.split(".")[-1]
269
+ if isinstance(model_name, str):
270
+ model_name = model_name.split(".")[-1]
267
271
  if model_name not in models:
268
272
  return model_name
269
273
  return models[model_name]
@@ -465,13 +469,15 @@ class Model(_MyMutableMapping):
465
469
 
466
470
 
467
471
  def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-return-statements, too-many-statements
468
- annotation: typing.Any, module: typing.Optional[str],
472
+ annotation: typing.Any, module: typing.Optional[str], rf: "_RestField" = None
469
473
  ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
470
474
  if not annotation or annotation in [int, float]:
471
475
  return None
472
476
 
473
477
  try:
474
478
  if module and _is_model(_get_model(module, annotation)):
479
+ if rf:
480
+ rf._is_model = True
475
481
  def _deserialize_model(model_deserializer: typing.Optional[typing.Callable], obj):
476
482
  if _is_model(obj):
477
483
  return obj
@@ -510,7 +516,7 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur
510
516
  if any(a for a in annotation.__args__ if a == type(None)):
511
517
 
512
518
  if_obj_deserializer = _get_deserialize_callable_from_annotation(
513
- next(a for a in annotation.__args__ if a != type(None)), module
519
+ next(a for a in annotation.__args__ if a != type(None)), module, rf
514
520
  )
515
521
 
516
522
  def _deserialize_with_optional(if_obj_deserializer: typing.Optional[typing.Callable], obj):
@@ -533,8 +539,8 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur
533
539
 
534
540
  try:
535
541
  if annotation._name == "Dict":
536
- key_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module)
537
- value_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[1], module)
542
+ key_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module, rf)
543
+ value_deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[1], module, rf)
538
544
 
539
545
  def _deserialize_dict(
540
546
  key_deserializer: typing.Optional[typing.Callable],
@@ -568,10 +574,10 @@ def _get_deserialize_callable_from_annotation( # pylint: disable=too-many-retur
568
574
  )
569
575
 
570
576
  entry_deserializers = [
571
- _get_deserialize_callable_from_annotation(dt, module) for dt in annotation.__args__
577
+ _get_deserialize_callable_from_annotation(dt, module, rf) for dt in annotation.__args__
572
578
  ]
573
579
  return functools.partial(_deserialize_multiple_sequence, entry_deserializers)
574
- deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module)
580
+ deserializer = _get_deserialize_callable_from_annotation(annotation.__args__[0], module, rf)
575
581
 
576
582
  def _deserialize_sequence(
577
583
  deserializer: typing.Optional[typing.Callable],
@@ -673,7 +679,7 @@ class _RestField:
673
679
  def _get_deserialize_callable_from_annotation(
674
680
  self, annotation: typing.Any
675
681
  ) -> typing.Optional[typing.Callable[[typing.Any], typing.Any]]:
676
- return _get_deserialize_callable_from_annotation(annotation, self._module)
682
+ return _get_deserialize_callable_from_annotation(annotation, self._module, self)
677
683
 
678
684
 
679
685
  def rest_field(
@@ -5,7 +5,7 @@
5
5
  # coding=utf-8
6
6
  {{ code_model.options['license_header'] }}
7
7
  {{ imports }}
8
-
8
+ {{ unset }}
9
9
  {% if code_model.options["builders_visibility"] == "embedded" and not async_mode %}
10
10
  {{ op_tools.declare_serializer(code_model) }}
11
11
  {% for operation_group in operation_groups %}
@@ -64,6 +64,7 @@ import xml.etree.ElementTree as ET
64
64
  import isodate # type: ignore
65
65
 
66
66
  from azure.core.exceptions import DeserializationError, SerializationError, raise_with_traceback
67
+ from azure.core.serialization import NULL as AzureCoreNull
67
68
 
68
69
  _BOM = codecs.BOM_UTF8.decode(encoding="utf-8")
69
70
 
@@ -804,6 +805,8 @@ class Serializer(object):
804
805
  raise ValueError("No value for given attribute")
805
806
 
806
807
  try:
808
+ if data is AzureCoreNull:
809
+ return None
807
810
  if data_type in self.basic_types.values():
808
811
  return self.serialize_basic(data, data_type, **kwargs)
809
812
 
@@ -546,6 +546,7 @@ class M4Reformatter(
546
546
  "isOverload": is_overload,
547
547
  "apiVersions": _get_api_versions(yaml_data.get("apiVersions", [])),
548
548
  "abstract": abstract,
549
+ "externalDocs": yaml_data.get("externalDocs"),
549
550
  }
550
551
 
551
552
  def get_operation_creator(
@@ -66,7 +66,7 @@ def add_overload(
66
66
  overload = copy.deepcopy(yaml_data)
67
67
  overload["isOverload"] = True
68
68
  overload["bodyParameter"]["type"] = body_type
69
-
69
+ overload["bodyParameter"]["defaultToUnsetSentinel"] = False
70
70
  overload["overloads"] = []
71
71
 
72
72
  if for_flatten_params:
@@ -95,6 +95,10 @@ def add_overload(
95
95
  if body_type["type"] == "binary" and len(content_types) > 1:
96
96
  content_types = "'" + "', '".join(content_types) + "'"
97
97
  content_type_param["description"] += f" Known values are: {content_types}."
98
+ overload["bodyParameter"]["inOverload"] = True
99
+ for parameter in overload["parameters"]:
100
+ parameter["inOverload"] = True
101
+ parameter["defaultToUnsetSentinel"] = False
98
102
  return overload
99
103
 
100
104
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.2.15",
3
+ "version": "6.3.0",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "main": "index.js",
6
6
  "repository": {
package/requirements.txt CHANGED
@@ -4,7 +4,7 @@ docutils==0.19
4
4
  Jinja2==3.1.2
5
5
  json-rpc==1.14.0
6
6
  m2r2==0.3.3
7
- MarkupSafe==2.1.1
7
+ MarkupSafe==2.1.2
8
8
  mistune==0.8.4
9
9
  mypy-extensions==0.4.3
10
10
  pathspec==0.10.3