From 182625875fd5b132b83ca065cada0c58b5461b88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 04:56:19 +0000 Subject: [PATCH 1/5] Initial plan From 526277edcc619caa7b0364b6e8beec035aa6f3ee Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 19 Jan 2026 05:02:00 +0000 Subject: [PATCH 2/5] Add enum array encoding test cases for http-client-python Co-authored-by: msyyc <70930885+msyyc@users.noreply.github.com> --- ...st-http-client-python-2026-0-19-4-57-10.md | 7 ++ .../asynctests/test_encode_array_async.py | 96 +++++++++++++++++++ .../test_encode_array.py | 88 +++++++++++++++++ packages/http-client-python/package.json | 2 +- 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 .chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md diff --git a/.chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md b/.chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md new file mode 100644 index 00000000000..d96914eced8 --- /dev/null +++ b/.chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md @@ -0,0 +1,7 @@ +--- +changeKind: internal +packages: + - "@typespec/http-client-python" +--- + +Add test cases for enum and extensible enum array encoding. diff --git a/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_encode_array_async.py b/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_encode_array_async.py index 27cae3caa09..925780cd4aa 100644 --- a/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_encode_array_async.py +++ b/packages/http-client-python/generator/test/generic_mock_api_tests/asynctests/test_encode_array_async.py @@ -41,3 +41,99 @@ async def test_newline_delimited(client: ArrayClient): body = models.NewlineDelimitedArrayProperty(value=["blue", "red", "green"]) result = await client.property.newline_delimited(body) assert result.value == ["blue", "red", "green"] + + +@pytest.mark.asyncio +async def test_enum_comma_delimited(client: ArrayClient): + body = models.CommaDelimitedEnumArrayProperty(value=[models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN]) + result = await client.property.enum_comma_delimited(body) + assert result.value == [models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN] + + +@pytest.mark.asyncio +async def test_enum_space_delimited(client: ArrayClient): + body = models.SpaceDelimitedEnumArrayProperty(value=[models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN]) + result = await client.property.enum_space_delimited(body) + assert result.value == [models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN] + + +@pytest.mark.asyncio +async def test_enum_pipe_delimited(client: ArrayClient): + body = models.PipeDelimitedEnumArrayProperty(value=[models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN]) + result = await client.property.enum_pipe_delimited(body) + assert result.value == [models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN] + + +@pytest.mark.asyncio +async def test_enum_newline_delimited(client: ArrayClient): + body = models.NewlineDelimitedEnumArrayProperty(value=[models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN]) + result = await client.property.enum_newline_delimited(body) + assert result.value == [models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN] + + +@pytest.mark.asyncio +async def test_extensible_enum_comma_delimited(client: ArrayClient): + body = models.CommaDelimitedExtensibleEnumArrayProperty( + value=[ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + ) + result = await client.property.extensible_enum_comma_delimited(body) + assert result.value == [ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + + +@pytest.mark.asyncio +async def test_extensible_enum_space_delimited(client: ArrayClient): + body = models.SpaceDelimitedExtensibleEnumArrayProperty( + value=[ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + ) + result = await client.property.extensible_enum_space_delimited(body) + assert result.value == [ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + + +@pytest.mark.asyncio +async def test_extensible_enum_pipe_delimited(client: ArrayClient): + body = models.PipeDelimitedExtensibleEnumArrayProperty( + value=[ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + ) + result = await client.property.extensible_enum_pipe_delimited(body) + assert result.value == [ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + + +@pytest.mark.asyncio +async def test_extensible_enum_newline_delimited(client: ArrayClient): + body = models.NewlineDelimitedExtensibleEnumArrayProperty( + value=[ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + ) + result = await client.property.extensible_enum_newline_delimited(body) + assert result.value == [ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] diff --git a/packages/http-client-python/generator/test/generic_mock_api_tests/test_encode_array.py b/packages/http-client-python/generator/test/generic_mock_api_tests/test_encode_array.py index 3e1b48c908e..a09a05a793b 100644 --- a/packages/http-client-python/generator/test/generic_mock_api_tests/test_encode_array.py +++ b/packages/http-client-python/generator/test/generic_mock_api_tests/test_encode_array.py @@ -36,3 +36,91 @@ def test_newline_delimited(client: ArrayClient): body = models.NewlineDelimitedArrayProperty(value=["blue", "red", "green"]) result = client.property.newline_delimited(body) assert result.value == ["blue", "red", "green"] + + +def test_enum_comma_delimited(client: ArrayClient): + body = models.CommaDelimitedEnumArrayProperty(value=[models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN]) + result = client.property.enum_comma_delimited(body) + assert result.value == [models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN] + + +def test_enum_space_delimited(client: ArrayClient): + body = models.SpaceDelimitedEnumArrayProperty(value=[models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN]) + result = client.property.enum_space_delimited(body) + assert result.value == [models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN] + + +def test_enum_pipe_delimited(client: ArrayClient): + body = models.PipeDelimitedEnumArrayProperty(value=[models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN]) + result = client.property.enum_pipe_delimited(body) + assert result.value == [models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN] + + +def test_enum_newline_delimited(client: ArrayClient): + body = models.NewlineDelimitedEnumArrayProperty(value=[models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN]) + result = client.property.enum_newline_delimited(body) + assert result.value == [models.Colors.BLUE, models.Colors.RED, models.Colors.GREEN] + + +def test_extensible_enum_comma_delimited(client: ArrayClient): + body = models.CommaDelimitedExtensibleEnumArrayProperty( + value=[ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + ) + result = client.property.extensible_enum_comma_delimited(body) + assert result.value == [ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + + +def test_extensible_enum_space_delimited(client: ArrayClient): + body = models.SpaceDelimitedExtensibleEnumArrayProperty( + value=[ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + ) + result = client.property.extensible_enum_space_delimited(body) + assert result.value == [ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + + +def test_extensible_enum_pipe_delimited(client: ArrayClient): + body = models.PipeDelimitedExtensibleEnumArrayProperty( + value=[ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + ) + result = client.property.extensible_enum_pipe_delimited(body) + assert result.value == [ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + + +def test_extensible_enum_newline_delimited(client: ArrayClient): + body = models.NewlineDelimitedExtensibleEnumArrayProperty( + value=[ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] + ) + result = client.property.extensible_enum_newline_delimited(body) + assert result.value == [ + models.ColorsExtensibleEnum.BLUE, + models.ColorsExtensibleEnum.RED, + models.ColorsExtensibleEnum.GREEN, + ] diff --git a/packages/http-client-python/package.json b/packages/http-client-python/package.json index 3a14ca246a4..a122c496717 100644 --- a/packages/http-client-python/package.json +++ b/packages/http-client-python/package.json @@ -94,7 +94,7 @@ "@typespec/sse": "~0.78.0", "@typespec/streams": "~0.78.0", "@typespec/xml": "~0.78.0", - "@typespec/http-specs": "0.1.0-alpha.31", + "@typespec/http-specs": "0.1.0-alpha.32-dev.1", "@types/js-yaml": "~4.0.5", "@types/node": "~24.1.0", "@types/semver": "7.5.8", From 5eae670b561402d4ba0ef66fb376302b63b2faf8 Mon Sep 17 00:00:00 2001 From: Yuchao Yan Date: Mon, 19 Jan 2026 07:37:36 +0000 Subject: [PATCH 3/5] support enum type --- ...test-case-http-client-2026-0-19-7-35-17.md | 7 ++++ .../codegen/templates/model_base.py.jinja2 | 41 ++++++++++++++----- packages/http-client-python/package-lock.json | 18 ++++---- 3 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 .chronus/changes/copilot-add-test-case-http-client-2026-0-19-7-35-17.md diff --git a/.chronus/changes/copilot-add-test-case-http-client-2026-0-19-7-35-17.md b/.chronus/changes/copilot-add-test-case-http-client-2026-0-19-7-35-17.md new file mode 100644 index 00000000000..a38cc874111 --- /dev/null +++ b/.chronus/changes/copilot-add-test-case-http-client-2026-0-19-7-35-17.md @@ -0,0 +1,7 @@ +--- +changeKind: feature +packages: + - "@typespec/http-client-python" +--- + +Support enum type for array encode \ No newline at end of file diff --git a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 index 28b877006da..1bb07171cce 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 @@ -848,6 +848,25 @@ def _deserialize_multiple_sequence( return type(obj)(_deserialize(deserializer, entry, module) for entry, deserializer in zip(obj, entry_deserializers)) + +def _deserialize_multiple_sequence( + entry_deserializers: list[typing.Optional[typing.Callable]], + module: typing.Optional[str], + obj, +): + if obj is None: + return obj + return type(obj)(_deserialize(deserializer, entry, module) for entry, deserializer in zip(obj, entry_deserializers)) + + +def _is_array_encoded_deserializer(deserializer: functools.partial) -> bool: + return ( + isinstance(deserializer, functools.partial) + and isinstance(deserializer.args[0], functools.partial) + and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable + ) + + def _deserialize_sequence( deserializer: typing.Optional[typing.Callable], module: typing.Optional[str], @@ -857,17 +876,19 @@ def _deserialize_sequence( return obj if isinstance(obj, ET.Element): obj = list(obj) - try: - if ( - isinstance(obj, str) - and isinstance(deserializer, functools.partial) - and isinstance(deserializer.args[0], functools.partial) - and deserializer.args[0].func == _deserialize_array_encoded # pylint: disable=comparison-with-callable - ): - # encoded string may be deserialized to sequence + + # encoded string may be deserialized to sequence + if isinstance(obj, str) and isinstance(deserializer, functools.partial): + # for list[str] + if _is_array_encoded_deserializer(deserializer): return deserializer(obj) - except: # pylint: disable=bare-except - pass + + # for list[Union[...]] + if isinstance(deserializer.args[0], list): + for sub_deserializer in deserializer.args[0]: + if _is_array_encoded_deserializer(sub_deserializer): + return sub_deserializer(obj) + return type(obj)(_deserialize(deserializer, entry, module) for entry in obj) diff --git a/packages/http-client-python/package-lock.json b/packages/http-client-python/package-lock.json index 1fc2eff24cb..6b39d9b20f6 100644 --- a/packages/http-client-python/package-lock.json +++ b/packages/http-client-python/package-lock.json @@ -29,7 +29,7 @@ "@typespec/compiler": "^1.8.0", "@typespec/events": "~0.78.0", "@typespec/http": "^1.8.0", - "@typespec/http-specs": "0.1.0-alpha.31", + "@typespec/http-specs": "0.1.0-alpha.32-dev.1", "@typespec/openapi": "^1.8.0", "@typespec/rest": "~0.78.0", "@typespec/spec-api": "0.1.0-alpha.12", @@ -2318,14 +2318,14 @@ } }, "node_modules/@typespec/http-specs": { - "version": "0.1.0-alpha.31", - "resolved": "https://registry.npmjs.org/@typespec/http-specs/-/http-specs-0.1.0-alpha.31.tgz", - "integrity": "sha512-ji+Zt4wB8NnVw3cFiE+LmkXCTfBWEq1xwBQrxJ83q28NQeKBMOVxynjiCz344gxOCcEfhAX4mwKcYeJUKAFVYQ==", + "version": "0.1.0-alpha.32-dev.1", + "resolved": "https://registry.npmjs.org/@typespec/http-specs/-/http-specs-0.1.0-alpha.32-dev.1.tgz", + "integrity": "sha512-b+uzFhToERrmV154eqnCoQiw4Jekn+DRamfZVAl7ndVeayDq9zLNZyPnCmeU1+bdKxUGO8WoGkpA9BeGP3teeA==", "dev": true, "license": "MIT", "dependencies": { - "@typespec/spec-api": "^0.1.0-alpha.12", - "@typespec/spector": "^0.1.0-alpha.22", + "@typespec/spec-api": "^0.1.0-alpha.12 || >=0.1.0-alpha.13-dev <0.1.0-alpha.13", + "@typespec/spector": "^0.1.0-alpha.22 || >=0.1.0-alpha.23-dev <0.1.0-alpha.23", "deep-equal": "^2.2.0" }, "engines": { @@ -2334,9 +2334,9 @@ "peerDependencies": { "@typespec/compiler": "^1.8.0", "@typespec/http": "^1.8.0", - "@typespec/rest": "^0.78.0", - "@typespec/versioning": "^0.78.0", - "@typespec/xml": "^0.78.0" + "@typespec/rest": "^0.78.0 || >=0.79.0-dev <0.79.0", + "@typespec/versioning": "^0.78.0 || >=0.79.0-dev <0.79.0", + "@typespec/xml": "^0.78.0 || >=0.79.0-dev <0.79.0" } }, "node_modules/@typespec/openapi": { From 26b300f09221afb775dd47224ee3b9e8f435c9b7 Mon Sep 17 00:00:00 2001 From: Yuchao Yan Date: Mon, 19 Jan 2026 15:40:47 +0800 Subject: [PATCH 4/5] Delete .chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md --- ...enum-array-test-http-client-python-2026-0-19-4-57-10.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md diff --git a/.chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md b/.chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md deleted file mode 100644 index d96914eced8..00000000000 --- a/.chronus/changes/add-enum-array-test-http-client-python-2026-0-19-4-57-10.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -changeKind: internal -packages: - - "@typespec/http-client-python" ---- - -Add test cases for enum and extensible enum array encoding. From 7ade01efac977a2d4e623d16d13bc11fc00a7964 Mon Sep 17 00:00:00 2001 From: Yuchao Yan Date: Mon, 19 Jan 2026 07:42:05 +0000 Subject: [PATCH 5/5] review --- .../pygen/codegen/templates/model_base.py.jinja2 | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 index 1bb07171cce..650204d2e35 100644 --- a/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 +++ b/packages/http-client-python/generator/pygen/codegen/templates/model_base.py.jinja2 @@ -838,17 +838,6 @@ def _deserialize_dict( return {k: _deserialize(value_deserializer, v, module) for k, v in obj.items()} -def _deserialize_multiple_sequence( - entry_deserializers: list[typing.Optional[typing.Callable]], - module: typing.Optional[str], - obj, -): - if obj is None: - return obj - return type(obj)(_deserialize(deserializer, entry, module) for entry, deserializer in zip(obj, entry_deserializers)) - - - def _deserialize_multiple_sequence( entry_deserializers: list[typing.Optional[typing.Callable]], module: typing.Optional[str],