@typespec/http-client-python 0.35.1 → 0.37.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/README.md +6 -0
- package/dist/emitter/code-model.d.ts +1 -1
- package/dist/emitter/code-model.d.ts.map +1 -1
- package/dist/emitter/code-model.js.map +1 -1
- package/dist/emitter/emitter.d.ts +2 -2
- package/dist/emitter/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +24 -3
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/external-process.d.ts +1 -15
- package/dist/emitter/external-process.d.ts.map +1 -1
- package/dist/emitter/external-process.js +1 -17
- package/dist/emitter/external-process.js.map +1 -1
- package/dist/emitter/http.d.ts +45 -2
- package/dist/emitter/http.d.ts.map +1 -1
- package/dist/emitter/http.js +97 -1
- package/dist/emitter/http.js.map +1 -1
- package/dist/emitter/index.d.ts +2 -1
- package/dist/emitter/index.d.ts.map +1 -1
- package/dist/emitter/index.js.map +1 -1
- package/dist/emitter/lib.d.ts +3 -2
- package/dist/emitter/lib.d.ts.map +1 -1
- package/dist/emitter/lib.js +6 -1
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/node-runner.browser.d.ts +1 -1
- package/dist/emitter/node-runner.browser.d.ts.map +1 -1
- package/dist/emitter/node-runner.browser.js.map +1 -1
- package/dist/emitter/node-runner.d.ts +3 -3
- package/dist/emitter/node-runner.d.ts.map +1 -1
- package/dist/emitter/node-runner.js.map +1 -1
- package/dist/emitter/system-requirements.d.ts.map +1 -1
- package/dist/emitter/system-requirements.js.map +1 -1
- package/dist/emitter/types.d.ts +4 -4
- package/dist/emitter/types.d.ts.map +1 -1
- package/dist/emitter/types.js +15 -3
- package/dist/emitter/types.js.map +1 -1
- package/dist/emitter/utils.d.ts +2 -2
- package/dist/emitter/utils.d.ts.map +1 -1
- package/dist/emitter/utils.js +1 -1
- package/dist/emitter/utils.js.map +1 -1
- package/dist/emitter/yaml-utils.d.ts +19 -0
- package/dist/emitter/yaml-utils.d.ts.map +1 -0
- package/dist/emitter/yaml-utils.js +22 -0
- package/dist/emitter/yaml-utils.js.map +1 -0
- package/emitter/src/code-model.ts +4 -2
- package/emitter/src/emitter.ts +31 -7
- package/emitter/src/external-process.ts +3 -19
- package/emitter/src/http.ts +159 -5
- package/emitter/src/index.ts +2 -1
- package/emitter/src/lib.ts +11 -6
- package/emitter/src/node-runner.browser.ts +2 -1
- package/emitter/src/node-runner.ts +6 -3
- package/emitter/src/system-requirements.ts +2 -1
- package/emitter/src/types.ts +23 -8
- package/emitter/src/utils.ts +5 -4
- package/emitter/src/yaml-utils.ts +22 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/emitter/test/browser-pyodide.test.ts +25 -0
- package/emitter/test/streaming.test.ts +155 -0
- package/emitter/test/types.test.ts +72 -0
- package/emitter/test/{external-process.test.ts → yaml-utils.test.ts} +2 -2
- package/eng/scripts/ci/dev_requirements.txt +1 -1
- package/eng/scripts/ci/regenerate-common.ts +1 -1
- package/eng/scripts/ci/regenerate.ts +1 -2
- package/eng/scripts/ci/run-tests.ts +2 -1
- package/eng/scripts/ci/run_pylint.py +1 -0
- package/eng/scripts/setup/__pycache__/package_manager.cpython-311.pyc +0 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-311.pyc +0 -0
- package/eng/scripts/setup/system-requirements.ts +2 -1
- package/generator/build/lib/pygen/__init__.py +24 -5
- package/generator/build/lib/pygen/codegen/models/__init__.py +3 -1
- package/generator/build/lib/pygen/codegen/models/code_model.py +29 -9
- package/generator/build/lib/pygen/codegen/models/enum_type.py +21 -9
- package/generator/build/lib/pygen/codegen/models/list_type.py +1 -0
- package/generator/build/lib/pygen/codegen/models/model_type.py +1 -3
- package/generator/build/lib/pygen/codegen/models/operation.py +13 -3
- package/generator/build/lib/pygen/codegen/models/parameter.py +3 -1
- package/generator/build/lib/pygen/codegen/models/response.py +152 -8
- package/generator/build/lib/pygen/codegen/models/utils.py +14 -0
- package/generator/build/lib/pygen/codegen/serializers/__init__.py +13 -6
- package/generator/build/lib/pygen/codegen/serializers/builder_serializer.py +219 -69
- package/generator/build/lib/pygen/codegen/serializers/client_serializer.py +13 -6
- package/generator/build/lib/pygen/codegen/serializers/general_serializer.py +4 -0
- package/generator/build/lib/pygen/codegen/serializers/import_serializer.py +27 -0
- package/generator/build/lib/pygen/codegen/serializers/model_serializer.py +4 -1
- package/generator/build/lib/pygen/codegen/serializers/types_serializer.py +44 -12
- package/generator/build/lib/pygen/codegen/templates/streaming_base.py.jinja2 +767 -0
- package/generator/build/lib/pygen/preprocess/__init__.py +70 -17
- package/generator/build/lib/pygen/utils.py +15 -0
- package/generator/component-detection-pip-report.json +9 -9
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/__init__.py +24 -5
- package/generator/pygen/codegen/models/__init__.py +3 -1
- package/generator/pygen/codegen/models/code_model.py +29 -9
- package/generator/pygen/codegen/models/enum_type.py +21 -9
- package/generator/pygen/codegen/models/list_type.py +1 -0
- package/generator/pygen/codegen/models/model_type.py +1 -3
- package/generator/pygen/codegen/models/operation.py +13 -3
- package/generator/pygen/codegen/models/parameter.py +3 -1
- package/generator/pygen/codegen/models/response.py +152 -8
- package/generator/pygen/codegen/models/utils.py +14 -0
- package/generator/pygen/codegen/serializers/__init__.py +13 -6
- package/generator/pygen/codegen/serializers/builder_serializer.py +219 -69
- package/generator/pygen/codegen/serializers/client_serializer.py +13 -6
- package/generator/pygen/codegen/serializers/general_serializer.py +4 -0
- package/generator/pygen/codegen/serializers/import_serializer.py +27 -0
- package/generator/pygen/codegen/serializers/model_serializer.py +4 -1
- package/generator/pygen/codegen/serializers/types_serializer.py +44 -12
- package/generator/pygen/codegen/templates/streaming_base.py.jinja2 +767 -0
- package/generator/pygen/preprocess/__init__.py +70 -17
- package/generator/pygen/utils.py +15 -0
- package/generator/pygen.egg-info/PKG-INFO +1 -1
- package/generator/pygen.egg-info/SOURCES.txt +1 -0
- package/generator/pygen.egg-info/requires.txt +1 -1
- package/generator/setup.py +1 -1
- package/package.json +33 -33
package/emitter/src/http.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { getNamespaceFullName, NoTarget } from "@typespec/compiler";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
getHttpOperationParameter,
|
|
3
|
+
import type {
|
|
5
4
|
SdkBasicServiceMethod,
|
|
6
5
|
SdkBodyParameter,
|
|
7
6
|
SdkClientType,
|
|
@@ -19,11 +18,13 @@ import {
|
|
|
19
18
|
SdkQueryParameter,
|
|
20
19
|
SdkServiceMethod,
|
|
21
20
|
SdkServiceResponseHeader,
|
|
21
|
+
SdkSseEventMetadata,
|
|
22
22
|
SdkType,
|
|
23
|
-
UsageFlags,
|
|
24
23
|
} from "@azure-tools/typespec-client-generator-core";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
24
|
+
import { getHttpOperationParameter, UsageFlags } from "@azure-tools/typespec-client-generator-core";
|
|
25
|
+
import type { HttpStatusCodeRange } from "@typespec/http";
|
|
26
|
+
import type { PythonSdkContext } from "./lib.js";
|
|
27
|
+
import { reportDiagnostic } from "./lib.js";
|
|
27
28
|
import { getType, KnownTypes } from "./types.js";
|
|
28
29
|
import {
|
|
29
30
|
emitParamBase,
|
|
@@ -42,6 +43,158 @@ export enum ReferredByOperationTypes {
|
|
|
42
43
|
NonPagingOnly = 2,
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
type StructuredStreamKind = "jsonl" | "sse";
|
|
47
|
+
type EmittedType = ReturnType<typeof getType>;
|
|
48
|
+
|
|
49
|
+
interface StructuredStreamEvent {
|
|
50
|
+
eventType: string | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Payload type for this one SSE event. For an event envelope, this is the type of the property
|
|
53
|
+
* marked `@Events.data`. Together with {@link eventType} these form the
|
|
54
|
+
* runtime dispatch table (wire event name -> model to deserialize) inside the generated
|
|
55
|
+
* `_callback`. This is a narrower type than {@link StructuredStreamingInfo.itemType}.
|
|
56
|
+
*/
|
|
57
|
+
payloadType: EmittedType;
|
|
58
|
+
/**
|
|
59
|
+
* True when this event is a `@terminalEvent` that carries a payload (a named / model event,
|
|
60
|
+
* not a bare string-constant sentinel). Such events are deserialized and yielded like any
|
|
61
|
+
* other event, and iteration stops immediately after one is yielded. Contrast with
|
|
62
|
+
* {@link StructuredStreamingInfo.terminalEvent}, the sentinel that stops without yielding.
|
|
63
|
+
*/
|
|
64
|
+
isTerminal?: boolean;
|
|
65
|
+
/** Content type of the payload, not the enclosing event. */
|
|
66
|
+
payloadContentType?: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface StructuredStreamingInfo {
|
|
70
|
+
kind: StructuredStreamKind;
|
|
71
|
+
/**
|
|
72
|
+
* The aggregate stream element type used for the `Stream[T]` / `AsyncStream[T]` return
|
|
73
|
+
* annotation (a single type expression). For homogeneous JSONL this is the one model; for
|
|
74
|
+
* heterogeneous SSE this is the union of every event payload.
|
|
75
|
+
*
|
|
76
|
+
* Note the deliberate overlap with the per-event {@link StructuredStreamEvent.payloadType}: for
|
|
77
|
+
* heterogeneous SSE this union is exactly the sum of the `events[]` payload types. Both are
|
|
78
|
+
* carried because the union alone cannot recover the wire-name -> member mapping needed for
|
|
79
|
+
* dispatch, and the events list alone is not a single valid type expression for the annotation.
|
|
80
|
+
*/
|
|
81
|
+
itemType: EmittedType;
|
|
82
|
+
events?: StructuredStreamEvent[];
|
|
83
|
+
/**
|
|
84
|
+
* A bare string-constant `@terminalEvent` with no event name (e.g. `"[DONE]"`). Iteration
|
|
85
|
+
* stops when an event's `data` equals this value, and the sentinel is NOT yielded. Named /
|
|
86
|
+
* model terminal events are carried in {@link events} with `isTerminal: true` instead.
|
|
87
|
+
*/
|
|
88
|
+
terminalEvent?: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** Whether pygen can deserialize the stream item type. */
|
|
92
|
+
export function isStructuredStreamType(type: SdkType): boolean {
|
|
93
|
+
switch (type.kind) {
|
|
94
|
+
case "model":
|
|
95
|
+
case "union":
|
|
96
|
+
return true;
|
|
97
|
+
case "nullable":
|
|
98
|
+
return isStructuredStreamType(type.type);
|
|
99
|
+
default:
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function getStructuredStreamKind(
|
|
105
|
+
response: SdkHttpResponse | SdkHttpErrorResponse,
|
|
106
|
+
): StructuredStreamKind | undefined {
|
|
107
|
+
if (response.sseMetadata) return "sse";
|
|
108
|
+
|
|
109
|
+
const contentTypes = response.streamMetadata?.contentTypes ?? response.contentTypes ?? [];
|
|
110
|
+
for (const contentType of contentTypes) {
|
|
111
|
+
const mediaType = contentType.split(";", 1)[0].trim().toLowerCase();
|
|
112
|
+
if (mediaType === "text/event-stream") return "sse";
|
|
113
|
+
if (mediaType === "application/jsonl") return "jsonl";
|
|
114
|
+
}
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function getStringConstantValue(type: SdkType): string | undefined {
|
|
119
|
+
if (type.kind === "nullable") return getStringConstantValue(type.type);
|
|
120
|
+
return type.kind === "constant" && typeof type.value === "string" ? type.value : undefined;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Split the SSE events into the runtime dispatch table and a bare string-constant sentinel.
|
|
125
|
+
*
|
|
126
|
+
* A `@terminalEvent` comes in two shapes:
|
|
127
|
+
* * a nameless string constant (e.g. `"[DONE]"`) -> a pure sentinel: iteration stops when an
|
|
128
|
+
* event's `data` equals this value and the event is NOT yielded. Returned as `terminalEvent`.
|
|
129
|
+
* * a named / model event (e.g. `error`, `response.completed`) -> carries a payload the consumer
|
|
130
|
+
* needs, so it is deserialized and yielded like any other event, then iteration stops. Returned
|
|
131
|
+
* in `events` with `isTerminal: true`.
|
|
132
|
+
*
|
|
133
|
+
* `toPayloadType` maps event payloads to emitted types; it is injected so this partitioning stays
|
|
134
|
+
* a pure function that can be unit-tested without a full emitter context.
|
|
135
|
+
*/
|
|
136
|
+
export function partitionSSEEvents(
|
|
137
|
+
events: readonly SdkSseEventMetadata[],
|
|
138
|
+
toPayloadType: (type: SdkType) => EmittedType,
|
|
139
|
+
): { events: StructuredStreamEvent[]; terminalEvent?: string } {
|
|
140
|
+
const dispatch: StructuredStreamEvent[] = [];
|
|
141
|
+
let terminalEvent: string | undefined;
|
|
142
|
+
for (const event of events) {
|
|
143
|
+
if (event.isTerminalEvent) {
|
|
144
|
+
const sentinelValue =
|
|
145
|
+
event.eventType === undefined
|
|
146
|
+
? (getStringConstantValue(event.payloadType) ?? getStringConstantValue(event.type))
|
|
147
|
+
: undefined;
|
|
148
|
+
if (sentinelValue !== undefined) {
|
|
149
|
+
// Keep the first sentinel; no current spec defines more than one.
|
|
150
|
+
terminalEvent ??= sentinelValue;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
dispatch.push({
|
|
154
|
+
eventType: event.eventType,
|
|
155
|
+
payloadType: toPayloadType(event.payloadType),
|
|
156
|
+
isTerminal: true,
|
|
157
|
+
payloadContentType: event.payloadContentType,
|
|
158
|
+
});
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
dispatch.push({
|
|
162
|
+
eventType: event.eventType,
|
|
163
|
+
payloadType: toPayloadType(event.payloadType),
|
|
164
|
+
payloadContentType: event.payloadContentType,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
return terminalEvent !== undefined ? { events: dispatch, terminalEvent } : { events: dispatch };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function emitStructuredStreamingInfo(
|
|
171
|
+
context: PythonSdkContext,
|
|
172
|
+
response: SdkHttpResponse | SdkHttpErrorResponse,
|
|
173
|
+
): StructuredStreamingInfo | undefined {
|
|
174
|
+
const streamMetadata = response.streamMetadata;
|
|
175
|
+
if (!streamMetadata || !isStructuredStreamType(streamMetadata.streamType)) return undefined;
|
|
176
|
+
|
|
177
|
+
const kind = getStructuredStreamKind(response);
|
|
178
|
+
if (!kind) return undefined;
|
|
179
|
+
|
|
180
|
+
const streaming: StructuredStreamingInfo = {
|
|
181
|
+
kind,
|
|
182
|
+
itemType: getType(context, streamMetadata.streamType),
|
|
183
|
+
};
|
|
184
|
+
if (kind !== "sse") return streaming;
|
|
185
|
+
|
|
186
|
+
const sseMetadata = response.sseMetadata;
|
|
187
|
+
if (!sseMetadata || sseMetadata.events.length === 0) return undefined;
|
|
188
|
+
|
|
189
|
+
const { events, terminalEvent } = partitionSSEEvents(sseMetadata.events, (type) =>
|
|
190
|
+
getType(context, type),
|
|
191
|
+
);
|
|
192
|
+
if (events.length > 0) streaming.events = events;
|
|
193
|
+
if (terminalEvent !== undefined) streaming.terminalEvent = terminalEvent;
|
|
194
|
+
|
|
195
|
+
return streaming;
|
|
196
|
+
}
|
|
197
|
+
|
|
45
198
|
function isEtagType(type: SdkType): boolean {
|
|
46
199
|
if (type.kind === "nullable") return isEtagType(type.type);
|
|
47
200
|
const raw = type.__raw;
|
|
@@ -682,6 +835,7 @@ function emitHttpResponse(
|
|
|
682
835
|
"invalid-lro-result",
|
|
683
836
|
method,
|
|
684
837
|
),
|
|
838
|
+
streaming: isException ? undefined : emitStructuredStreamingInfo(context, response),
|
|
685
839
|
};
|
|
686
840
|
}
|
|
687
841
|
|
package/emitter/src/index.ts
CHANGED
package/emitter/src/lib.ts
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "@azure-tools/typespec-client-generator-core";
|
|
6
|
-
import { createTypeSpecLibrary, JSONSchemaType, paramMessage } from "@typespec/compiler";
|
|
1
|
+
import type { SdkContext, SdkType } from "@azure-tools/typespec-client-generator-core";
|
|
2
|
+
import { UnbrandedSdkEmitterOptions } from "@azure-tools/typespec-client-generator-core";
|
|
3
|
+
import type { JSONSchemaType } from "@typespec/compiler";
|
|
4
|
+
import { createTypeSpecLibrary, paramMessage } from "@typespec/compiler";
|
|
7
5
|
|
|
8
6
|
export interface PythonEmitterOptions {
|
|
9
7
|
"api-version"?: string;
|
|
@@ -24,6 +22,7 @@ export interface PythonEmitterOptions {
|
|
|
24
22
|
"head-as-boolean"?: boolean;
|
|
25
23
|
"use-pyodide"?: boolean;
|
|
26
24
|
"keep-setup-py"?: boolean;
|
|
25
|
+
"generate-typeddict"?: boolean;
|
|
27
26
|
"keep-pyproject-fields"?: {
|
|
28
27
|
authors?: boolean;
|
|
29
28
|
description?: boolean;
|
|
@@ -111,6 +110,12 @@ export const PythonEmitterOptionsSchema: JSONSchemaType<PythonEmitterOptions> =
|
|
|
111
110
|
description:
|
|
112
111
|
"Whether to keep the existing `setup.py` when `generate-packaging-files` is `true`. If set to `false` and by default, `pyproject.toml` will be generated instead. To generate `setup.py`, use `basic-setup-py`.",
|
|
113
112
|
},
|
|
113
|
+
"generate-typeddict": {
|
|
114
|
+
type: "boolean",
|
|
115
|
+
nullable: true,
|
|
116
|
+
description:
|
|
117
|
+
"Whether to add TypedDict typing for JSON dictionary input in `models-mode: dpg`, instead of accepting only generic JSON. This enriches the typing on the existing overloads rather than adding another request-body overload. Defaults to `true`.",
|
|
118
|
+
},
|
|
114
119
|
"keep-pyproject-fields": {
|
|
115
120
|
type: "object",
|
|
116
121
|
nullable: true,
|
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import type { EmitContext } from "@typespec/compiler";
|
|
7
7
|
import { NoTarget } from "@typespec/compiler";
|
|
8
8
|
import type { PyodideInterface } from "pyodide";
|
|
9
|
-
import { PythonEmitterOptions
|
|
9
|
+
import type { PythonEmitterOptions } from "./lib.js";
|
|
10
|
+
import { reportDiagnostic } from "./lib.js";
|
|
10
11
|
|
|
11
12
|
export interface RunNodeEmitArgs {
|
|
12
13
|
context: EmitContext<PythonEmitterOptions>;
|
|
@@ -5,16 +5,19 @@
|
|
|
5
5
|
// `node-runner.browser.ts` stub is swapped in via the `"browser"` field in
|
|
6
6
|
// `package.json` when bundling with `platform: "browser"`.
|
|
7
7
|
|
|
8
|
-
import { EmitContext
|
|
8
|
+
import type { EmitContext } from "@typespec/compiler";
|
|
9
|
+
import { NoTarget } from "@typespec/compiler";
|
|
9
10
|
import { execSync } from "child_process";
|
|
10
11
|
import fs from "fs";
|
|
11
12
|
import os from "os";
|
|
12
13
|
import path, { dirname } from "path";
|
|
13
|
-
import {
|
|
14
|
+
import type { PyodideInterface } from "pyodide";
|
|
15
|
+
import { loadPyodide } from "pyodide";
|
|
14
16
|
import { fileURLToPath } from "url";
|
|
15
17
|
import { blackExcludeDirs, PYGEN_WHEEL_FILENAME } from "./constants.js";
|
|
16
18
|
import { saveCodeModelAsYaml } from "./external-process.js";
|
|
17
|
-
import { PythonEmitterOptions
|
|
19
|
+
import type { PythonEmitterOptions } from "./lib.js";
|
|
20
|
+
import { reportDiagnostic } from "./lib.js";
|
|
18
21
|
import { runPython3 } from "./run-python3.js";
|
|
19
22
|
import { quoteShellArg } from "./utils.js";
|
|
20
23
|
|
package/emitter/src/types.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isHttpMetadata,
|
|
1
|
+
import type {
|
|
3
2
|
SdkArrayType,
|
|
4
3
|
SdkBuiltInType,
|
|
5
4
|
SdkConstantType,
|
|
@@ -14,12 +13,14 @@ import {
|
|
|
14
13
|
SdkModelType,
|
|
15
14
|
SdkType,
|
|
16
15
|
SdkUnionType,
|
|
17
|
-
UsageFlags,
|
|
18
16
|
} from "@azure-tools/typespec-client-generator-core";
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
17
|
+
import { isHttpMetadata, UsageFlags } from "@azure-tools/typespec-client-generator-core";
|
|
18
|
+
import type { Type } from "@typespec/compiler";
|
|
19
|
+
import { getEncode } from "@typespec/compiler";
|
|
20
|
+
import type { HttpAuth } from "@typespec/http";
|
|
21
|
+
import { Visibility } from "@typespec/http";
|
|
21
22
|
import { dump } from "js-yaml";
|
|
22
|
-
import { PythonSdkContext } from "./lib.js";
|
|
23
|
+
import type { PythonSdkContext } from "./lib.js";
|
|
23
24
|
import {
|
|
24
25
|
camelToSnakeCase,
|
|
25
26
|
emitParamBase,
|
|
@@ -473,10 +474,17 @@ const sdkScalarKindToPythonKind: Record<string, string> = {
|
|
|
473
474
|
azureLocation: "string",
|
|
474
475
|
};
|
|
475
476
|
|
|
477
|
+
const supportedPythonEncodings = new Map<string, ReadonlySet<string>>([
|
|
478
|
+
["boolean", new Set(["string"])],
|
|
479
|
+
["integer", new Set(["string"])],
|
|
480
|
+
["bytes", new Set(["base64", "base64url"])],
|
|
481
|
+
]);
|
|
482
|
+
|
|
476
483
|
function emitBuiltInType(
|
|
477
484
|
context: PythonSdkContext,
|
|
478
485
|
type: SdkBuiltInType | SdkDurationType | SdkDateTimeType,
|
|
479
486
|
): Record<string, any> {
|
|
487
|
+
const pythonType = sdkScalarKindToPythonKind[type.kind] || type.kind;
|
|
480
488
|
if (type.encode) {
|
|
481
489
|
if (type.kind === "duration") {
|
|
482
490
|
if (type.encode === "ISO8601") {
|
|
@@ -508,7 +516,14 @@ function emitBuiltInType(
|
|
|
508
516
|
}
|
|
509
517
|
}
|
|
510
518
|
|
|
511
|
-
|
|
519
|
+
if (supportedPythonEncodings.get(pythonType)?.has(type.encode)) {
|
|
520
|
+
return getSimpleTypeResult(context, {
|
|
521
|
+
type: pythonType,
|
|
522
|
+
encode: type.encode,
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Python cannot apply unknown/custom encodings, so expose the wire type instead of silently ignoring the encoding.
|
|
512
527
|
if ("wireType" in type && type.wireType !== undefined) {
|
|
513
528
|
return getSimpleTypeResult(context, {
|
|
514
529
|
type: sdkScalarKindToPythonKind[type.wireType.kind] || type.wireType.kind,
|
|
@@ -518,7 +533,7 @@ function emitBuiltInType(
|
|
|
518
533
|
}
|
|
519
534
|
|
|
520
535
|
return getSimpleTypeResult(context, {
|
|
521
|
-
type:
|
|
536
|
+
type: pythonType,
|
|
522
537
|
encode: type.encode,
|
|
523
538
|
});
|
|
524
539
|
}
|
package/emitter/src/utils.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
InitializedByFlags,
|
|
1
|
+
import type {
|
|
3
2
|
SdkCredentialParameter,
|
|
4
3
|
SdkEndpointParameter,
|
|
5
4
|
SdkHeaderParameter,
|
|
@@ -13,9 +12,11 @@ import {
|
|
|
13
12
|
SdkServiceResponseHeader,
|
|
14
13
|
SdkType,
|
|
15
14
|
} from "@azure-tools/typespec-client-generator-core";
|
|
15
|
+
import { InitializedByFlags } from "@azure-tools/typespec-client-generator-core";
|
|
16
16
|
import { getNamespaceFullName } from "@typespec/compiler";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import type { Token } from "marked";
|
|
18
|
+
import { marked } from "marked";
|
|
19
|
+
import type { PythonSdkContext } from "./lib.js";
|
|
19
20
|
import { getSimpleTypeResult, getType } from "./types.js";
|
|
20
21
|
|
|
21
22
|
function IsFullyUpperCase(identifier: string, maxUppercasePreserve: number) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import jsyaml from "js-yaml";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Serialize the given codemodel to a YAML string.
|
|
5
|
+
*
|
|
6
|
+
* The generated YAML is consumed by the Python generator, which parses it with
|
|
7
|
+
* PyYAML (YAML 1.1). js-yaml, on the other hand, dumps using YAML 1.2 rules, so
|
|
8
|
+
* plain scalars such as `2020_01_01` (a snake-cased enum member name) are left
|
|
9
|
+
* unquoted because YAML 1.2 does not treat underscores as integer separators.
|
|
10
|
+
* PyYAML would then read `2020_01_01` back as the integer `20200101`, corrupting
|
|
11
|
+
* string values (e.g. enum member names, descriptions). Forcing every string
|
|
12
|
+
* scalar to be quoted guarantees that PyYAML round-trips them as strings.
|
|
13
|
+
*
|
|
14
|
+
* This module must remain browser-safe (no Node built-ins): `emitter.ts` calls
|
|
15
|
+
* this function on the browser/Pyodide path, and esbuild bundles it into the
|
|
16
|
+
* playground bundle with `platform: "browser"`.
|
|
17
|
+
* @param codemodel Codemodel to serialize
|
|
18
|
+
* @return the YAML representation of the codemodel.
|
|
19
|
+
*/
|
|
20
|
+
export function dumpCodeModelToYaml(codemodel: unknown): string {
|
|
21
|
+
return jsyaml.dump(codemodel, { forceQuotes: true, quotingType: '"' });
|
|
22
|
+
}
|