Skip to content

Commit d3de220

Browse files
committed
Drop Union construct in python client
Union is deprecated in favor of `A | B`. This updates the generation to not produce the old style and updates the old usage in __init__.
1 parent 80d817b commit d3de220

File tree

4 files changed

+75
-152
lines changed

4 files changed

+75
-152
lines changed

scripts/generate-schema/gen-python.test.ts

Lines changed: 0 additions & 70 deletions
This file was deleted.

scripts/generate-schema/gen-python.ts

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,20 @@ import { assert } from "jsr:@std/assert";
66
const header = (relativePath: string) =>
77
`# DO NOT EDIT: This file is auto-generated by ${relativePath}\n` +
88
"from enum import Enum\n" +
9-
"from typing import Any, Literal, Optional, Union\n" +
109
"import msgspec\n\n";
1110

1211
export function extractExportedNames(content: string): string[] {
1312
const names = new Set<string>();
1413

15-
// Match class definitions and enum assignments
14+
// Match class definitions and union type assignments
1615
const classRegex = /^class\s+([a-zA-Z_][a-zA-Z0-9_]*)/gm;
17-
const enumRegex = /^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*Union\[/gm;
16+
const unionRegex = /^([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*.+\|.+/gm;
1817

1918
let match;
2019
while ((match = classRegex.exec(content)) !== null) {
2120
names.add(match[1]);
2221
}
23-
while ((match = enumRegex.exec(content)) !== null) {
22+
while ((match = unionRegex.exec(content)) !== null) {
2423
names.add(match[1]);
2524
}
2625

@@ -51,10 +50,7 @@ export function generatePython(
5150
return output + content;
5251
}
5352

54-
function generateTypes(
55-
doc: Doc,
56-
name: string,
57-
) {
53+
function generateTypes(doc: Doc, name: string) {
5854
const writer = new Writer();
5955

6056
let definitions = "";
@@ -97,9 +93,7 @@ function generateTypes(
9793
return definitions + writer.output();
9894
}
9995

100-
function sortByRequired<T extends { required: boolean }>(
101-
properties: T[],
102-
): T[] {
96+
function sortByRequired<T extends { required: boolean }>(properties: T[]): T[] {
10397
return [...properties].sort((a, b) => {
10498
if (a.required === b.required) return 0;
10599
return a.required ? -1 : 1;
@@ -116,9 +110,8 @@ function generateNode(node: Node, writer: Writer) {
116110
.with({ type: "boolean" }, () => w("bool"))
117111
.with({ type: "string" }, () => w("str"))
118112
.with({ type: "literal" }, (node) => w(`Literal["${node.value}"]`))
119-
.with(
120-
{ type: "record" },
121-
(node) => w(`dict[str, ${mapPythonType(node.valueType)}]`),
113+
.with({ type: "record" }, (node) =>
114+
w(`dict[str, ${mapPythonType(node.valueType)}]`),
122115
)
123116
.with({ type: "enum" }, (node) => {
124117
wn(`class ${node.name}(str, Enum):`);
@@ -134,9 +127,10 @@ function generateNode(node: Node, writer: Writer) {
134127
if (m.name) {
135128
name = m.name;
136129
} else {
137-
const ident = m.type === "object"
138-
? m.properties?.find((p) => p.required)?.key ?? ""
139-
: "";
130+
const ident =
131+
m.type === "object"
132+
? (m.properties?.find((p) => p.required)?.key ?? "")
133+
: "";
140134
name = `${node.name}${cap(ident)}`;
141135
}
142136
if (!generatedDependentClasses.has(name)) {
@@ -148,17 +142,17 @@ function generateNode(node: Node, writer: Writer) {
148142
return name;
149143
});
150144
writer.append(depWriter.output());
151-
wn(`${node.name} = Union[${classes.join(", ")}]`);
145+
wn(`${node.name} = ${classes.join(" | ")}`);
152146
})
153147
.with({ type: "object" }, (node) => {
154148
match(context.parent)
155149
.with({ type: "union" }, () => {
156150
const name = context.closestName();
157151
const ident = node.properties.find((p) => p.required)?.key ?? "";
158152
wn(
159-
`class ${name}${
160-
cap(ident)
161-
}(msgspec.Struct, kw_only=True, omit_defaults=True):`,
153+
`class ${name}${cap(
154+
ident,
155+
)}(msgspec.Struct, kw_only=True, omit_defaults=True):`,
162156
);
163157
})
164158
.with(P.nullish, () => {
@@ -179,9 +173,8 @@ function generateNode(node: Node, writer: Writer) {
179173

180174
for (const { key, required, description, value } of sortedProperties) {
181175
w(` ${key}: `);
182-
if (!required) w("Union[");
183176
generateNode(value, writer);
184-
if (!required) w(", None] = None");
177+
if (!required) w(" | None = None");
185178
wn("");
186179
if (description) {
187180
wn(` """${description}"""`);
@@ -193,7 +186,6 @@ function generateNode(node: Node, writer: Writer) {
193186
const depWriter = new Writer();
194187
const { w: d, wn: dn } = depWriter.shorthand();
195188
const classes: string[] = [];
196-
w("Union[");
197189
for (const [name, properties] of Object.entries(node.members)) {
198190
for (const { value } of properties) {
199191
if (isComplexType(value)) {
@@ -213,15 +205,17 @@ function generateNode(node: Node, writer: Writer) {
213205

214206
const sortedProperties = sortByRequired(properties);
215207

216-
for (
217-
const { key, required, description, value } of sortedProperties
218-
) {
208+
for (const {
209+
key,
210+
required,
211+
description,
212+
value,
213+
} of sortedProperties) {
219214
d(` ${key}: `);
220-
if (!required) d("Union[");
221215
!isComplexType(value)
222216
? generateNode(value, depWriter)
223217
: d(value.name ?? value.type);
224-
if (!required) d(", None] = None");
218+
if (!required) d(" | None = None");
225219
dn("");
226220
if (description) {
227221
dn(` """${description}"""`);
@@ -230,9 +224,9 @@ function generateNode(node: Node, writer: Writer) {
230224
dn("");
231225
}
232226
}
233-
w(classes.join(", "));
227+
w(classes.join(" | "));
234228
writer.prepend(depWriter.output());
235-
wn("]");
229+
wn("");
236230
})
237231
.with({ type: "intersection" }, (node) => {
238232
assert(

src/clients/python/src/justbe_webview/__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import os
33
import platform
44
import subprocess
5-
from typing import Any, Callable, Literal, Union, cast, TypeVar
5+
from typing import Any, Callable, Literal, cast, TypeVar
66
from pathlib import Path
77
import aiofiles
88
import httpx
@@ -54,7 +54,7 @@
5454

5555

5656
def return_result(
57-
result: Union[AckResponse, ResultResponse, ErrResponse],
57+
result: AckResponse | ResultResponse | ErrResponse,
5858
expected_type: type[ResultType],
5959
) -> Any:
6060
print(f"Return result: {result}")
@@ -63,7 +63,7 @@ def return_result(
6363
raise ValueError(f"Expected {expected_type.__name__} result got: {result}")
6464

6565

66-
def return_ack(result: Union[AckResponse, ResultResponse, ErrResponse]) -> None:
66+
def return_ack(result: AckResponse | ResultResponse | ErrResponse) -> None:
6767
print(f"Return ack: {result}")
6868
if isinstance(result, AckResponse):
6969
return
@@ -212,11 +212,11 @@ async def __aexit__(
212212
async def send(self, request: WebViewRequest) -> WebViewResponse:
213213
if self.process is None:
214214
raise RuntimeError("Webview process not started")
215-
future: asyncio.Future[Union[AckResponse, ResultResponse, ErrResponse]] = (
215+
future: asyncio.Future[AckResponse | ResultResponse | ErrResponse] = (
216216
asyncio.Future()
217217
)
218218

219-
def set_result(event: Union[AckResponse, ResultResponse, ErrResponse]) -> None:
219+
def set_result(event: AckResponse | ResultResponse | ErrResponse) -> None:
220220
future.set_result(event)
221221

222222
self.internal_event.once(str(request.id), set_result) # type: ignore
@@ -229,7 +229,7 @@ def set_result(event: Union[AckResponse, ResultResponse, ErrResponse]) -> None:
229229
result = await future
230230
return result
231231

232-
async def recv(self) -> Union[WebViewNotification, None]:
232+
async def recv(self) -> WebViewNotification | None:
233233
if self.process is None:
234234
raise RuntimeError("Webview process not started")
235235

@@ -319,7 +319,7 @@ async def set_size(self, size: dict[Literal["width", "height"], float]):
319319

320320
async def get_size(
321321
self, include_decorations: bool = False
322-
) -> dict[Literal["width", "height", "scaleFactor"], Union[int, float]]:
322+
) -> dict[Literal["width", "height", "scaleFactor"], int | float]:
323323
result = await self.send(
324324
GetSizeRequest(id=self.message_id, include_decorations=include_decorations)
325325
)

0 commit comments

Comments
 (0)