From 86ac65530118393287102f74ec931b25debf563b Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Wed, 23 Jul 2025 16:58:27 -0400 Subject: [PATCH 01/15] initial attempt to take generation-subdir into accont --- .../http-client-python/emitter/src/lib.ts | 6 + .../generator/pygen/__init__.py | 1 + .../pygen/codegen/models/code_model.py | 36 ++++++ .../pygen/codegen/serializers/__init__.py | 109 +++++++----------- 4 files changed, 87 insertions(+), 65 deletions(-) diff --git a/packages/http-client-python/emitter/src/lib.ts b/packages/http-client-python/emitter/src/lib.ts index 834ab778cd4..9f49a6f2de2 100644 --- a/packages/http-client-python/emitter/src/lib.ts +++ b/packages/http-client-python/emitter/src/lib.ts @@ -86,6 +86,12 @@ export const PythonEmitterOptionsSchema: JSONSchemaType = description: "Whether to validate the versioning of the package. Defaults to `true`. If set to `false`, we will not validate the versioning of the package.", }, + "generation-subdir": { + type: "string", + nullable: true, + description: + "The subdirectory to generate the code in. If not specified, the code will be generated in the root folder. Note: if you're using this flag, you will need to add and maintain the versioning file yourself.", + }, }, required: [], }; diff --git a/packages/http-client-python/generator/pygen/__init__.py b/packages/http-client-python/generator/pygen/__init__.py index 8605553b2c4..ccfbd9f800a 100644 --- a/packages/http-client-python/generator/pygen/__init__.py +++ b/packages/http-client-python/generator/pygen/__init__.py @@ -39,6 +39,7 @@ class OptionsDict(MutableMapping): "polymorphic-examples": 5, "validate-versioning": True, "version-tolerant": True, + "generation-subdir": None, # subdirectory to generate the code in } def __init__(self, options: Optional[Dict[str, Any]] = None) -> None: 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 2d94fd74dab..24335641371 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 @@ -3,6 +3,7 @@ # Licensed under the MIT License. See License.txt in the project root for # license information. # -------------------------------------------------------------------------- +from pathlib import Path from typing import List, Dict, Any, Set, Union, Literal, Optional, cast from .base import BaseType @@ -452,3 +453,38 @@ def company_name(self) -> str: return self.yaml_data.get("licenseInfo", {}).get("company", "") # typespec azure case without custom license and swagger case return "Microsoft Corporation" + + def get_root_dir(self) -> Path: + if self.options["no-namespace-folders"] and not self.options["multiapi"]: + # when output folder contains parts different from the namespace, we fall back to current folder directly. + # (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md) + return Path(".") + return Path(*self.namespace.split(".")) + + def get_generation_dir(self, namespace: str) -> Path: + """The directory to generate the code in. If 'generation-subdir' is specified, it will be used as a subdirectory.""" + root_dir = self.get_root_dir() + return self._get_relative_generation_dir(root_dir, namespace) + + def get_samples_folder_generation_dir(self, namespace: str) -> Path: + # Same logic as get_generation_dir, but doesn't take `no-namespace-folders` into account + root_dir = Path(*self.namespace.split(".")) + return self._get_relative_generation_dir(root_dir, namespace) + + def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path: + if self.options.get("generation-subdir"): + # For the main namespace, return root_dir + generation-subdir + if namespace == self.namespace: + return root_dir / self.options['generation-subdir'] + + # For subnamespaces, extract the subnamespace part and append it to generation-subdir + if namespace.startswith(self.namespace + "."): + subnamespace_parts = namespace[len(self.namespace) + 1:].split(".") + return root_dir / self.options['generation-subdir'] / Path(*subnamespace_parts) + + # If namespace doesn't start with self.namespace, fall back to namespace path + namespace_path = Path(*namespace.split(".")) + return namespace_path / self.options['generation-subdir'] + + # No generation-subdir specified, use the namespace path directly + return Path(*namespace.split(".")) 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 d3a3766479a..666ef1b23b9 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -101,13 +101,13 @@ def keep_version_file(self) -> bool: return True # If the version file is already there and the version is greater than the current version, keep it. try: - serialized_version_file = self.read_file(self.exec_path(self.code_model.namespace) / "_version.py") + serialized_version_file = self.read_file(self.code_model.get_generation_dir(self.code_model.namespace) / "_version.py") match = re.search(r'VERSION\s*=\s*"([^"]+)"', str(serialized_version_file)) serialized_version = match.group(1) if match else "" except (FileNotFoundError, IndexError): serialized_version = "" return serialized_version > self.code_model.options.get("package-version", "") - + def serialize(self) -> None: env = Environment( loader=PackageLoader("pygen.codegen", "templates"), @@ -120,11 +120,11 @@ def serialize(self) -> None: general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False) for client_namespace, client_namespace_type in self.code_model.client_namespace_types.items(): - exec_path = self.exec_path(client_namespace) + generation_path = self.code_model.get_generation_dir(client_namespace) if client_namespace == "": # Write the setup file if self.code_model.options["basic-setup-py"]: - self.write_file(exec_path / Path("setup.py"), general_serializer.serialize_setup_file()) + self.write_file(generation_path / Path("setup.py"), general_serializer.serialize_setup_file()) # add packaging files in root namespace (e.g. setup.py, README.md, etc.) if self.code_model.options.get("package-mode"): @@ -133,7 +133,7 @@ def serialize(self) -> None: # write apiview-properties.json if self.code_model.options.get("emit-cross-language-definition-file"): self.write_file( - exec_path / Path("apiview-properties.json"), + generation_path / Path("apiview-properties.json"), general_serializer.serialize_cross_language_definition_file(), ) @@ -147,7 +147,7 @@ def serialize(self) -> None: # add _metadata.json if self.code_model.metadata: self.write_file( - exec_path / Path("_metadata.json"), + generation_path / Path("_metadata.json"), json.dumps(self.code_model.metadata, indent=2), ) elif client_namespace_type.clients: @@ -156,14 +156,14 @@ def serialize(self) -> None: else: # add pkgutil init file if no clients in this namespace self.write_file( - exec_path / Path("__init__.py"), + generation_path / Path("__init__.py"), general_serializer.serialize_pkgutil_init_file(), ) # _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) + self._serialize_and_write_top_level_folder(env=env) # add models folder if there are models in this namespace if ( @@ -178,7 +178,7 @@ def serialize(self) -> None: if not self.code_model.options["models-mode"]: # keep models file if users ended up just writing a models file - model_path = exec_path / Path("models.py") + model_path = generation_path / Path("models.py") if self.read_file(model_path): self.write_file(model_path, self.read_file(model_path)) @@ -188,18 +188,18 @@ def serialize(self) -> None: client_namespace_type.operation_groups, env=env, namespace=client_namespace ) if self.code_model.options["multiapi"]: - self._serialize_and_write_metadata(env=env, namespace=client_namespace) + self._serialize_and_write_metadata(env=env) # if there are only operations under this namespace, we need to add general __init__.py into `aio` folder # to make sure all generated files could be packed into .zip/.whl/.tgz package if not client_namespace_type.clients and client_namespace_type.operation_groups and self.has_aio_folder: self.write_file( - exec_path / Path("aio/__init__.py"), + generation_path / Path("aio/__init__.py"), general_serializer.serialize_pkgutil_init_file(), ) def _serialize_and_write_package_files(self, client_namespace: str) -> None: - root_of_sdk = self.exec_path(client_namespace) + root_of_sdk = Path(".") if self.code_model.options["package-mode"] in VALID_PACKAGE_MODE: env = Environment( loader=PackageLoader("pygen.codegen", "templates/packaging_templates"), @@ -249,7 +249,7 @@ def _serialize_and_write_models_folder( self, env: Environment, namespace: str, models: List[ModelType], enums: List[EnumType] ) -> None: # Write the models folder - models_path = self.exec_path(namespace) / "models" + models_path = self.code_model.get_generation_dir(namespace) / "models" serializer = DpgModelSerializer if self.code_model.options["models-mode"] == "dpg" else MsrestModelSerializer if self.code_model.has_non_json_models(models): self.write_file( @@ -317,7 +317,7 @@ def _serialize_and_write_operations_folder( self, operation_groups: List[OperationGroup], env: Environment, namespace: str ) -> None: operations_folder_name = self.code_model.operations_folder_name(namespace) - exec_path = self.exec_path(namespace) + generation_path = self.code_model.get_generation_dir(namespace) for async_mode, async_path in self.serialize_loop: prefix_path = f"{async_path}{operations_folder_name}" # write init file @@ -325,7 +325,7 @@ def _serialize_and_write_operations_folder( code_model=self.code_model, operation_groups=operation_groups, env=env, async_mode=async_mode ) self.write_file( - exec_path / Path(f"{prefix_path}/__init__.py"), + generation_path / Path(f"{prefix_path}/__init__.py"), operations_init_serializer.serialize(), ) @@ -344,26 +344,25 @@ def _serialize_and_write_operations_folder( client_namespace=namespace, ) self.write_file( - exec_path / Path(f"{prefix_path}/{filename}.py"), + generation_path / Path(f"{prefix_path}/{filename}.py"), operation_group_serializer.serialize(), ) # if there was a patch file before, we keep it - self._keep_patch_file(exec_path / Path(f"{prefix_path}/_patch.py"), env) + self._keep_patch_file(generation_path / Path(f"{prefix_path}/_patch.py"), env) def _serialize_and_write_version_file( self, - namespace: str, general_serializer: GeneralSerializer, ): - exec_path = self.exec_path(namespace) + generation_path = self.code_model.get_root_dir() def _read_version_file(original_version_file_name: str) -> str: - return self.read_file(exec_path / original_version_file_name) + return self.read_file(generation_path / original_version_file_name) def _write_version_file(original_version_file_name: str) -> None: self.write_file( - exec_path / Path("_version.py"), + generation_path / Path("_version.py"), _read_version_file(original_version_file_name), ) @@ -373,7 +372,7 @@ def _write_version_file(original_version_file_name: str) -> None: _write_version_file(original_version_file_name="version.py") elif self.code_model.options.get("package-version"): self.write_file( - exec_path / Path("_version.py"), + generation_path / Path("_version.py"), general_serializer.serialize_version_file(), ) @@ -383,47 +382,47 @@ def _serialize_client_and_config_files( clients: List[Client], env: Environment, ) -> None: - exec_path = self.exec_path(namespace) + generation_path = self.code_model.get_generation_dir(namespace) for async_mode, async_path in self.serialize_loop: general_serializer = GeneralSerializer( code_model=self.code_model, env=env, async_mode=async_mode, client_namespace=namespace ) # when there is client.py, there must be __init__.py self.write_file( - exec_path / Path(f"{async_path}__init__.py"), + generation_path / Path(f"{async_path}__init__.py"), general_serializer.serialize_init_file([c for c in clients if c.has_operations]), ) # if there was a patch file before, we keep it - self._keep_patch_file(exec_path / Path(f"{async_path}_patch.py"), env) + self._keep_patch_file(generation_path / Path(f"{async_path}_patch.py"), env) if self.code_model.clients_has_operations(clients): # write client file self.write_file( - exec_path / Path(f"{async_path}{self.code_model.client_filename}.py"), + generation_path / Path(f"{async_path}{self.code_model.client_filename}.py"), general_serializer.serialize_service_client_file(clients), ) # write config file self.write_file( - exec_path / Path(f"{async_path}_configuration.py"), + generation_path / Path(f"{async_path}_configuration.py"), general_serializer.serialize_config_file(clients), ) # sometimes we need define additional Mixin class for client in _utils.py - self._serialize_and_write_utils_folder(env, namespace) + self._serialize_and_write_utils_folder(env) - def _serialize_and_write_utils_folder(self, env: Environment, namespace: str) -> None: - exec_path = self.exec_path(namespace) + def _serialize_and_write_utils_folder(self, env: Environment) -> None: + root_dir = self.code_model.get_root_dir() 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): + utils_folder_path = root_dir / Path("_utils") + if self.code_model.need_utils_folder(async_mode=False, client_namespace=self.code_model.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): + if self.code_model.need_utils_utils(async_mode=False, client_namespace=self.code_model.namespace): self.write_file( utils_folder_path / Path("utils.py"), general_serializer.need_utils_utils_file(), @@ -442,56 +441,36 @@ def _serialize_and_write_utils_folder(self, env: Environment, namespace: str) -> 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) + def _serialize_and_write_top_level_folder(self, env: Environment) -> None: + root_dir = self.code_model.get_root_dir() # write _utils folder - self._serialize_and_write_utils_folder(env, namespace) + self._serialize_and_write_utils_folder(env) general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False) # write _version.py - self._serialize_and_write_version_file(namespace, general_serializer) + self._serialize_and_write_version_file(general_serializer) # write the empty py.typed file - self.write_file(exec_path / Path("py.typed"), "# Marker file for PEP 561.") + self.write_file(root_dir / Path("py.typed"), "# Marker file for PEP 561.") # 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( - exec_path / Path("_validation.py"), + root_dir / Path("_validation.py"), general_serializer.serialize_validation_file(), ) # write _types.py if self.code_model.named_unions: self.write_file( - exec_path / Path("_types.py"), + root_dir / Path("_types.py"), TypesSerializer(code_model=self.code_model, env=env).serialize(), ) - def _serialize_and_write_metadata(self, env: Environment, namespace: str) -> None: - metadata_serializer = MetadataSerializer(self.code_model, env, client_namespace=namespace) - self.write_file(self.exec_path(namespace) / Path("_metadata.json"), metadata_serializer.serialize()) - - @property - def exec_path_compensation(self) -> Path: - """Assume the process is running in the root folder of the package. If not, we need the path compensation.""" - return ( - Path("../" * (self.code_model.namespace.count(".") + 1)) - if self.code_model.options["no-namespace-folders"] - else Path(".") - ) - - def exec_path_for_test_sample(self, namespace: str) -> Path: - return self.exec_path_compensation / Path(*namespace.split(".")) - - # pylint: disable=line-too-long - def exec_path(self, namespace: str) -> Path: - if self.code_model.options["no-namespace-folders"] and not self.code_model.options["multiapi"]: - # when output folder contains parts different from the namespace, we fall back to current folder directly. - # (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md) - return Path(".") - return self.exec_path_compensation / Path(*namespace.split(".")) + def _serialize_and_write_metadata(self, env: Environment) -> None: + metadata_serializer = MetadataSerializer(self.code_model, env) + self.write_file(self.code_model.get_root_dir() / Path("_metadata.json"), metadata_serializer.serialize()) # pylint: disable=line-too-long @property @@ -512,7 +491,7 @@ def sample_additional_folder(self) -> Path: return Path("") def _serialize_and_write_sample(self, env: Environment, namespace: str): - out_path = self.exec_path_for_test_sample(namespace) / Path("generated_samples") + out_path = self.code_model.get_samples_folder_generation_dir(namespace) / Path("generated_samples") for client in self.code_model.clients: for op_group in client.operation_groups: for operation in op_group.operations: @@ -546,7 +525,7 @@ def _serialize_and_write_sample(self, env: Environment, namespace: str): def _serialize_and_write_test(self, env: Environment, namespace: str): self.code_model.for_test = True - out_path = self.exec_path_for_test_sample(namespace) / Path("generated_tests") + out_path = self.code_model.get_samples_folder_generation_dir(namespace) / Path("generated_tests") general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env) self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest()) if not self.code_model.options["azure-arm"]: From 3e8c8b16fec509c5e6efbee71c1c75249707819f Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Wed, 23 Jul 2025 17:27:21 -0400 Subject: [PATCH 02/15] utils folder for each namespace --- .../generator/pygen/codegen/serializers/__init__.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 666ef1b23b9..4d5dc3560b5 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -411,12 +411,12 @@ def _serialize_client_and_config_files( ) # sometimes we need define additional Mixin class for client in _utils.py - self._serialize_and_write_utils_folder(env) + self._serialize_and_write_utils_folder(env, namespace) - def _serialize_and_write_utils_folder(self, env: Environment) -> None: - root_dir = self.code_model.get_root_dir() + def _serialize_and_write_utils_folder(self, env: Environment, namespace: str) -> None: + generation_dir = self.code_model.get_generation_dir(namespace) general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False) - utils_folder_path = root_dir / Path("_utils") + utils_folder_path = generation_dir / Path("_utils") if self.code_model.need_utils_folder(async_mode=False, client_namespace=self.code_model.namespace): self.write_file( utils_folder_path / Path("__init__.py"), @@ -444,7 +444,7 @@ def _serialize_and_write_utils_folder(self, env: Environment) -> None: def _serialize_and_write_top_level_folder(self, env: Environment) -> None: root_dir = self.code_model.get_root_dir() # write _utils folder - self._serialize_and_write_utils_folder(env) + self._serialize_and_write_utils_folder(env, self.code_model.namespace) general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False) From 18fc2c9e775b6a1839a69380ebde070ab0b14a69 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Fri, 25 Jul 2025 14:48:53 -0400 Subject: [PATCH 03/15] debugging while finding errors --- .../generator/pygen/codegen/models/code_model.py | 6 +----- .../generator/pygen/codegen/serializers/__init__.py | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) 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 24335641371..19b13ff629f 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 @@ -474,17 +474,13 @@ def get_samples_folder_generation_dir(self, namespace: str) -> Path: def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path: if self.options.get("generation-subdir"): # For the main namespace, return root_dir + generation-subdir - if namespace == self.namespace: + if namespace == "" or namespace == self.namespace: return root_dir / self.options['generation-subdir'] # For subnamespaces, extract the subnamespace part and append it to generation-subdir if namespace.startswith(self.namespace + "."): subnamespace_parts = namespace[len(self.namespace) + 1:].split(".") return root_dir / self.options['generation-subdir'] / Path(*subnamespace_parts) - - # If namespace doesn't start with self.namespace, fall back to namespace path - namespace_path = Path(*namespace.split(".")) - return namespace_path / self.options['generation-subdir'] # No generation-subdir specified, use the namespace path directly return Path(*namespace.split(".")) 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 4d5dc3560b5..e021e4b33de 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -128,7 +128,7 @@ def serialize(self) -> None: # add packaging files in root namespace (e.g. setup.py, README.md, etc.) if self.code_model.options.get("package-mode"): - self._serialize_and_write_package_files(client_namespace) + self._serialize_and_write_package_files() # write apiview-properties.json if self.code_model.options.get("emit-cross-language-definition-file"): @@ -198,7 +198,7 @@ def serialize(self) -> None: general_serializer.serialize_pkgutil_init_file(), ) - def _serialize_and_write_package_files(self, client_namespace: str) -> None: + def _serialize_and_write_package_files(self) -> None: root_of_sdk = Path(".") if self.code_model.options["package-mode"] in VALID_PACKAGE_MODE: env = Environment( From ab317f127b3c0804b35acf19c33f7ae5f0824478 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Mon, 28 Jul 2025 14:18:36 -0400 Subject: [PATCH 04/15] get multiapi generation working --- .../generator/pygen/codegen/models/code_model.py | 7 ++++++- .../pygen/codegen/serializers/__init__.py | 16 ++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) 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 19b13ff629f..6228ad480f7 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 @@ -464,7 +464,12 @@ def get_root_dir(self) -> Path: def get_generation_dir(self, namespace: str) -> Path: """The directory to generate the code in. If 'generation-subdir' is specified, it will be used as a subdirectory.""" root_dir = self.get_root_dir() - return self._get_relative_generation_dir(root_dir, namespace) + retval = self._get_relative_generation_dir(root_dir, namespace) + # TODO: remove when we remove legacy multiapi generation + if self.options["multiapi"]: + compensation = Path("../" * (self.namespace.count(".") + 1)) + return compensation / retval + return retval def get_samples_folder_generation_dir(self, namespace: str) -> Path: # Same logic as get_generation_dir, but doesn't take `no-namespace-folders` into account 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 e021e4b33de..cec579461b1 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -163,7 +163,7 @@ def serialize(self) -> None: # _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) + self._serialize_and_write_top_level_folder(env=env, namespace=client_namespace) # add models folder if there are models in this namespace if ( @@ -188,7 +188,7 @@ def serialize(self) -> None: client_namespace_type.operation_groups, env=env, namespace=client_namespace ) if self.code_model.options["multiapi"]: - self._serialize_and_write_metadata(env=env) + self._serialize_and_write_metadata(env=env, namespace=client_namespace) # if there are only operations under this namespace, we need to add general __init__.py into `aio` folder # to make sure all generated files could be packed into .zip/.whl/.tgz package @@ -441,7 +441,7 @@ def _serialize_and_write_utils_folder(self, env: Environment, namespace: str) -> general_serializer.serialize_model_base_file(), ) - def _serialize_and_write_top_level_folder(self, env: Environment) -> None: + def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str) -> None: root_dir = self.code_model.get_root_dir() # write _utils folder self._serialize_and_write_utils_folder(env, self.code_model.namespace) @@ -452,7 +452,11 @@ def _serialize_and_write_top_level_folder(self, env: Environment) -> None: self._serialize_and_write_version_file(general_serializer) # write the empty py.typed file - self.write_file(root_dir / Path("py.typed"), "# Marker file for PEP 561.") + pytyped_value = "# Marker file for PEP 561." + # TODO: remove this when we remove legacy multiapi generation + if self.code_model.options["multiapi"]: + return self.write_file(self.code_model.get_generation_dir(namespace) / Path("py.typed"), pytyped_value) + self.write_file(root_dir / Path("py.typed"), pytyped_value) # write _validation.py if any(og for client in self.code_model.clients for og in client.operation_groups if og.need_validation): @@ -468,9 +472,9 @@ def _serialize_and_write_top_level_folder(self, env: Environment) -> None: TypesSerializer(code_model=self.code_model, env=env).serialize(), ) - def _serialize_and_write_metadata(self, env: Environment) -> None: + def _serialize_and_write_metadata(self, env: Environment, namespace: str) -> None: metadata_serializer = MetadataSerializer(self.code_model, env) - self.write_file(self.code_model.get_root_dir() / Path("_metadata.json"), metadata_serializer.serialize()) + self.write_file(self.code_model.get_generation_dir(namespace) / Path("_metadata.json"), metadata_serializer.serialize()) # pylint: disable=line-too-long @property From 42fbbbc3168644ee26b981422f9018bd60342ed3 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Mon, 28 Jul 2025 14:55:03 -0400 Subject: [PATCH 05/15] format and lint --- packages/http-client-python/CONTRIBUTING.md | 3 ++ .../pygen/codegen/models/code_model.py | 48 +++++++++---------- .../pygen/codegen/serializers/__init__.py | 12 +++-- 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/packages/http-client-python/CONTRIBUTING.md b/packages/http-client-python/CONTRIBUTING.md index 4ccf6b01766..9ec92304304 100644 --- a/packages/http-client-python/CONTRIBUTING.md +++ b/packages/http-client-python/CONTRIBUTING.md @@ -112,16 +112,19 @@ Due to the integration with `@azure-tools/typespec-python`, we require downstrea After your PR is created and CI passes: 1. **Get the build artifact URL**: + - In your PR's CI results, click on "5 published; 1 consumed" (or similar) - Navigate to: `Published artifacts` → `build_artifacts_python` → `packages` → `typespec-http-client-python-x.x.x.tgz` - Click the three dots and select "Copy download url" 2. **Trigger downstream testing**: + - Run [this pipeline](https://dev.azure.com/azure-sdk/internal/_build/results?buildId=4278466&view=results) with: - `PULL-REQUEST-URL`: Your PR URL from step 1 - `ARTIFACTS-URL`: The artifact URL from step 1 3. **Review downstream changes**: + - The pipeline will create a PR in [autorest.python](https://github.com/Azure/autorest.python) - Follow the [autorest.python CONTRIBUTING.md](https://github.com/Azure/autorest.python/blob/main/CONTRIBUTING.md) for any additional changes needed 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 6228ad480f7..8851213bbdc 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 @@ -453,24 +453,24 @@ def company_name(self) -> str: return self.yaml_data.get("licenseInfo", {}).get("company", "") # typespec azure case without custom license and swagger case return "Microsoft Corporation" - + def get_root_dir(self) -> Path: - if self.options["no-namespace-folders"] and not self.options["multiapi"]: - # when output folder contains parts different from the namespace, we fall back to current folder directly. - # (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md) - return Path(".") - return Path(*self.namespace.split(".")) + if self.options["no-namespace-folders"] and not self.options["multiapi"]: + # when output folder contains parts different from the namespace, we fall back to current folder directly. + # (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md) + return Path(".") + return Path(*self.namespace.split(".")) def get_generation_dir(self, namespace: str) -> Path: - """The directory to generate the code in. If 'generation-subdir' is specified, it will be used as a subdirectory.""" - root_dir = self.get_root_dir() - retval = self._get_relative_generation_dir(root_dir, namespace) - # TODO: remove when we remove legacy multiapi generation - if self.options["multiapi"]: - compensation = Path("../" * (self.namespace.count(".") + 1)) - return compensation / retval - return retval - + """The directory to generate the code in. If 'generation-subdir' is specified, it will be used as a subdirectory.""" + root_dir = self.get_root_dir() + retval = self._get_relative_generation_dir(root_dir, namespace) + # TODO: remove when we remove legacy multiapi generation + if self.options["multiapi"]: + compensation = Path("../" * (self.namespace.count(".") + 1)) + return compensation / retval + return retval + def get_samples_folder_generation_dir(self, namespace: str) -> Path: # Same logic as get_generation_dir, but doesn't take `no-namespace-folders` into account root_dir = Path(*self.namespace.split(".")) @@ -478,14 +478,14 @@ def get_samples_folder_generation_dir(self, namespace: str) -> Path: def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path: if self.options.get("generation-subdir"): - # For the main namespace, return root_dir + generation-subdir - if namespace == "" or namespace == self.namespace: - return root_dir / self.options['generation-subdir'] - - # For subnamespaces, extract the subnamespace part and append it to generation-subdir - if namespace.startswith(self.namespace + "."): - subnamespace_parts = namespace[len(self.namespace) + 1:].split(".") - return root_dir / self.options['generation-subdir'] / Path(*subnamespace_parts) - + # For the main namespace, return root_dir + generation-subdir + if namespace == "" or namespace == self.namespace: + return root_dir / self.options["generation-subdir"] + + # For subnamespaces, extract the subnamespace part and append it to generation-subdir + if namespace.startswith(self.namespace + "."): + subnamespace_parts = namespace[len(self.namespace) + 1 :].split(".") + return root_dir / self.options["generation-subdir"] / Path(*subnamespace_parts) + # No generation-subdir specified, use the namespace path directly return Path(*namespace.split(".")) 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 cec579461b1..5577b62dd3e 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -101,13 +101,15 @@ def keep_version_file(self) -> bool: return True # If the version file is already there and the version is greater than the current version, keep it. try: - serialized_version_file = self.read_file(self.code_model.get_generation_dir(self.code_model.namespace) / "_version.py") + serialized_version_file = self.read_file( + self.code_model.get_generation_dir(self.code_model.namespace) / "_version.py" + ) match = re.search(r'VERSION\s*=\s*"([^"]+)"', str(serialized_version_file)) serialized_version = match.group(1) if match else "" except (FileNotFoundError, IndexError): serialized_version = "" return serialized_version > self.code_model.options.get("package-version", "") - + def serialize(self) -> None: env = Environment( loader=PackageLoader("pygen.codegen", "templates"), @@ -413,7 +415,7 @@ def _serialize_client_and_config_files( # sometimes we need define additional Mixin class for client in _utils.py self._serialize_and_write_utils_folder(env, namespace) - def _serialize_and_write_utils_folder(self, env: Environment, namespace: str) -> None: + def _serialize_and_write_utils_folder(self, env: Environment, namespace: str): generation_dir = self.code_model.get_generation_dir(namespace) general_serializer = GeneralSerializer(code_model=self.code_model, env=env, async_mode=False) utils_folder_path = generation_dir / Path("_utils") @@ -474,7 +476,9 @@ def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str def _serialize_and_write_metadata(self, env: Environment, namespace: str) -> None: metadata_serializer = MetadataSerializer(self.code_model, env) - self.write_file(self.code_model.get_generation_dir(namespace) / Path("_metadata.json"), metadata_serializer.serialize()) + self.write_file( + self.code_model.get_generation_dir(namespace) / Path("_metadata.json"), metadata_serializer.serialize() + ) # pylint: disable=line-too-long @property From 8b47a62da5b53f5b9685aae860043bb2276cab2c Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Mon, 28 Jul 2025 14:57:03 -0400 Subject: [PATCH 06/15] add pytyped to cspell --- cspell.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/cspell.yaml b/cspell.yaml index 68ffdaf3a0f..b00b53a2ec6 100644 --- a/cspell.yaml +++ b/cspell.yaml @@ -199,6 +199,7 @@ words: - pyright - pyrightconfig - pytest + - pytyped - pyyaml - rcfile - reactivex From cbcc03b0f9d9194b568f031eafd3bd1aaf931776 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Mon, 28 Jul 2025 15:01:04 -0400 Subject: [PATCH 07/15] revert changes to contributing file --- packages/http-client-python/CONTRIBUTING.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/http-client-python/CONTRIBUTING.md b/packages/http-client-python/CONTRIBUTING.md index 9ec92304304..4ccf6b01766 100644 --- a/packages/http-client-python/CONTRIBUTING.md +++ b/packages/http-client-python/CONTRIBUTING.md @@ -112,19 +112,16 @@ Due to the integration with `@azure-tools/typespec-python`, we require downstrea After your PR is created and CI passes: 1. **Get the build artifact URL**: - - In your PR's CI results, click on "5 published; 1 consumed" (or similar) - Navigate to: `Published artifacts` → `build_artifacts_python` → `packages` → `typespec-http-client-python-x.x.x.tgz` - Click the three dots and select "Copy download url" 2. **Trigger downstream testing**: - - Run [this pipeline](https://dev.azure.com/azure-sdk/internal/_build/results?buildId=4278466&view=results) with: - `PULL-REQUEST-URL`: Your PR URL from step 1 - `ARTIFACTS-URL`: The artifact URL from step 1 3. **Review downstream changes**: - - The pipeline will create a PR in [autorest.python](https://github.com/Azure/autorest.python) - Follow the [autorest.python CONTRIBUTING.md](https://github.com/Azure/autorest.python/blob/main/CONTRIBUTING.md) for any additional changes needed From de7299af4355ebf3db3919a6c9551bf5e0ea9bfb Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Mon, 28 Jul 2025 15:12:28 -0400 Subject: [PATCH 08/15] fix pylint again --- .../generator/pygen/codegen/models/code_model.py | 8 +++++--- .../generator/pygen/codegen/serializers/__init__.py | 5 +++-- 2 files changed, 8 insertions(+), 5 deletions(-) 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 8851213bbdc..fb3cca8fc3e 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 @@ -457,12 +457,14 @@ def company_name(self) -> str: def get_root_dir(self) -> Path: if self.options["no-namespace-folders"] and not self.options["multiapi"]: # when output folder contains parts different from the namespace, we fall back to current folder directly. - # (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md) return Path(".") return Path(*self.namespace.split(".")) def get_generation_dir(self, namespace: str) -> Path: - """The directory to generate the code in. If 'generation-subdir' is specified, it will be used as a subdirectory.""" + """The directory to generate the code in. + + If 'generation-subdir' is specified, it will be used as a subdirectory. + """ root_dir = self.get_root_dir() retval = self._get_relative_generation_dir(root_dir, namespace) # TODO: remove when we remove legacy multiapi generation @@ -479,7 +481,7 @@ def get_samples_folder_generation_dir(self, namespace: str) -> Path: def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path: if self.options.get("generation-subdir"): # For the main namespace, return root_dir + generation-subdir - if namespace == "" or namespace == self.namespace: + if namespace in ("", self.namespace): return root_dir / self.options["generation-subdir"] # For subnamespaces, extract the subnamespace part and append it to generation-subdir 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 5577b62dd3e..c75db7a8879 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -457,8 +457,9 @@ def _serialize_and_write_top_level_folder(self, env: Environment, namespace: str pytyped_value = "# Marker file for PEP 561." # TODO: remove this when we remove legacy multiapi generation if self.code_model.options["multiapi"]: - return self.write_file(self.code_model.get_generation_dir(namespace) / Path("py.typed"), pytyped_value) - self.write_file(root_dir / Path("py.typed"), pytyped_value) + self.write_file(self.code_model.get_generation_dir(namespace) / Path("py.typed"), pytyped_value) + else: + self.write_file(root_dir / Path("py.typed"), pytyped_value) # write _validation.py if any(og for client in self.code_model.clients for og in client.operation_groups if og.need_validation): From 32166cc35f68a93c576060620fc31a102e6221dd Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Mon, 28 Jul 2025 15:22:00 -0400 Subject: [PATCH 09/15] add changesetg --- .chronus/changes/python-addSubdir-2025-6-28-15-21-55.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .chronus/changes/python-addSubdir-2025-6-28-15-21-55.md diff --git a/.chronus/changes/python-addSubdir-2025-6-28-15-21-55.md b/.chronus/changes/python-addSubdir-2025-6-28-15-21-55.md new file mode 100644 index 00000000000..e91fff9eb8f --- /dev/null +++ b/.chronus/changes/python-addSubdir-2025-6-28-15-21-55.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@typespec/http-client-python" +--- + +add `generation-subdir` flag \ No newline at end of file From 506708d83b512c85917624f15a30eee11b0fd862 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Tue, 29 Jul 2025 14:28:21 -0400 Subject: [PATCH 10/15] try with compensation for samples --- .../generator/pygen/codegen/models/code_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fb3cca8fc3e..6bb7c889475 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 @@ -476,7 +476,7 @@ def get_generation_dir(self, namespace: str) -> Path: def get_samples_folder_generation_dir(self, namespace: str) -> Path: # Same logic as get_generation_dir, but doesn't take `no-namespace-folders` into account root_dir = Path(*self.namespace.split(".")) - return self._get_relative_generation_dir(root_dir, namespace) + return Path("../" * (self.namespace.count(".") + 1)) / self._get_relative_generation_dir(root_dir, namespace) def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path: if self.options.get("generation-subdir"): From 5ac74c2e608b1680e6c255331e0dbae9a2ad0072 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Thu, 31 Jul 2025 16:22:19 -0400 Subject: [PATCH 11/15] temp --- .../generator/pygen/codegen/models/code_model.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 6bb7c889475..e9da9520ec4 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 @@ -467,8 +467,7 @@ def get_generation_dir(self, namespace: str) -> Path: """ root_dir = self.get_root_dir() retval = self._get_relative_generation_dir(root_dir, namespace) - # TODO: remove when we remove legacy multiapi generation - if self.options["multiapi"]: + if self.options["no-namespace-folders"]: compensation = Path("../" * (self.namespace.count(".") + 1)) return compensation / retval return retval From dbc65a07d3bb0aca4557c6473d546ae07629108b Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Fri, 1 Aug 2025 14:47:36 -0400 Subject: [PATCH 12/15] get azure-mgmt-test working --- .../generator/pygen/codegen/serializers/__init__.py | 3 +++ 1 file changed, 3 insertions(+) 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 c75db7a8879..2346ae82f6f 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -202,6 +202,9 @@ def serialize(self) -> None: def _serialize_and_write_package_files(self) -> None: root_of_sdk = Path(".") + if self.code_model.options["no-namespace-folders"]: + compensation = Path("../" * (self.code_model.namespace.count(".") + 1)) + root_of_sdk = root_of_sdk / compensation if self.code_model.options["package-mode"] in VALID_PACKAGE_MODE: env = Environment( loader=PackageLoader("pygen.codegen", "templates/packaging_templates"), From 59a855d59f63562202cce368218d699b87e1fd9d Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Fri, 1 Aug 2025 15:53:38 -0400 Subject: [PATCH 13/15] get samples spec passing --- .../generator/pygen/codegen/models/code_model.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 634c7d44264..8b95e8ca48d 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 @@ -455,7 +455,7 @@ def company_name(self) -> str: return "Microsoft Corporation" def get_root_dir(self) -> Path: - if self.options["no-namespace-folders"] and not self.options["multiapi"] and not self.options["azure-arm"]: + if self.options["no-namespace-folders"]: # when output folder contains parts different from the namespace, we fall back to current folder directly. return Path(".") return Path(*self.namespace.split(".")) @@ -467,9 +467,6 @@ def get_generation_dir(self, namespace: str) -> Path: """ root_dir = self.get_root_dir() retval = self._get_relative_generation_dir(root_dir, namespace) - if self.options["no-namespace-folders"]: - compensation = Path("../" * (self.namespace.count(".") + 1)) - return compensation / retval return retval def get_samples_folder_generation_dir(self, namespace: str) -> Path: @@ -478,6 +475,8 @@ def get_samples_folder_generation_dir(self, namespace: str) -> Path: return Path("../" * (self.namespace.count(".") + 1)) / self._get_relative_generation_dir(root_dir, namespace) def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path: + if self.options["no-namespace-folders"]: + return Path(".") if self.options.get("generation-subdir"): # For the main namespace, return root_dir + generation-subdir if namespace in ("", self.namespace): From 55329f29e7f478ac0885d74b4638307b3da1d334 Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Sun, 3 Aug 2025 19:52:24 -0400 Subject: [PATCH 14/15] fix generated_tests generation dir --- .../generator/pygen/codegen/models/code_model.py | 5 ----- .../generator/pygen/codegen/serializers/__init__.py | 4 ++-- 2 files changed, 2 insertions(+), 7 deletions(-) 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 8b95e8ca48d..6880472200c 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 @@ -469,11 +469,6 @@ def get_generation_dir(self, namespace: str) -> Path: retval = self._get_relative_generation_dir(root_dir, namespace) return retval - def get_samples_folder_generation_dir(self, namespace: str) -> Path: - # Same logic as get_generation_dir, but doesn't take `no-namespace-folders` into account - root_dir = Path(*self.namespace.split(".")) - return Path("../" * (self.namespace.count(".") + 1)) / self._get_relative_generation_dir(root_dir, namespace) - def _get_relative_generation_dir(self, root_dir: Path, namespace: str) -> Path: if self.options["no-namespace-folders"]: return Path(".") 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 44b7d24e96e..138405b6ee3 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -503,7 +503,7 @@ def sample_additional_folder(self) -> Path: return Path("") def _serialize_and_write_sample(self, env: Environment, namespace: str): - out_path = self.code_model.get_samples_folder_generation_dir(namespace) / Path("generated_samples") + out_path = self.code_model.get_generation_dir(namespace) / Path("generated_samples") for client in self.code_model.clients: for op_group in client.operation_groups: for operation in op_group.operations: @@ -537,7 +537,7 @@ def _serialize_and_write_sample(self, env: Environment, namespace: str): def _serialize_and_write_test(self, env: Environment, namespace: str): self.code_model.for_test = True - out_path = self.code_model.get_samples_folder_generation_dir(namespace) / Path("generated_tests") + out_path = self.code_model.get_generation_dir(namespace) / Path("generated_tests") general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env) self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest()) if not self.code_model.options["azure-arm"]: From 9232cbf85f6a6f5afd59f36ad4de49e4d8431a0d Mon Sep 17 00:00:00 2001 From: iscai-msft Date: Mon, 4 Aug 2025 12:54:11 -0400 Subject: [PATCH 15/15] switch to root --- .../generator/pygen/codegen/serializers/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 138405b6ee3..63c4814b2b9 100644 --- a/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py +++ b/packages/http-client-python/generator/pygen/codegen/serializers/__init__.py @@ -142,9 +142,9 @@ def serialize(self) -> None: # add generated samples and generated tests if self.code_model.options["show-operations"] and self.code_model.has_operations: if self.code_model.options["generate-sample"]: - self._serialize_and_write_sample(env, namespace=client_namespace) + self._serialize_and_write_sample(env) if self.code_model.options["generate-test"]: - self._serialize_and_write_test(env, namespace=client_namespace) + self._serialize_and_write_test(env) # add _metadata.json if self.code_model.metadata: @@ -502,8 +502,8 @@ def sample_additional_folder(self) -> Path: return Path("/".join(namespace_config.split(".")[num_of_package_namespace:])) return Path("") - def _serialize_and_write_sample(self, env: Environment, namespace: str): - out_path = self.code_model.get_generation_dir(namespace) / Path("generated_samples") + def _serialize_and_write_sample(self, env: Environment): + out_path = Path("./generated_samples") for client in self.code_model.clients: for op_group in client.operation_groups: for operation in op_group.operations: @@ -535,9 +535,9 @@ def _serialize_and_write_sample(self, env: Environment, namespace: str): log_error = f"error happens in sample {file}: {e}" _LOGGER.error(log_error) - def _serialize_and_write_test(self, env: Environment, namespace: str): + def _serialize_and_write_test(self, env: Environment): self.code_model.for_test = True - out_path = self.code_model.get_generation_dir(namespace) / Path("generated_tests") + out_path = Path("./generated_tests") general_serializer = TestGeneralSerializer(code_model=self.code_model, env=env) self.write_file(out_path / "conftest.py", general_serializer.serialize_conftest()) if not self.code_model.options["azure-arm"]: