Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(python): Config for Trailing Commas in Iterable Types #5486

Merged
merged 4 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
71 changes: 52 additions & 19 deletions generators/python-v2/ast/src/TypeInstantiation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { Reference } from "./Reference";

type InternalTypeInstantiation = Int | Float | Bool | Str | Bytes | List | Set | Tuple | Dict | None | Uuid;

interface StrFormatConfig {
interface IterableConfig {
endWithComma?: boolean;
}

interface StrConfig {
multiline?: boolean;
startOnNewLine?: boolean;
endWithNewLine?: boolean;
Expand All @@ -30,7 +34,7 @@ interface Bool {
interface Str {
type: "str";
value: string;
config?: StrFormatConfig;
config?: StrConfig;
}

interface Bytes {
Expand All @@ -41,21 +45,25 @@ interface Bytes {
interface List {
type: "list";
values: AstNode[];
config?: IterableConfig;
}

interface Set {
type: "set";
values: AstNode[];
config?: IterableConfig;
}

interface Tuple {
type: "tuple";
values: AstNode[];
config?: IterableConfig;
}

interface Dict {
type: "dict";
entries: DictEntry[];
config?: IterableConfig;
}

interface DictEntry {
Expand Down Expand Up @@ -91,7 +99,7 @@ export class TypeInstantiation extends AstNode {

public static str(
value: string,
config: StrFormatConfig = {
config: StrConfig = {
multiline: false,
startOnNewLine: false,
endWithNewLine: false
Expand All @@ -104,26 +112,26 @@ export class TypeInstantiation extends AstNode {
return new this({ type: "bytes", value });
}

public static list(values: AstNode[]): TypeInstantiation {
const list = new this({ type: "list", values });
public static list(values: AstNode[], config: IterableConfig = { endWithComma: false }): TypeInstantiation {
const list = new this({ type: "list", values, config });
values.forEach((value) => list.inheritReferences(value));
return list;
}

public static set(values: AstNode[]): TypeInstantiation {
const set = new this({ type: "set", values });
public static set(values: AstNode[], config: IterableConfig = { endWithComma: false }): TypeInstantiation {
const set = new this({ type: "set", values, config });
values.forEach((value) => set.inheritReferences(value));
return set;
}

public static tuple(values: AstNode[]): TypeInstantiation {
const tuple = new this({ type: "tuple", values });
public static tuple(values: AstNode[], config: IterableConfig = { endWithComma: false }): TypeInstantiation {
const tuple = new this({ type: "tuple", values, config });
values.forEach((value) => tuple.inheritReferences(value));
return tuple;
}

public static dict(entries: DictEntry[]): TypeInstantiation {
const dict = new this({ type: "dict", entries });
public static dict(entries: DictEntry[], config: IterableConfig = { endWithComma: false }): TypeInstantiation {
const dict = new this({ type: "dict", entries, config });
entries.forEach((entry) => {
dict.inheritReferences(entry.key);
dict.inheritReferences(entry.value);
Expand Down Expand Up @@ -174,48 +182,73 @@ export class TypeInstantiation extends AstNode {
case "bytes":
writer.write(`b"${this.internalType.value}"`);
break;
case "list":
case "list": {
const internalType = this.internalType;
writer.write("[");
this.internalType.values.forEach((value, index) => {
internalType.values.forEach((value, index) => {
if (index > 0) {
writer.write(", ");
}
value.write(writer);
if (index === internalType.values.length - 1 && internalType.config?.endWithComma) {
writer.write(",");
}
});
writer.write("]");
break;
case "set":
}
case "set": {
const internalType = this.internalType;
writer.write("{");
this.internalType.values.forEach((value, index) => {
internalType.values.forEach((value, index) => {
if (index > 0) {
writer.write(", ");
}
value.write(writer);
if (index === internalType.values.length - 1 && internalType.config?.endWithComma) {
writer.write(",");
}
});
writer.write("}");
break;
case "tuple":
}
case "tuple": {
const internalType = this.internalType;
writer.write("(");
this.internalType.values.forEach((value, index) => {
if (index > 0) {
writer.write(", ");
}
value.write(writer);
if (
// If the tuple is of length 1, then we must always add a trailing comma
internalType.values.length === 1 ||
// Otherwise, check the config that was specified
(index === internalType.values.length - 1 && internalType.config?.endWithComma)
) {
writer.write(",");
}
});
writer.write(")");
break;
case "dict":
}
case "dict": {
const internalType = this.internalType;
writer.write("{");
this.internalType.entries.forEach((entry, index) => {
internalType.entries.forEach((entry, index) => {
if (index > 0) {
writer.write(", ");
}
entry.key.write(writer);
writer.write(": ");
entry.value.write(writer);
if (index === internalType.entries.length - 1 && internalType.config?.endWithComma) {
writer.write(",");
}
});
writer.write("}");
break;
}
case "none":
writer.write("None");
break;
Expand All @@ -235,7 +268,7 @@ export class TypeInstantiation extends AstNode {
}: {
writer: Writer;
value: string;
} & Pick<StrFormatConfig, "startOnNewLine" | "endWithNewLine">): void {
} & Pick<StrConfig, "startOnNewLine" | "endWithNewLine">): void {
writer.write('"""');
const lines = value.split("\n");

Expand Down
80 changes: 59 additions & 21 deletions generators/python-v2/ast/src/__test__/TypeInstantiation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,31 +70,58 @@ describe("TypeInstantiation", () => {
expect(await writer.toStringFormatted()).toMatchSnapshot();
});

it("list", async () => {
TypeInstantiation.list([
TypeInstantiation.int(1),
TypeInstantiation.str("two"),
TypeInstantiation.bool(true)
]).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
describe("list", () => {
it("basic", async () => {
TypeInstantiation.list([
TypeInstantiation.int(1),
TypeInstantiation.str("two"),
TypeInstantiation.bool(true)
]).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
});
});

it("set", async () => {
TypeInstantiation.set([
TypeInstantiation.int(1),
TypeInstantiation.str("two"),
TypeInstantiation.bool(true)
]).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
describe("set", () => {
it("basic", async () => {
TypeInstantiation.set([
TypeInstantiation.int(1),
TypeInstantiation.str("two"),
TypeInstantiation.bool(true)
]).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
});

it("should support trailing comma", async () => {
TypeInstantiation.set(
[TypeInstantiation.int(1), TypeInstantiation.str("two"), TypeInstantiation.bool(true)],
{ endWithComma: true }
).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
});
});

it("tuple", async () => {
TypeInstantiation.tuple([
TypeInstantiation.int(1),
TypeInstantiation.str("two"),
TypeInstantiation.bool(true)
]).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
describe("tuple", () => {
it("basic", async () => {
TypeInstantiation.tuple([
TypeInstantiation.int(1),
TypeInstantiation.str("two"),
TypeInstantiation.bool(true)
]).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
});

it("should support trailing comma", async () => {
TypeInstantiation.tuple(
[TypeInstantiation.int(1), TypeInstantiation.str("two"), TypeInstantiation.bool(true)],
{ endWithComma: true }
).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
});

it("should handle single-element tuple", async () => {
TypeInstantiation.tuple([TypeInstantiation.int(1)]).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
});
});

describe("dict", () => {
Expand All @@ -115,6 +142,17 @@ describe("TypeInstantiation", () => {
expect(await writer.toStringFormatted()).toMatchSnapshot();
expect(dict.getReferences().length).toBe(2);
});

it("should support trailing comma", async () => {
TypeInstantiation.dict(
[
{ key: TypeInstantiation.str("one"), value: TypeInstantiation.int(1) },
{ key: TypeInstantiation.str("two"), value: TypeInstantiation.bool(true) }
],
{ endWithComma: true }
).write(writer);
expect(await writer.toStringFormatted()).toMatchSnapshot();
});
});

it("none", async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ exports[`TypeInstantiation > dict > should correctly write a dict with reference
"
`;

exports[`TypeInstantiation > dict > should support trailing comma 1`] = `
"{
"one": 1,
"two": True,
}
"
`;

exports[`TypeInstantiation > float 1`] = `
"3.14
"
Expand All @@ -35,7 +43,7 @@ exports[`TypeInstantiation > int 1`] = `
"
`;

exports[`TypeInstantiation > list 1`] = `
exports[`TypeInstantiation > list > basic 1`] = `
"[1, "two", True]
"
`;
Expand All @@ -45,11 +53,20 @@ exports[`TypeInstantiation > none 1`] = `
"
`;

exports[`TypeInstantiation > set 1`] = `
exports[`TypeInstantiation > set > basic 1`] = `
"{1, "two", True}
"
`;

exports[`TypeInstantiation > set > should support trailing comma 1`] = `
"{
1,
"two",
True,
}
"
`;

exports[`TypeInstantiation > str > should render a basic string 1`] = `
""hello"
"
Expand Down Expand Up @@ -86,11 +103,25 @@ exports[`TypeInstantiation > str > should render a string containing quote 1`] =
"
`;

exports[`TypeInstantiation > tuple 1`] = `
exports[`TypeInstantiation > tuple > basic 1`] = `
"(1, "two", True)
"
`;

exports[`TypeInstantiation > tuple > should handle single-element tuple 1`] = `
"(1,)
"
`;

exports[`TypeInstantiation > tuple > should support trailing comma 1`] = `
"(
1,
"two",
True,
)
"
`;

exports[`TypeInstantiation > uuid 1`] = `
"UUID("123e4567-e89b-12d3-a456-426614174000")
"
Expand Down
Loading