@typespec/http-client-python 0.6.11 → 0.7.1
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/emitter.d.ts.map +1 -1
- package/dist/emitter/emitter.js +114 -51
- package/dist/emitter/emitter.js.map +1 -1
- package/dist/emitter/lib.d.ts +28 -1
- package/dist/emitter/lib.d.ts.map +1 -1
- package/dist/emitter/lib.js +21 -1
- package/dist/emitter/lib.js.map +1 -1
- package/dist/emitter/types.d.ts.map +1 -1
- package/dist/emitter/types.js +3 -1
- package/dist/emitter/types.js.map +1 -1
- package/dist/emitter/utils.d.ts +1 -0
- package/dist/emitter/utils.d.ts.map +1 -1
- package/dist/emitter/utils.js +78 -0
- package/dist/emitter/utils.js.map +1 -1
- package/emitter/src/emitter.ts +119 -51
- package/emitter/src/lib.ts +22 -1
- package/emitter/src/types.ts +4 -1
- package/emitter/src/utils.ts +82 -0
- package/emitter/temp/tsconfig.tsbuildinfo +1 -1
- package/emitter/test/utils.test.ts +6 -1
- package/eng/scripts/Build-Packages.ps1 +3 -2
- package/eng/scripts/ci/regenerate.ts +31 -4
- package/eng/scripts/ci/run_apiview.py +5 -0
- package/eng/scripts/setup/__pycache__/venvtools.cpython-38.pyc +0 -0
- package/eng/scripts/setup/run_tsp.py +2 -3
- package/generator/build/lib/pygen/codegen/templates/model_dpg.py.jinja2 +0 -10
- package/generator/build/lib/pygen/codegen/templates/serialization.py.jinja2 +1 -1
- package/generator/component-detection-pip-report.json +2 -2
- package/generator/dist/pygen-0.1.0-py3-none-any.whl +0 -0
- package/generator/pygen/codegen/templates/model_dpg.py.jinja2 +0 -10
- package/generator/pygen/codegen/templates/serialization.py.jinja2 +1 -1
- package/generator/pygen.egg-info/PKG-INFO +0 -1
- package/generator/pygen.egg-info/SOURCES.txt +0 -1
- package/generator/pygen.egg-info/requires.txt +0 -1
- package/generator/setup.py +0 -1
- package/package.json +24 -23
- package/generator/build/lib/pygen/m2r.py +0 -65
- package/generator/pygen/m2r.py +0 -65
- package/generator/test/generic_mock_api_tests/unittests/test_m2r.py +0 -10
package/emitter/src/emitter.ts
CHANGED
|
@@ -8,13 +8,15 @@ import { EmitContext, NoTarget } from "@typespec/compiler";
|
|
|
8
8
|
import { execSync } from "child_process";
|
|
9
9
|
import fs from "fs";
|
|
10
10
|
import path, { dirname } from "path";
|
|
11
|
+
import process from "process";
|
|
11
12
|
import { loadPyodide } from "pyodide";
|
|
12
13
|
import { fileURLToPath } from "url";
|
|
13
14
|
import { emitCodeModel } from "./code-model.js";
|
|
14
15
|
import { saveCodeModelAsYaml } from "./external-process.js";
|
|
15
16
|
import { PythonEmitterOptions, PythonSdkContext, reportDiagnostic } from "./lib.js";
|
|
16
17
|
import { runPython3 } from "./run-python3.js";
|
|
17
|
-
import {
|
|
18
|
+
import { disableGenerationMap, simpleTypesMap, typesMap } from "./types.js";
|
|
19
|
+
import { md2Rst, removeUnderscoresFromNamespace } from "./utils.js";
|
|
18
20
|
|
|
19
21
|
export function getModelsMode(context: SdkContext): "dpg" | "none" {
|
|
20
22
|
const specifiedModelsMode = context.emitContext.options["models-mode"];
|
|
@@ -23,9 +25,11 @@ export function getModelsMode(context: SdkContext): "dpg" | "none" {
|
|
|
23
25
|
if (modelModes.includes(specifiedModelsMode)) {
|
|
24
26
|
return specifiedModelsMode;
|
|
25
27
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
reportDiagnostic(context.program, {
|
|
29
|
+
code: "invalid-models-mode",
|
|
30
|
+
target: NoTarget,
|
|
31
|
+
format: { inValidValue: specifiedModelsMode },
|
|
32
|
+
});
|
|
29
33
|
}
|
|
30
34
|
return "dpg";
|
|
31
35
|
}
|
|
@@ -76,7 +80,54 @@ async function createPythonSdkContext<TServiceOperation extends SdkServiceOperat
|
|
|
76
80
|
};
|
|
77
81
|
}
|
|
78
82
|
|
|
83
|
+
function walkThroughNodes(yamlMap: Record<string, any>): Record<string, any> {
|
|
84
|
+
const stack = [yamlMap];
|
|
85
|
+
const seen = new WeakSet();
|
|
86
|
+
|
|
87
|
+
while (stack.length > 0) {
|
|
88
|
+
const current = stack.pop();
|
|
89
|
+
|
|
90
|
+
if (seen.has(current!)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (current !== undefined && current !== null) {
|
|
94
|
+
seen.add(current);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (Array.isArray(current)) {
|
|
98
|
+
for (let i = 0; i < current.length; i++) {
|
|
99
|
+
if (current[i] !== undefined && typeof current[i] === "object") {
|
|
100
|
+
stack.push(current[i]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} else {
|
|
104
|
+
for (const key in current) {
|
|
105
|
+
if (key === "description" || key === "summary") {
|
|
106
|
+
if (current[key] !== undefined) {
|
|
107
|
+
current[key] = md2Rst(current[key]);
|
|
108
|
+
}
|
|
109
|
+
} else if (Array.isArray(current[key])) {
|
|
110
|
+
stack.push(current[key]);
|
|
111
|
+
} else if (current[key] !== undefined && typeof current[key] === "object") {
|
|
112
|
+
stack.push(current[key]);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return yamlMap;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function cleanAllCache() {
|
|
122
|
+
typesMap.clear();
|
|
123
|
+
simpleTypesMap.clear();
|
|
124
|
+
disableGenerationMap.clear();
|
|
125
|
+
}
|
|
126
|
+
|
|
79
127
|
export async function $onEmit(context: EmitContext<PythonEmitterOptions>) {
|
|
128
|
+
// clean all cache to make sure emitter could work in watch mode
|
|
129
|
+
cleanAllCache();
|
|
130
|
+
|
|
80
131
|
const program = context.program;
|
|
81
132
|
const sdkContext = await createPythonSdkContext<SdkHttpOperation>(context);
|
|
82
133
|
const root = path.join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
@@ -90,7 +141,10 @@ export async function $onEmit(context: EmitContext<PythonEmitterOptions>) {
|
|
|
90
141
|
});
|
|
91
142
|
return;
|
|
92
143
|
}
|
|
93
|
-
|
|
144
|
+
|
|
145
|
+
const parsedYamlMap = walkThroughNodes(yamlMap);
|
|
146
|
+
|
|
147
|
+
const yamlPath = await saveCodeModelAsYaml("python-yaml-path", parsedYamlMap);
|
|
94
148
|
const resolvedOptions = sdkContext.emitContext.options;
|
|
95
149
|
const commandArgs: Record<string, string> = {};
|
|
96
150
|
if (resolvedOptions["packaging-files-config"]) {
|
|
@@ -134,54 +188,68 @@ export async function $onEmit(context: EmitContext<PythonEmitterOptions>) {
|
|
|
134
188
|
}
|
|
135
189
|
}
|
|
136
190
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
fs.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
warnings.
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
await
|
|
167
|
-
await pyodide.runPythonAsync(pythonCode, { globals });
|
|
168
|
-
} else {
|
|
169
|
-
// here we run with native python
|
|
170
|
-
let venvPath = path.join(root, "venv");
|
|
171
|
-
if (fs.existsSync(path.join(venvPath, "bin"))) {
|
|
172
|
-
venvPath = path.join(venvPath, "bin", "python");
|
|
173
|
-
} else if (fs.existsSync(path.join(venvPath, "Scripts"))) {
|
|
174
|
-
venvPath = path.join(venvPath, "Scripts", "python.exe");
|
|
191
|
+
try {
|
|
192
|
+
if (resolvedOptions["use-pyodide"]) {
|
|
193
|
+
// here we run with pyodide
|
|
194
|
+
const pyodide = await setupPyodideCall(root);
|
|
195
|
+
// create the output folder if not exists
|
|
196
|
+
if (!fs.existsSync(outputDir)) {
|
|
197
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
198
|
+
}
|
|
199
|
+
// mount output folder to pyodide
|
|
200
|
+
pyodide.FS.mkdirTree("/output");
|
|
201
|
+
pyodide.FS.mount(pyodide.FS.filesystems.NODEFS, { root: outputDir }, "/output");
|
|
202
|
+
// mount yaml file to pyodide
|
|
203
|
+
pyodide.FS.mkdirTree("/yaml");
|
|
204
|
+
pyodide.FS.mount(pyodide.FS.filesystems.NODEFS, { root: path.dirname(yamlPath) }, "/yaml");
|
|
205
|
+
const globals = pyodide.toPy({
|
|
206
|
+
outputFolder: "/output",
|
|
207
|
+
yamlFile: `/yaml/${path.basename(yamlPath)}`,
|
|
208
|
+
commandArgs,
|
|
209
|
+
});
|
|
210
|
+
const pythonCode = `
|
|
211
|
+
async def main():
|
|
212
|
+
import warnings
|
|
213
|
+
with warnings.catch_warnings():
|
|
214
|
+
from pygen import preprocess, codegen, black
|
|
215
|
+
preprocess.PreProcessPlugin(output_folder=outputFolder, cadl_file=yamlFile, **commandArgs).process()
|
|
216
|
+
codegen.CodeGenerator(output_folder=outputFolder, cadl_file=yamlFile, **commandArgs).process()
|
|
217
|
+
black.BlackScriptPlugin(output_folder=outputFolder, **commandArgs).process()
|
|
218
|
+
|
|
219
|
+
await main()`;
|
|
220
|
+
await pyodide.runPythonAsync(pythonCode, { globals });
|
|
175
221
|
} else {
|
|
176
|
-
|
|
222
|
+
// here we run with native python
|
|
223
|
+
let venvPath = path.join(root, "venv");
|
|
224
|
+
if (fs.existsSync(path.join(venvPath, "bin"))) {
|
|
225
|
+
venvPath = path.join(venvPath, "bin", "python");
|
|
226
|
+
} else if (fs.existsSync(path.join(venvPath, "Scripts"))) {
|
|
227
|
+
venvPath = path.join(venvPath, "Scripts", "python.exe");
|
|
228
|
+
} else {
|
|
229
|
+
reportDiagnostic(program, {
|
|
230
|
+
code: "pyodide-flag-conflict",
|
|
231
|
+
target: NoTarget,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
commandArgs["output-folder"] = outputDir;
|
|
235
|
+
commandArgs["cadl-file"] = yamlPath;
|
|
236
|
+
const commandFlags = Object.entries(commandArgs)
|
|
237
|
+
.map(([key, value]) => `--${key}=${value}`)
|
|
238
|
+
.join(" ");
|
|
239
|
+
const command = `${venvPath} ${root}/eng/scripts/setup/run_tsp.py ${commandFlags}`;
|
|
240
|
+
execSync(command, { stdio: [process.stdin, process.stdout] });
|
|
177
241
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const
|
|
184
|
-
|
|
242
|
+
} catch (error: any) {
|
|
243
|
+
const errStackStart =
|
|
244
|
+
"========================================= error stack start ================================================";
|
|
245
|
+
const errStackEnd =
|
|
246
|
+
"========================================= error stack end ================================================";
|
|
247
|
+
const errStack = error.stack ? `\n${errStackStart}\n${error.stack}\n${errStackEnd}` : "";
|
|
248
|
+
reportDiagnostic(program, {
|
|
249
|
+
code: "unknown-error",
|
|
250
|
+
target: NoTarget,
|
|
251
|
+
format: { stack: errStack },
|
|
252
|
+
});
|
|
185
253
|
}
|
|
186
254
|
}
|
|
187
255
|
}
|
package/emitter/src/lib.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SdkContext, SdkServiceOperation } from "@azure-tools/typespec-client-generator-core";
|
|
2
|
-
import { createTypeSpecLibrary, JSONSchemaType } from "@typespec/compiler";
|
|
2
|
+
import { createTypeSpecLibrary, JSONSchemaType, paramMessage } from "@typespec/compiler";
|
|
3
3
|
|
|
4
4
|
export interface PythonEmitterOptions {
|
|
5
5
|
"package-version"?: string;
|
|
@@ -56,6 +56,27 @@ const EmitterOptionsSchema: JSONSchemaType<PythonEmitterOptions> = {
|
|
|
56
56
|
const libDef = {
|
|
57
57
|
name: "@typespec/http-client-python",
|
|
58
58
|
diagnostics: {
|
|
59
|
+
// error
|
|
60
|
+
"unknown-error": {
|
|
61
|
+
severity: "error",
|
|
62
|
+
messages: {
|
|
63
|
+
default: paramMessage`Can't generate Python client code from this TypeSpec. Please open an issue on https://github.com/microsoft/typespec'.${"stack"}`,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
"invalid-models-mode": {
|
|
67
|
+
severity: "error",
|
|
68
|
+
messages: {
|
|
69
|
+
default: paramMessage`Invalid value '${"inValidValue"}' for 'models-mode' of tspconfig.yaml and expected values are 'dpg'/'none'.`,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
"pyodide-flag-conflict": {
|
|
73
|
+
severity: "error",
|
|
74
|
+
messages: {
|
|
75
|
+
default:
|
|
76
|
+
"Python is not installed. Please follow https://www.python.org/ to install Python or set 'use-pyodide' to true.",
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
// warning
|
|
59
80
|
"no-valid-client": {
|
|
60
81
|
severity: "warning",
|
|
61
82
|
messages: {
|
package/emitter/src/types.ts
CHANGED
|
@@ -341,13 +341,16 @@ function emitEnum<TServiceOperation extends SdkServiceOperation>(
|
|
|
341
341
|
if (!type.isFixed) {
|
|
342
342
|
types.push(emitBuiltInType(type.valueType));
|
|
343
343
|
}
|
|
344
|
-
|
|
344
|
+
|
|
345
|
+
const newValue = {
|
|
345
346
|
description: "",
|
|
346
347
|
internal: true,
|
|
347
348
|
type: "combined",
|
|
348
349
|
types,
|
|
349
350
|
xmlMetadata: {},
|
|
350
351
|
};
|
|
352
|
+
typesMap.set(type, newValue);
|
|
353
|
+
return newValue;
|
|
351
354
|
}
|
|
352
355
|
const values: Record<string, any>[] = [];
|
|
353
356
|
const name = type.name;
|
package/emitter/src/utils.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
SdkType,
|
|
11
11
|
} from "@azure-tools/typespec-client-generator-core";
|
|
12
12
|
import { getNamespaceFullName } from "@typespec/compiler";
|
|
13
|
+
import { marked, Token } from "marked";
|
|
13
14
|
import { PythonSdkContext } from "./lib.js";
|
|
14
15
|
import { getSimpleTypeResult, getType } from "./types.js";
|
|
15
16
|
|
|
@@ -232,3 +233,84 @@ export function getClientNamespace<TServiceOperation extends SdkServiceOperation
|
|
|
232
233
|
? rootNamespace
|
|
233
234
|
: removeUnderscoresFromNamespace(clientNamespace).toLowerCase();
|
|
234
235
|
}
|
|
236
|
+
|
|
237
|
+
function parseToken(token: Token): string {
|
|
238
|
+
let parsed = "";
|
|
239
|
+
switch (token.type) {
|
|
240
|
+
case "heading":
|
|
241
|
+
parsed += `${"=".repeat(token.text.length)}\n${token.text}\n${"=".repeat(
|
|
242
|
+
token.text.length,
|
|
243
|
+
)}\n\n`;
|
|
244
|
+
break;
|
|
245
|
+
case "paragraph":
|
|
246
|
+
parsed += `${token.text}\n\n`;
|
|
247
|
+
break;
|
|
248
|
+
case "strong":
|
|
249
|
+
parsed += `**${token.text}**`;
|
|
250
|
+
break;
|
|
251
|
+
case "em":
|
|
252
|
+
parsed += `*${token.text}*`;
|
|
253
|
+
break;
|
|
254
|
+
case "codespan":
|
|
255
|
+
parsed += `\`\`${token.text}\`\``;
|
|
256
|
+
break;
|
|
257
|
+
case "code":
|
|
258
|
+
let codeBlockStyle = token.codeBlockStyle;
|
|
259
|
+
if (codeBlockStyle === undefined) {
|
|
260
|
+
codeBlockStyle = token.raw.split("\n")[0].replace("```", "").trim();
|
|
261
|
+
}
|
|
262
|
+
parsed += `\n\n.. code-block:: ${codeBlockStyle ?? ""}\n\n ${token.text.split("\n").join("\n ")}`;
|
|
263
|
+
break;
|
|
264
|
+
case "link":
|
|
265
|
+
if (token.href !== undefined) {
|
|
266
|
+
parsed += `\`${token.text} <${token.href}>\`_`;
|
|
267
|
+
break;
|
|
268
|
+
}
|
|
269
|
+
parsed += `${token.text}`;
|
|
270
|
+
break;
|
|
271
|
+
case "list":
|
|
272
|
+
if (!token.ordered) {
|
|
273
|
+
parsed += `\n\n${token.items.map((item: any) => `* ${item.text}`).join("\n")}`;
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
parsed += `\n\n${token.items.map((item: any, index: number) => `${index + 1}. ${item.text}`).join("\n")}`;
|
|
277
|
+
break;
|
|
278
|
+
default:
|
|
279
|
+
parsed += token.raw;
|
|
280
|
+
}
|
|
281
|
+
return parsed;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function md2Rst(text?: string): string | undefined {
|
|
285
|
+
try {
|
|
286
|
+
if (!text || text === "") return text;
|
|
287
|
+
const tokens = marked.lexer(text);
|
|
288
|
+
let rst = "";
|
|
289
|
+
|
|
290
|
+
tokens.forEach((token: Token) => {
|
|
291
|
+
if (token.type === "heading") {
|
|
292
|
+
// Heading tokens are block level, so we should check if there are additional tokens inside
|
|
293
|
+
const parsedHeadingText = md2Rst(token.text);
|
|
294
|
+
rst += `${"=".repeat(
|
|
295
|
+
parsedHeadingText!.length,
|
|
296
|
+
)}\n${parsedHeadingText}\n${"=".repeat(parsedHeadingText!.length)}\n\n`;
|
|
297
|
+
} else if ("tokens" in token && token.tokens !== undefined && token.tokens.length > 0) {
|
|
298
|
+
token.tokens.forEach((element: any) => {
|
|
299
|
+
rst += parseToken(element);
|
|
300
|
+
});
|
|
301
|
+
} else {
|
|
302
|
+
rst += parseToken(token);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Trim trailing whitespace or tabs
|
|
307
|
+
return rst.replace(/[ \t]+$/, "");
|
|
308
|
+
} catch (e) {
|
|
309
|
+
if (e instanceof RangeError) {
|
|
310
|
+
// The error is thrown by the tokenizer when the markdown is too long
|
|
311
|
+
// We can ignore it and return the original text
|
|
312
|
+
return text;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return text;
|
|
316
|
+
}
|