Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0f90f1d
redo scripts
Mar 31, 2026
2c30eec
fix scripts
Mar 31, 2026
42f9210
format emitter
Mar 31, 2026
af554e7
fix format call
Mar 31, 2026
3de915b
fix install
Mar 31, 2026
b1377b4
fix cspell
Mar 31, 2026
ad872d4
switch more over to uv
Mar 31, 2026
f988cd9
linting works now
Mar 31, 2026
d38a008
split out generated sdk checks, switch to typecheck
Mar 31, 2026
7cc68bf
add changeset
Mar 31, 2026
0a84224
cspell
Mar 31, 2026
2c97bd0
fix linting
Mar 31, 2026
5a2d220
remove old generator folder
Mar 31, 2026
848d794
fix paths to specs
Apr 1, 2026
91f89cf
Merge branch 'main' of https://github.com/microsoft/typespec into pyt…
Apr 1, 2026
cea173d
run with verbose logging
Apr 1, 2026
8611c39
fix async
Apr 1, 2026
2d29d36
fix import errors
Apr 1, 2026
f8cd516
consolidate scripts in eng/scripts and clean up powershell scripts
Apr 1, 2026
dd8e2f7
switch to glob for windows
Apr 1, 2026
e6f22e7
fix race conditions
Apr 1, 2026
ceb412e
switch to picocolors
Apr 1, 2026
e64cd42
format and fix formatter
Apr 1, 2026
058b303
fix linting calls for linux
Apr 2, 2026
8dbce38
fix lint again
Apr 2, 2026
bb53961
add node_modules.bin directories to path
Apr 2, 2026
43f3d0d
set node path
Apr 2, 2026
a3dfd13
switch to npm exec -- eslint
Apr 2, 2026
6b253d1
use special config file in http-client-python
Apr 2, 2026
8321431
add check for regen-docs
Apr 2, 2026
9595913
switch to test folder
Apr 2, 2026
9ebeaa0
switch back to tests
Apr 2, 2026
9a265db
default to pyodide in test scripts for windows for perf improvements
Apr 3, 2026
7aa9c54
fix imports for windows ci
Apr 3, 2026
da4d9b2
increase paralellism, esp for windows
Apr 3, 2026
635667e
improve windows generation time
Apr 3, 2026
431244b
lint
Apr 3, 2026
839dd60
spec improvements for windows
Apr 3, 2026
c795bfe
switch to single process
Apr 3, 2026
73453bc
revert windows image change
Apr 3, 2026
0ed9726
Merge branch 'main' of https://github.com/microsoft/typespec into pyt…
Apr 3, 2026
045ff96
fix lint
Apr 3, 2026
3571a99
group specs that must be run sequentially
Apr 4, 2026
39aa2d8
Merge branch 'main' of https://github.com/microsoft/typespec into pyt…
Apr 4, 2026
041936b
revert image changes
Apr 4, 2026
5e597dc
move maps to context to allow parallelism
Apr 4, 2026
2e9ed18
add pytest-xdist to speed up mockapi test running
Apr 4, 2026
631e05f
fix cspell
Apr 4, 2026
9f8fec6
update job number defaults
Apr 4, 2026
292f2c9
play around with job count for windows
Apr 4, 2026
a2c6544
use pytest hooks
Apr 5, 2026
3cf6178
fix cspell
Apr 5, 2026
72f8762
remove xdist bc of port issues on windows
Apr 5, 2026
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
changeKind: internal
packages:
- "@typespec/http-client-python"
---

Clean up test infrastructure, run in parallel, and use uv pip when acceptable. CI runs 2-3 x faster
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,8 @@ BenchmarkDotnet.Artifacts/
!packages/http-client-java/package-lock.json

# python emitter
packages/http-client-python/generator/test/**/generated/
packages/http-client-python/generator/test/**/cadl-ranch-coverage.json
packages/http-client-python/tests/**/generated/
packages/http-client-python/tests/**/cadl-ranch-coverage.json
!packages/http-client-python/package-lock.json
packages/http-client-python/micropip.lock
packages/http-client-python/venv_build_wheel/
Expand Down
2 changes: 2 additions & 0 deletions cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ words:
- unassignable
- Uncapitalize
- uncollapsed
- unconfigure
- undifferentiable
- undoc
- Ungroup
Expand Down Expand Up @@ -307,6 +308,7 @@ words:
- WHATWG
- WINDOWSARMVMIMAGE
- WINDOWSVMIMAGE
- workerid
- xiangyan
- xiaofei
- xlarge
Expand Down
17 changes: 5 additions & 12 deletions packages/http-client-python/emitter/src/code-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,7 @@ import {
emitPagingHttpMethod,
} from "./http.js";
import { PythonSdkContext } from "./lib.js";
import {
KnownTypes,
disableGenerationMap,
emitEndpointType,
getType,
simpleTypesMap,
typesMap,
} from "./types.js";
import { KnownTypes, emitEndpointType, getType } from "./types.js";
import { emitParamBase, getClientNamespace, getImplementation, getRootNamespace } from "./utils.js";

function emitBasicMethod<TServiceOperation extends SdkServiceOperation>(
Expand Down Expand Up @@ -366,7 +359,7 @@ export function emitCodeModel(sdkContext: PythonSdkContext) {
continue;
}
// filter out specific models not used in python, e.g., pageable models
if (disableGenerationMap.has(model)) {
if (sdkContext.__disableGenerationMap.has(model)) {
continue;
}
// filter out core models
Expand All @@ -391,7 +384,7 @@ export function emitCodeModel(sdkContext: PythonSdkContext) {
}

// clear usage when a model is only used by paging
for (const type of typesMap.values()) {
for (const type of sdkContext.__typesMap.values()) {
if (
type["type"] === "model" &&
type["referredByOperationType"] === ReferredByOperationTypes.PagingOnly &&
Expand All @@ -402,9 +395,9 @@ export function emitCodeModel(sdkContext: PythonSdkContext) {
}

codeModel["types"] = [
...typesMap.values(),
...sdkContext.__typesMap.values(),
...Object.values(KnownTypes),
...simpleTypesMap.values(),
...sdkContext.__simpleTypesMap.values(),
];
codeModel["crossLanguagePackageId"] = ignoreDiagnostics(getCrossLanguagePackageId(sdkContext));
if ((sdkContext.emitContext.options as any).flavor === "azure") {
Expand Down
60 changes: 29 additions & 31 deletions packages/http-client-python/emitter/src/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import { emitCodeModel } from "./code-model.js";
import { saveCodeModelAsYaml } from "./external-process.js";
import { PythonEmitterOptions, PythonSdkContext, reportDiagnostic } from "./lib.js";
import { runPython3 } from "./run-python3.js";
import { disableGenerationMap, simpleTypesMap, typesMap } from "./types.js";
import { getRootNamespace, md2Rst } from "./utils.js";

function addDefaultOptions(sdkContext: PythonSdkContext) {
Expand Down Expand Up @@ -59,6 +58,9 @@ async function createPythonSdkContext(
return {
...sdkContext,
__endpointPathParameters: [],
__typesMap: new Map(),
__simpleTypesMap: new Map(),
__disableGenerationMap: new Set(),
};
}

Expand Down Expand Up @@ -100,12 +102,6 @@ function walkThroughNodes(yamlMap: Record<string, any>): Record<string, any> {
return yamlMap;
}

function cleanAllCache() {
typesMap.clear();
simpleTypesMap.clear();
disableGenerationMap.clear();
}

export async function $onEmit(context: EmitContext<PythonEmitterOptions>) {
try {
await onEmitMain(context);
Expand All @@ -124,9 +120,6 @@ export async function $onEmit(context: EmitContext<PythonEmitterOptions>) {
}

async function onEmitMain(context: EmitContext<PythonEmitterOptions>) {
// clean all cache to make sure emitter could work in watch mode
cleanAllCache();

const program = context.program;
const sdkContext = await createPythonSdkContext(context);
const root = path.join(dirname(fileURLToPath(import.meta.url)), "..", "..");
Expand Down Expand Up @@ -168,7 +161,7 @@ async function onEmitMain(context: EmitContext<PythonEmitterOptions>) {
try {
await runPython3(path.join(root, "/eng/scripts/setup/install.py"));
await runPython3(path.join(root, "/eng/scripts/setup/prepare.py"));
} catch (error) {
} catch {
// if the python env is not ready, we use pyodide instead
resolvedOptions["use-pyodide"] = true;
}
Expand Down Expand Up @@ -250,7 +243,7 @@ async function onEmitMain(context: EmitContext<PythonEmitterOptions>) {
execSync(
`${venvPath} -m black --line-length=120 --quiet --fast ${outputDir} --exclude "${excludePattern}"`,
);
checkForPylintIssues(outputDir, excludePattern);
await checkForPylintIssues(outputDir, excludePattern);
}
}
}
Expand All @@ -269,7 +262,7 @@ async function setupPyodideCall(root: string) {
if (lockAge > 300) {
fs.unlinkSync(micropipLockPath);
}
} catch (err) {
} catch {
// ignore
}
}
Expand All @@ -288,14 +281,14 @@ async function setupPyodideCall(root: string) {
fs.closeSync(fd);
fs.unlinkSync(micropipLockPath);
break;
} catch (err) {
} catch {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
}
return pyodide;
}

function checkForPylintIssues(outputDir: string, excludePattern: string) {
async function checkForPylintIssues(outputDir: string, excludePattern: string) {
const excludeRegex = new RegExp(excludePattern);

const shouldExcludePath = (filePath: string): boolean => {
Expand All @@ -304,9 +297,8 @@ function checkForPylintIssues(outputDir: string, excludePattern: string) {
return excludeRegex.test(normalizedPath);
};

const processFile = (filePath: string) => {
let fileContent;
fileContent = fs.readFileSync(filePath, "utf-8");
const processFile = async (filePath: string) => {
let fileContent = await fs.promises.readFile(filePath, "utf-8");
const pylintDisables: string[] = [];
const lineEnding = fileContent.includes("\r\n") && os.platform() === "win32" ? "\r\n" : "\n";
const lines: string[] = fileContent.split(lineEnding);
Expand All @@ -321,32 +313,38 @@ function checkForPylintIssues(outputDir: string, excludePattern: string) {
fileContent = lines[0].includes("pylint: disable=")
? [lines[0] + "," + pylintDisables.join(",")].concat(lines.slice(1)).join(lineEnding)
: `# pylint: disable=${pylintDisables.join(",")}${lineEnding}` + fileContent;
await fs.promises.writeFile(filePath, fileContent);
}
}

fs.writeFileSync(filePath, fileContent);
};

const walkDir = (dir: string) => {
const collectPythonFiles = async (dir: string): Promise<string[]> => {
if (shouldExcludePath(dir)) {
return;
return [];
}

const files = fs.readdirSync(dir);
files.forEach((file) => {
const filePath = path.join(dir, file);
const entries = await fs.promises.readdir(dir, { withFileTypes: true });

const promises = entries.map(async (entry) => {
const filePath = path.join(dir, entry.name);

if (shouldExcludePath(filePath)) {
return;
return [];
}

if (fs.statSync(filePath).isDirectory()) {
walkDir(filePath);
} else if (file.endsWith(".py")) {
processFile(filePath);
if (entry.isDirectory()) {
return collectPythonFiles(filePath);
} else if (entry.name.endsWith(".py")) {
return [filePath];
}
return [];
});

const results = await Promise.all(promises);
return results.flat();
};

walkDir(outputDir);
// Collect all Python files first, then process in parallel
const pythonFiles = await collectPythonFiles(outputDir);
await Promise.all(pythonFiles.map(processFile));
}
4 changes: 4 additions & 0 deletions packages/http-client-python/emitter/src/lib.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
SdkContext,
SdkType,
UnbrandedSdkEmitterOptions,
} from "@azure-tools/typespec-client-generator-core";
import { createTypeSpecLibrary, JSONSchemaType, paramMessage } from "@typespec/compiler";
Expand Down Expand Up @@ -28,6 +29,9 @@ export interface PythonEmitterOptions {

export interface PythonSdkContext extends SdkContext<PythonEmitterOptions> {
__endpointPathParameters: Record<string, any>[];
__typesMap: Map<SdkType, Record<string, any>>;
__simpleTypesMap: Map<string | null, Record<string, any>>;
__disableGenerationMap: Set<SdkType>;
}

export const PythonEmitterOptionsSchema: JSONSchemaType<PythonEmitterOptions> = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const execute = (
options.onCreate(cp);
}

options.onStdOutData && cp.stdout.on("data", options.onStdOutData);
options.onStdErrData && cp.stderr.on("data", options.onStdErrData);
if (options.onStdOutData) cp.stdout.on("data", options.onStdOutData);
if (options.onStdErrData) cp.stderr.on("data", options.onStdErrData);

let err = "";
let out = "";
Expand All @@ -34,7 +34,7 @@ const execute = (
cp.on("error", (err) => {
reject(err);
});
cp.on("close", (code, signal) =>
cp.on("close", (code, _signal) =>
resolve({
stdout: out,
stderr: err,
Expand Down Expand Up @@ -110,7 +110,7 @@ const tryPython = async (
`"${PRINT_PYTHON_VERSION_SCRIPT}"`,
]);
return validateVersionRequirement(resolution, result.stdout.trim(), requirement);
} catch (e) {
} catch {
return {
error: true,
...resolution,
Expand Down
Loading
Loading