@typespec/http-client-python 0.18.1 → 0.19.0
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.
- package/dist/emitter/code-model.d.ts.map +1 -1
- package/dist/emitter/code-model.js +19 -4
- package/dist/emitter/code-model.js.map +1 -1
- package/dist/emitter/http.js +14 -6
- package/dist/emitter/http.js.map +1 -1
- package/emitter/src/code-model.ts +23 -6
- package/emitter/src/http.ts +14 -8
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/eng/scripts/Build-Packages.ps1 +3 -0
- package/eng/scripts/ci/regenerate.ts +8 -1
- package/eng/scripts/setup/__pycache__/package_manager.cpython-39.pyc +0 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-39.pyc +0 -0
- package/generator/build/lib/pygen/codegen/models/operation.py +5 -1
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +22 -6
- package/generator/build/lib/pygen/codegen/serializers/model_init_serializer.py +5 -2
- package/generator/build/lib/pygen/codegen/templates/model_init.py.jinja2 +6 -0
- package/generator/build/lib/pygen/codegen/templates/operation_tools.jinja2 +2 -1
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/codegen/models/operation.py +5 -1
- package/generator/pygen/codegen/serializers/builder_serializer.py +22 -6
- package/generator/pygen/codegen/serializers/model_init_serializer.py +5 -2
- package/generator/pygen/codegen/templates/model_init.py.jinja2 +6 -0
- package/generator/pygen/codegen/templates/operation_tools.jinja2 +2 -1
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_client_location_async.py +8 -1
- package/generator/test/azure/mock_api_tests/asynctests/test_azure_client_generator_core_override_async.py +63 -0
- package/generator/test/azure/mock_api_tests/asynctests/test_client_structure_clientoperationgroup_async.py +29 -0
- package/generator/test/azure/mock_api_tests/conftest.py +1 -1
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_location.py +5 -1
- package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_override.py +61 -0
- package/generator/test/azure/mock_api_tests/test_client_structure_clientoperationgroup.py +26 -0
- package/generator/test/azure/requirements.txt +1 -0
- package/package.json +2 -2
|
@@ -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"
|
|
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
|
|
@@ -97,9 +97,16 @@ const AZURE_EMITTER_OPTIONS: Record<string, Record<string, string> | Record<stri
|
|
|
97
97
|
"azure/payload/pageable": {
|
|
98
98
|
namespace: "specs.azure.payload.pageable",
|
|
99
99
|
},
|
|
100
|
+
"azure/versioning/previewVersion": {
|
|
101
|
+
namespace: "specs.azure.versioning.previewversion",
|
|
102
|
+
},
|
|
100
103
|
"client/structure/default": {
|
|
101
104
|
namespace: "client.structure.service",
|
|
102
105
|
},
|
|
106
|
+
"client/structure/client-operation-group": {
|
|
107
|
+
"package-name": "client-structure-clientoperationgroup",
|
|
108
|
+
namespace: "client.structure.clientoperationgroup",
|
|
109
|
+
},
|
|
103
110
|
"client/structure/multi-client": {
|
|
104
111
|
"package-name": "client-structure-multiclient",
|
|
105
112
|
namespace: "client.structure.multiclient",
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
1052
|
+
retval.append(
|
|
1053
|
+
f" error = _failsafe_deserialize({type_annotation},{pylint_disable}\n response)"
|
|
1054
|
+
)
|
|
1047
1055
|
else:
|
|
1048
1056
|
retval.append(
|
|
1049
|
-
|
|
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(
|
|
1090
|
+
retval.append(
|
|
1091
|
+
" error = _failsafe_deserialize_xml("
|
|
1092
|
+
f"{type_annotation},{pylint_disable}\n response)"
|
|
1093
|
+
)
|
|
1082
1094
|
else:
|
|
1083
|
-
retval.append(
|
|
1095
|
+
retval.append(
|
|
1096
|
+
" error = _failsafe_deserialize("
|
|
1097
|
+
f"{type_annotation},{pylint_disable}\n response)"
|
|
1098
|
+
)
|
|
1084
1099
|
else:
|
|
1085
1100
|
retval.append(
|
|
1086
|
-
|
|
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(
|
|
38
|
+
return template.render(
|
|
39
|
+
code_model=self.code_model, schemas=schemas, enums=enums, has_models=has_models, has_enums=has_enums
|
|
40
|
+
)
|
|
@@ -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__ = [
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
{% endfor %}
|
|
20
20
|
{% for description in param_description_and_response_docstring %}
|
|
21
21
|
{% if description %}
|
|
22
|
-
{
|
|
22
|
+
{% set description = wrap_string(description, wrapstring='\n ') %}
|
|
23
|
+
{{ " " if description[0] != ":" and description[0] != " " else "" }}{{ description}}
|
|
23
24
|
{% else %}
|
|
24
25
|
|
|
25
26
|
{% endif %}
|
|
Binary file
|
|
@@ -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
|
-
|
|
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(
|
|
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(
|
|
1052
|
+
retval.append(
|
|
1053
|
+
f" error = _failsafe_deserialize({type_annotation},{pylint_disable}\n response)"
|
|
1054
|
+
)
|
|
1047
1055
|
else:
|
|
1048
1056
|
retval.append(
|
|
1049
|
-
|
|
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(
|
|
1090
|
+
retval.append(
|
|
1091
|
+
" error = _failsafe_deserialize_xml("
|
|
1092
|
+
f"{type_annotation},{pylint_disable}\n response)"
|
|
1093
|
+
)
|
|
1082
1094
|
else:
|
|
1083
|
-
retval.append(
|
|
1095
|
+
retval.append(
|
|
1096
|
+
" error = _failsafe_deserialize("
|
|
1097
|
+
f"{type_annotation},{pylint_disable}\n response)"
|
|
1098
|
+
)
|
|
1084
1099
|
else:
|
|
1085
1100
|
retval.append(
|
|
1086
|
-
|
|
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(
|
|
38
|
+
return template.render(
|
|
39
|
+
code_model=self.code_model, schemas=schemas, enums=enums, has_models=has_models, has_enums=has_enums
|
|
40
|
+
)
|
|
@@ -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__ = [
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
{% endfor %}
|
|
20
20
|
{% for description in param_description_and_response_docstring %}
|
|
21
21
|
{% if description %}
|
|
22
|
-
{
|
|
22
|
+
{% set description = wrap_string(description, wrapstring='\n ') %}
|
|
23
|
+
{{ " " if description[0] != ":" and description[0] != " " else "" }}{{ description}}
|
|
23
24
|
{% else %}
|
|
24
25
|
|
|
25
26
|
{% endif %}
|
|
@@ -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)
|
package/generator/test/azure/mock_api_tests/test_azure_client_generator_core_client_location.py
CHANGED
|
@@ -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()
|
|
@@ -36,6 +36,7 @@ azure-mgmt-core==1.6.0
|
|
|
36
36
|
-e ./generated/client-naming-enum-conflict
|
|
37
37
|
-e ./generated/client-overload
|
|
38
38
|
-e ./generated/client-structure-default
|
|
39
|
+
-e ./generated/client-structure-clientoperationgroup
|
|
39
40
|
-e ./generated/client-structure-multiclient
|
|
40
41
|
-e ./generated/client-structure-renamedoperation
|
|
41
42
|
-e ./generated/client-structure-twooperationgroup
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typespec/http-client-python",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"author": "Microsoft Corporation",
|
|
5
5
|
"description": "TypeSpec emitter for Python SDKs",
|
|
6
6
|
"homepage": "https://typespec.io",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"@azure-tools/typespec-azure-resource-manager": "~0.60.0",
|
|
83
83
|
"@azure-tools/typespec-azure-rulesets": "~0.60.0",
|
|
84
84
|
"@azure-tools/typespec-client-generator-core": "~0.60.2",
|
|
85
|
-
"@azure-tools/azure-http-specs": "0.1.0-alpha.
|
|
85
|
+
"@azure-tools/azure-http-specs": "0.1.0-alpha.30-dev.6",
|
|
86
86
|
"@typespec/compiler": "^1.4.0",
|
|
87
87
|
"@typespec/http": "^1.4.0",
|
|
88
88
|
"@typespec/openapi": "^1.4.0",
|