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