@autorest/python 6.2.16 → 6.3.1

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.
package/README.md CHANGED
@@ -215,5 +215,5 @@ help-content:
215
215
 
216
216
  <!-- LINKS -->
217
217
 
218
- [python_docs]: https://github.com/Azure/autorest.python/tree/autorestv3/docs/readme.md
218
+ [python_docs]: https://github.com/Azure/autorest.python/tree/main/docs/readme.md
219
219
  [main_docs]: https://github.com/Azure/autorest/tree/master/docs
@@ -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:
@@ -33,5 +33,5 @@
33
33
  {{ keywords.await }}self._client.__{{ keywords.async_prefix }}enter__()
34
34
  return self
35
35
 
36
- {{ keywords.def }} __{{ keywords.async_prefix }}exit__(self, *exc_details) -> None:
36
+ {{ keywords.def }} __{{ keywords.async_prefix }}exit__(self, *exc_details: Any) -> None:
37
37
  {{ keywords.await }}self._client.__{{ keywords.async_prefix }}exit__(*exc_details)
@@ -14,7 +14,6 @@ import base64
14
14
  import re
15
15
  import copy
16
16
  import typing
17
- from collections.abc import MutableMapping
18
17
  from datetime import datetime, date, time, timedelta, timezone
19
18
  from json import JSONEncoder
20
19
  import isodate
@@ -23,6 +22,11 @@ from azure.core import CaseInsensitiveEnumMeta
23
22
  from azure.core.pipeline import PipelineResponse
24
23
  from azure.core.serialization import NULL as AzureCoreNull
25
24
 
25
+ if sys.version_info >= (3, 9):
26
+ from collections.abc import MutableMapping
27
+ else:
28
+ from typing import MutableMapping
29
+
26
30
  _LOGGER = logging.getLogger(__name__)
27
31
 
28
32
  __all__ = ["NULL", "AzureJSONEncoder", "Model", "rest_field", "rest_discriminator"]
@@ -276,7 +280,7 @@ def _get_model(module_name: str, model_name: str):
276
280
  _UNSET = object()
277
281
 
278
282
 
279
- class _MyMutableMapping(MutableMapping):
283
+ class _MyMutableMapping(MutableMapping[str, typing.Any]): # pylint: disable=unsubscriptable-object
280
284
  def __init__(self, data: typing.Dict[str, typing.Any]) -> None:
281
285
  self._data = copy.deepcopy(data)
282
286
 
@@ -301,13 +305,13 @@ class _MyMutableMapping(MutableMapping):
301
305
  def __ne__(self, other: typing.Any) -> bool:
302
306
  return not self.__eq__(other)
303
307
 
304
- def keys(self) -> typing.KeysView:
308
+ def keys(self) -> typing.KeysView[str]:
305
309
  return self._data.keys()
306
310
 
307
- def values(self) -> typing.ValuesView:
311
+ def values(self) -> typing.ValuesView[typing.Any]:
308
312
  return self._data.values()
309
313
 
310
- def items(self) -> typing.ItemsView:
314
+ def items(self) -> typing.ItemsView[str, typing.Any]:
311
315
  return self._data.items()
312
316
 
313
317
  def get(self, key: str, default: typing.Any = None) -> typing.Any:
@@ -399,7 +403,7 @@ def _create_value(rf: typing.Optional["_RestField"], value: typing.Any) -> typin
399
403
  class Model(_MyMutableMapping):
400
404
  _is_model = True
401
405
 
402
- def __init__(self, *args, **kwargs):
406
+ def __init__(self, *args: typing.Any, **kwargs: typing.Any) -> None:
403
407
  class_name = self.__class__.__name__
404
408
  if len(args) > 1:
405
409
  raise TypeError(f"{class_name}.__init__() takes 2 positional arguments but {len(args) + 1} were given")
@@ -420,10 +424,10 @@ class Model(_MyMutableMapping):
420
424
  dict_to_pass.update({self._attr_to_rest_field[k]._rest_name: _serialize(v) for k, v in kwargs.items()})
421
425
  super().__init__(dict_to_pass)
422
426
 
423
- def copy(self):
427
+ def copy(self) -> "Model":
424
428
  return Model(self.__dict__)
425
429
 
426
- def __new__(cls, *args: typing.Any, **kwargs: typing.Any): # pylint: disable=unused-argument
430
+ def __new__(cls, *args: typing.Any, **kwargs: typing.Any) -> "Model": # pylint: disable=unused-argument
427
431
  # we know the last three classes in mro are going to be 'Model', 'dict', and 'object'
428
432
  mros = cls.__mro__[:-3][::-1] # ignore model, dict, and object parents, and reverse the mro order
429
433
  attr_to_rest_field: typing.Dict[str, _RestField] = { # map attribute name to rest_field property
@@ -443,12 +447,12 @@ class Model(_MyMutableMapping):
443
447
  rf._rest_name_input = attr
444
448
  cls._attr_to_rest_field: typing.Dict[str, _RestField] = dict(attr_to_rest_field.items())
445
449
 
446
- return super().__new__(cls)
450
+ return super().__new__(cls) # pylint: disable=no-value-for-parameter
447
451
 
448
- def __init_subclass__(cls, discriminator=None):
452
+ def __init_subclass__(cls, discriminator: typing.Optional[str] = None) -> None:
449
453
  for base in cls.__bases__:
450
454
  if hasattr(base, "__mapping__"): # pylint: disable=no-member
451
- base.__mapping__[discriminator or cls.__name__] = cls # pylint: disable=no-member
455
+ base.__mapping__[discriminator or cls.__name__] = cls # type: ignore # pylint: disable=no-member
452
456
 
453
457
  @classmethod
454
458
  def _get_discriminator(cls) -> typing.Optional[str]:
@@ -56,7 +56,7 @@
56
56
  {% endif %}
57
57
  {% set initialize_properties = serializer.initialize_properties(model) %}
58
58
  {% if model.is_public and serializer.init_line(model) or initialize_properties %}
59
- def __init__(self, *args, **kwargs):{{ '# pylint: disable=useless-super-delegation' if not initialize_properties else '' }}
59
+ def __init__(self, *args: Any, **kwargs: Any) -> None:{{ '# pylint: disable=useless-super-delegation' if not initialize_properties else '' }}
60
60
  super().__init__(*args, **kwargs)
61
61
  {% for initialize_property in initialize_properties %}
62
62
  {{ initialize_property }}
@@ -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 %}
@@ -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,13 +1,13 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.2.16",
3
+ "version": "6.3.1",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "main": "index.js",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/Azure/autorest.python/tree/autorestv3"
8
+ "url": "https://github.com/Azure/autorest.python/tree/main"
9
9
  },
10
- "readme": "https://github.com/Azure/autorest.python/blob/autorestv3/README.md",
10
+ "readme": "https://github.com/Azure/autorest.python/blob/main/README.md",
11
11
  "keywords": [
12
12
  "autorest",
13
13
  "python"
@@ -17,7 +17,7 @@
17
17
  "bugs": {
18
18
  "url": "https://github.com/Azure/autorest.python/issues"
19
19
  },
20
- "homepage": "https://github.com/Azure/autorest.python/blob/autorestv3/README.md",
20
+ "homepage": "https://github.com/Azure/autorest.python/blob/main/README.md",
21
21
  "dependencies": {
22
22
  "@autorest/system-requirements": "~1.0.0"
23
23
  },
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