Skip to content

Commit 989207d

Browse files
authored
Prevent syntax error when keys are not Python identifiers (#79)
For now just filter these out.
1 parent 3fc80fb commit 989207d

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

type-generation/src/astToIR.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
getExpressionTypeArgs,
3535
getNodeLocation,
3636
groupMembers,
37+
isValidPythonIdentifier,
3738
} from "./astUtils";
3839
import { sanitizeReservedWords, uniqBy } from "./irToString";
3940
import { Needed } from "./types";
@@ -792,15 +793,15 @@ export class Converter {
792793
const spread = !!param.getDotDotDotToken();
793794
const optional = !!param.hasQuestionToken();
794795
const name = param.getName();
795-
const isValidPythonIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_]*$/.test(name);
796+
const isIdentifier = isValidPythonIdentifier(name);
796797
const oldNameContext = this.nameContext?.slice();
797798
const paramType = param.getTypeNode()!;
798799
const isLast = idx === params.length - 1;
799800
if (isLast && Node.isTypeLiteral(paramType)) {
800801
// If it's the last argument and the type is a type literal, we'll
801802
// destructure it so don't make a type.
802803
this.nameContext = undefined;
803-
} else if (isValidPythonIdentifier) {
804+
} else if (isIdentifier) {
804805
this.pushNameContext(name);
805806
} else {
806807
this.nameContext = undefined;
@@ -1119,10 +1120,12 @@ export class Converter {
11191120
isStatic: false,
11201121
});
11211122
const irProps = ([] as PropertyIR[]).concat(
1122-
astProperties.map((prop) => this.propertySignatureToIR(prop, false)),
1123-
staticAstProperties.map((prop) =>
1124-
this.propertySignatureToIR(prop, true),
1125-
),
1123+
astProperties
1124+
.filter((x) => isValidPythonIdentifier(x.getName()))
1125+
.map((prop) => this.propertySignatureToIR(prop, false)),
1126+
staticAstProperties
1127+
.filter((x) => isValidPythonIdentifier(x.getName()))
1128+
.map((prop) => this.propertySignatureToIR(prop, true)),
11261129
);
11271130
const props = uniqBy(irProps, ({ name }) => name);
11281131
return {

type-generation/src/astUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,7 @@ export function classifyIdentifier(ident: Identifier): ClassifiedIdentifier {
272272
}
273273
throw new Error("Unrecognized ident!");
274274
}
275+
276+
export function isValidPythonIdentifier(name: string): boolean {
277+
return /^[a-zA-Z_$][a-zA-Z0-9_]*$/.test(name);
278+
}

type-generation/tests/a.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2003,6 +2003,25 @@ describe("emit", () => {
20032003
`).trim(),
20042004
);
20052005
});
2006+
it("Type literal with keys that aren't identifiers", () => {
2007+
// Maybe we can do something with typed-dict for this? For now, just
2008+
// filter them out.
2009+
const res = emitFile(`
2010+
interface T {
2011+
"x-y": { x: string; };
2012+
};
2013+
declare function f(): T;
2014+
`);
2015+
assert.strictEqual(
2016+
removeTypeIgnores(res.slice(1).join("\n\n")),
2017+
dedent(`
2018+
def f() -> T_iface: ...
2019+
2020+
class T_iface(Protocol):
2021+
pass
2022+
`).trim(),
2023+
);
2024+
});
20062025
});
20072026
it("inheriting from jsobject is also jsobject", () => {
20082027
const res = emitFile(`

0 commit comments

Comments
 (0)