Skip to content

Commit

Permalink
Add Support for Python Upgrade Rules (#9776)
Browse files Browse the repository at this point in the history
Need this so we can update python code when we make a breaking change. I mostly just copied whatever we were doing for typescript and tried to do it for python as well.
  • Loading branch information
thsparks committed Nov 10, 2023
1 parent 9a745fc commit d00b972
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 16 deletions.
1 change: 1 addition & 0 deletions localtypings/pxtarget.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,7 @@ declare namespace ts.pxtc {
flashChecksumAddr?: number;
ramSize?: number;
patches?: pxt.Map<UpgradePolicy[]>; // semver range -> upgrade policies
pyPatches?: pxt.Map<UpgradePolicy[]>; // semver range -> upgrade policies
openocdScript?: string;
uf2Family?: string;
onStartText?: boolean;
Expand Down
19 changes: 19 additions & 0 deletions pxtcompiler/simpledriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ namespace pxt {
return mainPkg.getCompileOptionsAsync(target)
}).then(opts => {
patchTS(mainPkg.targetVersion(), opts)
if (mainPkg.getPreferredEditor() === pxt.PYTHON_PROJECT_NAME) {
patchPY(mainPkg.targetVersion(), opts)
}
prepPythonOptions(opts)
return opts
})
Expand Down Expand Up @@ -209,6 +212,22 @@ namespace pxt {
}
}

export function patchPY(version: string, opts: pxtc.CompileOptions) {
if (!version)
return
pxt.debug(`applying PY patches relative to ${version}`)
for (let fn of Object.keys(opts.fileSystem)) {
if (fn.indexOf("/") == -1 && U.endsWith(fn, ".py")) {
const initial = opts.fileSystem[fn]
const patched = pxt.patching.patchPython(version, initial)
if (initial != patched) {
pxt.debug(`applying PY patch to ${fn}`)
opts.fileSystem[fn] = patched
}
}
}
}

// eslint-disable-next-line no-var
declare var global: any;
// eslint-disable-next-line no-var
Expand Down
19 changes: 19 additions & 0 deletions pxtlib/patch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@ namespace pxt.patching {
export function computePatches(version: string, kind?: string): ts.pxtc.UpgradePolicy[] {
const patches = pxt.appTarget.compile ? pxt.appTarget.compile.patches : undefined;
if (!patches) return undefined;
return parsePatches(version, patches, kind);
}

export function computePyPatches(version: string, kind?: string): ts.pxtc.UpgradePolicy[] {
const patches = pxt.appTarget.compile ? pxt.appTarget.compile.pyPatches : undefined;
if (!patches) return undefined;
return parsePatches(version, patches, kind);
}

function parsePatches(version: string, patches: Map<ts.pxtc.UpgradePolicy[]>, kind?: string): ts.pxtc.UpgradePolicy[] {
const v = pxt.semver.tryParse(version || "0.0.0") || pxt.semver.tryParse("0.0.0");
let r: ts.pxtc.UpgradePolicy[] = [];
Object.keys(patches)
Expand Down Expand Up @@ -30,6 +40,15 @@ namespace pxt.patching {

export function patchJavaScript(pkgTargetVersion: string, fileContents: string): string {
const upgrades = pxt.patching.computePatches(pkgTargetVersion);
return patchTextCode(pkgTargetVersion, fileContents, upgrades);
}

export function patchPython(pkgTargetVersion: string, fileContents: string): string {
const upgrades = pxt.patching.computePyPatches(pkgTargetVersion);
return patchTextCode(pkgTargetVersion, fileContents, upgrades);
}

function patchTextCode(pkgTargetVersion: string, fileContents: string, upgrades: pxtc.UpgradePolicy[]): string {
let updatedContents = fileContents;
if (upgrades) {
upgrades.filter(u => u.type === "api").forEach(rule => {
Expand Down
70 changes: 54 additions & 16 deletions webapp/src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -835,7 +835,12 @@ export function applyUpgradesAsync(): Promise<UpgradeResult> {
});
}

const upgradeOp = epkg.header.editor !== pxt.BLOCKS_PROJECT_NAME ? upgradeFromTSAsync : upgradeFromBlocksAsync;
const upgradeOp =
epkg.header.editor !== pxt.BLOCKS_PROJECT_NAME
? epkg.header.editor !== pxt.PYTHON_PROJECT_NAME
? upgradeFromTSAsync
: upgradeFromPythonAsync
: upgradeFromBlocksAsync;

let projectNeverCompiled = false;

Expand Down Expand Up @@ -922,7 +927,7 @@ function upgradeFromBlocksAsync(): Promise<UpgradeResult> {
});
}

function upgradeFromTSAsync(): Promise<UpgradeResult> {
async function upgradeFromTSAsync(): Promise<UpgradeResult> {
const mainPkg = pkg.mainPkg;
const project = pkg.getEditorPkg(mainPkg);
const targetVersion = project.header.targetVersion;
Expand All @@ -937,20 +942,49 @@ function upgradeFromTSAsync(): Promise<UpgradeResult> {

pxt.debug("Applying upgrades to TypeScript")

return checkPatchAsync(patchedFiles)
.then(() => {
return {
success: true,
editor: pxt.JAVASCRIPT_PROJECT_NAME,
patchedFiles
};
})
.catch(e => {
return {
success: false,
errorCodes: e.errorCodes
};
});
try {
await checkPatchAsync(patchedFiles)
return {
success: true,
editor: pxt.JAVASCRIPT_PROJECT_NAME,
patchedFiles
};
} catch (e) {
return {
success: false,
errorCodes: e.errorCodes
};
};
}

async function upgradeFromPythonAsync(): Promise<UpgradeResult> {
const mainPkg = pkg.mainPkg;
const project = pkg.getEditorPkg(mainPkg);
const targetVersion = project.header.targetVersion;

const patchedFiles: pxt.Map<string> = {};
pxt.Util.values(project.files).filter(isPyFile).forEach(file => {
const patched = pxt.patching.patchPython(targetVersion, file.content);
if (patched != file.content) {
patchedFiles[file.name] = patched;
}
});

pxt.debug("Applying upgrades to Python")

try {
await checkPatchAsync(patchedFiles);
return {
success: true,
editor: pxt.PYTHON_PROJECT_NAME,
patchedFiles
};
} catch (e) {
return {
success: false,
errorCodes: e.errorCodes
};
}
}

interface UpgradeError extends Error {
Expand Down Expand Up @@ -1000,6 +1034,10 @@ function isTsFile(file: pkg.File) {
return pxt.Util.endsWith(file.getName(), ".ts");
}

function isPyFile(file: pkg.File) {
return pxt.Util.endsWith(file.getName(), ".py");
}

export function updatePackagesAsync(packages: pkg.EditorPackage[], token?: pxt.Util.CancellationToken): Promise<boolean> {
const epkg = pkg.mainEditorPkg();
let backup: pxt.workspace.Header;
Expand Down

0 comments on commit d00b972

Please sign in to comment.