Skip to content
Closed
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
7 changes: 7 additions & 0 deletions .chronus/changes/fix-python-wire-encoding-2026-08-11.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: fix
packages:
- "@typespec/http-client-python"
---

Preserve Python boolean, integer, and bytes client types when using supported string, base64, or base64url wire encodings.
18 changes: 16 additions & 2 deletions packages/http-client-python/emitter/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,10 +474,17 @@ const sdkScalarKindToPythonKind: Record<string, string> = {
azureLocation: "string",
};

const supportedPythonEncodings = new Map<string, ReadonlySet<string>>([
["boolean", new Set(["string"])],
["integer", new Set(["string"])],
["bytes", new Set(["base64", "base64url"])],
]);

function emitBuiltInType(
context: PythonSdkContext,
type: SdkBuiltInType | SdkDurationType | SdkDateTimeType,
): Record<string, any> {
const pythonType = sdkScalarKindToPythonKind[type.kind] || type.kind;
if (type.encode) {
if (type.kind === "duration") {
if (type.encode === "ISO8601") {
Expand Down Expand Up @@ -509,7 +516,14 @@ function emitBuiltInType(
}
}

// fallback to wire type for unknown or unsupported encode
if (supportedPythonEncodings.get(pythonType)?.has(type.encode)) {
return getSimpleTypeResult(context, {
type: pythonType,
encode: type.encode,
});
}

// Python cannot apply unknown/custom encodings, so expose the wire type instead of silently ignoring the encoding.
if ("wireType" in type && type.wireType !== undefined) {
return getSimpleTypeResult(context, {
type: sdkScalarKindToPythonKind[type.wireType.kind] || type.wireType.kind,
Expand All @@ -519,7 +533,7 @@ function emitBuiltInType(
}

return getSimpleTypeResult(context, {
type: sdkScalarKindToPythonKind[type.kind] || type.kind, // TODO: switch to kind
type: pythonType, // TODO: switch to kind
encode: type.encode,
});
}
Expand Down
72 changes: 72 additions & 0 deletions packages/http-client-python/emitter/test/types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import type { SdkBuiltInType } from "@azure-tools/typespec-client-generator-core";
import { deepStrictEqual } from "assert";
import { describe, it } from "vitest";
import type { PythonSdkContext } from "../src/lib.js";
import { getType } from "../src/types.js";

function createBuiltInType(
kind: SdkBuiltInType["kind"],
encode?: string,
wireType?: SdkBuiltInType,
): SdkBuiltInType {
return {
kind,
name: kind,
crossLanguageDefinitionId: `TypeSpec.${kind}`,
decorators: [],
encode,
wireType,
};
}

function emitBuiltInType(type: SdkBuiltInType): Record<string, any> {
const context = {
__simpleTypesMap: new Map(),
} as PythonSdkContext;
return getType(context, type);
}

describe("typespec-python: built-in types", () => {
it("preserves boolean encoded as string", () => {
deepStrictEqual(
emitBuiltInType(createBuiltInType("boolean", "string", createBuiltInType("string"))),
{
type: "boolean",
encode: "string",
},
);
});

it.each(["safeint", "uint32", "uint8"] as const)(
"preserves %s encoded as string as an integer",
(kind) => {
deepStrictEqual(
emitBuiltInType(createBuiltInType(kind, "string", createBuiltInType("string"))),
{
type: "integer",
encode: "string",
},
);
},
);

it.each(["base64", "base64url"] as const)("preserves bytes encoded as %s", (encode) => {
deepStrictEqual(
emitBuiltInType(createBuiltInType("bytes", encode, createBuiltInType("string"))),
{
type: "bytes",
encode,
},
);
});

it("falls back to the wire type for a custom encoding", () => {
deepStrictEqual(
emitBuiltInType(createBuiltInType("string", "abc", createBuiltInType("int32"))),
{
type: "integer",
encode: "abc",
},
);
});
});
Loading