@azure-tools/typespec-python 0.60.0 → 0.60.2

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.
@@ -3,9 +3,20 @@ import { exec as execCallback } from "child_process";
3
3
  import { promisify } from "util";
4
4
  import yargs from "yargs";
5
5
  import { hideBin } from "yargs/helpers";
6
- import { dirname, join, relative, resolve } from "path";
6
+ import { join, resolve } from "path";
7
7
  import { promises, rmSync } from "fs";
8
8
  import { fileURLToPath } from "url";
9
+ import {
10
+ BASE_AZURE_EMITTER_OPTIONS,
11
+ BASE_EMITTER_OPTIONS,
12
+ buildOptions,
13
+ regenerate,
14
+ toPosix,
15
+ type RegenerateConfig,
16
+ type RegenerateFlags,
17
+ type RegenerateFlagsInput,
18
+ type TspCommand,
19
+ } from "./regenerate-common.js";
9
20
 
10
21
  // Promisify the exec function
11
22
  const exec = promisify(execCallback);
@@ -14,196 +25,14 @@ const exec = promisify(execCallback);
14
25
  const PLUGIN_DIR = resolve(fileURLToPath(import.meta.url), "../../../");
15
26
  const AZURE_HTTP_SPECS = resolve(PLUGIN_DIR, "node_modules/@azure-tools/azure-http-specs/specs");
16
27
  const HTTP_SPECS = resolve(PLUGIN_DIR, "node_modules/@typespec/http-specs/specs");
17
- interface TspCommand {
18
- outputDir: string;
19
- command: string;
20
- }
21
-
22
- // Add this near the top with other constants
23
- const SKIP_SPECS: string[] = [];
28
+ const EMITTER_NAME = "@azure-tools/typespec-python";
24
29
 
25
30
  const AZURE_EMITTER_OPTIONS: Record<string, Record<string, string> | Record<string, string>[]> = {
26
- "azure/client-generator-core/access": {
27
- namespace: "specs.azure.clientgenerator.core.access",
28
- },
29
- "azure/client-generator-core/alternate-type": {
30
- namespace: "specs.azure.clientgenerator.core.alternatetype",
31
- },
32
- "azure/client-generator-core/api-version": {
33
- namespace: "specs.azure.clientgenerator.core.apiversion",
34
- },
35
- "azure/client-generator-core/client-initialization": {
36
- namespace: "specs.azure.clientgenerator.core.clientinitialization",
37
- },
38
- "azure/client-generator-core/client-location": {
39
- namespace: "specs.azure.clientgenerator.core.clientlocation",
40
- },
41
- "azure/client-generator-core/deserialize-empty-string-as-null": {
42
- namespace: "specs.azure.clientgenerator.core.emptystring",
43
- },
44
- "azure/client-generator-core/flatten-property": {
45
- namespace: "specs.azure.clientgenerator.core.flattenproperty",
46
- },
47
- "azure/client-generator-core/usage": {
48
- namespace: "specs.azure.clientgenerator.core.usage",
49
- },
50
- "azure/client-generator-core/override": {
51
- namespace: "specs.azure.clientgenerator.core.override",
52
- },
53
- "azure/client-generator-core/hierarchy-building": {
54
- namespace: "specs.azure.clientgenerator.core.hierarchybuilding",
55
- },
56
- "azure/core/basic": {
57
- namespace: "specs.azure.core.basic",
58
- },
59
- "azure/core/lro/rpc": {
60
- namespace: "specs.azure.core.lro.rpc",
61
- },
62
- "azure/core/lro/standard": {
63
- namespace: "specs.azure.core.lro.standard",
64
- },
65
- "azure/core/model": {
66
- namespace: "specs.azure.core.model",
67
- },
68
- "azure/core/page": {
69
- namespace: "specs.azure.core.page",
70
- },
71
- "azure/core/scalar": {
72
- namespace: "specs.azure.core.scalar",
73
- },
74
- "azure/core/traits": {
75
- namespace: "specs.azure.core.traits",
76
- },
77
- "azure/encode/duration": {
78
- namespace: "specs.azure.encode.duration",
79
- },
80
- "azure/example/basic": {
81
- namespace: "specs.azure.example.basic",
82
- },
83
- "azure/payload/pageable": {
84
- namespace: "specs.azure.payload.pageable",
85
- },
86
- "azure/versioning/previewVersion": {
87
- namespace: "specs.azure.versioning.previewversion",
88
- },
89
- "client/structure/default": {
90
- namespace: "client.structure.service",
91
- },
92
- "client/structure/multi-client": {
93
- "package-name": "client-structure-multiclient",
94
- "namespace": "client.structure.multiclient",
95
- },
96
- "client/structure/renamed-operation": {
97
- "package-name": "client-structure-renamedoperation",
98
- "namespace": "client.structure.renamedoperation",
99
- },
100
- "client/structure/two-operation-group": {
101
- "package-name": "client-structure-twooperationgroup",
102
- "namespace": "client.structure.twooperationgroup",
103
- },
104
- "client/naming": {
105
- namespace: "client.naming.main",
106
- },
107
- "client/overload": {
108
- namespace: "client.overload",
109
- },
110
- "encode/duration": {
111
- namespace: "encode.duration",
112
- },
113
- "encode/numeric": {
114
- namespace: "encode.numeric",
115
- },
116
- "parameters/basic": {
117
- namespace: "parameters.basic",
118
- },
119
- "parameters/spread": {
120
- namespace: "parameters.spread",
121
- },
122
- "payload/content-negotiation": {
123
- namespace: "payload.contentnegotiation",
124
- },
125
- "payload/multipart": {
126
- namespace: "payload.multipart",
127
- },
128
- "serialization/encoded-name/json": {
129
- namespace: "serialization.encodedname.json",
130
- },
131
- "special-words": {
132
- namespace: "specialwords",
133
- },
134
- "service/multi-service": {
135
- namespace: "service.multiservice",
136
- },
31
+ ...BASE_AZURE_EMITTER_OPTIONS,
137
32
  };
138
33
 
139
34
  const EMITTER_OPTIONS: Record<string, Record<string, string> | Record<string, string>[]> = {
140
- "resiliency/srv-driven/old.tsp": {
141
- "package-name": "resiliency-srv-driven1",
142
- "namespace": "resiliency.srv.driven1",
143
- "package-mode": "azure-dataplane",
144
- "package-pprint-name": "ResiliencySrvDriven1",
145
- },
146
- "resiliency/srv-driven": {
147
- "package-name": "resiliency-srv-driven2",
148
- "namespace": "resiliency.srv.driven2",
149
- "package-mode": "azure-dataplane",
150
- "package-pprint-name": "ResiliencySrvDriven2",
151
- },
152
- "authentication/api-key": {
153
- "clear-output-folder": "true",
154
- },
155
- "authentication/http/custom": {
156
- "package-name": "authentication-http-custom",
157
- "namespace": "authentication.http.custom",
158
- "package-pprint-name": "Authentication Http Custom",
159
- },
160
- "authentication/union": [
161
- {
162
- "package-name": "authentication-union",
163
- "namespace": "authentication.union",
164
- },
165
- {
166
- "package-name": "setuppy-authentication-union",
167
- "namespace": "setuppy.authentication.union",
168
- "keep-setup-py": "true",
169
- },
170
- ],
171
- "type/array": {
172
- "package-name": "typetest-array",
173
- "namespace": "typetest.array",
174
- },
175
- "type/dictionary": {
176
- "package-name": "typetest-dictionary",
177
- "namespace": "typetest.dictionary",
178
- },
179
- "type/enum/extensible": {
180
- "package-name": "typetest-enum-extensible",
181
- "namespace": "typetest.enum.extensible",
182
- },
183
- "type/enum/fixed": {
184
- "package-name": "typetest-enum-fixed",
185
- "namespace": "typetest.enum.fixed",
186
- },
187
- "type/model/empty": {
188
- "package-name": "typetest-model-empty",
189
- "namespace": "typetest.model.empty",
190
- },
191
- "type/model/inheritance/enum-discriminator": {
192
- "package-name": "typetest-model-enumdiscriminator",
193
- "namespace": "typetest.model.enumdiscriminator",
194
- },
195
- "type/model/inheritance/nested-discriminator": {
196
- "package-name": "typetest-model-nesteddiscriminator",
197
- "namespace": "typetest.model.nesteddiscriminator",
198
- },
199
- "type/model/inheritance/not-discriminated": {
200
- "package-name": "typetest-model-notdiscriminated",
201
- "namespace": "typetest.model.notdiscriminated",
202
- },
203
- "type/model/inheritance/single-discriminator": {
204
- "package-name": "typetest-model-singlediscriminator",
205
- "namespace": "typetest.model.singlediscriminator",
206
- },
35
+ ...BASE_EMITTER_OPTIONS,
207
36
  "type/model/inheritance/recursive": [
208
37
  {
209
38
  "package-name": "typetest-model-recursive",
@@ -216,54 +45,6 @@ const EMITTER_OPTIONS: Record<string, Record<string, string> | Record<string, st
216
45
  "clear-output-folder": "true",
217
46
  },
218
47
  ],
219
- "type/model/usage": {
220
- "package-name": "typetest-model-usage",
221
- "namespace": "typetest.model.usage",
222
- },
223
- "type/model/visibility": [
224
- {
225
- "package-name": "typetest-model-visibility",
226
- "namespace": "typetest.model.visibility",
227
- },
228
- {
229
- "package-name": "headasbooleantrue",
230
- "namespace": "headasbooleantrue",
231
- "head-as-boolean": "true",
232
- },
233
- {
234
- "package-name": "headasbooleanfalse",
235
- "namespace": "headasbooleanfalse",
236
- "head-as-boolean": "false",
237
- },
238
- ],
239
- "type/property/nullable": {
240
- "package-name": "typetest-property-nullable",
241
- "namespace": "typetest.property.nullable",
242
- },
243
- "type/property/optionality": {
244
- "package-name": "typetest-property-optional",
245
- "namespace": "typetest.property.optional",
246
- },
247
- "type/property/additional-properties": {
248
- "package-name": "typetest-property-additionalproperties",
249
- "namespace": "typetest.property.additionalproperties",
250
- },
251
- "type/scalar": {
252
- "package-name": "typetest-scalar",
253
- "namespace": "typetest.scalar",
254
- },
255
- "type/property/value-types": {
256
- "package-name": "typetest-property-valuetypes",
257
- "namespace": "typetest.property.valuetypes",
258
- },
259
- "type/union": {
260
- "package-name": "typetest-union",
261
- "namespace": "typetest.union",
262
- },
263
- "type/union/discriminated": {
264
- "package-name": "typetest-discriminatedunion",
265
- "namespace": "typetest.discriminatedunion",
266
- },
267
48
  "client/structure/client-operation-group": {
268
49
  "package-name": "client-structure-clientoperationgroup",
269
50
  "namespace": "client.structure.clientoperationgroup",
@@ -280,30 +61,14 @@ const EMITTER_OPTIONS: Record<string, Record<string, string> | Record<string, st
280
61
  "package-name": "client-structure-twooperationgroup",
281
62
  "namespace": "client.structure.twooperationgroup",
282
63
  },
283
- "documentation": {
284
- "package-name": "specs-documentation",
285
- "namespace": "specs.documentation",
286
- },
287
64
  };
288
65
 
289
- function toPosix(dir: string): string {
290
- return dir.replace(/\\/g, "/");
291
- }
292
-
293
- function getEmitterOption(spec: string, flavor: string): Record<string, string>[] {
294
- const specDir = spec.includes("azure") ? AZURE_HTTP_SPECS : HTTP_SPECS;
295
- const relativeSpec = toPosix(relative(specDir, spec));
296
- const key = relativeSpec.includes("resiliency/srv-driven/old.tsp") ? relativeSpec : dirname(relativeSpec);
297
- const emitter_options = EMITTER_OPTIONS[key] || (flavor === "azure" ? AZURE_EMITTER_OPTIONS[key] : [{}]) || [{}];
298
- const result = Array.isArray(emitter_options) ? emitter_options : [emitter_options];
299
- return result;
300
- }
301
-
302
66
  // Function to execute CLI commands asynchronously
303
67
  async function executeCommand(tspCommand: TspCommand): Promise<void> {
304
68
  try {
305
- console.log(`exec: ${tspCommand.command}`);
306
- const { stdout, stderr } = await exec(tspCommand.command);
69
+ const cmd = tspCommand.command as string;
70
+ console.log(`exec: ${cmd}`);
71
+ const { stdout, stderr } = await exec(cmd);
307
72
  if (stdout) console.log(`stdout: ${stdout}`);
308
73
  if (stderr) console.error(`stderr: ${stderr}`);
309
74
  } catch (error) {
@@ -313,139 +78,6 @@ async function executeCommand(tspCommand: TspCommand): Promise<void> {
313
78
  }
314
79
  }
315
80
 
316
- interface RegenerateFlagsInput {
317
- flavor?: "azure" | "unbranded";
318
- debug?: boolean;
319
- name?: string;
320
- }
321
-
322
- interface RegenerateFlags {
323
- flavor: "azure" | "unbranded";
324
- debug: boolean;
325
- name?: string;
326
- }
327
-
328
- const SpecialFlags: Record<string, Record<string, any>> = {
329
- azure: {
330
- "generate-test": true,
331
- "generate-sample": true,
332
- },
333
- };
334
-
335
- async function getSubdirectories(baseDir: string, flags: RegenerateFlags): Promise<string[]> {
336
- const subdirectories: string[] = [];
337
-
338
- async function searchDir(currentDir: string) {
339
- const items = await promises.readdir(currentDir, { withFileTypes: true });
340
-
341
- const promisesArray = items.map(async (item) => {
342
- const subDirPath = join(currentDir, item.name);
343
- if (item.isDirectory()) {
344
- const mainTspPath = join(subDirPath, "main.tsp");
345
- const clientTspPath = join(subDirPath, "client.tsp");
346
-
347
- const mainTspRelativePath = toPosix(relative(baseDir, mainTspPath));
348
-
349
- // Replace the individual skip checks with:
350
- if (SKIP_SPECS.some((skipSpec) => mainTspRelativePath.includes(skipSpec))) return;
351
-
352
- const hasMainTsp = await promises
353
- .access(mainTspPath)
354
- .then(() => true)
355
- .catch(() => false);
356
- const hasClientTsp = await promises
357
- .access(clientTspPath)
358
- .then(() => true)
359
- .catch(() => false);
360
-
361
- if (mainTspRelativePath.toLowerCase().includes(flags.name || "")) {
362
- if (mainTspRelativePath.includes("resiliency/srv-driven")) {
363
- subdirectories.push(resolve(subDirPath, "old.tsp"));
364
- }
365
- if (hasClientTsp) {
366
- subdirectories.push(resolve(subDirPath, "client.tsp"));
367
- } else if (hasMainTsp) {
368
- subdirectories.push(resolve(subDirPath, "main.tsp"));
369
- }
370
- }
371
-
372
- // Recursively search in the subdirectory
373
- await searchDir(subDirPath);
374
- }
375
- });
376
-
377
- await Promise.all(promisesArray);
378
- }
379
-
380
- await searchDir(baseDir);
381
- return subdirectories;
382
- }
383
-
384
- function defaultPackageName(spec: string): string {
385
- const specDir = spec.includes("azure") ? AZURE_HTTP_SPECS : HTTP_SPECS;
386
- return toPosix(relative(specDir, dirname(spec)))
387
- .replace(/\//g, "-")
388
- .toLowerCase();
389
- }
390
-
391
- interface EmitterConfig {
392
- optionsStr: string;
393
- outputDir: string;
394
- }
395
-
396
- function addOptions(spec: string, generatedFolder: string, flags: RegenerateFlags): EmitterConfig[] {
397
- const emitterConfigs: EmitterConfig[] = [];
398
- for (const config of getEmitterOption(spec, flags.flavor)) {
399
- const options: Record<string, string> = { ...config };
400
- options["flavor"] = flags.flavor;
401
- for (const [k, v] of Object.entries(SpecialFlags[flags.flavor] ?? {})) {
402
- options[k] = v;
403
- }
404
- if (options["emitter-output-dir"] === undefined) {
405
- const packageName = options["package-name"] || defaultPackageName(spec);
406
- options["emitter-output-dir"] = toPosix(`${generatedFolder}/test/${flags.flavor}/generated/${packageName}`);
407
- }
408
- if (flags.debug) {
409
- options["debug"] = "true";
410
- }
411
- options["examples-dir"] = toPosix(join(dirname(spec), "examples"));
412
- const configs = Object.entries(options).flatMap(([k, v]) => {
413
- return `--option @azure-tools/typespec-python.${k}=${typeof v === "string" && v.indexOf(" ") > -1 ? `"${v}"` : v}`;
414
- });
415
- emitterConfigs.push({
416
- optionsStr: configs.join(" "),
417
- outputDir: options["emitter-output-dir"],
418
- });
419
- }
420
- return emitterConfigs;
421
- }
422
- function _getCmdList(spec: string, flags: RegenerateFlags): TspCommand[] {
423
- return addOptions(spec, PLUGIN_DIR, flags).map((option) => {
424
- return {
425
- outputDir: option.outputDir,
426
- command: `tsp compile ${spec} --emit=${toPosix(PLUGIN_DIR)} ${option.optionsStr}`,
427
- };
428
- });
429
- }
430
-
431
- async function runTaskPool(tasks: Array<() => Promise<void>>, poolLimit: number): Promise<void> {
432
- async function worker(start: number, end: number) {
433
- while (start < end) {
434
- await tasks[start]();
435
- start++;
436
- }
437
- }
438
-
439
- const workers = [];
440
- let start = 0;
441
- while (start < tasks.length) {
442
- const end = Math.min(start + poolLimit, tasks.length);
443
- workers.push((async () => await worker(start, end))());
444
- start = end;
445
- }
446
- await Promise.all(workers);
447
- }
448
-
449
81
  // create some files before regeneration. After regeneration, these files should be deleted and we will test it
450
82
  // in test case
451
83
  async function preprocess(flags: RegenerateFlagsInput): Promise<void> {
@@ -479,33 +111,30 @@ async function preprocess(flags: RegenerateFlagsInput): Promise<void> {
479
111
  }
480
112
  }
481
113
 
482
- async function regenerate(flags: RegenerateFlagsInput): Promise<void> {
483
- if (flags.flavor === undefined) {
484
- await regenerate({ ...flags, flavor: "azure" });
485
- await regenerate({ ...flags, flavor: "unbranded" });
486
- } else {
487
- await preprocess(flags);
488
- const flagsResolved = { debug: false, flavor: flags.flavor, ...flags };
489
- const subdirectoriesForAzure = await getSubdirectories(AZURE_HTTP_SPECS, flagsResolved);
490
- const subdirectoriesForNonAzure = await getSubdirectories(HTTP_SPECS, flagsResolved);
491
- const subdirectories =
492
- flags.flavor === "azure"
493
- ? [...subdirectoriesForAzure, ...subdirectoriesForNonAzure]
494
- : subdirectoriesForNonAzure;
495
- const cmdList: TspCommand[] = subdirectories.flatMap((subdirectory) =>
496
- _getCmdList(subdirectory, flagsResolved),
497
- );
498
-
499
- // Create tasks as functions for the pool
500
- const tasks: Array<() => Promise<void>> = cmdList.map((tspCommand) => {
501
- return () => executeCommand(tspCommand);
502
- });
503
-
504
- // Run tasks with a concurrency limit
505
- await runTaskPool(tasks, 30);
506
- }
114
+ function _getCmdList(spec: string, flags: RegenerateFlags): TspCommand[] {
115
+ return buildOptions(spec, PLUGIN_DIR, flags, config).map((po) => {
116
+ const optionsStr = Object.entries(po.options)
117
+ .flatMap(([k, v]) => {
118
+ return `--option ${EMITTER_NAME}.${k}=${typeof v === "string" && v.indexOf(" ") > -1 ? `"${v}"` : v}`;
119
+ })
120
+ .join(" ");
121
+ return {
122
+ outputDir: po.outputDir,
123
+ command: `tsp compile ${spec} --emit=${toPosix(PLUGIN_DIR)} ${optionsStr}`,
124
+ };
125
+ });
507
126
  }
508
127
 
128
+ const config: RegenerateConfig = {
129
+ azureHttpSpecs: AZURE_HTTP_SPECS,
130
+ httpSpecs: HTTP_SPECS,
131
+ emitterOptions: EMITTER_OPTIONS,
132
+ azureEmitterOptions: AZURE_EMITTER_OPTIONS,
133
+ preprocess,
134
+ getCmdList: _getCmdList,
135
+ executeCommand,
136
+ };
137
+
509
138
  // PARSE INPUT ARGUMENTS
510
139
  const argv = yargs(hideBin(process.argv))
511
140
  .option("flavor", {
@@ -525,6 +154,6 @@ const argv = yargs(hideBin(process.argv))
525
154
  }).argv;
526
155
 
527
156
  const start = performance.now();
528
- regenerate(argv as RegenerateFlags)
157
+ regenerate(argv as RegenerateFlags, config)
529
158
  .then(() => console.log(`Regeneration successful, time taken: ${Math.round((performance.now() - start) / 1000)} s`))
530
159
  .catch((error) => console.error(`Regeneration failed: ${error.message}`));