Skip to content

Commit b7762a1

Browse files
authored
feat(native): Jinja - support passing Map/Seq to Python (#7125)
1 parent 1df04da commit b7762a1

File tree

6 files changed

+84
-3
lines changed

6 files changed

+84
-3
lines changed

packages/cubejs-backend-native/src/python/template/mj_value.rs

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ pub fn from_minijinja_value(from: &mj::value::Value) -> Result<CLRepr, mj::Error
189189
} else {
190190
Err(mj::Error::new(
191191
mj::ErrorKind::InvalidOperation,
192-
format!("Converting from {:?} to python is not supported", from),
192+
format!("Converting from {:?} to Python is not supported", from),
193193
))
194194
}
195195
}
@@ -201,9 +201,60 @@ pub fn from_minijinja_value(from: &mj::value::Value) -> Result<CLRepr, mj::Error
201201
Ok(CLRepr::String(from.as_str().unwrap().to_string()))
202202
}
203203
}
204+
ValueKind::Seq => {
205+
let seq = if let Some(seq) = from.as_seq() {
206+
seq
207+
} else {
208+
return Err(mj::Error::new(
209+
mj::ErrorKind::InvalidOperation,
210+
format!("Unable to convert Seq to Python"),
211+
));
212+
};
213+
214+
let mut arr = Vec::with_capacity(seq.item_count());
215+
216+
for idx in 0..seq.item_count() {
217+
let v = if let Some(value) = seq.get_item(idx) {
218+
from_minijinja_value(&value)?
219+
} else {
220+
CLRepr::Null
221+
};
222+
223+
arr.push(v)
224+
}
225+
226+
Ok(CLRepr::Array(arr))
227+
}
228+
ValueKind::Map => {
229+
let mut obj = CLReprObject::new();
230+
231+
for key in from.try_iter()? {
232+
let value = if let Ok(v) = from.get_item(&key) {
233+
from_minijinja_value(&v)?
234+
} else {
235+
CLRepr::Null
236+
};
237+
238+
let key_str = if let Some(key) = key.as_str() {
239+
key.to_string()
240+
} else {
241+
return Err(mj::Error::new(
242+
mj::ErrorKind::InvalidOperation,
243+
format!(
244+
"Unable to convert Map to Python object: key must be string, actual: {}",
245+
key.kind()
246+
),
247+
));
248+
};
249+
250+
obj.insert(key_str, value);
251+
}
252+
253+
Ok(CLRepr::Object(obj))
254+
}
204255
other => Err(mj::Error::new(
205256
mj::ErrorKind::InvalidOperation,
206-
format!("Converting from {:?} to python is not supported", other),
257+
format!("Converting from {:?} to Python is not supported", other),
207258
)),
208259
}
209260
}

packages/cubejs-backend-native/test/__snapshots__/jinja.test.ts.snap

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,10 @@ exports[`Jinja render arguments-test.yml.jinja: arguments-test.yml.jinja 1`] = `
181181
arg_bool_true: 1
182182
arg_bool_false: 0
183183
arg_str: hello world
184-
arg_null: none"
184+
arg_null: none
185+
arg_seq_1: [1, 2, 3, 4, 5]
186+
arg_seq_2: [5, 4, 3, 2, 1]
187+
arg_sum_map: 20"
185188
`;
186189

187190
exports[`Jinja render data-model.yml.jinja: data-model.yml.jinja 1`] = `

packages/cubejs-backend-native/test/jinja.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ suite('Python model', () => {
6363
arg_sum_integers: expect.any(Object),
6464
arg_str: expect.any(Object),
6565
arg_null: expect.any(Object),
66+
arg_sum_map: expect.any(Object),
67+
arg_seq: expect.any(Object),
6668
});
6769
});
6870
});
@@ -78,6 +80,8 @@ darwinSuite('Scope Python model', () => {
7880
arg_sum_integers: expect.any(Object),
7981
arg_str: expect.any(Object),
8082
arg_null: expect.any(Object),
83+
arg_sum_map: expect.any(Object),
84+
arg_seq: expect.any(Object),
8185
});
8286
});
8387
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
{%- set my_sequence_1 = [1,2,3,4,5] -%}
2+
{%- set my_sequence_2 = [5,4,3,2,1] -%}
3+
{%- set my_map = {'field_a' : 5, 'field_b' : 15} -%}
4+
15
test:
26
arg_sum_integers_int_int: {{ arg_sum_integers(1, 1) }}
37
arg_sum_integers_int_float: {{ arg_sum_integers(1, 3.14) }}
48
arg_bool_true: {{ arg_bool(true) }}
59
arg_bool_false: {{ arg_bool(false) }}
610
arg_str: {{ arg_str("hello world") }}
711
arg_null: {{ arg_null(null) }}
12+
arg_seq_1: {{ arg_seq(my_sequence_1) }}
13+
arg_seq_2: {{ arg_seq(my_sequence_2) }}
14+
arg_sum_map: {{ arg_sum_map(my_map) }}

packages/cubejs-backend-native/test/templates/scoped-utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ def arg_str(a):
1717
def arg_null(a):
1818
return a
1919
20+
@context_func
21+
def arg_sum_map(obj):
22+
return obj['field_a'] + obj['field_b']
23+
24+
@context_func
25+
def arg_seq(a):
26+
return a
27+
2028
@context_func
2129
def load_data_sync():
2230
client = MyApiClient("google.com")

packages/cubejs-backend-native/test/templates/utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ def arg_str(a):
1616
def arg_null(a):
1717
return a
1818

19+
@context_func
20+
def arg_sum_map(obj):
21+
return obj['field_a'] + obj['field_b']
22+
23+
@context_func
24+
def arg_seq(a):
25+
return a
26+
1927
@context_func
2028
def load_data_sync():
2129
client = MyApiClient("google.com")

0 commit comments

Comments
 (0)