From 2ea9ec4cb203f2a1b2ca4c524bf105e9efa0b35a Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Fri, 29 Aug 2025 18:38:21 +0200 Subject: [PATCH 1/6] feat(native): Initial support for Python 3.13 --- .github/workflows/publish.yml | 4 +- .github/workflows/rust-cubesql.yml | 4 +- packages/cubejs-backend-native/Cargo.lock | 60 ++++++++++++------- packages/cubejs-backend-native/Cargo.toml | 9 +-- packages/cubejs-backend-native/package.json | 1 + .../cubejs-backend-native/src/cross/clrepr.rs | 4 +- .../src/python/runtime.rs | 10 ++-- .../src/template/mj_value/python.rs | 2 +- 8 files changed, 55 insertions(+), 39 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 72915644b357e..7f8b3fe8eedb0 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -74,7 +74,7 @@ jobs: strategy: matrix: node-version: [22] - python-version: ["3.9", "3.10", "3.11", "3.12", "fallback"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "fallback"] target: ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] include: - target: x86_64-unknown-linux-gnu @@ -155,7 +155,7 @@ jobs: node-version: [22.x] os-version: ["macos-13"] target: ["x86_64-apple-darwin", "aarch64-apple-darwin"] - python-version: ["3.9", "3.10", "3.11", "3.12", "fallback"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "fallback"] include: - target: x86_64-apple-darwin package_target_arch: x64 diff --git a/.github/workflows/rust-cubesql.yml b/.github/workflows/rust-cubesql.yml index 8c84fe30ba79f..319eb3ce9f2f5 100644 --- a/.github/workflows/rust-cubesql.yml +++ b/.github/workflows/rust-cubesql.yml @@ -128,7 +128,7 @@ jobs: # Current used version + 1 LTS # TODO: Add 24 after it's been released (don't forget to uncomment excludes below!) node-version: [22] - python-version: ["3.9", "3.10", "3.11", "3.12", "fallback"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "fallback"] target: ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"] # minimize number of jobs exclude: @@ -243,6 +243,8 @@ jobs: python-version: "3.11" - target: x86_64-apple-darwin python-version: "3.12" + - target: x86_64-apple-darwin + python-version: "3.13" - target: x86_64-apple-darwin python-version: "fallback" - target: aarch64-apple-darwin diff --git a/packages/cubejs-backend-native/Cargo.lock b/packages/cubejs-backend-native/Cargo.lock index 2b6c21e12b496..fdd6ce370e739 100644 --- a/packages/cubejs-backend-native/Cargo.lock +++ b/packages/cubejs-backend-native/Cargo.lock @@ -715,7 +715,7 @@ dependencies = [ "neon", "once_cell", "pyo3", - "pyo3-asyncio", + "pyo3-async-runtimes", "serde", "serde_json", "simple_logger", @@ -1315,6 +1315,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -2337,6 +2343,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + [[package]] name = "postgres" version = "0.19.7" @@ -2450,15 +2462,16 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.20.0" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b" +checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" dependencies = [ "cfg-if", "indoc", "libc", "memoffset", - "parking_lot", + "once_cell", + "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", @@ -2466,35 +2479,35 @@ dependencies = [ ] [[package]] -name = "pyo3-asyncio" -version = "0.20.0" +name = "pyo3-async-runtimes" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea6b68e93db3622f3bb3bf363246cf948ed5375afe7abff98ccbdd50b184995" +checksum = "2529f0be73ffd2be0cc43c013a640796558aa12d7ca0aab5cc14f375b4733031" dependencies = [ "futures", "once_cell", "pin-project-lite", "pyo3", - "pyo3-asyncio-macros", + "pyo3-async-runtimes-macros", "tokio", ] [[package]] -name = "pyo3-asyncio-macros" -version = "0.20.0" +name = "pyo3-async-runtimes-macros" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c467178e1da6252c95c29ecf898b133f742e9181dca5def15dc24e19d45a39" +checksum = "22c26fd8e9fc19f53f0c1e00bf61471de6789f7eb263056f7f944a9cceb5823e" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.98", ] [[package]] name = "pyo3-build-config" -version = "0.20.0" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96fe70b176a89cff78f2fa7b3c930081e163d5379b4dcdf993e3ae29ca662e5" +checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" dependencies = [ "once_cell", "target-lexicon", @@ -2502,9 +2515,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.20.0" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214929900fd25e6604661ed9cf349727c8920d47deff196c4e28165a6ef2a96b" +checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" dependencies = [ "libc", "pyo3-build-config", @@ -2512,9 +2525,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.20.0" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac53072f717aa1bfa4db832b39de8c875b7c7af4f4a6fe93cdbf9264cf8383b" +checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -2524,12 +2537,13 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.20.0" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7774b5a8282bd4f25f803b1f0d945120be959a36c72e08e7cd031c792fdfd424" +checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", + "pyo3-build-config", "quote", "syn 2.0.98", ] @@ -3266,9 +3280,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-lexicon" -version = "0.12.12" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tempfile" diff --git a/packages/cubejs-backend-native/Cargo.toml b/packages/cubejs-backend-native/Cargo.toml index ec59d90f8250e..bad835da5b5ac 100644 --- a/packages/cubejs-backend-native/Cargo.toml +++ b/packages/cubejs-backend-native/Cargo.toml @@ -34,11 +34,8 @@ log-reroute = "0.1" minijinja = { version = "1", features = ["json", "loader"] } once_cell = "1.10" # python -pyo3 = { version = "0.20.0", features = [], optional = true } -pyo3-asyncio = { version = "0.20.0", features = [ - "tokio-runtime", - "attributes", -], optional = true } +pyo3 = { version = "0.22.6", features = ["gil-refs", "py-clone"], optional = true } +pyo3-async-runtimes = { version = "0.22.0", features = ["tokio-runtime", "attributes"], optional = true } serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.127" simple_logger = "1.7.0" @@ -54,4 +51,4 @@ features = ["napi-1", "napi-4", "napi-6", "futures"] default = ["neon-entrypoint"] neon-debug = [] neon-entrypoint = [] -python = ["pyo3", "pyo3-asyncio"] +python = ["pyo3", "pyo3-async-runtimes"] diff --git a/packages/cubejs-backend-native/package.json b/packages/cubejs-backend-native/package.json index d4b34df0d3ce0..b19aba13e7e09 100644 --- a/packages/cubejs-backend-native/package.json +++ b/packages/cubejs-backend-native/package.json @@ -55,6 +55,7 @@ "value": [ "libpython", [ + "3.13", "3.12", "3.11", "3.10", diff --git a/packages/cubejs-backend-native/src/cross/clrepr.rs b/packages/cubejs-backend-native/src/cross/clrepr.rs index 9e0b17b256668..44a045ee17cf5 100644 --- a/packages/cubejs-backend-native/src/cross/clrepr.rs +++ b/packages/cubejs-backend-native/src/cross/clrepr.rs @@ -6,6 +6,8 @@ use crate::cross::py_in_js::{ }; #[cfg(feature = "python")] use crate::utils::bind_method; +#[cfg(feature = "python")] +use pyo3::Python; use neon::prelude::*; use neon::result::Throw; use neon::types::JsDate; @@ -240,7 +242,7 @@ impl CLRepr { #[cfg(feature = "python")] if from.is_a::(cx) { let ref_wrap = from.downcast_or_throw::(cx)?; - let fun = ref_wrap.borrow().get_fun().clone(); + let fun = Python::with_gil(|py| ref_wrap.borrow().get_fun().clone_ref(py)); return Ok(CLRepr::PythonRef(PythonRef::PyFunction(fun))); } diff --git a/packages/cubejs-backend-native/src/python/runtime.rs b/packages/cubejs-backend-native/src/python/runtime.rs index 3a0308243a33e..2d3e5c157a10b 100644 --- a/packages/cubejs-backend-native/src/python/runtime.rs +++ b/packages/cubejs-backend-native/src/python/runtime.rs @@ -111,7 +111,7 @@ impl PyRuntime { let is_coroutine = unsafe { pyo3::ffi::PyCoro_CheckExact(call_res.as_ptr()) == 1 }; if is_coroutine { - let fut = pyo3_asyncio::tokio::into_future(call_res.as_ref(py))?; + let fut = pyo3_async_runtimes::tokio::into_future(call_res.bind(py).clone())?; Ok(PyScheduledFunResult::Poll(Box::pin(fut))) } else { Ok(PyScheduledFunResult::Ready(CLRepr::from_python_ref( @@ -206,12 +206,12 @@ impl PyRuntime { trace!("Initializing executor in a separate thread"); std::thread::spawn(|| { - pyo3_asyncio::tokio::get_runtime() - .block_on(pyo3_asyncio::tokio::re_exports::pending::<()>()) + pyo3_async_runtimes::tokio::get_runtime() + .block_on(pyo3_async_runtimes::tokio::re_exports::pending::<()>()) }); let res = Python::with_gil(|py| -> Result<(), PyErr> { - pyo3_asyncio::tokio::run(py, async move { + pyo3_async_runtimes::tokio::run(py, async move { loop { if let Some(task) = receiver.recv().await { trace!("New task"); @@ -247,7 +247,7 @@ pub fn py_runtime_init<'a, C: Context<'a>>( pyo3::prepare_freethreaded_python(); // it's safe to unwrap - pyo3_asyncio::tokio::init_with_runtime(runtime).unwrap(); + pyo3_async_runtimes::tokio::init_with_runtime(runtime).unwrap(); if PY_RUNTIME.set(PyRuntime::new(channel)).is_err() { cx.throw_error("Error on setting PyRuntime") diff --git a/packages/cubejs-backend-native/src/template/mj_value/python.rs b/packages/cubejs-backend-native/src/template/mj_value/python.rs index 510b121fa0c9a..ca48165ce767d 100644 --- a/packages/cubejs-backend-native/src/template/mj_value/python.rs +++ b/packages/cubejs-backend-native/src/template/mj_value/python.rs @@ -254,7 +254,7 @@ impl Object for JinjaPythonFunction { ) })?; - let call_future = py_runtime.call_async(self.inner.clone(), arguments); + let call_future = Python::with_gil(|py| py_runtime.call_async(self.inner.clone_ref(py), arguments)); let tokio = tokio_runtime().map_err(|err| { mj::Error::new( From 44c8d916f7dbce24e061ec9d064759782fcda55a Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Fri, 29 Aug 2025 18:41:25 +0200 Subject: [PATCH 2/6] chore: fmt --- packages/cubejs-backend-native/src/cross/clrepr.rs | 4 ++-- .../cubejs-backend-native/src/template/mj_value/python.rs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cubejs-backend-native/src/cross/clrepr.rs b/packages/cubejs-backend-native/src/cross/clrepr.rs index 44a045ee17cf5..0867bf886a729 100644 --- a/packages/cubejs-backend-native/src/cross/clrepr.rs +++ b/packages/cubejs-backend-native/src/cross/clrepr.rs @@ -6,12 +6,12 @@ use crate::cross::py_in_js::{ }; #[cfg(feature = "python")] use crate::utils::bind_method; -#[cfg(feature = "python")] -use pyo3::Python; use neon::prelude::*; use neon::result::Throw; use neon::types::JsDate; #[cfg(feature = "python")] +use pyo3::Python; +#[cfg(feature = "python")] use std::cell::RefCell; use std::collections::hash_map::{IntoIter, Iter, Keys}; use std::collections::HashMap; diff --git a/packages/cubejs-backend-native/src/template/mj_value/python.rs b/packages/cubejs-backend-native/src/template/mj_value/python.rs index ca48165ce767d..7edcd6199d528 100644 --- a/packages/cubejs-backend-native/src/template/mj_value/python.rs +++ b/packages/cubejs-backend-native/src/template/mj_value/python.rs @@ -254,7 +254,8 @@ impl Object for JinjaPythonFunction { ) })?; - let call_future = Python::with_gil(|py| py_runtime.call_async(self.inner.clone_ref(py), arguments)); + let call_future = + Python::with_gil(|py| py_runtime.call_async(self.inner.clone_ref(py), arguments)); let tokio = tokio_runtime().map_err(|err| { mj::Error::new( From f5ea3e085dbeb30cc18c30244cb4233748747df7 Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Fri, 29 Aug 2025 18:43:34 +0200 Subject: [PATCH 3/6] chore(ci): Upgrade cross images to 31072025 --- .github/workflows/drivers-tests.yml | 2 +- .github/workflows/master.yml | 2 +- .github/workflows/publish.yml | 4 ++-- .github/workflows/push.yml | 4 ++-- .github/workflows/rust-cubesql.yml | 4 ++-- .github/workflows/rust-cubestore-master.yml | 2 +- .github/workflows/rust-cubestore.yml | 2 +- rust/cubestore/Cross.toml | 6 +++--- 8 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/drivers-tests.yml b/.github/workflows/drivers-tests.yml index 7f16a79527882..4455adbcdb4b8 100644 --- a/.github/workflows/drivers-tests.yml +++ b/.github/workflows/drivers-tests.yml @@ -103,7 +103,7 @@ jobs: target: [ "x86_64-unknown-linux-gnu" ] fail-fast: false container: - image: cubejs/rust-cross:${{ matrix.target }}-15082024 + image: cubejs/rust-cross:${{ matrix.target }}-31072025 steps: - name: Checkout diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 3e654726528dc..ae05328a20af6 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -48,7 +48,7 @@ jobs: timeout-minutes: 60 name: Build Linux Native backend for Dev image container: - image: cubejs/rust-cross:x86_64-unknown-linux-gnu-15082024-python-3.11 + image: cubejs/rust-cross:x86_64-unknown-linux-gnu-31072025-python-3.11 steps: - name: Checkout diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7f8b3fe8eedb0..bc19f20f5a2c2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -87,7 +87,7 @@ jobs: package_target_libc: glibc fail-fast: false container: - image: cubejs/rust-cross:${{ matrix.target }}-15082024${{ matrix.python-version != 'fallback' && format('-python-{0}', matrix.python-version) || '' }} + image: cubejs/rust-cross:${{ matrix.target }}-31072025${{ matrix.python-version != 'fallback' && format('-python-{0}', matrix.python-version) || '' }} permissions: contents: write steps: @@ -610,7 +610,7 @@ jobs: compress: false fail-fast: false container: - image: cubejs/rust-cross:${{ matrix.target }}-15082024 + image: cubejs/rust-cross:${{ matrix.target }}-31072025 permissions: contents: write steps: diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 4d97bd72d4f2f..c25f5af516709 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -230,7 +230,7 @@ jobs: timeout-minutes: 60 if: (needs['latest-tag-sha'].outputs.sha != github.sha) container: - image: cubejs/rust-cross:x86_64-unknown-linux-gnu-15082024 + image: cubejs/rust-cross:x86_64-unknown-linux-gnu-31072025 steps: - name: Checkout uses: actions/checkout@v4 @@ -269,7 +269,7 @@ jobs: python-version: [ 3.11 ] fail-fast: false container: - image: cubejs/rust-cross:x86_64-unknown-linux-gnu-15082024-python-${{ matrix.python-version }} + image: cubejs/rust-cross:x86_64-unknown-linux-gnu-31072025-python-${{ matrix.python-version }} steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/rust-cubesql.yml b/.github/workflows/rust-cubesql.yml index 319eb3ce9f2f5..af83ebfc34723 100644 --- a/.github/workflows/rust-cubesql.yml +++ b/.github/workflows/rust-cubesql.yml @@ -24,7 +24,7 @@ jobs: timeout-minutes: 20 name: Check fmt/clippy container: - image: cubejs/rust-cross:x86_64-unknown-linux-gnu-15082024 + image: cubejs/rust-cross:x86_64-unknown-linux-gnu-31072025 steps: - name: Checkout @@ -141,7 +141,7 @@ jobs: target: "aarch64-unknown-linux-gnu" fail-fast: false container: - image: cubejs/rust-cross:${{ matrix.target }}-15082024${{ matrix.python-version != 'fallback' && format('-python-{0}', matrix.python-version) || '' }} + image: cubejs/rust-cross:${{ matrix.target }}-31072025${{ matrix.python-version != 'fallback' && format('-python-{0}', matrix.python-version) || '' }} steps: - name: Checkout diff --git a/.github/workflows/rust-cubestore-master.yml b/.github/workflows/rust-cubestore-master.yml index db2c66df847bd..96f020558ea49 100644 --- a/.github/workflows/rust-cubestore-master.yml +++ b/.github/workflows/rust-cubestore-master.yml @@ -282,7 +282,7 @@ jobs: compress: false fail-fast: false container: - image: cubejs/rust-cross:${{ matrix.target }}-15082024 + image: cubejs/rust-cross:${{ matrix.target }}-31072025 steps: - uses: actions/checkout@v4 - name: Setup Rust toolchain diff --git a/.github/workflows/rust-cubestore.yml b/.github/workflows/rust-cubestore.yml index 4468563362fa5..791b65e2cf948 100644 --- a/.github/workflows/rust-cubestore.yml +++ b/.github/workflows/rust-cubestore.yml @@ -221,7 +221,7 @@ jobs: compress: false fail-fast: false container: - image: cubejs/rust-cross:${{ matrix.target }}-15082024 + image: cubejs/rust-cross:${{ matrix.target }}-31072025 steps: - uses: actions/checkout@v4 - name: Setup Rust toolchain diff --git a/rust/cubestore/Cross.toml b/rust/cubestore/Cross.toml index 880787846bbc3..a1d340dccc35f 100644 --- a/rust/cubestore/Cross.toml +++ b/rust/cubestore/Cross.toml @@ -1,11 +1,11 @@ [target.x86_64-unknown-linux-gnu] -image = "cubejs/rust-cross:x86_64-unknown-linux-gnu-15082024" +image = "cubejs/rust-cross:x86_64-unknown-linux-gnu-31072025" xargo = false [target.x86_64-unknown-linux-musl] -image = "cubejs/rust-cross:x86_64-unknown-linux-musl-15082024" +image = "cubejs/rust-cross:x86_64-unknown-linux-musl-31072025" xargo = false [target.aarch64-unknown-linux-gnu] -image = "cubejs/rust-cross:aarch64-unknown-linux-gnu-15082024" +image = "cubejs/rust-cross:aarch64-unknown-linux-gnu-31072025" xargo = false From d797114ef7dba0a8fdbbc06f87f20de496151739 Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Fri, 29 Aug 2025 18:57:09 +0200 Subject: [PATCH 4/6] chore: use new Bound API --- packages/cubejs-backend-native/Cargo.toml | 2 +- .../src/cross/clrepr_python.rs | 41 ++++++------- .../src/python/cube_config.rs | 7 ++- .../cubejs-backend-native/src/python/entry.rs | 57 +++++++++++-------- .../src/python/neon_py.rs | 2 +- .../src/python/runtime.rs | 13 +++-- .../cubejs-backend-native/src/python/utils.rs | 12 ++-- .../src/template/mj_value/python.rs | 6 +- 8 files changed, 76 insertions(+), 64 deletions(-) diff --git a/packages/cubejs-backend-native/Cargo.toml b/packages/cubejs-backend-native/Cargo.toml index bad835da5b5ac..21c84d4198687 100644 --- a/packages/cubejs-backend-native/Cargo.toml +++ b/packages/cubejs-backend-native/Cargo.toml @@ -34,7 +34,7 @@ log-reroute = "0.1" minijinja = { version = "1", features = ["json", "loader"] } once_cell = "1.10" # python -pyo3 = { version = "0.22.6", features = ["gil-refs", "py-clone"], optional = true } +pyo3 = { version = "0.22.6", features = ["py-clone"], optional = true } pyo3-async-runtimes = { version = "0.22.0", features = ["tokio-runtime", "attributes"], optional = true } serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.127" diff --git a/packages/cubejs-backend-native/src/cross/clrepr_python.rs b/packages/cubejs-backend-native/src/cross/clrepr_python.rs index 9712206845cc9..3a584e44b82bc 100644 --- a/packages/cubejs-backend-native/src/cross/clrepr_python.rs +++ b/packages/cubejs-backend-native/src/cross/clrepr_python.rs @@ -2,10 +2,11 @@ use crate::cross::clrepr::CLReprObject; use crate::cross::{CLRepr, CLReprObjectKind, StringType}; use pyo3::exceptions::{PyNotImplementedError, PyTypeError}; use pyo3::types::{ - PyBool, PyComplex, PyDate, PyDict, PyFloat, PyFrame, PyFunction, PyInt, PyList, PySequence, - PySet, PyString, PyTraceback, PyTuple, + PyAnyMethods, PyBool, PyBoolMethods, PyComplex, PyDate, PyDict, PyDictMethods, PyFloat, + PyFloatMethods, PyFrame, PyFunction, PyInt, PyList, PyListMethods, PySequence, PySet, + PySetMethods, PyString, PyTraceback, PyTuple, PyTupleMethods, PyTypeMethods, }; -use pyo3::{Py, PyAny, PyErr, PyObject, Python, ToPyObject}; +use pyo3::{Bound, Py, PyAny, PyErr, PyObject, Python, ToPyObject}; #[derive(Debug, Clone)] pub enum PythonRef { @@ -18,7 +19,7 @@ pub enum PythonRef { impl CLRepr { /// Convert python value to CLRepr - pub fn from_python_ref(v: &PyAny) -> Result { + pub fn from_python_ref(v: &Bound<'_, PyAny>) -> Result { if v.is_none() { return Ok(Self::Null); } @@ -30,7 +31,7 @@ impl CLRepr { StringType::Normal }; - Self::String(v.to_string(), string_type) + Self::String(v.str()?.to_string(), string_type) } else if v.get_type().is_subclass_of::()? { Self::Bool(v.downcast::()?.is_true()) } else if v.get_type().is_subclass_of::()? { @@ -47,7 +48,7 @@ impl CLRepr { if k.get_type().is_subclass_of::()? { let key_str = k.downcast::()?; - obj.insert(key_str.to_string(), Self::from_python_ref(v)?); + obj.insert(key_str.to_string(), Self::from_python_ref(&v)?); } } @@ -57,7 +58,7 @@ impl CLRepr { let mut r = Vec::with_capacity(l.len()); for v in l.iter() { - r.push(Self::from_python_ref(v)?); + r.push(Self::from_python_ref(&v)?); } Self::Array(r) @@ -66,7 +67,7 @@ impl CLRepr { let mut r = Vec::with_capacity(l.len()); for v in l.iter() { - r.push(Self::from_python_ref(v)?); + r.push(Self::from_python_ref(&v)?); } Self::Array(r) @@ -75,12 +76,12 @@ impl CLRepr { let mut r = Vec::with_capacity(l.len()); for v in l.iter() { - r.push(Self::from_python_ref(v)?); + r.push(Self::from_python_ref(&v)?); } Self::Tuple(r) } else if v.get_type().is_subclass_of::()? { - let fun: Py = v.downcast::()?.into(); + let fun: Py = v.downcast::()?.clone().unbind(); Self::PythonRef(PythonRef::PyFunction(fun)) } else if v.get_type().is_subclass_of::()? { @@ -116,12 +117,12 @@ impl CLRepr { ))); } - Self::PythonRef(PythonRef::PyObject(v.into())) + Self::PythonRef(PythonRef::PyObject(v.clone().unbind())) }) } - fn into_py_dict_impl(obj: CLReprObject, py: Python) -> Result<&PyDict, PyErr> { - let r = PyDict::new(py); + fn into_py_dict_impl(obj: CLReprObject, py: Python) -> Result, PyErr> { + let r = PyDict::new_bound(py); for (k, v) in obj.into_iter() { r.set_item(k, Self::into_py_impl(v, py)?)?; @@ -132,11 +133,11 @@ impl CLRepr { fn into_py_impl(from: CLRepr, py: Python) -> Result { Ok(match from { - CLRepr::String(v, _) => PyString::new(py, &v).to_object(py), - CLRepr::Bool(v) => PyBool::new(py, v).to_object(py), - CLRepr::Float(v) => PyFloat::new(py, v).to_object(py), + CLRepr::String(v, _) => PyString::new_bound(py, &v).to_object(py), + CLRepr::Bool(v) => PyBool::new_bound(py, v).to_object(py), + CLRepr::Float(v) => PyFloat::new_bound(py, v).to_object(py), CLRepr::Int(v) => { - let py_int: &PyInt = unsafe { py.from_owned_ptr(pyo3::ffi::PyLong_FromLong(v)) }; + let py_int = unsafe { Bound::from_owned_ptr(py, pyo3::ffi::PyLong_FromLong(v)) }; py_int.to_object(py) } @@ -147,7 +148,7 @@ impl CLRepr { elements.push(Self::into_py_impl(el, py)?); } - PyList::new(py, elements).to_object(py) + PyList::new_bound(py, elements).to_object(py) } CLRepr::Tuple(arr) => { let mut elements = Vec::with_capacity(arr.len()); @@ -156,7 +157,7 @@ impl CLRepr { elements.push(Self::into_py_impl(el, py)?); } - PyTuple::new(py, elements).to_object(py) + PyTuple::new_bound(py, elements).to_object(py) } CLRepr::Object(obj) => { let r = Self::into_py_dict_impl(obj, py)?; @@ -189,7 +190,7 @@ impl CLRepr { }) } - pub fn into_py_dict(self, py: Python) -> Result<&PyDict, PyErr> { + pub fn into_py_dict(self, py: Python) -> Result, PyErr> { Ok(match self { CLRepr::Object(obj) => Self::into_py_dict_impl(obj, py)?, other => { diff --git a/packages/cubejs-backend-native/src/python/cube_config.rs b/packages/cubejs-backend-native/src/python/cube_config.rs index 0dcb71a44531f..73d2a17bfd3b1 100644 --- a/packages/cubejs-backend-native/src/python/cube_config.rs +++ b/packages/cubejs-backend-native/src/python/cube_config.rs @@ -1,6 +1,7 @@ use convert_case::{Case, Casing}; use neon::prelude::*; -use pyo3::{PyAny, PyResult}; +use pyo3::types::PyAnyMethods; +use pyo3::{Bound, PyAny, PyResult}; use crate::cross::{CLRepr, CLReprObject, CLReprObjectKind}; @@ -69,10 +70,10 @@ impl CubeConfigPy { ] } - pub fn attr(&mut self, config_module: &PyAny, key: &str) -> PyResult<()> { + pub fn attr(&mut self, config_module: &Bound<'_, PyAny>, key: &str) -> PyResult<()> { let v = config_module.getattr(key)?; if !v.is_none() { - let value = CLRepr::from_python_ref(v)?; + let value = CLRepr::from_python_ref(&v)?; self.properties.insert(key.to_case(Case::Camel), value); }; diff --git a/packages/cubejs-backend-native/src/python/entry.rs b/packages/cubejs-backend-native/src/python/entry.rs index 6305a0ab77502..161a75b9f6c0b 100644 --- a/packages/cubejs-backend-native/src/python/entry.rs +++ b/packages/cubejs-backend-native/src/python/entry.rs @@ -6,18 +6,19 @@ use crate::python::runtime::py_runtime_init; use neon::prelude::*; use pyo3::exceptions::PyException; use pyo3::prelude::*; -use pyo3::types::{PyDict, PyFunction, PyList, PyString, PyTuple}; +use pyo3::types::{PyAnyMethods, PyDict, PyFunction, PyList, PyString, PyTuple}; use std::path::Path; fn extend_sys_path(py: Python, file_name: &String) -> PyResult<()> { - let sys_path = py.import("sys")?.getattr("path")?.downcast::()?; + let binding = py.import_bound("sys")?.getattr("path")?; + let sys_path = binding.downcast::()?; let config_dir = Path::new(&file_name) .parent() .unwrap_or_else(|| Path::new(".")); let config_dir_str = config_dir.to_str().unwrap_or("."); - sys_path.insert(0, PyString::new(py, config_dir_str))?; + sys_path.insert(0, PyString::new_bound(py, config_dir_str))?; Ok(()) } @@ -40,9 +41,10 @@ fn python_load_config(mut cx: FunctionContext) -> JsResult { env!("CARGO_MANIFEST_DIR"), "/python/cube/src/__init__.py" )); - PyModule::from_code(py, cube_code, "__init__.py", "cube")?; + PyModule::from_code_bound(py, cube_code, "__init__.py", "cube")?; - let config_module = PyModule::from_code(py, &file_content_arg, &options_file_name, "")?; + let config_module = + PyModule::from_code_bound(py, &file_content_arg, &options_file_name, "")?; let settings_py = if config_module.hasattr("config")? { config_module.getattr("config")? } else { @@ -60,7 +62,7 @@ fn python_load_config(mut cx: FunctionContext) -> JsResult { let mut cube_conf = CubeConfigPy::new(); for attr_name in cube_conf.get_attrs() { - cube_conf.attr(settings_py, attr_name)?; + cube_conf.attr(&settings_py, attr_name)?; } Ok(cube_conf) @@ -90,9 +92,9 @@ fn python_load_model(mut cx: FunctionContext) -> JsResult { env!("CARGO_MANIFEST_DIR"), "/python/cube/src/__init__.py" )); - PyModule::from_code(py, cube_code, "__init__.py", "cube")?; + PyModule::from_code_bound(py, cube_code, "__init__.py", "cube")?; - let model_module = PyModule::from_code(py, &model_content, &model_file_name, "")?; + let model_module = PyModule::from_code_bound(py, &model_content, &model_file_name, "")?; let mut collected_functions = CLReprObject::new(CLReprObjectKind::Object); let mut collected_variables = CLReprObject::new(CLReprObjectKind::Object); @@ -101,48 +103,55 @@ fn python_load_model(mut cx: FunctionContext) -> JsResult { if model_module.hasattr("template")? { let template = model_module.getattr("template")?; - let functions = template.getattr("functions")?.downcast::()?; + let functions_attr = template.getattr("functions")?; + let functions = functions_attr.downcast::()?; for (local_key, local_value) in functions.iter() { if local_value.is_instance_of::() { - let fun: Py = local_value.downcast::()?.into(); + let fun: Py = + local_value.downcast::()?.clone().unbind(); collected_functions.insert( - local_key.to_string(), + local_key.str()?.to_string(), CLRepr::PythonRef(PythonRef::PyExternalFunction(fun)), ); } } - let variables = template.getattr("variables")?.downcast::()?; + let variables_attr = template.getattr("variables")?; + let variables = variables_attr.downcast::()?; for (local_key, local_value) in variables.iter() { - collected_variables - .insert(local_key.to_string(), CLRepr::from_python_ref(local_value)?); + collected_variables.insert( + local_key.str()?.to_string(), + CLRepr::from_python_ref(&local_value)?, + ); } - let filters = template.getattr("filters")?.downcast::()?; + let filters_attr = template.getattr("filters")?; + let filters = filters_attr.downcast::()?; for (local_key, local_value) in filters.iter() { - let fun: Py = local_value.downcast::()?.into(); + let fun: Py = local_value.downcast::()?.clone().unbind(); collected_filters.insert( - local_key.to_string(), + local_key.str()?.to_string(), CLRepr::PythonRef(PythonRef::PyExternalFunction(fun)), ); } } else { // backward compatibility, was used in private preview, not as Public API // TODO: Remove after 1.4 - let inspect_module = py.import("inspect")?; + let inspect_module = py.import_bound("inspect")?; let args = (model_module, inspect_module.getattr("isfunction")?); - let functions_with_names = inspect_module - .call_method1("getmembers", args)? - .downcast::()?; + let getmembers_result = inspect_module.call_method1("getmembers", args)?; + let functions_with_names = getmembers_result.downcast::()?; for function_details in functions_with_names.iter() { let function_details = function_details.downcast::()?; - let fun_name = function_details.get_item(0)?.downcast::()?; - let fun = function_details.get_item(1)?.downcast::()?; + let fun_name_item = function_details.get_item(0)?; + let fun_name = fun_name_item.downcast::()?; + let fun_item = function_details.get_item(1)?; + let fun = fun_item.downcast::()?; let has_attr = fun.hasattr("cube_context_func")?; if has_attr { - let fun: Py = fun.into(); + let fun: Py = fun.clone().unbind(); collected_functions.insert( fun_name.to_string(), CLRepr::PythonRef(PythonRef::PyExternalFunction(fun)), diff --git a/packages/cubejs-backend-native/src/python/neon_py.rs b/packages/cubejs-backend-native/src/python/neon_py.rs index c2049195574f8..573583aff5cdf 100644 --- a/packages/cubejs-backend-native/src/python/neon_py.rs +++ b/packages/cubejs-backend-native/src/python/neon_py.rs @@ -5,7 +5,7 @@ pub(crate) fn format_python_error(py_err: PyErr) -> String { let err = format!("Python error: {}", py_err); let bt = Python::with_gil(move |py| -> PyResult> { - if let Some(trace_back) = py_err.traceback(py) { + if let Some(trace_back) = py_err.traceback_bound(py) { Ok(Some(trace_back.format()?)) } else { Ok(None) diff --git a/packages/cubejs-backend-native/src/python/runtime.rs b/packages/cubejs-backend-native/src/python/runtime.rs index 2d3e5c157a10b..d9f5cd97ab111 100644 --- a/packages/cubejs-backend-native/src/python/runtime.rs +++ b/packages/cubejs-backend-native/src/python/runtime.rs @@ -96,18 +96,19 @@ impl PyRuntime { let task_result = Python::with_gil(move |py| -> PyResult { let mut prep_tuple = Vec::with_capacity(args.len()); - let mut py_kwargs = None; + let mut kwargs_dict_opt = None; for arg in args { if arg.is_kwarg() { - py_kwargs = Some(arg.into_py_dict(py)?); + kwargs_dict_opt = Some(arg.into_py_dict(py)?); } else { prep_tuple.push(arg.into_py(py)?); } } - let py_args = PyTuple::new(py, prep_tuple); - let call_res = fun.call(py, py_args, py_kwargs)?; + let py_args = PyTuple::new_bound(py, prep_tuple); + let py_kwargs = kwargs_dict_opt.as_ref(); + let call_res = fun.call_bound(py, py_args, py_kwargs)?; let is_coroutine = unsafe { pyo3::ffi::PyCoro_CheckExact(call_res.as_ptr()) == 1 }; if is_coroutine { @@ -115,7 +116,7 @@ impl PyRuntime { Ok(PyScheduledFunResult::Poll(Box::pin(fut))) } else { Ok(PyScheduledFunResult::Ready(CLRepr::from_python_ref( - call_res.as_ref(py), + &call_res.bind(py), )?)) } }); @@ -155,7 +156,7 @@ impl PyRuntime { let res = Python::with_gil(move |py| -> Result { let res = match fut_res { - Ok(r) => CLRepr::from_python_ref(r.as_ref(py)), + Ok(r) => CLRepr::from_python_ref(&r.bind(py)), Err(err) => Err(err), }; diff --git a/packages/cubejs-backend-native/src/python/utils.rs b/packages/cubejs-backend-native/src/python/utils.rs index ea740a0422715..79d9421fc829e 100644 --- a/packages/cubejs-backend-native/src/python/utils.rs +++ b/packages/cubejs-backend-native/src/python/utils.rs @@ -11,7 +11,7 @@ pub fn python_fn_call_sync(py_fun: &Py, arguments: Vec) -> P args_tuple.push(arg.into_py(py)?); } - let tuple = PyTuple::new(py, args_tuple); + let tuple = PyTuple::new_bound(py, args_tuple); let call_res = py_fun.call1(py, tuple)?; @@ -21,7 +21,7 @@ pub fn python_fn_call_sync(py_fun: &Py, arguments: Vec) -> P "Calling function with async response is not supported", )) } else { - CLRepr::from_python_ref(call_res.as_ref(py)) + CLRepr::from_python_ref(&call_res.bind(py)) } }) } @@ -34,7 +34,7 @@ pub fn python_obj_call_sync(py_fun: &PyObject, arguments: Vec) -> PyResu args_tuple.push(arg.into_py(py)?); } - let tuple = PyTuple::new(py, args_tuple); + let tuple = PyTuple::new_bound(py, args_tuple); let call_res = py_fun.call1(py, tuple)?; @@ -44,7 +44,7 @@ pub fn python_obj_call_sync(py_fun: &PyObject, arguments: Vec) -> PyResu "Calling object with async response is not supported", )) } else { - CLRepr::from_python_ref(call_res.as_ref(py)) + CLRepr::from_python_ref(&call_res.bind(py)) } }) } @@ -64,7 +64,7 @@ where args_tuple.push(arg.into_py(py)?); } - let tuple = PyTuple::new(py, args_tuple); + let tuple = PyTuple::new_bound(py, args_tuple); let call_res = py_fun.call_method1(py, name, tuple)?; @@ -74,7 +74,7 @@ where "Calling object method with async response is not supported", )) } else { - CLRepr::from_python_ref(call_res.as_ref(py)) + CLRepr::from_python_ref(&call_res.bind(py)) } }) } diff --git a/packages/cubejs-backend-native/src/template/mj_value/python.rs b/packages/cubejs-backend-native/src/template/mj_value/python.rs index 7edcd6199d528..6d09bba9b0eab 100644 --- a/packages/cubejs-backend-native/src/template/mj_value/python.rs +++ b/packages/cubejs-backend-native/src/template/mj_value/python.rs @@ -8,7 +8,7 @@ use log::error; use minijinja as mj; use minijinja::value as mjv; use minijinja::value::{Object, ObjectKind, StructObject, Value}; -use pyo3::types::{PyDict, PyFunction}; +use pyo3::types::{PyDict, PyDictMethods, PyFunction}; use pyo3::{Py, PyObject, PyResult, Python}; use std::convert::TryInto; use std::sync::Arc; @@ -171,7 +171,7 @@ impl StructObject for JinjaPythonObject { let res = Python::with_gil(move |py| -> PyResult { let attr_name = obj_ref.getattr(py, name)?; - CLRepr::from_python_ref(attr_name.as_ref(py)) + CLRepr::from_python_ref(&attr_name.bind(py)) }); match res { @@ -190,7 +190,7 @@ impl StructObject for JinjaPythonObject { Python::with_gil(|py| { let mut fields: Vec> = vec![]; - match obj_ref.downcast::(py) { + match obj_ref.downcast_bound::(py) { Ok(dict_ref) => { for key in dict_ref.keys() { fields.push(key.to_string().into()); From 729943bf266876dde139f5b164117e6b1467fcb6 Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Fri, 29 Aug 2025 19:23:37 +0200 Subject: [PATCH 5/6] chore: Clone should hold GIL --- packages/cubejs-backend-native/.gitignore | 2 ++ packages/cubejs-backend-native/src/cross/clrepr.rs | 4 ++-- .../cubejs-backend-native/src/cross/clrepr_python.rs | 12 +++++++++++- packages/cubejs-backend-native/src/cross/py_in_js.rs | 4 ++-- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/packages/cubejs-backend-native/.gitignore b/packages/cubejs-backend-native/.gitignore index 46a96f32f5b0c..e53504e5c9260 100644 --- a/packages/cubejs-backend-native/.gitignore +++ b/packages/cubejs-backend-native/.gitignore @@ -6,3 +6,5 @@ index.node **/node_modules **/.DS_Store npm-debug.log +test/__pycache__/ +test/subdir_for_test/__pycache__/ diff --git a/packages/cubejs-backend-native/src/cross/clrepr.rs b/packages/cubejs-backend-native/src/cross/clrepr.rs index 0867bf886a729..5f29d657e15d2 100644 --- a/packages/cubejs-backend-native/src/cross/clrepr.rs +++ b/packages/cubejs-backend-native/src/cross/clrepr.rs @@ -98,9 +98,9 @@ pub enum StringType { Safe, } -/// Cross language representation is abstraction to transfer values between +/// Cross-language representation is an abstraction to transfer values between /// JavaScript and Python across Rust. Converting between two different languages requires -/// to use Context which is available on the call (one for python and one for js), which result as +/// using Context which is available on the call (one for python and one for js), which results as /// blocking. #[derive(Debug, Clone)] pub enum CLRepr { diff --git a/packages/cubejs-backend-native/src/cross/clrepr_python.rs b/packages/cubejs-backend-native/src/cross/clrepr_python.rs index 3a584e44b82bc..22381ae3dda62 100644 --- a/packages/cubejs-backend-native/src/cross/clrepr_python.rs +++ b/packages/cubejs-backend-native/src/cross/clrepr_python.rs @@ -8,7 +8,7 @@ use pyo3::types::{ }; use pyo3::{Bound, Py, PyAny, PyErr, PyObject, Python, ToPyObject}; -#[derive(Debug, Clone)] +#[derive(Debug)] pub enum PythonRef { PyObject(PyObject), PyFunction(Py), @@ -17,6 +17,16 @@ pub enum PythonRef { PyExternalFunction(Py), } +impl Clone for PythonRef { + fn clone(&self) -> Self { + Python::with_gil(|py| match self { + PythonRef::PyObject(obj) => PythonRef::PyObject(obj.clone_ref(py)), + PythonRef::PyFunction(fun) => PythonRef::PyFunction(fun.clone_ref(py)), + PythonRef::PyExternalFunction(fun) => PythonRef::PyExternalFunction(fun.clone_ref(py)), + }) + } +} + impl CLRepr { /// Convert python value to CLRepr pub fn from_python_ref(v: &Bound<'_, PyAny>) -> Result { diff --git a/packages/cubejs-backend-native/src/cross/py_in_js.rs b/packages/cubejs-backend-native/src/cross/py_in_js.rs index 2d1626d3f3060..fad7fe2dbd40b 100644 --- a/packages/cubejs-backend-native/src/cross/py_in_js.rs +++ b/packages/cubejs-backend-native/src/cross/py_in_js.rs @@ -2,7 +2,7 @@ use crate::cross::CLRepr; use crate::python::runtime::py_runtime; use neon::prelude::*; use pyo3::types::PyFunction; -use pyo3::Py; +use pyo3::{Py, Python}; use std::cell::RefCell; pub struct JsPyFunctionWrapper { @@ -43,7 +43,7 @@ pub fn cl_repr_py_function_wrapper(mut cx: FunctionContext) -> JsResult r, Err(err) => return cx.throw_error(format!("Unable to init python runtime: {:?}", err)), From de7fac844cc6e9d3b686d679af3408ba13609769 Mon Sep 17 00:00:00 2001 From: Dmitry Patsura Date: Fri, 29 Aug 2025 19:33:47 +0200 Subject: [PATCH 6/6] chore: fix clippy issues --- packages/cubejs-backend-native/src/python/runtime.rs | 4 ++-- packages/cubejs-backend-native/src/python/utils.rs | 6 +++--- .../cubejs-backend-native/src/template/mj_value/python.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cubejs-backend-native/src/python/runtime.rs b/packages/cubejs-backend-native/src/python/runtime.rs index d9f5cd97ab111..d21b063e5d6d1 100644 --- a/packages/cubejs-backend-native/src/python/runtime.rs +++ b/packages/cubejs-backend-native/src/python/runtime.rs @@ -116,7 +116,7 @@ impl PyRuntime { Ok(PyScheduledFunResult::Poll(Box::pin(fut))) } else { Ok(PyScheduledFunResult::Ready(CLRepr::from_python_ref( - &call_res.bind(py), + call_res.bind(py), )?)) } }); @@ -156,7 +156,7 @@ impl PyRuntime { let res = Python::with_gil(move |py| -> Result { let res = match fut_res { - Ok(r) => CLRepr::from_python_ref(&r.bind(py)), + Ok(r) => CLRepr::from_python_ref(r.bind(py)), Err(err) => Err(err), }; diff --git a/packages/cubejs-backend-native/src/python/utils.rs b/packages/cubejs-backend-native/src/python/utils.rs index 79d9421fc829e..fccd711b34379 100644 --- a/packages/cubejs-backend-native/src/python/utils.rs +++ b/packages/cubejs-backend-native/src/python/utils.rs @@ -21,7 +21,7 @@ pub fn python_fn_call_sync(py_fun: &Py, arguments: Vec) -> P "Calling function with async response is not supported", )) } else { - CLRepr::from_python_ref(&call_res.bind(py)) + CLRepr::from_python_ref(call_res.bind(py)) } }) } @@ -44,7 +44,7 @@ pub fn python_obj_call_sync(py_fun: &PyObject, arguments: Vec) -> PyResu "Calling object with async response is not supported", )) } else { - CLRepr::from_python_ref(&call_res.bind(py)) + CLRepr::from_python_ref(call_res.bind(py)) } }) } @@ -74,7 +74,7 @@ where "Calling object method with async response is not supported", )) } else { - CLRepr::from_python_ref(&call_res.bind(py)) + CLRepr::from_python_ref(call_res.bind(py)) } }) } diff --git a/packages/cubejs-backend-native/src/template/mj_value/python.rs b/packages/cubejs-backend-native/src/template/mj_value/python.rs index 6d09bba9b0eab..54c6667561e58 100644 --- a/packages/cubejs-backend-native/src/template/mj_value/python.rs +++ b/packages/cubejs-backend-native/src/template/mj_value/python.rs @@ -171,7 +171,7 @@ impl StructObject for JinjaPythonObject { let res = Python::with_gil(move |py| -> PyResult { let attr_name = obj_ref.getattr(py, name)?; - CLRepr::from_python_ref(&attr_name.bind(py)) + CLRepr::from_python_ref(attr_name.bind(py)) }); match res {