@autorest/python 6.3.2 → 6.4.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.
@@ -13,7 +13,7 @@ from black.report import NothingChanged
13
13
  from .. import Plugin, PluginAutorest
14
14
  from .._utils import parse_args
15
15
 
16
- logging.getLogger("blib2to3").setLevel(logging.ERROR)
16
+ _LOGGER = logging.getLogger("blib2to3")
17
17
 
18
18
  _BLACK_MODE = black.Mode() # pyright: ignore [reportPrivateImportUsage]
19
19
  _BLACK_MODE.line_length = 120
@@ -40,8 +40,16 @@ class BlackScriptPlugin(Plugin): # pylint: disable=abstract-method
40
40
  return True
41
41
 
42
42
  def format_file(self, file: Path) -> None:
43
- file_content = self.read_file(file)
44
- if not file.suffix == ".py":
43
+ try:
44
+ file_content = self.read_file(file)
45
+ except Exception as e: # pylint: disable=broad-except
46
+ if file.suffix != ".py":
47
+ _LOGGER.warning(
48
+ "Can not read file %s, not blacking this file", file.name
49
+ )
50
+ return
51
+ raise e # still want to raise if we fail reading a py file
52
+ if file.suffix != ".py":
45
53
  self.write_file(file, file_content)
46
54
  return
47
55
  try:
@@ -164,6 +164,7 @@ class CodeGenerator(Plugin):
164
164
  low_level_client or version_tolerant,
165
165
  ),
166
166
  "generate_sample": self.options.get("generate-sample", False),
167
+ "default_api_version": self.options.get("default-api-version"),
167
168
  }
168
169
 
169
170
  if options["builders_visibility"] is None:
@@ -276,6 +277,7 @@ class CodeGeneratorAutorest(CodeGenerator, PluginAutorest):
276
277
  "default-optional-constants-to-none"
277
278
  ),
278
279
  "generate-sample": self._autorestapi.get_boolean_value("generate-sample"),
280
+ "default-api-version": self._autorestapi.get_value("default-api-version"),
279
281
  }
280
282
  return {k: v for k, v in options.items() if v is not None}
281
283
 
@@ -8,6 +8,7 @@ from typing import List, Dict, Any, Set, Union
8
8
  from .base import BaseType
9
9
  from .enum_type import EnumType
10
10
  from .model_type import ModelType
11
+ from .combined_type import CombinedType
11
12
  from .client import Client
12
13
  from .request_builder import RequestBuilder, OverloadedRequestBuilder
13
14
  from .constant_type import ConstantType
@@ -72,6 +73,9 @@ class CodeModel: # pylint: disable=too-many-public-methods
72
73
  if self.options["models_mode"] and self.model_types:
73
74
  self.sort_model_types()
74
75
  self.is_subnamespace = is_subnamespace
76
+ self.named_unions: List[CombinedType] = [
77
+ t for t in self.types_map.values() if isinstance(t, CombinedType) and t.name
78
+ ]
75
79
 
76
80
  @property
77
81
  def has_operations(self) -> bool:
@@ -5,7 +5,7 @@
5
5
  # --------------------------------------------------------------------------
6
6
  from typing import Any, Dict, List, Optional, TYPE_CHECKING
7
7
  import re
8
- from autorest.codegen.models.imports import FileImport, ImportType
8
+ from autorest.codegen.models.imports import FileImport, ImportType, TypingSection
9
9
  from .base import BaseType
10
10
  from .model_type import JSONModelType
11
11
 
@@ -29,6 +29,7 @@ class CombinedType(BaseType):
29
29
  ) -> None:
30
30
  super().__init__(yaml_data, code_model)
31
31
  self.types = types # the types that this type is combining
32
+ self.name = yaml_data.get("name")
32
33
 
33
34
  @property
34
35
  def serialization_type(self) -> str:
@@ -62,11 +63,16 @@ class CombinedType(BaseType):
62
63
  return " or ".join(t.docstring_type(**kwargs) for t in self.types)
63
64
 
64
65
  def type_annotation(self, **kwargs: Any) -> str:
66
+ if self.name:
67
+ ret = f"_types.{self.name}"
68
+ return ret if kwargs.get("is_operation_file") else f'"{ret}"'
69
+ return self.type_definition(**kwargs)
70
+
71
+ def type_definition(self, **kwargs: Any) -> str:
65
72
  """The python type used for type annotation
66
73
 
67
74
  Special case for enum, for instance: Union[str, "EnumName"]
68
75
  """
69
- kwargs["is_operation_file"] = True
70
76
  inside_types = [type.type_annotation(**kwargs) for type in self.types]
71
77
 
72
78
  # If the inside types has been a Union, peel first and then re-union
@@ -96,6 +102,14 @@ class CombinedType(BaseType):
96
102
 
97
103
  def imports(self, **kwargs: Any) -> FileImport:
98
104
  file_import = FileImport()
105
+ if self.name and not kwargs.get("is_types_file"):
106
+ file_import.add_submodule_import(
107
+ kwargs.pop("relative_path"),
108
+ "_types",
109
+ ImportType.LOCAL,
110
+ TypingSection.TYPING,
111
+ )
112
+ return file_import
99
113
  for type in self.types:
100
114
  file_import.merge(type.imports(**kwargs))
101
115
  file_import.add_submodule_import("typing", "Union", ImportType.STDLIB)
@@ -194,6 +194,12 @@ class EnumType(BaseType):
194
194
  if self.code_model.options["models_mode"] and relative_path:
195
195
  # add import for enums in operations file
196
196
  file_import.add_submodule_import(
197
- relative_path, "models", ImportType.LOCAL, alias="_models"
197
+ relative_path,
198
+ "models",
199
+ ImportType.LOCAL,
200
+ alias="_models",
201
+ typing_section=TypingSection.TYPING
202
+ if kwargs.get("model_typing")
203
+ else TypingSection.REGULAR,
198
204
  )
199
205
  return file_import
@@ -303,9 +303,15 @@ class GeneratedModelType(ModelType): # pylint: disable=abstract-method
303
303
  file_import = super().imports(**kwargs)
304
304
  relative_path = kwargs.pop("relative_path", None)
305
305
  if relative_path:
306
- # add import for models in operations file
306
+ # add import for models in operations or _types file
307
307
  file_import.add_submodule_import(
308
- relative_path, "models", ImportType.LOCAL, alias="_models"
308
+ relative_path,
309
+ "models",
310
+ ImportType.LOCAL,
311
+ alias="_models",
312
+ typing_section=TypingSection.TYPING
313
+ if kwargs.get("model_typing")
314
+ else TypingSection.REGULAR,
309
315
  )
310
316
  return file_import
311
317
 
@@ -17,7 +17,7 @@ from typing import (
17
17
  Generic,
18
18
  )
19
19
 
20
- from .imports import FileImport, ImportType
20
+ from .imports import FileImport, ImportType, TypingSection
21
21
  from .base import BaseModel
22
22
  from .base import BaseType
23
23
  from .constant_type import ConstantType
@@ -165,6 +165,13 @@ class _ParameterBase(
165
165
  "api_version_validation",
166
166
  ImportType.LOCAL,
167
167
  )
168
+ if isinstance(self.type, CombinedType) and self.type.name:
169
+ file_import.add_submodule_import(
170
+ "..",
171
+ "_types",
172
+ ImportType.LOCAL,
173
+ TypingSection.TYPING,
174
+ )
168
175
  return file_import
169
176
 
170
177
  def imports(self, async_mode: bool, **kwargs: Any) -> FileImport:
@@ -8,7 +8,7 @@ from typing import Any, Dict, Optional, TYPE_CHECKING, List
8
8
  from .base import BaseModel
9
9
  from .constant_type import ConstantType
10
10
  from .base import BaseType
11
- from .imports import FileImport, ImportType, TypingSection
11
+ from .imports import FileImport, ImportType
12
12
  from .utils import add_to_description, add_to_pylint_disable
13
13
 
14
14
  if TYPE_CHECKING:
@@ -129,32 +129,12 @@ class Property(BaseModel): # pylint: disable=too-many-instance-attributes
129
129
  retval.update(self.type.validation or {})
130
130
  return retval or None
131
131
 
132
- @staticmethod
133
- def contain_model_type(t: BaseType) -> bool:
134
- from . import ListType, DictionaryType, ModelType
135
-
136
- if isinstance(t, ModelType):
137
- return True
138
- if isinstance(t, ListType):
139
- return Property.contain_model_type(t.element_type)
140
- if isinstance(t, DictionaryType):
141
- return Property.contain_model_type(t.element_type)
142
- if isinstance(t, ConstantType):
143
- return Property.contain_model_type(t.value_type)
144
- return False
145
-
146
132
  def imports(self, **kwargs) -> FileImport:
147
- file_import = self.type.imports(**kwargs, is_operation_file=False)
133
+ file_import = self.type.imports(
134
+ **kwargs, is_operation_file=False, relative_path="..", model_typing=True
135
+ )
148
136
  if self.optional and self.client_default_value is None:
149
137
  file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB)
150
- if self.contain_model_type(self.type):
151
- file_import.add_submodule_import(
152
- "..",
153
- "models",
154
- ImportType.LOCAL,
155
- TypingSection.TYPING,
156
- alias="_models",
157
- )
158
138
  if self.code_model.options["models_mode"] == "dpg":
159
139
  file_import.add_submodule_import(
160
140
  ".._model_base",
@@ -7,11 +7,12 @@ from typing import Dict, Optional, List, Any, TYPE_CHECKING, Union
7
7
 
8
8
  from .base import BaseModel
9
9
  from .base import BaseType
10
- from .imports import FileImport, ImportType
10
+ from .imports import FileImport, ImportType, TypingSection
11
11
  from .primitive_types import BinaryType, BinaryIteratorType
12
12
  from .dictionary_type import DictionaryType
13
13
  from .list_type import ListType
14
14
  from .model_type import ModelType
15
+ from .combined_type import CombinedType
15
16
 
16
17
  if TYPE_CHECKING:
17
18
  from .code_model import CodeModel
@@ -105,6 +106,13 @@ class Response(BaseModel):
105
106
  file_import.merge(self.type.imports(is_operation_file=True, **kwargs))
106
107
  if self.nullable:
107
108
  file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB)
109
+ if isinstance(self.type, CombinedType) and self.type.name:
110
+ file_import.add_submodule_import(
111
+ "..",
112
+ "_types",
113
+ ImportType.LOCAL,
114
+ TypingSection.TYPING,
115
+ )
108
116
  return file_import
109
117
 
110
118
  def imports(self, **kwargs: Any) -> FileImport:
@@ -27,6 +27,7 @@ from .metadata_serializer import MetadataSerializer
27
27
  from .request_builders_serializer import RequestBuildersSerializer
28
28
  from .patch_serializer import PatchSerializer
29
29
  from .sample_serializer import SampleSerializer
30
+ from .types_serializer import TypesSerializer
30
31
  from ..._utils import to_snake_case
31
32
  from .utils import extract_sample_name
32
33
 
@@ -125,7 +126,6 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
125
126
  self.code_model.options["show_operations"]
126
127
  and self.code_model.has_operations
127
128
  and self.code_model.options["generate_sample"]
128
- and not self.code_model.options["multiapi"]
129
129
  ):
130
130
  self._serialize_and_write_sample(env, namespace_path)
131
131
 
@@ -193,6 +193,11 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
193
193
  namespace_path / Path("models.py"),
194
194
  self.read_file(namespace_path / Path("models.py")),
195
195
  )
196
+ if self.code_model.named_unions:
197
+ self.write_file(
198
+ namespace_path / Path("_types.py"),
199
+ TypesSerializer(code_model=self.code_model, env=env).serialize(),
200
+ )
196
201
 
197
202
  def _serialize_and_write_package_files(self, namespace_path: Path) -> None:
198
203
  root_of_sdk = self._package_root_folder(namespace_path)
@@ -549,6 +554,12 @@ class JinjaSerializer(ReaderAndWriter): # pylint: disable=abstract-method
549
554
  for client in self.code_model.clients:
550
555
  for op_group in client.operation_groups:
551
556
  for operation in op_group.operations:
557
+ if (
558
+ self.code_model.options["multiapi"]
559
+ and operation.api_versions[0]
560
+ != self.code_model.options["default_api_version"]
561
+ ):
562
+ continue
552
563
  samples = operation.yaml_data["samples"]
553
564
  if not samples or operation.name.startswith("_"):
554
565
  continue
@@ -110,7 +110,7 @@ class ClientSerializer:
110
110
  def initialize_pipeline_client(self, async_mode: bool) -> str:
111
111
  pipeline_client_name = self.client.pipeline_class(async_mode)
112
112
  return (
113
- f"self._client = {pipeline_client_name}(base_url={self.host_variable_name}, "
113
+ f"self._client: {pipeline_client_name} = {pipeline_client_name}(base_url={self.host_variable_name}, "
114
114
  "config=self._config, **kwargs)"
115
115
  )
116
116
 
@@ -0,0 +1,38 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+ from jinja2 import Environment
7
+ from ..models import CodeModel
8
+ from ..models.imports import FileImport, ImportType
9
+ from .import_serializer import FileImportSerializer
10
+
11
+
12
+ class TypesSerializer:
13
+ def __init__(self, code_model: CodeModel, env: Environment) -> None:
14
+ self.code_model = code_model
15
+ self.env = env
16
+
17
+ def imports(self) -> FileImport:
18
+ file_import = FileImport()
19
+ if self.code_model.named_unions:
20
+ file_import.add_submodule_import(
21
+ "typing",
22
+ "Union",
23
+ ImportType.STDLIB,
24
+ )
25
+ for nu in self.code_model.named_unions:
26
+ file_import.merge(
27
+ nu.imports(relative_path=".", model_typing=True, is_types_file=True)
28
+ )
29
+ return file_import
30
+
31
+ def serialize(self) -> str:
32
+ # Generate the models
33
+ template = self.env.get_template("types.py.jinja2")
34
+ return template.render(
35
+ code_model=self.code_model,
36
+ imports=FileImportSerializer(self.imports()),
37
+ serializer=self,
38
+ )
@@ -0,0 +1,8 @@
1
+ # coding=utf-8
2
+ # pylint: disable=too-many-lines
3
+ {{ code_model.options['license_header'] }}
4
+
5
+ {{ imports }}
6
+ {% for nu in code_model.named_unions %}
7
+ {{nu.name}} = {{nu.type_definition()}}
8
+ {% endfor %}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autorest/python",
3
- "version": "6.3.2",
3
+ "version": "6.4.1",
4
4
  "description": "The Python extension for generators in AutoRest.",
5
5
  "main": "index.js",
6
6
  "repository": {
package/requirements.txt CHANGED
@@ -6,7 +6,7 @@ json-rpc==1.14.0
6
6
  m2r2==0.3.3
7
7
  MarkupSafe==2.1.2
8
8
  mistune==0.8.4
9
- pathspec==0.10.3
9
+ pathspec==0.11.0
10
10
  platformdirs==2.6.2
11
11
  PyYAML==6.0
12
12
  tomli==2.0.1