diff --git a/packages/cubejs-backend-native/src/python/cross.rs b/packages/cubejs-backend-native/src/python/cross.rs index 5ab4ad200fd0a..d87ca714bda3c 100644 --- a/packages/cubejs-backend-native/src/python/cross.rs +++ b/packages/cubejs-backend-native/src/python/cross.rs @@ -4,7 +4,7 @@ use neon::prelude::*; use neon::result::Throw; use neon::types::JsDate; use pyo3::exceptions::{PyNotImplementedError, PyTypeError}; -use pyo3::types::{PyBool, PyDict, PyFloat, PyFunction, PyInt, PyList, PySet, PyString}; +use pyo3::types::{PyBool, PyDict, PyFloat, PyFunction, PyInt, PyList, PySet, PyString, PyTuple}; use pyo3::{Py, PyAny, PyErr, PyObject, Python, ToPyObject}; use std::cell::RefCell; use std::collections::hash_map::{IntoIter, Iter, Keys}; @@ -59,6 +59,7 @@ pub enum CLReprKind { Bool, Float, Int, + Tuple, Array, Object, JsFunction, @@ -77,6 +78,7 @@ pub enum CLRepr { Bool(bool), Float(f64), Int(i64), + Tuple(Vec), Array(Vec), Object(CLReprObject), JsFunction(Arc>), @@ -157,6 +159,7 @@ impl CLRepr { CLRepr::Bool(_) => CLReprKind::Bool, CLRepr::Float(_) => CLReprKind::Float, CLRepr::Int(_) => CLReprKind::Int, + CLRepr::Tuple(_) => CLReprKind::Tuple, CLRepr::Array(_) => CLReprKind::Array, CLRepr::Object(_) => CLReprKind::Object, CLRepr::JsFunction(_) => CLReprKind::JsFunction, @@ -274,6 +277,15 @@ impl CLRepr { } Self::Array(r) + } else if v.get_type().is_subclass_of::()? { + let l = v.downcast::()?; + let mut r = Vec::with_capacity(l.len()); + + for v in l.iter() { + r.push(Self::from_python_ref(v)?); + } + + Self::Tuple(r) } else { return Err(PyErr::new::(format!( "Unable to represent {} type as CLR from Python", @@ -295,7 +307,7 @@ impl CLRepr { CLRepr::Bool(v) => cx.boolean(v).upcast(), CLRepr::Float(v) => cx.number(v).upcast(), CLRepr::Int(v) => cx.number(v as f64).upcast(), - CLRepr::Array(arr) => { + CLRepr::Tuple(arr) | CLRepr::Array(arr) => { let r = cx.empty_array(); for (k, v) in arr.into_iter().enumerate() { @@ -389,6 +401,15 @@ impl CLRepr { PyList::new(py, elements).to_object(py) } + CLRepr::Tuple(arr) => { + let mut elements = Vec::with_capacity(arr.len()); + + for el in arr.into_iter() { + elements.push(Self::into_py_impl(el, py)?); + } + + PyTuple::new(py, elements).to_object(py) + } CLRepr::Object(obj) => { let r = PyDict::new(py); diff --git a/packages/cubejs-backend-native/src/python/template/mj_value.rs b/packages/cubejs-backend-native/src/python/template/mj_value.rs index 84a4526bd2288..b907e71af729e 100644 --- a/packages/cubejs-backend-native/src/python/template/mj_value.rs +++ b/packages/cubejs-backend-native/src/python/template/mj_value.rs @@ -261,7 +261,9 @@ pub fn from_minijinja_value(from: &mj::value::Value) -> Result Value { match from { - CLRepr::Array(inner) => Value::from_seq_object(JinjaSequenceObject { inner }), + CLRepr::Tuple(inner) | CLRepr::Array(inner) => { + Value::from_seq_object(JinjaSequenceObject { inner }) + } CLRepr::Object(inner) => Value::from_object(JinjaDynamicObject { inner }), CLRepr::String(v) => Value::from(v), CLRepr::Float(v) => Value::from(v), diff --git a/packages/cubejs-backend-native/test/__snapshots__/jinja.test.ts.snap b/packages/cubejs-backend-native/test/__snapshots__/jinja.test.ts.snap index c3ac9aac6e04f..a1f6f6c0221e1 100644 --- a/packages/cubejs-backend-native/test/__snapshots__/jinja.test.ts.snap +++ b/packages/cubejs-backend-native/test/__snapshots__/jinja.test.ts.snap @@ -184,6 +184,7 @@ exports[`Jinja render arguments-test.yml.jinja: arguments-test.yml.jinja 1`] = ` arg_null: none arg_seq_1: [1, 2, 3, 4, 5] arg_seq_2: [5, 4, 3, 2, 1] + arg_sum_tuple: 3 arg_sum_map: 20" `; diff --git a/packages/cubejs-backend-native/test/jinja.test.ts b/packages/cubejs-backend-native/test/jinja.test.ts index 1ca38aaac33e7..79b849a9890d6 100644 --- a/packages/cubejs-backend-native/test/jinja.test.ts +++ b/packages/cubejs-backend-native/test/jinja.test.ts @@ -63,8 +63,11 @@ suite('Python model', () => { arg_sum_integers: expect.any(Object), arg_str: expect.any(Object), arg_null: expect.any(Object), + arg_sum_tuple: expect.any(Object), arg_sum_map: expect.any(Object), arg_seq: expect.any(Object), + new_int_tuple: expect.any(Object), + new_str_tuple: expect.any(Object), }); }); }); @@ -80,8 +83,11 @@ darwinSuite('Scope Python model', () => { arg_sum_integers: expect.any(Object), arg_str: expect.any(Object), arg_null: expect.any(Object), + arg_sum_tuple: expect.any(Object), arg_sum_map: expect.any(Object), arg_seq: expect.any(Object), + new_int_tuple: expect.any(Object), + new_str_tuple: expect.any(Object), }); }); }); diff --git a/packages/cubejs-backend-native/test/templates/arguments-test.yml.jinja b/packages/cubejs-backend-native/test/templates/arguments-test.yml.jinja index e26d872c7bf88..7d96023c37c68 100644 --- a/packages/cubejs-backend-native/test/templates/arguments-test.yml.jinja +++ b/packages/cubejs-backend-native/test/templates/arguments-test.yml.jinja @@ -1,6 +1,8 @@ {%- set my_sequence_1 = [1,2,3,4,5] -%} {%- set my_sequence_2 = [5,4,3,2,1] -%} {%- set my_map = {'field_a' : 5, 'field_b' : 15} -%} +{%- set my_int_tuple = new_int_tuple() -%} +{%- set my_str_tuple = new_str_tuple() -%} test: arg_sum_integers_int_int: {{ arg_sum_integers(1, 1) }} @@ -11,4 +13,5 @@ test: arg_null: {{ arg_null(null) }} arg_seq_1: {{ arg_seq(my_sequence_1) }} arg_seq_2: {{ arg_seq(my_sequence_2) }} + arg_sum_tuple: {{ arg_sum_tuple(my_int_tuple) }} arg_sum_map: {{ arg_sum_map(my_map) }} diff --git a/packages/cubejs-backend-native/test/templates/scoped-utils.py b/packages/cubejs-backend-native/test/templates/scoped-utils.py index 961e4af806344..77b32cc7aafbf 100644 --- a/packages/cubejs-backend-native/test/templates/scoped-utils.py +++ b/packages/cubejs-backend-native/test/templates/scoped-utils.py @@ -17,6 +17,10 @@ def arg_str(a): def arg_null(a): return a +@context_func +def arg_sum_tuple(tu): + return tu[0] + tu[1] + @context_func def arg_sum_map(obj): return obj['field_a'] + obj['field_b'] @@ -25,6 +29,14 @@ def arg_sum_map(obj): def arg_seq(a): return a +@context_func +def new_int_tuple(): + return (1,2) + +@context_func +def new_str_tuple(): + return ("hello", "word") + @context_func def load_data_sync(): client = MyApiClient("google.com") diff --git a/packages/cubejs-backend-native/test/templates/utils.py b/packages/cubejs-backend-native/test/templates/utils.py index c5917f22762e4..6e85783de28ea 100644 --- a/packages/cubejs-backend-native/test/templates/utils.py +++ b/packages/cubejs-backend-native/test/templates/utils.py @@ -16,6 +16,10 @@ def arg_str(a): def arg_null(a): return a +@context_func +def arg_sum_tuple(tu): + return tu[0] + tu[1] + @context_func def arg_sum_map(obj): return obj['field_a'] + obj['field_b'] @@ -24,6 +28,14 @@ def arg_sum_map(obj): def arg_seq(a): return a +@context_func +def new_int_tuple(): + return (1,2) + +@context_func +def new_str_tuple(): + return ("1", "2") + @context_func def load_data_sync(): client = MyApiClient("google.com")