Skip to content

Commit d20fe79

Browse files
author
zach
authored
Refactor bindgen (#14)
1 parent 4828a02 commit d20fe79

8 files changed

Lines changed: 109 additions & 82 deletions

File tree

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ jobs:
6363
6464
- name: Run Bindgen Test
6565
run: |
66+
sh bundle.sh
6667
sh bindgen-test.sh install
6768
sh bindgen-test.sh run
6869

package-lock.json

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"typescript": "^5.3.2"
1818
},
1919
"dependencies": {
20-
"@dylibso/xtp-bindgen": "1.0.0-rc.11",
20+
"@dylibso/xtp-bindgen": "1.0.0-rc.13",
2121
"ejs": "^3.1.10"
2222
}
2323
}

src/index.ts

Lines changed: 72 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,77 @@
11
import ejs from "ejs";
2-
import { getContext, helpers, Property } from "@dylibso/xtp-bindgen";
2+
import {
3+
helpers,
4+
getContext,
5+
ObjectType,
6+
EnumType,
7+
ArrayType,
8+
XtpNormalizedType,
9+
XtpTyped
10+
} from "@dylibso/xtp-bindgen"
311

4-
function toPythonType(property: Property): string {
5-
let tp
12+
function pythonTypeName(s: string): string {
13+
return helpers.snakeToPascalCase(s);
14+
}
15+
16+
function pythonFunctionName(s: string): string {
17+
return helpers.camelToSnakeCase(s);
18+
}
19+
20+
function isOptional(type: String): boolean {
21+
return type.startsWith('Optional[')
22+
}
623

7-
if (property.$ref) {
8-
tp = property.$ref.name
9-
} else {
10-
switch (property.type) {
11-
case "string":
12-
if (property.format === "date-time") {
13-
tp = "datetime"
14-
} else {
15-
tp = "str"
16-
}
17-
break
18-
case "number":
19-
// @ts-ignore
20-
if (property.contentType === "application/json") {
21-
tp = "str"
22-
} else if (property.format === "float" || property.format === "double") {
23-
tp = "float"
24-
} else {
25-
tp = "int"
26-
}
27-
break
28-
case "integer":
29-
// @ts-ignore
30-
if (property.contentType === "application/json") {
31-
tp = "str"
32-
} else {
33-
tp = "int"
34-
}
35-
break
36-
case "boolean":
37-
tp = "bool"
38-
break
39-
case "object":
40-
tp = "dict"
41-
break
42-
case "array":
43-
if (!property.items) {
44-
tp = "list"
45-
} else {
46-
tp = `List[${toPythonType(property.items as Property)}]`
47-
}
48-
break
49-
case "buffer":
50-
tp = "bytes"
51-
break
52-
default:
53-
throw new Error("Can't convert property to Python type: " + property.type);
54-
}
24+
function toPythonTypeX(type: XtpNormalizedType): string {
25+
const opt = (t: string) => {
26+
return type.nullable ? `Optional[${t}]` : t
5527
}
28+
switch (type.kind) {
29+
case 'string':
30+
return opt('str');
31+
case 'int32':
32+
return opt('int');
33+
case 'float':
34+
return opt('float');
35+
case 'double':
36+
return opt('float')
37+
case 'byte':
38+
return opt('byte');
39+
case 'date-time':
40+
return opt("datetime");
41+
case 'boolean':
42+
return opt('bool');
43+
case 'array':
44+
const arrayType = type as ArrayType
45+
return opt(`List[${toPythonTypeX(arrayType.elementType)}]`);
46+
case 'buffer':
47+
return opt('bytes');
48+
case 'map':
49+
// TODO: improve typing of dicts
50+
return opt('dict');
51+
case 'object':
52+
return opt(pythonTypeName((type as ObjectType).name));
53+
case 'enum':
54+
return opt(pythonTypeName((type as EnumType).name));
55+
default:
56+
throw new Error("Can't convert XTP type to Python type: " + type)
57+
}
58+
}
59+
60+
61+
function toPythonType(property: XtpTyped): string {
62+
let t = toPythonTypeX(property.xtpType);
63+
if (isOptional(t)) return t;
64+
return `Optional[${t}]`;
65+
}
5666

57-
if (!tp) throw new Error("Cant convert property to Python type: " + property.type)
58-
if (!property.nullable && !property.required) return tp
59-
return `Optional[${tp}]`
67+
function toPythonParamType(property: XtpTyped): string {
68+
let t = toPythonTypeX(property.xtpType);
69+
// We need to represent bare int/float as a str in Python for now,
70+
// there may be some updates to the encoder we can make to handle
71+
// this case at some point
72+
t = t.replace('int', 'str');
73+
t = t.replace('float', 'str');
74+
return t;
6075
}
6176

6277
export function render() {
@@ -65,6 +80,9 @@ export function render() {
6580
...helpers,
6681
...getContext(),
6782
toPythonType,
83+
toPythonParamType,
84+
pythonTypeName,
85+
pythonFunctionName
6886
};
6987

7088
const output = ejs.render(tmpl, ctx);

template/plugin/__init__.py.ejs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
# THIS FILE WAS GENERATED BY `xtp-python-bindgen`. DO NOT EDIT.
22

3+
from typing import Optional, List # noqa: F401
4+
from datetime import datetime # noqa: F401
35
import extism # pyright: ignore
46
import plugin
57

68
<% if (Object.values(schema.schemas).length > 0){ %>
7-
from pdk_types import <%- Object.values(schema.schemas).map(schema => schema.name).join(", ") %> # noqa: F401
9+
from pdk_types import <%- Object.values(schema.schemas).map(schema => pythonTypeName(schema.name)).join(", ") %> # noqa: F401
810
<% } %>
911

1012
# Imports
@@ -13,13 +15,13 @@ from pdk_types import <%- Object.values(schema.schemas).map(schema => schema.nam
1315
<% if (hasComment(imp)) -%>
1416
#<%- imp.name %> <%- formatCommentBlock(imp.description, "# ") %>
1517
<% if (hasComment(imp.input)) { -%>
16-
# It takes input of <%- toPythonType(imp.input) %> (<%- formatCommentLine(imp.input.description) %>)
18+
# It takes input of <%- toPythonParamType(imp.input) %> (<%- formatCommentLine(imp.input.description) %>)
1719
<% } -%>
1820
<% if (hasComment(imp.output)) { -%>
19-
# And it returns an output <%- toPythonType(imp.output) %> (<%- formatCommentLine(imp.output.description) %>)
21+
# And it returns an output <%- toPythonParamType(imp.output) %> (<%- formatCommentLine(imp.output.description) %>)
2022
<% } -%>
2123
@extism.import_fn("extism:host/user", "<%- imp.name %>")
22-
def <%- camelToSnakeCase(imp.name) %>(<% if (imp.input) { -%>input: <%- toPythonType(imp.input) %><%} -%>) <% if (imp.output) { %> -> <%- toPythonType(imp.output) %><% } %>: # pyright: ignore [reportReturnType]
24+
def <%- pythonFunctionName(imp.name) %>(<% if (imp.input) { -%>input: <%- toPythonParamType(imp.input) %><%} -%>) <% if (imp.output) { %> -> <%- toPythonParamType(imp.output) %><% } %>: # pyright: ignore [reportReturnType]
2325
pass
2426
<% }) %>
2527

@@ -32,7 +34,7 @@ def <%- camelToSnakeCase(imp.name) %>(<% if (imp.input) { -%>input: <%- toPython
3234
<% } -%>
3335
@extism.plugin_fn
3436
def <%- ex.name %>():
35-
res = plugin.<%- camelToSnakeCase(ex.name) %>(<% if (ex.input) { %> extism.input(<%- toPythonType(ex.input) %>) <% } %>)
37+
res = plugin.<%- pythonFunctionName(ex.name) %>(<% if (ex.input) { %> extism.input(<%- toPythonParamType(ex.input) %>) <% } %>)
3638
extism.output(res)
3739
3840
<% }) %>

template/plugin/pdk_imports.py.ejs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
# THIS FILE WAS GENERATED BY `xtp-python-bindgen`. DO NOT EDIT.
22

3+
from typing import Optional, List # noqa: F401
4+
from datetime import datetime # noqa: F401
35
import extism # noqa: F401 # pyright: ignore
46

57
<% if (Object.values(schema.schemas).length > 0) { %>
6-
from pdk_types import <%- Object.values(schema.schemas).map(schema => schema.name).join(", ") %> # noqa: F401
8+
from pdk_types import <%- Object.values(schema.schemas).map(schema => pythonTypeName(schema.name)).join(", ") %> # noqa: F401
79
<% } %>
810

911
<% schema.imports.forEach(imp => { %>
1012
<% if (hasComment(imp)) -%>
1113
#<%- imp.name %> <%- formatCommentBlock(imp.description, "# ") %>
1214
<% if (hasComment(imp.input)) { -%>
13-
# It takes input of <%- toPythonType(imp.input) %> (<%- formatCommentLine(imp.input.description) %>)
15+
# It takes input of <%- toPythonParamType(imp.input) %> (<%- formatCommentLine(imp.input.description) %>)
1416
<% } -%>
1517
<% if (hasComment(imp.output)) { -%>
16-
# And it returns an output <%- toPythonType(imp.output) %> (<%- formatCommentLine(imp.output.description) %>)
18+
# And it returns an output <%- toPythonParamType(imp.output) %> (<%- formatCommentLine(imp.output.description) %>)
1719
<% } -%>
1820
@extism.import_fn("extism:host/user", "<%- imp.name %>")
19-
def <%- camelToSnakeCase(imp.name) %>(<% if (imp.input) { -%>input: <%- toPythonType(imp.input) %><%} -%>) <% if (imp.output) { %> -> <%- toPythonType(imp.output) %><% } %>: # pyright: ignore [reportReturnType]
21+
def <%- pythonFunctionName(imp.name) %>(<% if (imp.input) { -%>input: <%- toPythonParamType(imp.input) %><%} -%>) <% if (imp.output) { %> -> <%- toPythonParamType(imp.output) %><% } %>: # pyright: ignore [reportReturnType]
2022
pass
2123
<% }) %>
2224

template/plugin/pdk_types.py.ejs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,27 @@ import extism # noqa: F401 # pyright: ignore
1010

1111
<% Object.values(schema.schemas).forEach(schema => { %>
1212
<% if (schema.enum) { %>
13-
class <%- capitalize(schema.name) %>(Enum):
13+
class <%- pythonTypeName(schema.name) %>(Enum):
1414
<% schema.enum.forEach(variant => { -%>
15-
<%- capitalize(variant) %> = "<%- variant %>"
15+
<%- pythonTypeName(variant) %> = "<%- variant %>"
1616
<% }) %>
1717
<% } else { %>
1818
@dataclass
19-
class <%- capitalize(schema.name) %>(extism.Json):
19+
class <%- pythonTypeName(schema.name) %>(extism.Json):
2020
<% schema.properties.forEach(p => { -%>
21-
<% if (p.description) { -%>
22-
# <%- formatCommentBlock(p.description, "# ") %>
23-
<% } -%>
24-
<% if (!p.nullable) {%>
21+
<% if (!p.nullable || p.required) {%>
22+
<% if (p.description) { -%>
23+
# <%- formatCommentBlock(p.description, "# ") %>
24+
<% } -%>
2525
<%- p.name %>: <%- toPythonType(p) %>
2626
<% } %>
2727
<% }) %>
2828
2929
<% schema.properties.forEach(p => { -%>
30-
<% if (p.description) { -%>
31-
# <%- formatCommentBlock(p.description, "# ") %>
32-
<% } -%>
33-
<% if (p.nullable) {%>
30+
<% if (p.nullable && !p.required) {%>
31+
<% if (p.description) { -%>
32+
# <%- formatCommentBlock(p.description, "# ") %>
33+
<% } -%>
3434
<%- p.name %>: <%- toPythonType(p) %> = None
3535
<% } %>
3636
<% }) %>

template/plugin/plugin.py.ejs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1+
from typing import Optional, List # noqa: F401
2+
from datetime import datetime # noqa: F401
13
import extism # noqa: F401 # pyright: ignore
4+
25
<% if (Object.values(schema.schemas).length > 0) { %>
3-
from pdk_types import <%- Object.values(schema.schemas).map(schema => schema.name).join(", ") %> # noqa: F401
6+
from pdk_types import <%- Object.values(schema.schemas).map(schema => pythonTypeName(schema.name)).join(", ") %> # noqa: F401
47
<% } %>
58
<% if (schema.imports.length > 0) { %>
6-
from pdk_imports import <%- schema.imports.map(schema => camelToSnakeCase(schema.name)).join(", ") %> # noqa: F401
9+
from pdk_imports import <%- schema.imports.map(schema => pythonFunctionName(schema.name)).join(", ") %> # noqa: F401
710
<% } %>
811
from typing import List, Optional # noqa: F401
912

1013
<% schema.exports.forEach(ex => { -%>
1114
<% if (hasComment(ex)) { -%>
1215
# <%- formatCommentBlock(ex.description, "# ") %>
1316
<% } -%>
14-
def <%- camelToSnakeCase(ex.name) %>(<% if (ex.input) { %>input: <%- toPythonType(ex.input) %> <% } %>) <% if (ex.output) {%>-> <%- toPythonType(ex.output) %><%}%>:
17+
def <%- pythonFunctionName(ex.name) %>(<% if (ex.input) { %>input: <%- toPythonParamType(ex.input) %> <% } %>) <% if (ex.output) {%>-> <%- toPythonParamType(ex.output) %><%}%>:
1518
<% if (featureFlags['stub-with-code-samples'] && codeSamples(ex, 'python').length > 0) { -%>
1619
<%- codeSamples(ex, 'python')[0].source %>
1720
<% } else { -%>

0 commit comments

Comments
 (0)