Skip to content

Commit 297b70e

Browse files
authored
Merge pull request #2295 from CosmWasm/tkulik/python_codegen
Python codegen
2 parents 1b38229 + 285abd6 commit 297b70e

14 files changed

+460
-1
lines changed

packages/cw-schema-codegen/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod go;
2+
pub mod python;
23
pub mod rust;
34
pub mod typescript;

packages/cw-schema-codegen/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ where
7474
Language::Typescript => {
7575
cw_schema_codegen::typescript::process_node(output, schema, node, add_imports)
7676
}
77-
Language::Go | Language::Python => todo!(),
77+
Language::Python => cw_schema_codegen::python::process_node(output, schema, node),
78+
Language::Go => todo!(),
7879
}
7980
})?;
8081

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use self::template::{
2+
EnumTemplate, EnumVariantTemplate, FieldTemplate, StructTemplate, TypeTemplate,
3+
};
4+
use std::{borrow::Cow, io};
5+
6+
pub mod template;
7+
8+
fn expand_node_name<'a>(
9+
schema: &'a cw_schema::SchemaV1,
10+
node: &'a cw_schema::Node,
11+
) -> Cow<'a, str> {
12+
match node.value {
13+
cw_schema::NodeType::Array { items } => {
14+
let items = &schema.definitions[items];
15+
format!("{}[]", expand_node_name(schema, items)).into()
16+
}
17+
cw_schema::NodeType::Float => "float".into(),
18+
cw_schema::NodeType::Double => "float".into(),
19+
cw_schema::NodeType::Boolean => "bool".into(),
20+
cw_schema::NodeType::String => "str".into(),
21+
cw_schema::NodeType::Integer { .. } => "int".into(),
22+
cw_schema::NodeType::Binary => "bytes".into(),
23+
cw_schema::NodeType::Optional { inner } => {
24+
let inner = &schema.definitions[inner];
25+
format!("typing.Optional[{}]", expand_node_name(schema, inner)).into()
26+
}
27+
cw_schema::NodeType::Struct(..) => node.name.as_ref().into(),
28+
cw_schema::NodeType::Tuple { ref items } => {
29+
let items = items
30+
.iter()
31+
.map(|item| expand_node_name(schema, &schema.definitions[*item]))
32+
.collect::<Vec<_>>()
33+
.join(", ");
34+
35+
format!("[{}]", items).into()
36+
}
37+
cw_schema::NodeType::Enum { .. } => node.name.as_ref().into(),
38+
39+
cw_schema::NodeType::Decimal { .. } => "decimal.Decimal".into(),
40+
cw_schema::NodeType::Address => "str".into(),
41+
cw_schema::NodeType::Checksum => todo!(),
42+
cw_schema::NodeType::HexBinary => todo!(),
43+
cw_schema::NodeType::Timestamp => todo!(),
44+
cw_schema::NodeType::Unit => "None".into(),
45+
_ => todo!(),
46+
}
47+
}
48+
49+
fn prepare_docs(desc: Option<&str>) -> Cow<'_, [Cow<'_, str>]> {
50+
desc.map(|desc| desc.lines().map(Into::into).collect())
51+
.unwrap_or(Cow::Borrowed(&[]))
52+
}
53+
54+
pub fn process_node<O>(
55+
output: &mut O,
56+
schema: &cw_schema::SchemaV1,
57+
node: &cw_schema::Node,
58+
) -> io::Result<()>
59+
where
60+
O: io::Write,
61+
{
62+
match node.value {
63+
cw_schema::NodeType::Struct(ref sty) => {
64+
let structt = StructTemplate {
65+
name: node.name.clone(),
66+
docs: prepare_docs(node.description.as_deref()),
67+
ty: match sty {
68+
cw_schema::StructType::Unit => TypeTemplate::Unit,
69+
cw_schema::StructType::Named { ref properties } => TypeTemplate::Named {
70+
fields: properties
71+
.iter()
72+
.map(|(name, prop)| FieldTemplate {
73+
name: Cow::Borrowed(name),
74+
docs: prepare_docs(prop.description.as_deref()),
75+
ty: expand_node_name(schema, &schema.definitions[prop.value]),
76+
})
77+
.collect(),
78+
},
79+
cw_schema::StructType::Tuple { ref items } => TypeTemplate::Tuple(
80+
items
81+
.iter()
82+
.map(|item| expand_node_name(schema, &schema.definitions[*item]))
83+
.collect(),
84+
),
85+
_ => todo!(),
86+
},
87+
};
88+
89+
writeln!(output, "{structt}")?;
90+
}
91+
cw_schema::NodeType::Enum { ref cases, .. } => {
92+
let enumm = EnumTemplate {
93+
name: node.name.clone(),
94+
docs: prepare_docs(node.description.as_deref()),
95+
variants: cases
96+
.iter()
97+
.map(|(name, case)| EnumVariantTemplate {
98+
name: name.clone(),
99+
docs: prepare_docs(case.description.as_deref()),
100+
ty: match case.value {
101+
cw_schema::EnumValue::Unit => TypeTemplate::Unit,
102+
cw_schema::EnumValue::Tuple { ref items } => {
103+
let items = items
104+
.iter()
105+
.map(|item| {
106+
expand_node_name(schema, &schema.definitions[*item])
107+
})
108+
.collect();
109+
110+
TypeTemplate::Tuple(items)
111+
}
112+
cw_schema::EnumValue::Named { ref properties, .. } => {
113+
TypeTemplate::Named {
114+
fields: properties
115+
.iter()
116+
.map(|(name, prop)| FieldTemplate {
117+
name: Cow::Borrowed(name),
118+
docs: prepare_docs(prop.description.as_deref()),
119+
ty: expand_node_name(
120+
schema,
121+
&schema.definitions[prop.value],
122+
),
123+
})
124+
.collect(),
125+
}
126+
}
127+
_ => todo!(),
128+
},
129+
})
130+
.collect(),
131+
};
132+
133+
writeln!(output, "{enumm}")?;
134+
}
135+
_ => (),
136+
}
137+
138+
Ok(())
139+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use askama::Template;
2+
use std::borrow::Cow;
3+
4+
#[derive(Clone)]
5+
pub struct EnumVariantTemplate<'a> {
6+
pub name: Cow<'a, str>,
7+
pub docs: Cow<'a, [Cow<'a, str>]>,
8+
pub ty: TypeTemplate<'a>,
9+
}
10+
11+
#[derive(Template)]
12+
#[template(escape = "none", path = "python/enum.tpl.py")]
13+
pub struct EnumTemplate<'a> {
14+
pub name: Cow<'a, str>,
15+
pub docs: Cow<'a, [Cow<'a, str>]>,
16+
pub variants: Cow<'a, [EnumVariantTemplate<'a>]>,
17+
}
18+
19+
#[derive(Clone)]
20+
pub struct FieldTemplate<'a> {
21+
pub name: Cow<'a, str>,
22+
pub docs: Cow<'a, [Cow<'a, str>]>,
23+
pub ty: Cow<'a, str>,
24+
}
25+
26+
#[derive(Clone)]
27+
pub enum TypeTemplate<'a> {
28+
Unit,
29+
Tuple(Cow<'a, [Cow<'a, str>]>),
30+
Named {
31+
fields: Cow<'a, [FieldTemplate<'a>]>,
32+
},
33+
}
34+
35+
#[derive(Template)]
36+
#[template(escape = "none", path = "python/struct.tpl.py")]
37+
pub struct StructTemplate<'a> {
38+
pub name: Cow<'a, str>,
39+
pub docs: Cow<'a, [Cow<'a, str>]>,
40+
pub ty: TypeTemplate<'a>,
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This code is @generated by cw-schema-codegen. Do not modify this manually.
2+
3+
import typing
4+
import decimal
5+
from pydantic import BaseModel, RootModel
6+
7+
class {{ name }}(RootModel):
8+
"""{% for doc in docs %}
9+
{{ doc }}
10+
{% endfor %}"""
11+
12+
{% for variant in variants %}
13+
{% match variant.ty %}
14+
{% when TypeTemplate::Unit %}
15+
class {{ variant.name }}(RootModel):
16+
"""{% for doc in variant.docs %}
17+
{{ doc }}
18+
{% endfor %}"""
19+
root: typing.Literal['{{ variant.name }}']
20+
{% when TypeTemplate::Tuple with (types) %}
21+
class {{ variant.name }}(BaseModel):
22+
"""{% for doc in variant.docs %}
23+
{{ doc }}
24+
{% endfor %}"""
25+
{{ variant.name }}: typing.Tuple[{{ types|join(", ") }}]
26+
{% when TypeTemplate::Named with { fields } %}
27+
class {{ variant.name }}(BaseModel):
28+
class __Inner(BaseModel):
29+
"""{% for doc in variant.docs %}
30+
{{ doc }}
31+
{% endfor %}"""
32+
{% for field in fields %}
33+
{{ field.name }}: {{ field.ty }}
34+
"""{% for doc in field.docs %}
35+
{{ doc }}
36+
{% endfor %}"""
37+
{% endfor %}
38+
{{ variant.name }}: __Inner
39+
{% endmatch %}
40+
{% endfor %}
41+
root: typing.Union[ {% for variant in variants %} {{ variant.name }}, {% endfor %} ]
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# This code is @generated by cw-schema-codegen. Do not modify this manually.
2+
3+
import typing
4+
import decimal
5+
from pydantic import BaseModel, RootModel
6+
7+
8+
{% match ty %}
9+
{% when TypeTemplate::Unit %}
10+
class {{ name }}(RootModel):
11+
'''{% for doc in docs %}
12+
{{ doc }}
13+
{% endfor %}'''
14+
root: None
15+
{% when TypeTemplate::Tuple with (types) %}
16+
class {{ name }}(RootModel):
17+
'''{% for doc in docs %}
18+
{{ doc }}
19+
{% endfor %}'''
20+
root: typing.Tuple[{{ types|join(", ") }}]
21+
{% when TypeTemplate::Named with { fields } %}
22+
class {{ name }}(BaseModel):
23+
'''{% for doc in docs %}
24+
{{ doc }}
25+
{% endfor %}'''
26+
{% for field in fields %}
27+
{{ field.name }}: {{ field.ty }}
28+
'''{% for doc in field.docs %}
29+
# {{ doc }}
30+
{% endfor %}'''
31+
{% endfor %}
32+
{% endmatch %}

0 commit comments

Comments
 (0)