diff --git a/.chronus/changes/python-moveToVendorFolder-2025-3-17-13-43-15.md b/.chronus/changes/python-moveToVendorFolder-2025-3-17-13-43-15.md new file mode 100644 index 00000000000..7edd29c8815 --- /dev/null +++ b/.chronus/changes/python-moveToVendorFolder-2025-3-17-13-43-15.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@typespec/http-client-python" +--- + +Reorder generated `_vendor` file into a `_utils_` folder diff --git a/packages/http-client-python/generator/pygen/codegen/models/code_model.py b/packages/http-client-python/generator/pygen/codegen/models/code_model.py index 600385c41ba..84426d282d5 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/code_model.py +++ b/packages/http-client-python/generator/pygen/codegen/models/code_model.py @@ -242,16 +242,26 @@ def is_top_namespace(self, client_namespace: str) -> bool: """ return client_namespace == self.namespace - def need_vendored_code(self, async_mode: bool, client_namespace: str) -> bool: - """Whether we need to vendor code in the _vendor.py in specific namespace""" + def need_utils_folder(self, async_mode: bool, client_namespace: str) -> bool: return ( - self.need_vendored_form_data(async_mode, client_namespace) - or self.need_vendored_etag(client_namespace) - or self.need_vendored_abstract(client_namespace) - or self.need_vendored_mixin(client_namespace) + self.need_utils_utils(async_mode, client_namespace) + or self.need_utils_serialization + or self.options["models_mode"] == "dpg" ) - def need_vendored_form_data(self, async_mode: bool, client_namespace: str) -> bool: + @property + def need_utils_serialization(self) -> bool: + return not self.options["client_side_validation"] + + def need_utils_utils(self, async_mode: bool, client_namespace: str) -> bool: + return ( + self.need_utils_form_data(async_mode, client_namespace) + or self.need_utils_etag(client_namespace) + or self.need_utils_abstract(client_namespace) + or self.need_utils_mixin + ) + + def need_utils_form_data(self, async_mode: bool, client_namespace: str) -> bool: return ( (not async_mode) and self.is_top_namespace(client_namespace) @@ -259,14 +269,15 @@ def need_vendored_form_data(self, async_mode: bool, client_namespace: str) -> bo and self.options["models_mode"] == "dpg" ) - def need_vendored_etag(self, client_namespace: str) -> bool: + def need_utils_etag(self, client_namespace: str) -> bool: return self.is_top_namespace(client_namespace) and self.has_etag - def need_vendored_abstract(self, client_namespace: str) -> bool: + def need_utils_abstract(self, client_namespace: str) -> bool: return self.is_top_namespace(client_namespace) and self.has_abstract_operations - def need_vendored_mixin(self, client_namespace: str) -> bool: - return self.has_mixin(client_namespace) + @property + def need_utils_mixin(self) -> bool: + return any(c_n for c_n in self.client_namespace_types if self.has_mixin(c_n)) def has_mixin(self, client_namespace: str) -> bool: return any(c for c in self.get_clients(client_namespace) if c.has_mixin) diff --git a/packages/http-client-python/generator/pygen/codegen/models/imports.py b/packages/http-client-python/generator/pygen/codegen/models/imports.py index ed434df81de..59d99ca8638 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/imports.py +++ b/packages/http-client-python/generator/pygen/codegen/models/imports.py @@ -30,9 +30,11 @@ class TypingSection(str, Enum): class MsrestImportType(Enum): - Module = auto() # import _serialization.py or msrest.serialization as Module - Serializer = auto() # from _serialization.py or msrest.serialization import Serializer - SerializerDeserializer = auto() # from _serialization.py or msrest.serialization import Serializer and Deserializer + Module = auto() # import _utils/serialization.py or msrest.serialization as Module + Serializer = auto() # from _utils/serialization.py or msrest.serialization import Serializer + SerializerDeserializer = ( + auto() + ) # from _utils/serialization.py or msrest.serialization import Serializer and Deserializer class ImportModel: @@ -261,21 +263,23 @@ def add_msrest_import( if msrest_import_type == MsrestImportType.SerializerDeserializer: self.add_submodule_import("msrest", "Deserializer", ImportType.THIRDPARTY, typing_section) else: - # _serialization.py is always in root namespace - imported_namespace = self.code_model.namespace + # _utils/serialization.py is always in root namespace + imported_namespace = f"{self.code_model.namespace}._utils" if self.code_model.options["multiapi"]: - # for multiapi, the namespace is azure.mgmt.xxx.v20XX_XX_XX while _serialization.py is in azure.mgmt.xxx - imported_namespace = get_parent_namespace(imported_namespace) + # for multiapi, the namespace is azure.mgmt.xxx.v20XX_XX_XX + # while _utils/serialization.py is in azure.mgmt.xxx + imported_namespace = f"{get_parent_namespace(imported_namespace)}._utils" if msrest_import_type == MsrestImportType.Module: self.add_submodule_import( self.code_model.get_relative_import_path(serialize_namespace, imported_namespace), - "_serialization", + "serialization", ImportType.LOCAL, typing_section, + alias="_serialization", ) else: relative_path = self.code_model.get_relative_import_path( - serialize_namespace, imported_namespace, module_name="_serialization" + serialize_namespace, f"{self.code_model.namespace}._utils.serialization" ) self.add_submodule_import(relative_path, "Serializer", ImportType.LOCAL, typing_section) if msrest_import_type == MsrestImportType.SerializerDeserializer: diff --git a/packages/http-client-python/generator/pygen/codegen/models/lro_operation.py b/packages/http-client-python/generator/pygen/codegen/models/lro_operation.py index 8717954aa5c..9ae3fcbf3a9 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/lro_operation.py +++ b/packages/http-client-python/generator/pygen/codegen/models/lro_operation.py @@ -134,7 +134,7 @@ def imports(self, async_mode: bool, **kwargs: Any) -> FileImport: # but final call returns a model serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) file_import.add_submodule_import( - self.code_model.get_relative_import_path(serialize_namespace, module_name="_model_base"), + self.code_model.get_relative_import_path(serialize_namespace, module_name="_utils.model_base"), "_deserialize", ImportType.LOCAL, ) diff --git a/packages/http-client-python/generator/pygen/codegen/models/model_type.py b/packages/http-client-python/generator/pygen/codegen/models/model_type.py index 3b0952c61a9..98b1881c5b4 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/model_type.py +++ b/packages/http-client-python/generator/pygen/codegen/models/model_type.py @@ -318,9 +318,10 @@ def imports(self, **kwargs: Any) -> FileImport: ) if self.is_form_data: file_import.add_submodule_import( - self.code_model.get_relative_import_path(serialize_namespace), - "_model_base", + self.code_model.get_relative_import_path(serialize_namespace, module_name="_utils.model_base"), + "Model", ImportType.LOCAL, + alias="_Model", ) elif serialize_namespace_type == NamespaceType.TYPES_FILE or ( serialize_namespace_type == NamespaceType.MODEL and called_by_property diff --git a/packages/http-client-python/generator/pygen/codegen/models/operation.py b/packages/http-client-python/generator/pygen/codegen/models/operation.py index 1f156009854..f3a00264291 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/operation.py +++ b/packages/http-client-python/generator/pygen/codegen/models/operation.py @@ -391,7 +391,9 @@ def imports( # pylint: disable=too-many-branches, disable=too-many-statements ImportType.SDKCORE, ) if not async_mode: - relative_path = self.code_model.get_relative_import_path(serialize_namespace, module_name="_vendor") + relative_path = self.code_model.get_relative_import_path( + serialize_namespace, module_name="_utils.utils" + ) file_import.add_submodule_import( relative_path, "prep_if_match", @@ -436,12 +438,17 @@ def imports( # pylint: disable=too-many-branches, disable=too-many-statements if self.overloads: file_import.add_submodule_import("typing", "overload", ImportType.STDLIB) if self.code_model.options["models_mode"] == "dpg": - relative_path = self.code_model.get_relative_import_path(serialize_namespace, module_name="_model_base") + relative_path = self.code_model.get_relative_import_path( + serialize_namespace, module_name="_utils.model_base" + ) body_param = self.parameters.body_parameter if self.parameters.has_body else None if body_param and not isinstance(body_param.type, BinaryType): if self.has_form_data_body: file_import.add_submodule_import( - self.code_model.get_relative_import_path(serialize_namespace), "_model_base", ImportType.LOCAL + relative_path, + "Model", + ImportType.LOCAL, + alias="_Model", ) elif xml_serializable(self.parameters.body_parameter.default_content_type): file_import.add_submodule_import( diff --git a/packages/http-client-python/generator/pygen/codegen/models/operation_group.py b/packages/http-client-python/generator/pygen/codegen/models/operation_group.py index 09eabbc9903..0be9f5bf3d1 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/operation_group.py +++ b/packages/http-client-python/generator/pygen/codegen/models/operation_group.py @@ -63,11 +63,11 @@ def has_non_abstract_operations(self) -> bool: operation_group.has_non_abstract_operations for operation_group in self.operation_groups ) - @property - def base_class(self) -> str: + def base_class(self, async_mode: bool) -> str: + pipeline_client = f"{'Async' if async_mode else ''}PipelineClient" base_classes: List[str] = [] if self.is_mixin: - base_classes.append(f"{self.client.name}MixinABC") + base_classes.append(f"ClientMixinABC[{pipeline_client}, {self.client.name}Configuration]") return ", ".join(base_classes) def imports_for_multiapi(self, async_mode: bool, **kwargs) -> FileImport: @@ -100,8 +100,12 @@ def need_validation(self) -> bool: def imports(self, async_mode: bool, **kwargs: Any) -> FileImport: file_import = FileImport(self.code_model) - serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) + utils_path = self.code_model.get_relative_import_path( + serialize_namespace, + f"{self.code_model.namespace}._utils.utils", + ) + for operation in self.operations: file_import.merge(operation.imports(async_mode, **kwargs)) if not self.code_model.options["combine_operation_files"]: @@ -141,32 +145,28 @@ def imports(self, async_mode: bool, **kwargs: Any) -> FileImport: ImportType.LOCAL, alias="_models", ) + file_import.add_submodule_import( + self.code_model.get_relative_import_path( + serialize_namespace, + self.code_model.get_imported_namespace_for_client(self.client.client_namespace, async_mode), + module_name="_configuration", + ), + f"{self.client.name}Configuration", + ImportType.LOCAL, + ) + file_import.add_submodule_import( + "" if self.code_model.is_azure_flavor else "runtime", + f"{'Async' if async_mode else ''}PipelineClient", + ImportType.SDKCORE, + ) if self.is_mixin: file_import.add_submodule_import( - # XxxMixinABC is always defined in _vendor of client namespace - self.code_model.get_relative_import_path( - serialize_namespace, - self.code_model.get_imported_namespace_for_client(self.client.client_namespace, async_mode), - module_name="_vendor", - ), - f"{self.client.name}MixinABC", + # XxxMixinABC is always defined in _utils of client namespace + utils_path, + "ClientMixinABC", ImportType.LOCAL, ) else: - file_import.add_submodule_import( - "" if self.code_model.is_azure_flavor else "runtime", - f"{'Async' if async_mode else ''}PipelineClient", - ImportType.SDKCORE, - ) - file_import.add_submodule_import( - self.code_model.get_relative_import_path( - serialize_namespace, - self.code_model.get_imported_namespace_for_client(self.client.client_namespace, async_mode), - module_name="_configuration", - ), - f"{self.client.name}Configuration", - ImportType.LOCAL, - ) file_import.add_msrest_import( serialize_namespace=kwargs.get("serialize_namespace", self.code_model.namespace), msrest_import_type=MsrestImportType.Serializer, @@ -179,12 +179,8 @@ def imports(self, async_mode: bool, **kwargs: Any) -> FileImport: ) if self.has_abstract_operations: file_import.add_submodule_import( - # raise_if_not_implemented is always defined in _vendor of top namespace - self.code_model.get_relative_import_path( - serialize_namespace, - self.code_model.get_imported_namespace_for_client(self.code_model.namespace, async_mode), - module_name="_vendor", - ), + # raise_if_not_implemented is always defined in _utils of top namespace + utils_path, "raise_if_not_implemented", ImportType.LOCAL, ) diff --git a/packages/http-client-python/generator/pygen/codegen/models/paging_operation.py b/packages/http-client-python/generator/pygen/codegen/models/paging_operation.py index f376191dc22..cb2fb11eceb 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/paging_operation.py +++ b/packages/http-client-python/generator/pygen/codegen/models/paging_operation.py @@ -157,7 +157,9 @@ def imports(self, async_mode: bool, **kwargs: Any) -> FileImport: ImportType.SDKCORE, ) if self.code_model.options["models_mode"] == "dpg": - relative_path = self.code_model.get_relative_import_path(serialize_namespace, module_name="_model_base") + relative_path = self.code_model.get_relative_import_path( + serialize_namespace, module_name="_utils.model_base" + ) file_import.merge(self.item_type.imports(**kwargs)) if self.default_error_deserialization or self.need_deserialize: file_import.add_submodule_import(relative_path, "_deserialize", ImportType.LOCAL) diff --git a/packages/http-client-python/generator/pygen/codegen/models/parameter.py b/packages/http-client-python/generator/pygen/codegen/models/parameter.py index 5da3d505803..401a9b062a8 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/parameter.py +++ b/packages/http-client-python/generator/pygen/codegen/models/parameter.py @@ -275,7 +275,7 @@ def imports(self, async_mode: bool, **kwargs: Any) -> FileImport: if self.is_form_data: serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) file_import.add_submodule_import( - self.code_model.get_relative_import_path(serialize_namespace, module_name="_vendor"), + self.code_model.get_relative_import_path(serialize_namespace, module_name="_utils.utils"), "prepare_multipart_form_data", ImportType.LOCAL, ) diff --git a/packages/http-client-python/generator/pygen/codegen/models/primitive_types.py b/packages/http-client-python/generator/pygen/codegen/models/primitive_types.py index 08eb8de03df..ec4cf03dd2c 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/primitive_types.py +++ b/packages/http-client-python/generator/pygen/codegen/models/primitive_types.py @@ -618,13 +618,13 @@ def type_annotation(self, **kwargs: Any) -> str: return self.name def docstring_type(self, **kwargs: Any) -> str: - return f"~{self.code_model.namespace}._vendor.{self.name}" + return f"~{self.code_model.namespace}._utils.utils.{self.name}" def imports(self, **kwargs: Any) -> FileImport: file_import = super().imports(**kwargs) serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) file_import.add_submodule_import( - self.code_model.get_relative_import_path(serialize_namespace, module_name="_vendor"), + self.code_model.get_relative_import_path(serialize_namespace, module_name="_utils.utils"), self.name, ImportType.LOCAL, ) diff --git a/packages/http-client-python/generator/pygen/codegen/models/property.py b/packages/http-client-python/generator/pygen/codegen/models/property.py index 27d4ce8e723..45381d1cff9 100644 --- a/packages/http-client-python/generator/pygen/codegen/models/property.py +++ b/packages/http-client-python/generator/pygen/codegen/models/property.py @@ -149,7 +149,7 @@ def imports(self, **kwargs) -> FileImport: if self.code_model.options["models_mode"] == "dpg": serialize_namespace = kwargs.get("serialize_namespace", self.code_model.namespace) file_import.add_submodule_import( - self.code_model.get_relative_import_path(serialize_namespace, module_name="_model_base"), + self.code_model.get_relative_import_path(serialize_namespace, module_name="_utils.model_base"), "rest_discriminator" if self.is_discriminator else "rest_field", ImportType.LOCAL, ) diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py index c7cd1d6cd07..62089274a77 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -153,7 +153,7 @@ def serialize(self) -> None: general_serializer.serialize_pkgutil_init_file(), ) - # _model_base.py/_serialization.py/_vendor.py/py.typed/_types.py/_validation.py + # _utils/py.typed/_types.py/_validation.py # is always put in top level namespace if self.code_model.is_top_namespace(client_namespace): self._serialize_and_write_top_level_folder(env=env, namespace=client_namespace) @@ -404,25 +404,41 @@ def _serialize_client_and_config_files( general_serializer.serialize_config_file(clients), ) - # sometimes we need define additional Mixin class for client in _vendor.py - self._serialize_and_write_vendor_file(env, namespace) + # sometimes we need define additional Mixin class for client in _utils.py + self._serialize_and_write_utils_folder(env, namespace) - def _serialize_and_write_vendor_file(self, env: Environment, namespace: str) -> None: + def _serialize_and_write_utils_folder(self, env: Environment, namespace: str) -> None: exec_path = self.exec_path(namespace) - # write _vendor.py - for async_mode, async_path in self.serialize_loop: - if self.code_model.need_vendored_code(async_mode=async_mode, client_namespace=namespace): - self.write_file( - exec_path / Path(f"{async_path}_vendor.py"), - GeneralSerializer( - code_model=self.code_model, env=env, async_mode=async_mode, client_namespace=namespace - ).serialize_vendor_file(), - ) + general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False) + utils_folder_path = exec_path / Path("_utils") + if self.code_model.need_utils_folder(async_mode=False, client_namespace=namespace): + self.write_file( + utils_folder_path / Path("__init__.py"), + self.code_model.license_header, + ) + if self.code_model.need_utils_utils(async_mode=False, client_namespace=namespace): + self.write_file( + utils_folder_path / Path("utils.py"), + general_serializer.need_utils_utils_file(), + ) + # write _utils/serialization.py + if self.code_model.need_utils_serialization: + self.write_file( + utils_folder_path / Path("serialization.py"), + general_serializer.serialize_serialization_file(), + ) + + # write _model_base.py + if self.code_model.options["models_mode"] == "dpg": + self.write_file( + utils_folder_path / Path("model_base.py"), + general_serializer.serialize_model_base_file(), + ) def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str) -> None: exec_path = self.exec_path(namespace) - # write _vendor.py - self._serialize_and_write_vendor_file(env, namespace) + # write _utils folder + self._serialize_and_write_utils_folder(env, namespace) general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False) @@ -432,20 +448,6 @@ def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str # write the empty py.typed file self.write_file(exec_path / Path("py.typed"), "# Marker file for PEP 561.") - # write _serialization.py - if not self.code_model.options["client_side_validation"] and not self.code_model.options["multiapi"]: - self.write_file( - exec_path / Path("_serialization.py"), - general_serializer.serialize_serialization_file(), - ) - - # write _model_base.py - if self.code_model.options["models_mode"] == "dpg": - self.write_file( - exec_path / Path("_model_base.py"), - general_serializer.serialize_model_base_file(), - ) - # write _validation.py if any(og for client in self.code_model.clients for og in client.operation_groups if og.need_validation): self.write_file( diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py index 07ba2cc581d..e8b882be267 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/builder_serializer.py @@ -672,7 +672,7 @@ def _serialize_body_parameter(self, builder: OperationType) -> List[str]: [ "_body = (", f" {body_param.client_name}.as_dict()", - f" if isinstance({body_param.client_name}, _model_base.Model) else", + f" if isinstance({body_param.client_name}, _Model) else", f" {body_param.client_name}", ")", f"_file_fields: List[str] = {file_fields}", diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/general_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/general_serializer.py index 09405cecefd..77dd80a5a8c 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/general_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/general_serializer.py @@ -107,44 +107,33 @@ def serialize_service_client_file(self, clients: List[Client]) -> str: serialize_namespace=self.serialize_namespace, ) - def serialize_vendor_file(self) -> str: - template = self.env.get_template("vendor.py.jinja2") + def need_utils_utils_file(self) -> str: + template = self.env.get_template("utils.py.jinja2") clients = self.code_model.get_clients(self.client_namespace) # configure imports file_import = FileImport(self.code_model) - if self.code_model.need_vendored_mixin(self.client_namespace): + if self.code_model.need_utils_mixin: file_import.add_submodule_import( "abc", "ABC", ImportType.STDLIB, ) - file_import.add_submodule_import( - "" if self.code_model.is_azure_flavor else "runtime", - f"{'Async' if self.async_mode else ''}PipelineClient", - ImportType.SDKCORE, - TypingSection.TYPING, - ) file_import.add_msrest_import( - serialize_namespace=self.serialize_namespace, + serialize_namespace=f"{self.serialize_namespace}._utils", msrest_import_type=MsrestImportType.SerializerDeserializer, typing_section=TypingSection.TYPING, ) - for client in clients: - if client.has_mixin: - file_import.add_submodule_import( - "._configuration", - f"{client.name}Configuration", - ImportType.LOCAL, - ) - if self.code_model.need_vendored_etag(self.client_namespace): + file_import.add_submodule_import("typing", "TypeVar", ImportType.STDLIB) + file_import.add_submodule_import("typing", "Generic", ImportType.STDLIB) + if self.code_model.need_utils_etag(self.client_namespace): file_import.add_submodule_import("typing", "Optional", ImportType.STDLIB) file_import.add_submodule_import( "", "MatchConditions", ImportType.SDKCORE, ) - if self.code_model.need_vendored_form_data(self.async_mode, self.client_namespace): + if self.code_model.need_utils_form_data(self.async_mode, self.client_namespace): file_import.add_submodule_import("typing", "IO", ImportType.STDLIB) file_import.add_submodule_import("typing", "Tuple", ImportType.STDLIB) file_import.add_submodule_import("typing", "Union", ImportType.STDLIB) @@ -154,12 +143,12 @@ def serialize_vendor_file(self) -> str: file_import.add_submodule_import("typing", "Any", ImportType.STDLIB) file_import.add_submodule_import("typing", "List", ImportType.STDLIB) file_import.add_submodule_import( - "._model_base", + ".._utils.model_base", "SdkJSONEncoder", ImportType.LOCAL, ) file_import.add_submodule_import( - "._model_base", + ".._utils.model_base", "Model", ImportType.LOCAL, ) diff --git a/packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py b/packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py index a552129642d..ff04aa63754 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/model_serializer.py @@ -230,10 +230,11 @@ def imports(self) -> FileImport: file_import = FileImport(self.code_model) if any(not m.parents for m in self.models): file_import.add_submodule_import( - self.code_model.get_relative_import_path(self.serialize_namespace), - "_model_base", + self.code_model.get_relative_import_path(self.serialize_namespace, module_name="_utils.model_base"), + "Model", ImportType.LOCAL, TypingSection.REGULAR, + alias="_Model", ) for model in self.models: if model.base == "json": @@ -272,7 +273,7 @@ def imports(self) -> FileImport: return file_import def declare_model(self, model: ModelType) -> str: - basename = "_model_base.Model" + basename = "_Model" if model.parents: basename = ", ".join([m.name for m in model.parents]) if model.discriminator_value: diff --git a/packages/http-client-python/generator/pygen/codegen/templates/model_dpg.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/model_dpg.py.jinja2 index c7a874872bb..4b18df84909 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/model_dpg.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/model_dpg.py.jinja2 @@ -19,7 +19,7 @@ """ {% if model.is_polymorphic %} - __mapping__: Dict[str, _model_base.Model] = {} + __mapping__: Dict[str, _Model] = {} {% endif %} {% for p in serializer.get_properties_to_declare(model)%} {{ serializer.declare_property(p) }} diff --git a/packages/http-client-python/generator/pygen/codegen/templates/operation_group.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/operation_group.py.jinja2 index bba82eaccbe..3cc8e586292 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/operation_group.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/operation_group.py.jinja2 @@ -1,4 +1,4 @@ -{% set base_class = ("(" + operation_group.base_class + ")") if operation_group.base_class else "" %} +{% set base_class = ("(" + operation_group.base_class(async_mode) + ")") if operation_group.base_class(async_mode) else "" %} {% macro check_abstract_methods() %} {% if operation_group.has_abstract_operations %} raise_if_not_implemented(self.__class__, [ @@ -8,9 +8,9 @@ ]) {% endif %} {% endmacro %} -{% if operation_group.base_class %} +{% if operation_group.base_class(async_mode) %} class {{ operation_group.class_name }}( {{ operation_group.pylint_disable() }} - {{ operation_group.base_class }} + {{ operation_group.base_class(async_mode) }} ): {% else %} class {{ operation_group.class_name }}: {{ operation_group.pylint_disable() }} diff --git a/packages/http-client-python/generator/pygen/codegen/templates/vendor.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/utils.py.jinja2 similarity index 83% rename from packages/http-client-python/generator/pygen/codegen/templates/vendor.py.jinja2 rename to packages/http-client-python/generator/pygen/codegen/templates/utils.py.jinja2 index 42aad23e621..93c8da4f1de 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/vendor.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/utils.py.jinja2 @@ -5,20 +5,19 @@ {{ imports }} -{% if code_model.need_vendored_mixin(client_namespace) %} - {% for client in clients | selectattr("has_mixin") %} -{% set pylint_disable = "# pylint: disable=name-too-long" if (client.name | length) + ("MixinABC" | length) > 40 else "" %} -class {{ client.name }}MixinABC( {{ pylint_disable }} - ABC -): +{% if code_model.need_utils_mixin %} + +TClient = TypeVar("TClient") +TConfig = TypeVar("TConfig") + +class ClientMixinABC(ABC, Generic[TClient, TConfig]): """DO NOT use this class. It is for internal typing use only.""" - _client: "{{ keywords.async_class }}PipelineClient" - _config: {{ client.name }}Configuration + _client: TClient + _config: TConfig _serialize: "Serializer" _deserialize: "Deserializer" - {% endfor %} {% endif %} -{% if code_model.need_vendored_abstract(client_namespace) %} +{% if code_model.need_utils_abstract(client_namespace) %} def raise_if_not_implemented(cls, abstract_methods): not_implemented = [f for f in abstract_methods if not callable(getattr(cls, f, None))] @@ -29,7 +28,7 @@ def raise_if_not_implemented(cls, abstract_methods): ) {% endif %} -{% if code_model.need_vendored_etag(client_namespace) %} +{% if code_model.need_utils_etag(client_namespace) %} def quote_etag(etag: Optional[str]) -> Optional[str]: if not etag or etag == "*": return etag @@ -59,7 +58,7 @@ def prep_if_none_match(etag: Optional[str], match_condition: Optional[MatchCondi return "*" return None {% endif %} -{% if code_model.need_vendored_form_data(async_mode, client_namespace) %} +{% if code_model.need_utils_form_data(async_mode, client_namespace) %} # file-like tuple could be `(filename, IO (or bytes))` or `(filename, IO (or bytes), content_type)` FileContent = Union[str, bytes, IO[str], IO[bytes]] diff --git a/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_encode_bytes_async.py b/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_encode_bytes_async.py index f40eebf58ae..52612763836 100644 --- a/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_encode_bytes_async.py +++ b/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_encode_bytes_async.py @@ -97,6 +97,7 @@ async def test_header(client: BytesClient): ], ) + @pytest.mark.asyncio async def test_request_body(client: BytesClient, png_data: bytes): await client.request_body.default( diff --git a/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_nullable_async.py b/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_nullable_async.py index 77c6aed8b4a..a5dc1322110 100644 --- a/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_nullable_async.py +++ b/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_typetest_property_nullable_async.py @@ -8,7 +8,7 @@ import pytest from typetest.property.nullable import models from typetest.property.nullable.aio import NullableClient -from typetest.property.nullable._model_base import ( # pylint: disable=protected-access +from typetest.property.nullable._utils.model_base import ( # pylint: disable=protected-access SdkJSONEncoder, ) diff --git a/packages/http-client-python/generator/test/generic_mock_api_tests/test_encode_bytes.py b/packages/http-client-python/generator/test/generic_mock_api_tests/test_encode_bytes.py index 0b48756ab17..666f6d14fba 100644 --- a/packages/http-client-python/generator/test/generic_mock_api_tests/test_encode_bytes.py +++ b/packages/http-client-python/generator/test/generic_mock_api_tests/test_encode_bytes.py @@ -94,6 +94,7 @@ def test_header(client: BytesClient): ], ) + def test_request_body(client: BytesClient, png_data: bytes): client.request_body.default( value=png_data, diff --git a/packages/http-client-python/generator/test/generic_mock_api_tests/test_typetest_property_nullable.py b/packages/http-client-python/generator/test/generic_mock_api_tests/test_typetest_property_nullable.py index c69f89d4883..dcf3a44954a 100644 --- a/packages/http-client-python/generator/test/generic_mock_api_tests/test_typetest_property_nullable.py +++ b/packages/http-client-python/generator/test/generic_mock_api_tests/test_typetest_property_nullable.py @@ -7,7 +7,7 @@ import json import pytest from typetest.property.nullable import NullableClient, models -from typetest.property.nullable._model_base import ( # pylint: disable=protected-access +from typetest.property.nullable._utils.model_base import ( # pylint: disable=protected-access SdkJSONEncoder, ) diff --git a/packages/http-client-python/generator/test/unittests/conftest.py b/packages/http-client-python/generator/test/unittests/conftest.py index f85aad8ea70..9cabb68857c 100644 --- a/packages/http-client-python/generator/test/unittests/conftest.py +++ b/packages/http-client-python/generator/test/unittests/conftest.py @@ -6,6 +6,7 @@ import importlib import pytest + @pytest.fixture def core_library(): try: diff --git a/packages/http-client-python/generator/test/unittests/test_model_base_serialization.py b/packages/http-client-python/generator/test/unittests/test_model_base_serialization.py index b13e0b52dd2..0900f9103d4 100644 --- a/packages/http-client-python/generator/test/unittests/test_model_base_serialization.py +++ b/packages/http-client-python/generator/test/unittests/test_model_base_serialization.py @@ -26,7 +26,7 @@ import sys from enum import Enum -from specialwords._model_base import ( +from specialwords._utils.model_base import ( SdkJSONEncoder, Model, rest_field, diff --git a/packages/http-client-python/generator/test/unittests/test_model_base_xml_serialization.py b/packages/http-client-python/generator/test/unittests/test_model_base_xml_serialization.py index 2337da9d89a..78912e305b1 100644 --- a/packages/http-client-python/generator/test/unittests/test_model_base_xml_serialization.py +++ b/packages/http-client-python/generator/test/unittests/test_model_base_xml_serialization.py @@ -10,7 +10,7 @@ Dict, ) -from specialwords._model_base import ( +from specialwords._utils.model_base import ( _get_element, Model, rest_field,