@typespec/http-client-python 0.18.1 → 0.19.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 (38) hide show
  1. package/dist/emitter/code-model.d.ts.map +1 -1
  2. package/dist/emitter/code-model.js +19 -4
  3. package/dist/emitter/code-model.js.map +1 -1
  4. package/dist/emitter/http.js +14 -6
  5. package/dist/emitter/http.js.map +1 -1
  6. package/emitter/src/code-model.ts +23 -6
  7. package/emitter/src/http.ts +14 -8
  8. package/emitter/temp/tsconfig.tsbuildinfo +1 -1
  9. package/eng/scripts/Build-Packages.ps1 +3 -0
  10. package/eng/scripts/ci/regenerate.ts +11 -1
  11. package/eng/scripts/setup/__pycache__/package_manager.cpython-39.pyc +0 -0
  12. package/eng/scripts/setup/__pycache__/venvtools.cpython-39.pyc +0 -0
  13. package/generator/build/lib/pygen/codegen/models/operation.py +5 -1
  14. package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +22 -6
  15. package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +5 -2
  16. package/generator/build/lib/pygen/codegen/templates/macros.jinja2 +14 -1
  17. package/generator/build/lib/pygen/codegen/templates/model_init.py.jinja2 +6 -0
  18. package/generator/build/lib/pygen/codegen/templates/operation_tools.jinja2 +17 -2
  19. package/generator/build/lib/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +2 -0
  20. package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
  21. package/generator/pygen/codegen/models/operation.py +5 -1
  22. package/generator/pygen/codegen/serializers/builder_serializer.py +22 -6
  23. package/generator/pygen/codegen/serializers/model_init_serializer.py +5 -2
  24. package/generator/pygen/codegen/templates/macros.jinja2 +14 -1
  25. package/generator/pygen/codegen/templates/model_init.py.jinja2 +6 -0
  26. package/generator/pygen/codegen/templates/operation_tools.jinja2 +17 -2
  27. package/generator/pygen/codegen/templates/packaging_templates/pyproject.toml.jinja2 +2 -0
  28. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_location_async.py +8 -1
  29. package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_override_async.py +63 -0
  30. package/generator/test/azure/mock_api_tests/asynctests/test_client_structure_clientoperationgroup_async.py +29 -0
  31. package/generator/test/azure/mock_api_tests/conftest.py +1 -1
  32. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_location.py +5 -1
  33. package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_override.py +61 -0
  34. package/generator/test/azure/mock_api_tests/test_client_structure_clientoperationgroup.py +26 -0
  35. package/generator/test/azure/requirements.txt +1 -0
  36. package/generator/test/generic_mock_api_tests/asynctests/test_payload_pageable_async.py +1 -1
  37. package/generator/test/generic_mock_api_tests/test_payload_pageable.py +1 -1
  38. package/package.json +32 -32
@@ -67,6 +67,9 @@ try {
67
67
  Invoke-LoggedCommand "npm pack"
68
68
  Copy-Item "typespec-http-client-python-$emitterVersion.tgz" -Destination "$outputPath/packages"
69
69
 
70
+ # install packed emitter to check dependencies
71
+ Invoke-LoggedCommand "npm install typespec-http-client-python-$emitterVersion.tgz" -GroupOutput
72
+
70
73
  Write-PackageInfo -packageName "typespec-http-client-python" -directoryPath "packages/http-client-python/emitter/src" -version $emitterVersion
71
74
  }
72
75
  finally {
@@ -22,7 +22,7 @@ const argv = parseArgs({
22
22
  });
23
23
 
24
24
  // Add this near the top with other constants
25
- const SKIP_SPECS = ["type/union/discriminated", "client-operation-group"];
25
+ const SKIP_SPECS = ["type/union/discriminated"];
26
26
 
27
27
  // Get the directory of the current file
28
28
  const PLUGIN_DIR = argv.values.pluginDir
@@ -43,6 +43,9 @@ const AZURE_EMITTER_OPTIONS: Record<string, Record<string, string> | Record<stri
43
43
  "azure/client-generator-core/access": {
44
44
  namespace: "specs.azure.clientgenerator.core.access",
45
45
  },
46
+ "azure/client-generator-core/alternate-type": {
47
+ namespace: "specs.azure.clientgenerator.core.alternatetype",
48
+ },
46
49
  "azure/client-generator-core/api-version": {
47
50
  namespace: "specs.azure.clientgenerator.core.apiversion",
48
51
  },
@@ -97,9 +100,16 @@ const AZURE_EMITTER_OPTIONS: Record<string, Record<string, string> | Record<stri
97
100
  "azure/payload/pageable": {
98
101
  namespace: "specs.azure.payload.pageable",
99
102
  },
103
+ "azure/versioning/previewVersion": {
104
+ namespace: "specs.azure.versioning.previewversion",
105
+ },
100
106
  "client/structure/default": {
101
107
  namespace: "client.structure.service",
102
108
  },
109
+ "client/structure/client-operation-group": {
110
+ "package-name": "client-structure-clientoperationgroup",
111
+ namespace: "client.structure.clientoperationgroup",
112
+ },
103
113
  "client/structure/multi-client": {
104
114
  "package-name": "client-structure-multiclient",
105
115
  namespace: "client.structure.multiclient",
@@ -198,7 +198,11 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
198
198
  return None
199
199
  exception_schema = default_exceptions[0].type
200
200
  if isinstance(exception_schema, ModelType):
201
- return exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)
201
+ pylint_disable = " # pylint: disable=protected-access" if exception_schema.internal else ""
202
+ return (
203
+ exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)
204
+ + pylint_disable
205
+ )
202
206
  return None if self.code_model.options["models-mode"] == "dpg" else "'object'"
203
207
 
204
208
  @property
@@ -1014,7 +1014,9 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1014
1014
  retval.extend(deserialize_code)
1015
1015
  return retval
1016
1016
 
1017
- def handle_error_response(self, builder: OperationType) -> list[str]:
1017
+ def handle_error_response( # pylint: disable=too-many-statements, too-many-branches
1018
+ self, builder: OperationType
1019
+ ) -> list[str]:
1018
1020
  async_await = "await " if self.async_mode else ""
1019
1021
  retval = [f"if response.status_code not in {str(builder.success_status_codes)}:"]
1020
1022
  response_read = [
@@ -1036,6 +1038,10 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1036
1038
  retval.append(" error = None")
1037
1039
  for e in builder.non_default_errors:
1038
1040
  # single status code
1041
+ if isinstance(e.type, ModelType) and e.type.internal:
1042
+ pylint_disable = " # pylint: disable=protected-access"
1043
+ else:
1044
+ pylint_disable = ""
1039
1045
  if isinstance(e.status_codes[0], int):
1040
1046
  for status_code in e.status_codes:
1041
1047
  retval.append(f" {condition} response.status_code == {status_code}:")
@@ -1043,10 +1049,13 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1043
1049
  is_operation_file=True, skip_quote=True, serialize_namespace=self.serialize_namespace
1044
1050
  )
1045
1051
  if self.code_model.options["models-mode"] == "dpg":
1046
- retval.append(f" error = _failsafe_deserialize({type_annotation}, response)")
1052
+ retval.append(
1053
+ f" error = _failsafe_deserialize({type_annotation},{pylint_disable}\n response)"
1054
+ )
1047
1055
  else:
1048
1056
  retval.append(
1049
- f" error = self._deserialize.failsafe_deserialize({type_annotation}, "
1057
+ " error = self._deserialize.failsafe_deserialize("
1058
+ f"{type_annotation},{pylint_disable}\n "
1050
1059
  "pipeline_response)"
1051
1060
  )
1052
1061
  # add build-in error type
@@ -1078,12 +1087,19 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1078
1087
  )
1079
1088
  if self.code_model.options["models-mode"] == "dpg":
1080
1089
  if xml_serializable(str(e.default_content_type)):
1081
- retval.append(f" error = _failsafe_deserialize_xml({type_annotation}, response)")
1090
+ retval.append(
1091
+ " error = _failsafe_deserialize_xml("
1092
+ f"{type_annotation},{pylint_disable}\n response)"
1093
+ )
1082
1094
  else:
1083
- retval.append(f" error = _failsafe_deserialize({type_annotation}, response)")
1095
+ retval.append(
1096
+ " error = _failsafe_deserialize("
1097
+ f"{type_annotation},{pylint_disable}\n response)"
1098
+ )
1084
1099
  else:
1085
1100
  retval.append(
1086
- f" error = self._deserialize.failsafe_deserialize({type_annotation}, "
1101
+ " error = self._deserialize.failsafe_deserialize("
1102
+ f"{type_annotation},{pylint_disable}\n "
1087
1103
  "pipeline_response)"
1088
1104
  )
1089
1105
  condition = "elif"
@@ -32,6 +32,9 @@ class ModelInitSerializer:
32
32
  ", ".join(model_enum_name_intersection)
33
33
  )
34
34
  )
35
-
35
+ has_models = self.models
36
+ has_enums = self.enums
36
37
  template = self.env.get_template("model_init.py.jinja2")
37
- return template.render(code_model=self.code_model, schemas=schemas, enums=enums)
38
+ return template.render(
39
+ code_model=self.code_model, schemas=schemas, enums=enums, has_models=has_models, has_enums=has_enums
40
+ )
@@ -1,5 +1,18 @@
1
1
  {% macro wrap_model_string(doc_string, wrap_string, suffix_string="") %}
2
- {% set original_result = doc_string | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) %}
2
+ {%- set lines = doc_string.split('\n') -%}
3
+ {%- set processed_lines = [] -%}
4
+ {%- for line in lines -%}
5
+ {%- set stripped = line.strip() -%}
6
+ {%- if stripped.startswith('* ') -%}
7
+ {%- set bullet_with_indent = ' ' + stripped -%}
8
+ {%- set wrapped = bullet_with_indent | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string + ' ') -%}
9
+ {%- set _ = processed_lines.append(wrapped) -%}
10
+ {%- elif stripped -%}
11
+ {%- set wrapped = line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
12
+ {%- set _ = processed_lines.append(wrapped) -%}
13
+ {%- endif -%}
14
+ {%- endfor -%}
15
+ {%- set original_result = processed_lines | join('\n') -%}
3
16
  {% set list_result = original_result.split('\n') %}
4
17
  {% for line in list_result %}
5
18
  {% set prefix = "" if loop.index == 1 else " " %}
@@ -11,6 +11,9 @@ from .{{ code_model.models_filename }} import ( # type: ignore
11
11
  {{ schema }},
12
12
  {% endfor %}
13
13
  )
14
+ {% elif has_models %}
15
+
16
+ from . import _models
14
17
  {% endif %}
15
18
  {% if enums %}
16
19
 
@@ -19,6 +22,9 @@ from .{{ code_model.enums_filename }} import ( # type: ignore
19
22
  {{ enum }},
20
23
  {% endfor %}
21
24
  )
25
+ {% elif has_enums %}
26
+
27
+ from . import _enums
22
28
  {% endif %}
23
29
  {{ keywords.patch_imports() }}
24
30
  __all__ = [
@@ -1,4 +1,18 @@
1
- {% macro wrap_string(string, wrapstring, width=95) %}{{ string | replace("\\", "\\\\") | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring)}}{% endmacro %}
1
+ {% macro wrap_string(string, wrapstring, width=95) %}
2
+ {%- set lines = string.split('\n') -%}
3
+ {%- set processed_lines = [] -%}
4
+ {%- for line in lines -%}
5
+ {%- set stripped = line.strip() -%}
6
+ {%- if stripped.startswith('* ') -%}
7
+ {%- set bullet_with_indent = stripped -%}
8
+ {%- set wrapped = bullet_with_indent | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring + ' ') -%}
9
+ {%- set _ = processed_lines.append(wrapped) -%}
10
+ {%- elif stripped -%}
11
+ {%- set wrapped = line | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring) -%}
12
+ {%- set _ = processed_lines.append(wrapped) -%}
13
+ {%- endif -%}
14
+ {%- endfor -%}
15
+ {{ processed_lines | join('\n') | replace("\\", "\\\\") }}{%- endmacro %}
2
16
 
3
17
  {% macro description(builder, serializer) %}
4
18
  {% set example_template = serializer.example_template(builder) %}
@@ -19,7 +33,8 @@
19
33
  {% endfor %}
20
34
  {% for description in param_description_and_response_docstring %}
21
35
  {% if description %}
22
- {{ wrap_string(description, wrapstring='\n ') }}
36
+ {% set description = wrap_string(description, wrapstring='\n ') %}
37
+ {{ " " if description[0] != ":" and description[0] != " " else "" }}{{ description}}
23
38
  {% else %}
24
39
 
25
40
  {% endif %}
@@ -94,7 +94,9 @@ readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"}
94
94
  [tool.setuptools.packages.find]
95
95
  exclude = [
96
96
  "tests*",
97
+ "generated_tests*",
97
98
  "samples*",
99
+ "generated_samples*",
98
100
  "doc*",
99
101
  {% for pkgutil_name in pkgutil_names %}
100
102
  "{{ pkgutil_name }}",
@@ -198,7 +198,11 @@ class OperationBase( # pylint: disable=too-many-public-methods,too-many-instanc
198
198
  return None
199
199
  exception_schema = default_exceptions[0].type
200
200
  if isinstance(exception_schema, ModelType):
201
- return exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)
201
+ pylint_disable = " # pylint: disable=protected-access" if exception_schema.internal else ""
202
+ return (
203
+ exception_schema.type_annotation(skip_quote=True, serialize_namespace=serialize_namespace)
204
+ + pylint_disable
205
+ )
202
206
  return None if self.code_model.options["models-mode"] == "dpg" else "'object'"
203
207
 
204
208
  @property
@@ -1014,7 +1014,9 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1014
1014
  retval.extend(deserialize_code)
1015
1015
  return retval
1016
1016
 
1017
- def handle_error_response(self, builder: OperationType) -> list[str]:
1017
+ def handle_error_response( # pylint: disable=too-many-statements, too-many-branches
1018
+ self, builder: OperationType
1019
+ ) -> list[str]:
1018
1020
  async_await = "await " if self.async_mode else ""
1019
1021
  retval = [f"if response.status_code not in {str(builder.success_status_codes)}:"]
1020
1022
  response_read = [
@@ -1036,6 +1038,10 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1036
1038
  retval.append(" error = None")
1037
1039
  for e in builder.non_default_errors:
1038
1040
  # single status code
1041
+ if isinstance(e.type, ModelType) and e.type.internal:
1042
+ pylint_disable = " # pylint: disable=protected-access"
1043
+ else:
1044
+ pylint_disable = ""
1039
1045
  if isinstance(e.status_codes[0], int):
1040
1046
  for status_code in e.status_codes:
1041
1047
  retval.append(f" {condition} response.status_code == {status_code}:")
@@ -1043,10 +1049,13 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1043
1049
  is_operation_file=True, skip_quote=True, serialize_namespace=self.serialize_namespace
1044
1050
  )
1045
1051
  if self.code_model.options["models-mode"] == "dpg":
1046
- retval.append(f" error = _failsafe_deserialize({type_annotation}, response)")
1052
+ retval.append(
1053
+ f" error = _failsafe_deserialize({type_annotation},{pylint_disable}\n response)"
1054
+ )
1047
1055
  else:
1048
1056
  retval.append(
1049
- f" error = self._deserialize.failsafe_deserialize({type_annotation}, "
1057
+ " error = self._deserialize.failsafe_deserialize("
1058
+ f"{type_annotation},{pylint_disable}\n "
1050
1059
  "pipeline_response)"
1051
1060
  )
1052
1061
  # add build-in error type
@@ -1078,12 +1087,19 @@ class _OperationSerializer(_BuilderBaseSerializer[OperationType]):
1078
1087
  )
1079
1088
  if self.code_model.options["models-mode"] == "dpg":
1080
1089
  if xml_serializable(str(e.default_content_type)):
1081
- retval.append(f" error = _failsafe_deserialize_xml({type_annotation}, response)")
1090
+ retval.append(
1091
+ " error = _failsafe_deserialize_xml("
1092
+ f"{type_annotation},{pylint_disable}\n response)"
1093
+ )
1082
1094
  else:
1083
- retval.append(f" error = _failsafe_deserialize({type_annotation}, response)")
1095
+ retval.append(
1096
+ " error = _failsafe_deserialize("
1097
+ f"{type_annotation},{pylint_disable}\n response)"
1098
+ )
1084
1099
  else:
1085
1100
  retval.append(
1086
- f" error = self._deserialize.failsafe_deserialize({type_annotation}, "
1101
+ " error = self._deserialize.failsafe_deserialize("
1102
+ f"{type_annotation},{pylint_disable}\n "
1087
1103
  "pipeline_response)"
1088
1104
  )
1089
1105
  condition = "elif"
@@ -32,6 +32,9 @@ class ModelInitSerializer:
32
32
  ", ".join(model_enum_name_intersection)
33
33
  )
34
34
  )
35
-
35
+ has_models = self.models
36
+ has_enums = self.enums
36
37
  template = self.env.get_template("model_init.py.jinja2")
37
- return template.render(code_model=self.code_model, schemas=schemas, enums=enums)
38
+ return template.render(
39
+ code_model=self.code_model, schemas=schemas, enums=enums, has_models=has_models, has_enums=has_enums
40
+ )
@@ -1,5 +1,18 @@
1
1
  {% macro wrap_model_string(doc_string, wrap_string, suffix_string="") %}
2
- {% set original_result = doc_string | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) %}
2
+ {%- set lines = doc_string.split('\n') -%}
3
+ {%- set processed_lines = [] -%}
4
+ {%- for line in lines -%}
5
+ {%- set stripped = line.strip() -%}
6
+ {%- if stripped.startswith('* ') -%}
7
+ {%- set bullet_with_indent = ' ' + stripped -%}
8
+ {%- set wrapped = bullet_with_indent | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string + ' ') -%}
9
+ {%- set _ = processed_lines.append(wrapped) -%}
10
+ {%- elif stripped -%}
11
+ {%- set wrapped = line | wordwrap(width=95, break_long_words=False, break_on_hyphens=False, wrapstring=wrap_string) -%}
12
+ {%- set _ = processed_lines.append(wrapped) -%}
13
+ {%- endif -%}
14
+ {%- endfor -%}
15
+ {%- set original_result = processed_lines | join('\n') -%}
3
16
  {% set list_result = original_result.split('\n') %}
4
17
  {% for line in list_result %}
5
18
  {% set prefix = "" if loop.index == 1 else " " %}
@@ -11,6 +11,9 @@ from .{{ code_model.models_filename }} import ( # type: ignore
11
11
  {{ schema }},
12
12
  {% endfor %}
13
13
  )
14
+ {% elif has_models %}
15
+
16
+ from . import _models
14
17
  {% endif %}
15
18
  {% if enums %}
16
19
 
@@ -19,6 +22,9 @@ from .{{ code_model.enums_filename }} import ( # type: ignore
19
22
  {{ enum }},
20
23
  {% endfor %}
21
24
  )
25
+ {% elif has_enums %}
26
+
27
+ from . import _enums
22
28
  {% endif %}
23
29
  {{ keywords.patch_imports() }}
24
30
  __all__ = [
@@ -1,4 +1,18 @@
1
- {% macro wrap_string(string, wrapstring, width=95) %}{{ string | replace("\\", "\\\\") | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring)}}{% endmacro %}
1
+ {% macro wrap_string(string, wrapstring, width=95) %}
2
+ {%- set lines = string.split('\n') -%}
3
+ {%- set processed_lines = [] -%}
4
+ {%- for line in lines -%}
5
+ {%- set stripped = line.strip() -%}
6
+ {%- if stripped.startswith('* ') -%}
7
+ {%- set bullet_with_indent = stripped -%}
8
+ {%- set wrapped = bullet_with_indent | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring + ' ') -%}
9
+ {%- set _ = processed_lines.append(wrapped) -%}
10
+ {%- elif stripped -%}
11
+ {%- set wrapped = line | wordwrap(width=width, break_long_words=False, break_on_hyphens=False, wrapstring=wrapstring) -%}
12
+ {%- set _ = processed_lines.append(wrapped) -%}
13
+ {%- endif -%}
14
+ {%- endfor -%}
15
+ {{ processed_lines | join('\n') | replace("\\", "\\\\") }}{%- endmacro %}
2
16
 
3
17
  {% macro description(builder, serializer) %}
4
18
  {% set example_template = serializer.example_template(builder) %}
@@ -19,7 +33,8 @@
19
33
  {% endfor %}
20
34
  {% for description in param_description_and_response_docstring %}
21
35
  {% if description %}
22
- {{ wrap_string(description, wrapstring='\n ') }}
36
+ {% set description = wrap_string(description, wrapstring='\n ') %}
37
+ {{ " " if description[0] != ":" and description[0] != " " else "" }}{{ description}}
23
38
  {% else %}
24
39
 
25
40
  {% endif %}
@@ -94,7 +94,9 @@ readme = {file = ["README.md", "CHANGELOG.md"], content-type = "text/markdown"}
94
94
  [tool.setuptools.packages.find]
95
95
  exclude = [
96
96
  "tests*",
97
+ "generated_tests*",
97
98
  "samples*",
99
+ "generated_samples*",
98
100
  "doc*",
99
101
  {% for pkgutil_name in pkgutil_names %}
100
102
  "{{ pkgutil_name }}",
@@ -9,7 +9,7 @@ from specs.azure.clientgenerator.core.clientlocation.aio import ClientLocationCl
9
9
 
10
10
  @pytest.fixture
11
11
  async def client():
12
- async with ClientLocationClient() as client:
12
+ async with ClientLocationClient(storage_account="testaccount") as client:
13
13
  yield client
14
14
 
15
15
 
@@ -46,3 +46,10 @@ async def test_move_to_new_sub_client_product_operations_list_products(client: C
46
46
  @pytest.mark.asyncio
47
47
  async def test_move_to_root_client_resource_operations_get_resource(client: ClientLocationClient):
48
48
  await client.move_to_root_client.resource_operations.get_resource()
49
+
50
+
51
+ @pytest.mark.asyncio
52
+ async def test_move_method_parameter_to_client_blob_operations_get_blob(client: ClientLocationClient):
53
+ await client.move_method_parameter_to_client.blob_operations.get_blob(
54
+ container="testcontainer", blob="testblob.txt"
55
+ )
@@ -24,6 +24,17 @@ async def test_group_parameters(client: OverrideClient):
24
24
  await client.group_parameters.group(param1="param1", param2="param2")
25
25
 
26
26
 
27
+ @pytest.mark.asyncio
28
+ async def test_require_optional_parameter(client: OverrideClient):
29
+ await client.require_optional_parameter.require_optional("param1", "param2")
30
+
31
+
32
+ @pytest.mark.asyncio
33
+ async def test_remove_optional_parameter(client: OverrideClient):
34
+ # Test with optional param2 provided
35
+ await client.remove_optional_parameter.remove_optional("param1", param2="param2")
36
+
37
+
27
38
  def test_reorder_parameters_unit_async(client: OverrideClient):
28
39
  # make sure signature name of `reorder_parameters` are ["param1", "param2"]
29
40
  # Get the reorder method from the reorder_parameters operation
@@ -41,3 +52,55 @@ def test_reorder_parameters_unit_async(client: OverrideClient):
41
52
 
42
53
  # Assert that the parameter names are exactly ["param1", "param2"]
43
54
  assert param_names == ["param1", "param2"], f"Expected parameter names ['param1', 'param2'], but got {param_names}"
55
+
56
+
57
+ def test_require_optional_parameter_signature(client: OverrideClient):
58
+ # Get the require_optional method
59
+ require_optional_method = client.require_optional_parameter.require_optional
60
+
61
+ # Inspect the method signature
62
+ sig = inspect.signature(require_optional_method)
63
+
64
+ # Get parameter details
65
+ params = sig.parameters
66
+
67
+ # Check that both param1 and param2 are required (no default values)
68
+ assert "param1" in params, "param1 should be present in signature"
69
+ assert "param2" in params, "param2 should be present in signature"
70
+ assert params["param1"].default == params["param1"].empty, "param1 should have no default value"
71
+ assert params["param2"].default == params["param2"].empty, "param2 should have no default value"
72
+
73
+
74
+ def test_remove_optional_parameter_signature(client: OverrideClient):
75
+ """Test that remove_optional_parameter.remove_optional method signature has correct parameters.
76
+
77
+ The @override decorator should remove some optional parameters from the method signature.
78
+ Only param1 (required) and param2 (optional) should remain.
79
+ """
80
+ # Get the remove_optional method
81
+ remove_optional_method = client.remove_optional_parameter.remove_optional
82
+
83
+ # Inspect the method signature
84
+ sig = inspect.signature(remove_optional_method)
85
+
86
+ # Get parameter names excluding 'self' and '**kwargs'
87
+ param_names = [
88
+ param_name
89
+ for param_name, param in sig.parameters.items()
90
+ if param_name not in ("self", "kwargs") and param.kind != param.VAR_KEYWORD
91
+ ]
92
+
93
+ # Should have param1 (required) and param2 (keyword-only optional)
94
+ assert "param1" in param_names, "param1 should be present in signature"
95
+ assert "param2" in sig.parameters, "param2 should be present in signature"
96
+
97
+ # param1 should be required (positional)
98
+ assert sig.parameters["param1"].default == sig.parameters["param1"].empty, "param1 should have no default value"
99
+
100
+ # param2 should be optional keyword-only with None default
101
+ assert sig.parameters["param2"].kind == sig.parameters["param2"].KEYWORD_ONLY, "param2 should be keyword-only"
102
+ assert sig.parameters["param2"].default is None, "param2 should have None as default value"
103
+
104
+ # param3 and param4 should be removed
105
+ assert "param3" not in param_names, "param3 should not be present in signature"
106
+ assert "param4" not in param_names, "param4 should not be present in signature"
@@ -0,0 +1,29 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+ import pytest
7
+ from client.structure.clientoperationgroup.models import ClientType
8
+ from client.structure.clientoperationgroup.aio import FirstClient, SecondClient
9
+
10
+
11
+ @pytest.mark.asyncio
12
+ async def test_first_client_operations():
13
+ client = FirstClient(endpoint="http://localhost:3000", client=ClientType.CLIENT_OPERATION_GROUP)
14
+
15
+ await client.one()
16
+
17
+ await client.group3.two()
18
+ await client.group3.three()
19
+
20
+ await client.group4.four()
21
+
22
+
23
+ @pytest.mark.asyncio
24
+ async def test_second_client_operations():
25
+ client = SecondClient(endpoint="http://localhost:3000", client=ClientType.CLIENT_OPERATION_GROUP)
26
+
27
+ await client.five()
28
+
29
+ await client.group5.six()
@@ -18,7 +18,7 @@ def start_server_process():
18
18
  azure_http_path = Path(os.path.dirname(__file__)) / Path("../../../../node_modules/@azure-tools/azure-http-specs")
19
19
  http_path = Path(os.path.dirname(__file__)) / Path("../../../../node_modules/@typespec/http-specs")
20
20
  os.chdir(azure_http_path.resolve())
21
- cmd = f"tsp-spector serve ./specs {(http_path / 'specs').resolve()}"
21
+ cmd = f"npx tsp-spector serve ./specs {(http_path / 'specs').resolve()}"
22
22
  if os.name == "nt":
23
23
  return subprocess.Popen(cmd, shell=True)
24
24
  return subprocess.Popen(cmd, shell=True, preexec_fn=os.setsid)
@@ -9,7 +9,7 @@ from specs.azure.clientgenerator.core.clientlocation import ClientLocationClient
9
9
 
10
10
  @pytest.fixture
11
11
  def client():
12
- with ClientLocationClient() as client:
12
+ with ClientLocationClient(storage_account="testaccount") as client:
13
13
  yield client
14
14
 
15
15
 
@@ -39,3 +39,7 @@ def test_move_to_new_sub_client_product_operations_list_products(client: ClientL
39
39
 
40
40
  def test_move_to_root_client_resource_operations_get_resource(client: ClientLocationClient):
41
41
  client.move_to_root_client.resource_operations.get_resource()
42
+
43
+
44
+ def test_move_method_parameter_to_client_blob_operations_get_blob(client: ClientLocationClient):
45
+ client.move_method_parameter_to_client.blob_operations.get_blob(container="testcontainer", blob="testblob.txt")
@@ -22,6 +22,15 @@ def test_group_parameters(client: OverrideClient):
22
22
  client.group_parameters.group(param1="param1", param2="param2")
23
23
 
24
24
 
25
+ def test_require_optional_parameter(client: OverrideClient):
26
+ client.require_optional_parameter.require_optional("param1", "param2")
27
+
28
+
29
+ def test_remove_optional_parameter(client: OverrideClient):
30
+ # Test with optional param2 provided
31
+ client.remove_optional_parameter.remove_optional("param1", param2="param2")
32
+
33
+
25
34
  # make sure signature name of `reorder_parameters` are ["param1", "param2"]
26
35
  def test_reorder_parameters_unit(client: OverrideClient):
27
36
  # Get the reorder method from the reorder_parameters operation
@@ -39,3 +48,55 @@ def test_reorder_parameters_unit(client: OverrideClient):
39
48
 
40
49
  # Assert that the parameter names are exactly ["param1", "param2"]
41
50
  assert param_names == ["param1", "param2"], f"Expected parameter names ['param1', 'param2'], but got {param_names}"
51
+
52
+
53
+ def test_require_optional_parameter_signature(client: OverrideClient):
54
+ # Get the require_optional method
55
+ require_optional_method = client.require_optional_parameter.require_optional
56
+
57
+ # Inspect the method signature
58
+ sig = inspect.signature(require_optional_method)
59
+
60
+ # Get parameter details
61
+ params = sig.parameters
62
+
63
+ # Check that both param1 and param2 are required (no default values)
64
+ assert "param1" in params, "param1 should be present in signature"
65
+ assert "param2" in params, "param2 should be present in signature"
66
+ assert params["param1"].default == params["param1"].empty, "param1 should have no default value"
67
+ assert params["param2"].default == params["param2"].empty, "param2 should have no default value"
68
+
69
+
70
+ def test_remove_optional_parameter_signature(client: OverrideClient):
71
+ """Test that remove_optional_parameter.remove_optional method signature has correct parameters.
72
+
73
+ The @override decorator should remove some optional parameters from the method signature.
74
+ Only param1 (required) and param2 (optional) should remain.
75
+ """
76
+ # Get the remove_optional method
77
+ remove_optional_method = client.remove_optional_parameter.remove_optional
78
+
79
+ # Inspect the method signature
80
+ sig = inspect.signature(remove_optional_method)
81
+
82
+ # Get parameter names excluding 'self' and '**kwargs'
83
+ param_names = [
84
+ param_name
85
+ for param_name, param in sig.parameters.items()
86
+ if param_name not in ("self", "kwargs") and param.kind != param.VAR_KEYWORD
87
+ ]
88
+
89
+ # Should have param1 (required) and param2 (keyword-only optional)
90
+ assert "param1" in param_names, "param1 should be present in signature"
91
+ assert "param2" in sig.parameters, "param2 should be present in signature"
92
+
93
+ # param1 should be required (positional)
94
+ assert sig.parameters["param1"].default == sig.parameters["param1"].empty, "param1 should have no default value"
95
+
96
+ # param2 should be optional keyword-only with None default
97
+ assert sig.parameters["param2"].kind == sig.parameters["param2"].KEYWORD_ONLY, "param2 should be keyword-only"
98
+ assert sig.parameters["param2"].default is None, "param2 should have None as default value"
99
+
100
+ # param3 and param4 should be removed
101
+ assert "param3" not in param_names, "param3 should not be present in signature"
102
+ assert "param4" not in param_names, "param4 should not be present in signature"
@@ -0,0 +1,26 @@
1
+ # -------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # --------------------------------------------------------------------------
6
+ from client.structure.clientoperationgroup.models import ClientType
7
+ from client.structure.clientoperationgroup import FirstClient, SecondClient
8
+
9
+
10
+ def test_first_client_operations():
11
+ client = FirstClient(endpoint="http://localhost:3000", client=ClientType.CLIENT_OPERATION_GROUP)
12
+
13
+ client.one()
14
+
15
+ client.group3.two()
16
+ client.group3.three()
17
+
18
+ client.group4.four()
19
+
20
+
21
+ def test_second_client_operations():
22
+ client = SecondClient(endpoint="http://localhost:3000", client=ClientType.CLIENT_OPERATION_GROUP)
23
+
24
+ client.five()
25
+
26
+ client.group5.six()