Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: feature
packages:
- "@typespec/http-client-python"
---

Add mock API test coverage for `@encode(string)` on boolean properties (`encode/boolean` Spector scenarios). Fix Python generator to correctly serialize and deserialize boolean values encoded as strings (case-insensitive `true`/`false`)
8 changes: 6 additions & 2 deletions packages/http-client-python/emitter/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
SdkUnionType,
UsageFlags,
} from "@azure-tools/typespec-client-generator-core";
import { Type } from "@typespec/compiler";
import { getEncode, Type } from "@typespec/compiler";
import { HttpAuth, Visibility } from "@typespec/http";
import { dump } from "js-yaml";
import { PythonSdkContext } from "./lib.js";
Expand Down Expand Up @@ -234,6 +234,10 @@ function emitProperty(
addDisableGenerationMap(context, property.type);
}
const isNullable = !isMultipartFileInput && sourceType.kind === "nullable";
const booleanEncode =
property.type.kind === "boolean" && property.__raw
? getEncode(context.program, property.__raw)
: undefined;
return {
clientName: getClientName(property),
isExactName: property.isExactName,
Expand All @@ -252,7 +256,7 @@ function emitProperty(
flatten: property.flatten,
isMultipartFileInput: isMultipartFileInput,
xmlMetadata: getXmlMetadata(property),
encode: property.encode,
encode: property.encode ?? (booleanEncode?.type.name === "string" ? "str" : undefined),
clientDefaultValue: property.clientDefaultValue,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@ export const AZURE_EMITTER_OPTIONS: Record<
"client/overload": {
namespace: "client.overload",
},
"encode/boolean": {
namespace: "encode.boolean",
},
"encode/duration": {
namespace: "encode.duration",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ def default_template_representation_declaration(self) -> str:


class BooleanType(PrimitiveType):
def __init__(self, yaml_data: dict[str, Any], code_model: "CodeModel") -> None:
super().__init__(yaml_data=yaml_data, code_model=code_model)
if yaml_data.get("encode") == "string":
self.encode = "str"

def serialization_type(self, **kwargs: Any) -> str:
return "bool"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,12 @@ def _deserialize_int_as_str(attr):
return int(attr)


def _deserialize_bool_as_str(attr):
if isinstance(attr, bool):
return attr
return attr.lower() == "true"


_DESERIALIZE_MAPPING = {
datetime: _deserialize_datetime,
date: _deserialize_date,
Expand Down Expand Up @@ -375,6 +381,8 @@ _DESERIALIZE_MAPPING_WITHFORMAT = {
def get_deserializer(annotation: typing.Any, rf: typing.Optional["_RestField"] = None):
if annotation is int and rf and rf._format == "str":
return _deserialize_int_as_str
if annotation is bool and rf and rf._format == "str":
return _deserialize_bool_as_str
if annotation is str and rf and rf._format in _ARRAY_ENCODE_MAPPING:
return functools.partial(_deserialize_array_encoded, _ARRAY_ENCODE_MAPPING[rf._format])
if rf and rf._format:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import pytest
import pytest_asyncio
from encode.boolean.aio import BooleanClient
from encode.boolean import models


@pytest_asyncio.fixture
async def client():
async with BooleanClient() as client:
yield client


@pytest.mark.asyncio
async def test_property_true_lower(client: BooleanClient):
result = await client.property.true_lower(models.BoolAsStringProperty(value=True))
assert result.value is True
assert result["value"] == "true"


@pytest.mark.asyncio
async def test_property_false_lower(client: BooleanClient):
result = await client.property.false_lower(models.BoolAsStringProperty(value=False))
assert result.value is False
assert result["value"] == "false"


@pytest.mark.asyncio
async def test_property_true_upper(client: BooleanClient):
result = await client.property.true_upper(models.BoolAsStringProperty(value=True))
assert result.value is True
assert result["value"] == "TRUE"


@pytest.mark.asyncio
async def test_property_false_mixed(client: BooleanClient):
result = await client.property.false_mixed(models.BoolAsStringProperty(value=False))
assert result.value is False
assert result["value"] == "FaLsE"
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import pytest
from encode.boolean import BooleanClient, models


@pytest.fixture
def client():
with BooleanClient() as client:
yield client


def test_property_true_lower(client: BooleanClient):
result = client.property.true_lower(models.BoolAsStringProperty(value=True))
assert result.value is True
assert result["value"] == "true"


def test_property_false_lower(client: BooleanClient):
result = client.property.false_lower(models.BoolAsStringProperty(value=False))
assert result.value is False
assert result["value"] == "false"


def test_property_true_upper(client: BooleanClient):
result = client.property.true_upper(models.BoolAsStringProperty(value=True))
assert result.value is True
assert result["value"] == "TRUE"


def test_property_false_mixed(client: BooleanClient):
result = client.property.false_mixed(models.BoolAsStringProperty(value=False))
assert result.value is False
assert result["value"] == "FaLsE"
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import pytest
import pytest_asyncio
from encode.boolean.aio import BooleanClient
from encode.boolean.property import models


@pytest_asyncio.fixture
async def client():
async with BooleanClient() as client:
yield client


@pytest.mark.asyncio
async def test_property_true_lower(client: BooleanClient):
result = await client.property.true_lower(models.BoolAsStringProperty(value=True))
assert result.value is True
assert result["value"] == "true"


@pytest.mark.asyncio
async def test_property_false_lower(client: BooleanClient):
result = await client.property.false_lower(models.BoolAsStringProperty(value=False))
assert result.value is False
assert result["value"] == "false"


@pytest.mark.asyncio
async def test_property_true_upper(client: BooleanClient):
result = await client.property.true_upper(models.BoolAsStringProperty(value=True))
assert result.value is True
assert result["value"] == "TRUE"


@pytest.mark.asyncio
async def test_property_false_mixed(client: BooleanClient):
result = await client.property.false_mixed(models.BoolAsStringProperty(value=False))
assert result.value is False
assert result["value"] == "FaLsE"
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# -------------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License. See License.txt in the project root for
# license information.
# --------------------------------------------------------------------------
import pytest
from encode.boolean import BooleanClient
from encode.boolean.property import models


@pytest.fixture
def client():
with BooleanClient() as client:
yield client


def test_property_true_lower(client: BooleanClient):
result = client.property.true_lower(models.BoolAsStringProperty(value=True))
assert result.value is True
assert result["value"] == "true"


def test_property_false_lower(client: BooleanClient):
result = client.property.false_lower(models.BoolAsStringProperty(value=False))
assert result.value is False
assert result["value"] == "false"


def test_property_true_upper(client: BooleanClient):
result = client.property.true_upper(models.BoolAsStringProperty(value=True))
assert result.value is True
assert result["value"] == "TRUE"


def test_property_false_mixed(client: BooleanClient):
result = client.property.false_mixed(models.BoolAsStringProperty(value=False))
assert result.value is False
assert result["value"] == "FaLsE"
Loading