@typespec/http-client-python 0.8.1 → 0.9.0-dev.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.
Files changed (44) hide show
  1. package/dist/emitter/code-model.d.ts.map +1 -1
  2. package/dist/emitter/code-model.js +2 -2
  3. package/dist/emitter/code-model.js.map +1 -1
  4. package/dist/emitter/emitter.d.ts.map +1 -1
  5. package/dist/emitter/emitter.js +9 -5
  6. package/dist/emitter/emitter.js.map +1 -1
  7. package/dist/emitter/lib.d.ts +11 -2
  8. package/dist/emitter/lib.d.ts.map +1 -1
  9. package/dist/emitter/lib.js +7 -1
  10. package/dist/emitter/lib.js.map +1 -1
  11. package/dist/emitter/utils.d.ts +1 -0
  12. package/dist/emitter/utils.d.ts.map +1 -1
  13. package/dist/emitter/utils.js +31 -16
  14. package/dist/emitter/utils.js.map +1 -1
  15. package/emitter/src/code-model.ts +3 -13
  16. package/emitter/src/emitter.ts +9 -7
  17. package/emitter/src/lib.ts +8 -4
  18. package/emitter/src/utils.ts +32 -18
  19. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  20. package/eng/scripts/ci/regenerate.ts +96 -41
  21. package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
  22. package/generator/build/lib/pygen/codegen/__init__.py +0 -2
  23. package/generator/build/lib/pygen/codegen/models/code_model.py +5 -5
  24. package/generator/build/lib/pygen/codegen/serializers/__init__.py +18 -14
  25. package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +5 -2
  26. package/generator/build/lib/pygen/codegen/serializers/test_serializer.py +1 -1
  27. package/generator/build/lib/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +6 -2
  28. package/generator/build/lib/pygen/codegen/templates/packaging_templates/README.md.jinja2 +5 -5
  29. package/generator/build/lib/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +3 -3
  30. package/generator/build/lib/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +14 -5
  31. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  32. package/generator/pygen/codegen/__init__.py +0 -2
  33. package/generator/pygen/codegen/models/code_model.py +5 -5
  34. package/generator/pygen/codegen/serializers/__init__.py +18 -14
  35. package/generator/pygen/codegen/serializers/general_serializer.py +5 -2
  36. package/generator/pygen/codegen/serializers/test_serializer.py +1 -1
  37. package/generator/pygen/codegen/templates/packaging_templates/MANIFEST.in.jinja2 +6 -2
  38. package/generator/pygen/codegen/templates/packaging_templates/README.md.jinja2 +5 -5
  39. package/generator/pygen/codegen/templates/packaging_templates/dev_requirements.txt.jinja2 +3 -3
  40. package/generator/pygen/codegen/templates/packaging_templates/setup.py.jinja2 +14 -5
  41. package/generator/pygen.egg-info/PKG-INFO +2 -2
  42. package/generator/pygen.egg-info/requires.txt +2 -2
  43. package/generator/setup.py +2 -2
  44. package/package.json +4 -4
@@ -38,96 +38,171 @@ interface TspCommand {
38
38
  command: string;
39
39
  }
40
40
 
41
+ const AZURE_EMITTER_OPTIONS: Record<string, Record<string, string> | Record<string, string>[]> = {
42
+ "azure/example/basic": {
43
+ namespace: "specs.azure.example.basic",
44
+ },
45
+ "azure/client-generator-core/access": {
46
+ namespace: "specs.azure.clientgenerator.core.access",
47
+ },
48
+ "azure/client-generator-core/usage": {
49
+ namespace: "specs.azure.clientgenerator.core.usage",
50
+ },
51
+ "azure/core/lro/rpc": {
52
+ "package-name": "azurecore-lro-rpc",
53
+ namespace: "azurecore.lro.rpc",
54
+ },
55
+ "client/structure/default": {
56
+ namespace: "client.structure.service",
57
+ },
58
+ "client/structure/multi-client": {
59
+ "package-name": "client-structure-multiclient",
60
+ namespace: "client.structure.multiclient",
61
+ },
62
+ "client/structure/renamed-operation": {
63
+ "package-name": "client-structure-renamedoperation",
64
+ namespace: "client.structure.renamedoperation",
65
+ },
66
+ "client/structure/two-operation-group": {
67
+ "package-name": "client-structure-twooperationgroup",
68
+ namespace: "client.structure.twooperationgroup",
69
+ },
70
+ "client/naming": {
71
+ namespace: "client.naming",
72
+ },
73
+ "encode/duration": {
74
+ namespace: "encode.duration",
75
+ },
76
+ "encode/numeric": {
77
+ namespace: "encode.numeric",
78
+ },
79
+ "parameters/basic": {
80
+ namespace: "parameters.basic",
81
+ },
82
+ "parameters/spread": {
83
+ namespace: "parameters.spread",
84
+ },
85
+ "payload/content-negotiation": {
86
+ namespace: "payload.contentnegotiation",
87
+ },
88
+ "payload/multipart": {
89
+ namespace: "payload.multipart",
90
+ },
91
+ "serialization/encoded-name/json": {
92
+ namespace: "serialization.encodedname.json",
93
+ },
94
+ "special-words": {
95
+ namespace: "specialwords",
96
+ },
97
+ };
98
+
41
99
  const EMITTER_OPTIONS: Record<string, Record<string, string> | Record<string, string>[]> = {
42
100
  "resiliency/srv-driven/old.tsp": {
43
101
  "package-name": "resiliency-srv-driven1",
102
+ namespace: "resiliency.srv.driven1",
44
103
  "package-mode": "azure-dataplane",
45
104
  "package-pprint-name": "ResiliencySrvDriven1",
46
105
  },
47
106
  "resiliency/srv-driven": {
48
107
  "package-name": "resiliency-srv-driven2",
108
+ namespace: "resiliency.srv.driven2",
49
109
  "package-mode": "azure-dataplane",
50
110
  "package-pprint-name": "ResiliencySrvDriven2",
51
111
  },
52
112
  "authentication/http/custom": {
53
113
  "package-name": "authentication-http-custom",
114
+ namespace: "authentication.http.custom",
54
115
  "package-pprint-name": "Authentication Http Custom",
55
116
  },
56
117
  "authentication/union": {
57
118
  "package-name": "authentication-union",
119
+ namespace: "authentication.union",
58
120
  },
59
121
  "type/array": {
60
122
  "package-name": "typetest-array",
123
+ namespace: "typetest.array",
61
124
  "use-pyodide": "true",
62
125
  },
63
126
  "type/dictionary": {
64
127
  "package-name": "typetest-dictionary",
128
+ namespace: "typetest.dictionary",
65
129
  },
66
130
  "type/enum/extensible": {
67
131
  "package-name": "typetest-enum-extensible",
132
+ namespace: "typetest.enum.extensible",
68
133
  },
69
134
  "type/enum/fixed": {
70
135
  "package-name": "typetest-enum-fixed",
136
+ namespace: "typetest.enum.fixed",
71
137
  },
72
138
  "type/model/empty": {
73
139
  "package-name": "typetest-model-empty",
140
+ namespace: "typetest.model.empty",
74
141
  },
75
142
  "type/model/inheritance/enum-discriminator": {
76
143
  "package-name": "typetest-model-enumdiscriminator",
144
+ namespace: "typetest.model.enumdiscriminator",
77
145
  },
78
146
  "type/model/inheritance/nested-discriminator": {
79
147
  "package-name": "typetest-model-nesteddiscriminator",
148
+ namespace: "typetest.model.nesteddiscriminator",
80
149
  },
81
150
  "type/model/inheritance/not-discriminated": {
82
151
  "package-name": "typetest-model-notdiscriminated",
152
+ namespace: "typetest.model.notdiscriminated",
83
153
  },
84
154
  "type/model/inheritance/single-discriminator": {
85
155
  "package-name": "typetest-model-singlediscriminator",
156
+ namespace: "typetest.model.singlediscriminator",
86
157
  },
87
158
  "type/model/inheritance/recursive": {
88
159
  "package-name": "typetest-model-recursive",
160
+ namespace: "typetest.model.recursive",
89
161
  "use-pyodide": "true",
90
162
  },
91
163
  "type/model/usage": {
92
164
  "package-name": "typetest-model-usage",
165
+ namespace: "typetest.model.usage",
93
166
  },
94
167
  "type/model/visibility": [
95
- { "package-name": "typetest-model-visibility" },
96
- { "package-name": "headasbooleantrue", "head-as-boolean": "true" },
97
- { "package-name": "headasbooleanfalse", "head-as-boolean": "false" },
168
+ {
169
+ "package-name": "typetest-model-visibility",
170
+ namespace: "typetest.model.visibility",
171
+ },
172
+ {
173
+ "package-name": "headasbooleantrue",
174
+ namespace: "headasbooleantrue",
175
+ "head-as-boolean": "true",
176
+ },
177
+ {
178
+ "package-name": "headasbooleanfalse",
179
+ namespace: "headasbooleanfalse",
180
+ "head-as-boolean": "false",
181
+ },
98
182
  ],
99
183
  "type/property/nullable": {
100
184
  "package-name": "typetest-property-nullable",
185
+ namespace: "typetest.property.nullable",
101
186
  },
102
187
  "type/property/optionality": {
103
188
  "package-name": "typetest-property-optional",
189
+ namespace: "typetest.property.optional",
104
190
  },
105
191
  "type/property/additional-properties": {
106
192
  "package-name": "typetest-property-additionalproperties",
193
+ namespace: "typetest.property.additionalproperties",
107
194
  },
108
195
  "type/scalar": {
109
196
  "package-name": "typetest-scalar",
197
+ namespace: "typetest.scalar",
110
198
  },
111
199
  "type/property/value-types": {
112
200
  "package-name": "typetest-property-valuetypes",
201
+ namespace: "typetest.property.valuetypes",
113
202
  },
114
203
  "type/union": {
115
204
  "package-name": "typetest-union",
116
- },
117
- "azure/core/lro/rpc": {
118
- "package-name": "azurecore-lro-rpc",
119
- },
120
- "client/structure/multi-client": {
121
- "package-name": "client-structure-multiclient",
122
- },
123
- "client/structure/renamed-operation": {
124
- "package-name": "client-structure-renamedoperation",
125
- },
126
- "client/structure/two-operation-group": {
127
- "package-name": "client-structure-twooperationgroup",
128
- },
129
- "client/namespace": {
130
- "enable-typespec-namespace": "true",
205
+ namespace: "typetest.union",
131
206
  },
132
207
  };
133
208
 
@@ -141,29 +216,9 @@ function getEmitterOption(spec: string, flavor: string): Record<string, string>[
141
216
  const key = relativeSpec.includes("resiliency/srv-driven/old.tsp")
142
217
  ? relativeSpec
143
218
  : dirname(relativeSpec);
144
- const emitter_options = EMITTER_OPTIONS[key] || [{}];
145
- const result = Array.isArray(emitter_options) ? emitter_options : [emitter_options];
146
-
147
- function updateOptions(options: Record<string, string>): void {
148
- if (options["package-name"] && options["enable-typespec-namespace"] === undefined) {
149
- options["enable-typespec-namespace"] = "false";
150
- }
151
- }
152
-
153
- // when package name is different with typespec namespace, disable typespec namespace
154
- if (flavor !== "azure") {
155
- for (const options of result) {
156
- if (Array.isArray(options)) {
157
- for (const option of options) {
158
- updateOptions(option);
159
- }
160
- } else {
161
- updateOptions(options);
162
- }
163
- }
164
- }
165
-
166
- return result;
219
+ const emitter_options = EMITTER_OPTIONS[key] ||
220
+ (flavor === "azure" ? AZURE_EMITTER_OPTIONS[key] : [{}]) || [{}];
221
+ return Array.isArray(emitter_options) ? emitter_options : [emitter_options];
167
222
  }
168
223
 
169
224
  // Function to execute CLI commands asynchronously
@@ -40,7 +40,6 @@ class OptionsRetriever:
40
40
  "generate-test": False,
41
41
  "from-typespec": False,
42
42
  "emit-cross-language-definition-file": False,
43
- "enable-typespec-namespace": False,
44
43
  }
45
44
 
46
45
  @property
@@ -317,7 +316,6 @@ class CodeGenerator(Plugin):
317
316
  "flavor",
318
317
  "company_name",
319
318
  "emit_cross_language_definition_file",
320
- "enable_typespec_namespace",
321
319
  ]
322
320
  return {f: getattr(self.options_retriever, f) for f in flags}
323
321
 
@@ -145,12 +145,8 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
145
145
  return result
146
146
  return f"{result}{module_name}" if result.endswith(".") else f"{result}.{module_name}"
147
147
 
148
- @property
149
- def need_unique_model_alias(self) -> bool:
150
- return self.has_subnamespace and self.options["enable_typespec_namespace"]
151
-
152
148
  def get_unique_models_alias(self, serialize_namespace: str, imported_namespace: str) -> str:
153
- if not self.need_unique_model_alias:
149
+ if not self.has_subnamespace:
154
150
  return "_models"
155
151
  relative_path = self.get_relative_import_path(
156
152
  serialize_namespace, self.get_imported_namespace_for_model(imported_namespace)
@@ -406,3 +402,7 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
406
402
  @staticmethod
407
403
  def has_non_json_models(models: List[ModelType]) -> bool:
408
404
  return any(m for m in models if m.base != "json")
405
+
406
+ @property
407
+ def is_tsp(self) -> bool:
408
+ return self.options.get("tsp_file") is not None
@@ -197,6 +197,8 @@ class JinjaSerializer(ReaderAndWriter):
197
197
  env = Environment(
198
198
  loader=PackageLoader("pygen.codegen", "templates/packaging_templates"),
199
199
  undefined=StrictUndefined,
200
+ trim_blocks=True,
201
+ lstrip_blocks=True,
200
202
  )
201
203
 
202
204
  package_files = _PACKAGE_FILES
@@ -460,21 +462,11 @@ class JinjaSerializer(ReaderAndWriter):
460
462
  metadata_serializer = MetadataSerializer(self.code_model, env, client_namespace=namespace)
461
463
  self.write_file(self.exec_path(namespace) / Path("_metadata.json"), metadata_serializer.serialize())
462
464
 
463
- @property
464
- def _namespace_from_package_name(self) -> str:
465
- return get_namespace_from_package_name(self.code_model.options["package_name"])
466
-
467
- def _name_space(self) -> str:
468
- if self.code_model.namespace.count(".") >= self._namespace_from_package_name.count("."):
469
- return self.code_model.namespace
470
-
471
- return self._namespace_from_package_name
472
-
473
465
  @property
474
466
  def exec_path_compensation(self) -> Path:
475
467
  """Assume the process is running in the root folder of the package. If not, we need the path compensation."""
476
468
  return (
477
- Path("../" * (self._name_space().count(".") + 1))
469
+ Path("../" * (self.code_model.namespace.count(".") + 1))
478
470
  if self.code_model.options["no_namespace_folders"]
479
471
  else Path(".")
480
472
  )
@@ -482,16 +474,28 @@ class JinjaSerializer(ReaderAndWriter):
482
474
  def exec_path_for_test_sample(self, namespace: str) -> Path:
483
475
  return self.exec_path_compensation / Path(*namespace.split("."))
484
476
 
477
+ # pylint: disable=line-too-long
485
478
  def exec_path(self, namespace: str) -> Path:
486
479
  if self.code_model.options["no_namespace_folders"] and not self.code_model.options["multiapi"]:
480
+ # when output folder contains parts different from the namespace, we fall back to current folder directly.
481
+ # (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md)
487
482
  return Path(".")
488
483
  return self.exec_path_compensation / Path(*namespace.split("."))
489
484
 
485
+ # pylint: disable=line-too-long
490
486
  @property
491
- def _additional_folder(self) -> Path:
487
+ def sample_additional_folder(self) -> Path:
488
+ # For special package, we need to additional folder when generate samples.
489
+ # For example, azure-mgmt-resource is combined by multiple modules, and each module is multiapi package.
490
+ # one of namespace is "azure.mgmt.resource.resources.v2020_01_01", then additional folder is "resources"
491
+ # so that we could avoid conflict when generate samples.
492
+ # python config: https://github.com/Azure/azure-rest-api-specs/blob/main/specification/resources/resource-manager/readme.python.md
493
+ # generated SDK: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/resources/azure-mgmt-resource/generated_samples
492
494
  namespace_config = get_namespace_config(self.code_model.namespace, self.code_model.options["multiapi"])
493
495
  num_of_namespace = namespace_config.count(".") + 1
494
- num_of_package_namespace = self._namespace_from_package_name.count(".") + 1
496
+ num_of_package_namespace = (
497
+ get_namespace_from_package_name(self.code_model.options["package_name"]).count(".") + 1
498
+ )
495
499
  if num_of_namespace > num_of_package_namespace:
496
500
  return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
497
501
  return Path("")
@@ -514,7 +518,7 @@ class JinjaSerializer(ReaderAndWriter):
514
518
  file_name = to_snake_case(extract_sample_name(file)) + ".py"
515
519
  try:
516
520
  self.write_file(
517
- out_path / self._additional_folder / _sample_output_path(file) / file_name,
521
+ out_path / self.sample_additional_folder / _sample_output_path(file) / file_name,
518
522
  SampleSerializer(
519
523
  code_model=self.code_model,
520
524
  env=env,
@@ -28,7 +28,11 @@ class GeneralSerializer(BaseSerializer):
28
28
 
29
29
  def serialize_package_file(self, template_name: str, **kwargs: Any) -> str:
30
30
  template = self.env.get_template(template_name)
31
- package_parts = (self.code_model.options["package_name"] or "").split("-")[:-1]
31
+ package_parts = (
32
+ self.code_model.namespace.split(".")[:-1]
33
+ if self.code_model.is_tsp
34
+ else (self.code_model.options["package_name"] or "").split("-")[:-1]
35
+ )
32
36
  token_credential = any(
33
37
  c for c in self.code_model.clients if isinstance(getattr(c.credential, "type", None), TokenCredentialType)
34
38
  )
@@ -44,7 +48,6 @@ class GeneralSerializer(BaseSerializer):
44
48
  "pkgutil_names": [".".join(package_parts[: i + 1]) for i in range(len(package_parts))],
45
49
  "init_names": ["/".join(package_parts[: i + 1]) + "/__init__.py" for i in range(len(package_parts))],
46
50
  "client_name": self.code_model.clients[0].name,
47
- "namespace": self.code_model.namespace,
48
51
  }
49
52
  params.update(self.code_model.options)
50
53
  params.update(kwargs)
@@ -19,7 +19,7 @@ from ..models import (
19
19
  CombinedType,
20
20
  FileImport,
21
21
  )
22
- from .utils import get_namespace_from_package_name, json_dumps_template
22
+ from .utils import json_dumps_template
23
23
 
24
24
 
25
25
  def is_lro(operation_type: str) -> bool:
@@ -1,8 +1,12 @@
1
1
  include *.md
2
2
  include LICENSE
3
+ {% if code_model.is_tsp %}
4
+ include {{ code_model.namespace.replace('.', '/') }}/py.typed
5
+ {% else %}
3
6
  include {{ package_name.replace('-', '/') }}/py.typed
7
+ {% endif %}
4
8
  recursive-include tests *.py
5
9
  recursive-include samples *.py *.md
6
- {%- for init_name in init_names %}
10
+ {% for init_name in init_names %}
7
11
  include {{ init_name }}
8
- {%- endfor %}
12
+ {% endfor %}
@@ -1,5 +1,5 @@
1
1
  {% if code_model.is_azure_flavor %}
2
- {% if package_mode == "mgmtplane" -%}
2
+ {% if package_mode == "mgmtplane" %}
3
3
  # Microsoft Azure SDK for Python
4
4
 
5
5
  This is the Microsoft {{package_pprint_name}} Client Library.
@@ -40,7 +40,7 @@ python -m pip install {{ package_name }}
40
40
  - You need an [Azure subscription][azure_sub] to use this package.
41
41
  - An existing {{ package_pprint_name }} instance.
42
42
 
43
- {%- if token_credential %}
43
+ {% if token_credential %}
44
44
  #### Create with an Azure Active Directory Credential
45
45
  To use an [Azure Active Directory (AAD) token credential][authenticate_with_token],
46
46
  provide an instance of the desired credential type obtained from the
@@ -57,7 +57,7 @@ Set the values of the client ID, tenant ID, and client secret of the AAD applica
57
57
  Use the returned token credential to authenticate the client:
58
58
 
59
59
  ```python
60
- >>> from {{ namespace }} import {{ client_name }}
60
+ >>> from {{ code_model.namespace }} import {{ client_name }}
61
61
  >>> from azure.identity import DefaultAzureCredential
62
62
  >>> client = {{ client_name }}(endpoint='<endpoint>', credential=DefaultAzureCredential())
63
63
  ```
@@ -65,7 +65,7 @@ Use the returned token credential to authenticate the client:
65
65
  ## Examples
66
66
 
67
67
  ```python
68
- >>> from {{ namespace }} import {{ client_name }}
68
+ >>> from {{ code_model.namespace }} import {{ client_name }}
69
69
  >>> from azure.identity import DefaultAzureCredential
70
70
  >>> from {{ code_model.core_library }}.exceptions import HttpResponseError
71
71
 
@@ -76,7 +76,7 @@ Use the returned token credential to authenticate the client:
76
76
  print('service responds error: {}'.format(e.response.json()))
77
77
 
78
78
  ```
79
- {%- endif %}
79
+ {% endif %}
80
80
 
81
81
  ## Contributing
82
82
 
@@ -1,9 +1,9 @@
1
1
  -e ../../../tools/azure-sdk-tools
2
2
  ../../core/azure-core
3
- {% if token_credential -%}
3
+ {% if token_credential %}
4
4
  ../../identity/azure-identity
5
5
  {% endif -%}
6
- {% if azure_arm -%}
6
+ {% if azure_arm %}
7
7
  ../../core/azure-mgmt-core
8
- {% endif -%}
8
+ {% endif %}
9
9
  aiohttp
@@ -2,19 +2,27 @@
2
2
  {{ license_header }}
3
3
  # coding: utf-8
4
4
  {% if package_mode %}
5
+
5
6
  import os
6
7
  import re
7
- {% endif -%}
8
+ {% endif %}
8
9
  from setuptools import setup, find_packages
9
10
 
10
11
  {% set package_name = package_name or code_model.clients[0].name %}
11
12
 
12
13
  PACKAGE_NAME = "{{ package_name|lower }}"
13
- {% if package_mode -%}
14
+ {% if package_mode %}
14
15
  PACKAGE_PPRINT_NAME = "{{ package_pprint_name }}"
16
+ {% if code_model.is_tsp %}
17
+ PACKAGE_NAMESPACE = "{{ code_model.namespace|lower }}"
18
+
19
+ # a.b.c => a/b/c
20
+ package_folder_path = PACKAGE_NAMESPACE.replace(".", "/")
21
+ {% else %}
15
22
 
16
23
  # a-b-c => a/b/c
17
24
  package_folder_path = PACKAGE_NAME.replace("-", "/")
25
+ {% endif %}
18
26
 
19
27
  # Version extraction inspired from 'requests'
20
28
  with open(os.path.join(package_folder_path, "_version.py"), "r") as fd:
@@ -33,7 +41,8 @@ version = "{{ package_version }}"
33
41
  {% set long_description = code_model.description %}
34
42
  {% set author_email = "" %}
35
43
  {% set url = "" %}
36
- {% endif -%}
44
+ {% endif %}
45
+
37
46
 
38
47
  setup(
39
48
  name=PACKAGE_NAME,
@@ -70,9 +79,9 @@ setup(
70
79
  {% if pkgutil_names %}
71
80
  # Exclude packages that will be covered by PEP420 or nspkg
72
81
  {% endif %}
73
- {%- for pkgutil_name in pkgutil_names %}
82
+ {% for pkgutil_name in pkgutil_names %}
74
83
  "{{ pkgutil_name }}",
75
- {%- endfor %}
84
+ {% endfor %}
76
85
  ]
77
86
  ),
78
87
  include_package_data=True,
@@ -40,7 +40,6 @@ class OptionsRetriever:
40
40
  "generate-test": False,
41
41
  "from-typespec": False,
42
42
  "emit-cross-language-definition-file": False,
43
- "enable-typespec-namespace": False,
44
43
  }
45
44
 
46
45
  @property
@@ -317,7 +316,6 @@ class CodeGenerator(Plugin):
317
316
  "flavor",
318
317
  "company_name",
319
318
  "emit_cross_language_definition_file",
320
- "enable_typespec_namespace",
321
319
  ]
322
320
  return {f: getattr(self.options_retriever, f) for f in flags}
323
321
 
@@ -145,12 +145,8 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
145
145
  return result
146
146
  return f"{result}{module_name}" if result.endswith(".") else f"{result}.{module_name}"
147
147
 
148
- @property
149
- def need_unique_model_alias(self) -> bool:
150
- return self.has_subnamespace and self.options["enable_typespec_namespace"]
151
-
152
148
  def get_unique_models_alias(self, serialize_namespace: str, imported_namespace: str) -> str:
153
- if not self.need_unique_model_alias:
149
+ if not self.has_subnamespace:
154
150
  return "_models"
155
151
  relative_path = self.get_relative_import_path(
156
152
  serialize_namespace, self.get_imported_namespace_for_model(imported_namespace)
@@ -406,3 +402,7 @@ class CodeModel: # pylint: disable=too-many-public-methods, disable=too-many-in
406
402
  @staticmethod
407
403
  def has_non_json_models(models: List[ModelType]) -> bool:
408
404
  return any(m for m in models if m.base != "json")
405
+
406
+ @property
407
+ def is_tsp(self) -> bool:
408
+ return self.options.get("tsp_file") is not None
@@ -197,6 +197,8 @@ class JinjaSerializer(ReaderAndWriter):
197
197
  env = Environment(
198
198
  loader=PackageLoader("pygen.codegen", "templates/packaging_templates"),
199
199
  undefined=StrictUndefined,
200
+ trim_blocks=True,
201
+ lstrip_blocks=True,
200
202
  )
201
203
 
202
204
  package_files = _PACKAGE_FILES
@@ -460,21 +462,11 @@ class JinjaSerializer(ReaderAndWriter):
460
462
  metadata_serializer = MetadataSerializer(self.code_model, env, client_namespace=namespace)
461
463
  self.write_file(self.exec_path(namespace) / Path("_metadata.json"), metadata_serializer.serialize())
462
464
 
463
- @property
464
- def _namespace_from_package_name(self) -> str:
465
- return get_namespace_from_package_name(self.code_model.options["package_name"])
466
-
467
- def _name_space(self) -> str:
468
- if self.code_model.namespace.count(".") >= self._namespace_from_package_name.count("."):
469
- return self.code_model.namespace
470
-
471
- return self._namespace_from_package_name
472
-
473
465
  @property
474
466
  def exec_path_compensation(self) -> Path:
475
467
  """Assume the process is running in the root folder of the package. If not, we need the path compensation."""
476
468
  return (
477
- Path("../" * (self._name_space().count(".") + 1))
469
+ Path("../" * (self.code_model.namespace.count(".") + 1))
478
470
  if self.code_model.options["no_namespace_folders"]
479
471
  else Path(".")
480
472
  )
@@ -482,16 +474,28 @@ class JinjaSerializer(ReaderAndWriter):
482
474
  def exec_path_for_test_sample(self, namespace: str) -> Path:
483
475
  return self.exec_path_compensation / Path(*namespace.split("."))
484
476
 
477
+ # pylint: disable=line-too-long
485
478
  def exec_path(self, namespace: str) -> Path:
486
479
  if self.code_model.options["no_namespace_folders"] and not self.code_model.options["multiapi"]:
480
+ # when output folder contains parts different from the namespace, we fall back to current folder directly.
481
+ # (e.g. https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/communication/azure-communication-callautomation/swagger/SWAGGER.md)
487
482
  return Path(".")
488
483
  return self.exec_path_compensation / Path(*namespace.split("."))
489
484
 
485
+ # pylint: disable=line-too-long
490
486
  @property
491
- def _additional_folder(self) -> Path:
487
+ def sample_additional_folder(self) -> Path:
488
+ # For special package, we need to additional folder when generate samples.
489
+ # For example, azure-mgmt-resource is combined by multiple modules, and each module is multiapi package.
490
+ # one of namespace is "azure.mgmt.resource.resources.v2020_01_01", then additional folder is "resources"
491
+ # so that we could avoid conflict when generate samples.
492
+ # python config: https://github.com/Azure/azure-rest-api-specs/blob/main/specification/resources/resource-manager/readme.python.md
493
+ # generated SDK: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/resources/azure-mgmt-resource/generated_samples
492
494
  namespace_config = get_namespace_config(self.code_model.namespace, self.code_model.options["multiapi"])
493
495
  num_of_namespace = namespace_config.count(".") + 1
494
- num_of_package_namespace = self._namespace_from_package_name.count(".") + 1
496
+ num_of_package_namespace = (
497
+ get_namespace_from_package_name(self.code_model.options["package_name"]).count(".") + 1
498
+ )
495
499
  if num_of_namespace > num_of_package_namespace:
496
500
  return Path("/".join(namespace_config.split(".")[num_of_package_namespace:]))
497
501
  return Path("")
@@ -514,7 +518,7 @@ class JinjaSerializer(ReaderAndWriter):
514
518
  file_name = to_snake_case(extract_sample_name(file)) + ".py"
515
519
  try:
516
520
  self.write_file(
517
- out_path / self._additional_folder / _sample_output_path(file) / file_name,
521
+ out_path / self.sample_additional_folder / _sample_output_path(file) / file_name,
518
522
  SampleSerializer(
519
523
  code_model=self.code_model,
520
524
  env=env,
@@ -28,7 +28,11 @@ class GeneralSerializer(BaseSerializer):
28
28
 
29
29
  def serialize_package_file(self, template_name: str, **kwargs: Any) -> str:
30
30
  template = self.env.get_template(template_name)
31
- package_parts = (self.code_model.options["package_name"] or "").split("-")[:-1]
31
+ package_parts = (
32
+ self.code_model.namespace.split(".")[:-1]
33
+ if self.code_model.is_tsp
34
+ else (self.code_model.options["package_name"] or "").split("-")[:-1]
35
+ )
32
36
  token_credential = any(
33
37
  c for c in self.code_model.clients if isinstance(getattr(c.credential, "type", None), TokenCredentialType)
34
38
  )
@@ -44,7 +48,6 @@ class GeneralSerializer(BaseSerializer):
44
48
  "pkgutil_names": [".".join(package_parts[: i + 1]) for i in range(len(package_parts))],
45
49
  "init_names": ["/".join(package_parts[: i + 1]) + "/__init__.py" for i in range(len(package_parts))],
46
50
  "client_name": self.code_model.clients[0].name,
47
- "namespace": self.code_model.namespace,
48
51
  }
49
52
  params.update(self.code_model.options)
50
53
  params.update(kwargs)
@@ -19,7 +19,7 @@ from ..models import (
19
19
  CombinedType,
20
20
  FileImport,
21
21
  )
22
- from .utils import get_namespace_from_package_name, json_dumps_template
22
+ from .utils import json_dumps_template
23
23
 
24
24
 
25
25
  def is_lro(operation_type: str) -> bool:
@@ -1,8 +1,12 @@
1
1
  include *.md
2
2
  include LICENSE
3
+ {% if code_model.is_tsp %}
4
+ include {{ code_model.namespace.replace('.', '/') }}/py.typed
5
+ {% else %}
3
6
  include {{ package_name.replace('-', '/') }}/py.typed
7
+ {% endif %}
4
8
  recursive-include tests *.py
5
9
  recursive-include samples *.py *.md
6
- {%- for init_name in init_names %}
10
+ {% for init_name in init_names %}
7
11
  include {{ init_name }}
8
- {%- endfor %}
12
+ {% endfor %}