Skip to content

Commit a8878f8

Browse files
committed
chore(native): Make python as optional feature
1 parent 9086be4 commit a8878f8

File tree

13 files changed

+172
-73
lines changed

13 files changed

+172
-73
lines changed

.github/workflows/rust-cubesql.yml

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,14 @@ jobs:
120120
run: cd rust/cubesql && cargo test
121121

122122
native_linux:
123-
needs: [lint,unit,unit_legacy]
123+
needs: [lint]
124124
runs-on: ubuntu-20.04
125125
timeout-minutes: 60
126-
name: Build Linux GNU ${{ matrix.node-version }}.x ${{ matrix.target }}
126+
name: Build Linux GNU ${{ matrix.node-version }}.x ${{ matrix.target }} with Python ${{ matrix.python-version }}
127127
strategy:
128128
matrix:
129129
node-version: [14, 16, 18]
130+
python-version: [3.11, "false"]
130131
target: ["x86_64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"]
131132
fail-fast: false
132133
container:
@@ -177,10 +178,16 @@ jobs:
177178
command: yarn install --frozen-lockfile
178179
- name: Lerna tsc
179180
run: yarn tsc
180-
- name: Build native
181+
- name: Build native (fallback)
182+
if: (matrix.python-version == 'false')
181183
env:
182184
CARGO_BUILD_TARGET: ${{ matrix.target }}
183-
run: cd packages/cubejs-backend-native && yarn run native:build
185+
run: cd packages/cubejs-backend-native && yarn run native:build-debug
186+
- name: Build native (with Python)
187+
if: (matrix.python-version != 'false')
188+
env:
189+
CARGO_BUILD_TARGET: ${{ matrix.target }}
190+
run: cd packages/cubejs-backend-native && yarn run native:build-debug-python
184191
- name: Test native (GNU only)
185192
if: (matrix.target == 'x86_64-unknown-linux-gnu')
186193
env:
@@ -194,16 +201,17 @@ jobs:
194201
run: cd packages/cubejs-testing && yarn smoke:cubesql
195202

196203
native:
197-
needs: [lint,unit,unit_legacy]
204+
needs: [lint]
198205
runs-on: ${{ matrix.os-version }}
199206
timeout-minutes: 60
200-
name: Build ${{ matrix.os-version }} ${{ matrix.node-version }}
207+
name: Build ${{ matrix.os-version }} ${{ matrix.node-version }} with Python ${{ matrix.python-version }}
201208

202209
strategy:
203210
matrix:
204211
# We dont need to test under all versions, we do it under linux
205212
node-version: [16.x]
206213
os-version: [windows-2019, macos-11]
214+
python-version: ["3.9", "3.10", "3.11", "false"]
207215
fail-fast: false
208216

209217
steps:
@@ -221,8 +229,9 @@ jobs:
221229
components: rustfmt
222230
- name: Install Python
223231
uses: actions/setup-python@v4
232+
if: (matrix.python-version != 'false')
224233
with:
225-
python-version: '3.11'
234+
python-version: ${{ matrix.python-version }}
226235
- name: Install Node.js ${{ matrix.node-version }}
227236
uses: actions/setup-node@v3
228237
with:
@@ -252,8 +261,15 @@ jobs:
252261
command: yarn install --frozen-lockfile
253262
- name: Lerna tsc
254263
run: yarn tsc
255-
- name: Build native
264+
- name: Build native (fallback)
265+
if: (matrix.python-version == 'false')
256266
env:
257267
CUBESQL_STREAM_MODE: true
258268
CUBEJS_NATIVE_INTERNAL_DEBUG: true
259269
run: cd packages/cubejs-backend-native && yarn run native:build && yarn run test:unit
270+
- name: Build native (with Python)
271+
if: (matrix.python-version != 'false')
272+
env:
273+
CUBESQL_STREAM_MODE: true
274+
CUBEJS_NATIVE_INTERNAL_DEBUG: true
275+
run: cd packages/cubejs-backend-native && yarn run native:build-debug-python && yarn run test:unit

packages/cubejs-backend-native/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ convert_case = "0.4"
3232
version = "=0.10.0"
3333
default-features = false
3434
features = ["napi-1", "napi-4", "napi-6", "channel-api", "promise-api", "task-api", "proc-macros"]
35+
36+
[features]
37+
default = []
38+
python = []

packages/cubejs-backend-native/js/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,11 @@ export const setupLogger = (logger: (extra: any) => unknown, logLevel: LogLevel)
198198
native.setupLogger({logger: wrapNativeFunctionWithChannelCallback(logger), logLevel});
199199
}
200200

201+
export const isFallbackBuild = (): boolean => {
202+
const native = loadNative();
203+
return native.isFallbackBuild();
204+
}
205+
201206
export type SqlInterfaceInstance = { __typename: 'sqlinterfaceinstance' };
202207

203208
export const registerInterface = async (options: SQLInterfaceOptions): Promise<SqlInterfaceInstance> => {
@@ -245,6 +250,10 @@ interface PyConfiguration {
245250
}
246251

247252
export const pythonLoadConfig = async (context: string, options: { file: string }): Promise<PyConfiguration> => {
253+
if (isFallbackBuild()) {
254+
throw new Error('Python is not supported in fallback build');
255+
}
256+
248257
const native = loadNative();
249258
const config = await native.pythonLoadConfig(context, options);
250259

packages/cubejs-backend-native/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
"native:build": "tsc && cargo-cp-artifact -a cdylib cubejs-native index.node -- cargo build --message-format=json-render-diagnostics",
1616
"native:build-debug": "npm run native:build --",
1717
"native:build-release": "npm run native:build -- --release",
18+
"native:build-debug-python": "npm run native:build -- --features python",
19+
"native:build-release-python": "npm run native:build -- --release --features python",
1820
"install": "node-pre-gyp install || echo 'Your system is not supported by @cubejs-backend/native, some feature will be unavailable.'",
1921
"upload-binary-cross": "mkdir -p native && cp index.node native/index.node && node-pre-gyp package --target_arch=$PACKAGE_TARGET_ARCH --target_platform=$PACKAGE_TARGET_PLATFORM --target_libc=$PACKAGE_TARGET_LIBC && node-pre-gyp-github publish",
2022
"upload-binary": "mkdir -p native && cp index.node native/index.node && node-pre-gyp package && node-pre-gyp-github publish",

packages/cubejs-backend-native/src/lib.rs

Lines changed: 11 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod auth;
44
mod channel;
55
mod config;
66
mod logger;
7+
#[cfg(feature = "python")]
78
mod python;
89
mod stream;
910
mod transport;
@@ -13,14 +14,12 @@ use once_cell::sync::OnceCell;
1314

1415
use std::sync::Arc;
1516

16-
use crate::python::CubeConfigPy;
1717
use auth::NodeBridgeAuthService;
1818
use config::NodeConfig;
1919
use cubesql::{config::CubeServices, telemetry::ReportingLogger};
2020
use log::Level;
2121
use logger::NodeBridgeLogger;
2222
use neon::prelude::*;
23-
use pyo3::prelude::*;
2423
use simple_logger::SimpleLogger;
2524
use tokio::runtime::{Builder, Runtime};
2625
use transport::NodeBridgeTransport;
@@ -48,19 +47,6 @@ fn runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&'static Runtime> {
4847
})
4948
}
5049

51-
fn py_runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&()> {
52-
static PY_RUNTIME: OnceCell<()> = OnceCell::new();
53-
54-
let runtime = runtime(cx)?;
55-
56-
PY_RUNTIME.get_or_try_init(|| {
57-
pyo3::prepare_freethreaded_python();
58-
pyo3_asyncio::tokio::init_with_runtime(runtime).unwrap();
59-
60-
Ok(())
61-
})
62-
}
63-
6450
fn setup_logger(mut cx: FunctionContext) -> JsResult<JsUndefined> {
6551
let options = cx.argument::<JsObject>(0)?;
6652
let cube_logger = options
@@ -202,50 +188,25 @@ fn shutdown_interface(mut cx: FunctionContext) -> JsResult<JsPromise> {
202188
Ok(promise)
203189
}
204190

205-
fn python_load_config(mut cx: FunctionContext) -> JsResult<JsPromise> {
206-
let config_file_content = cx.argument::<JsString>(0)?.value(&mut cx);
207-
208-
let (deferred, promise) = cx.promise();
209-
let channel = cx.channel();
210-
211-
py_runtime(&mut cx)?;
212-
213-
let conf_res = Python::with_gil(|py| -> PyResult<CubeConfigPy> {
214-
let cube_conf_code = include_str!(concat!(
215-
env!("CARGO_MANIFEST_DIR"),
216-
"/python/cube/src/conf/__init__.py"
217-
));
218-
PyModule::from_code(py, cube_conf_code, "__init__.py", "cube.conf")?;
219-
220-
let config_module = PyModule::from_code(py, &config_file_content, "config.py", "")?;
221-
let settings_py = config_module.getattr("settings")?;
222-
223-
let mut cube_conf = CubeConfigPy::new();
224-
225-
for attr_name in cube_conf.get_static_attrs() {
226-
cube_conf.static_from_attr(settings_py, attr_name)?;
227-
}
228-
229-
cube_conf.apply_dynamic_functions(settings_py)?;
230-
231-
Ok(cube_conf)
232-
});
233-
234-
deferred.settle_with(&channel, move |mut cx| match conf_res {
235-
Ok(c) => c.to_object(&mut cx),
236-
Err(err) => cx.throw_error(format!("Python error: {}", err)),
237-
});
191+
fn is_fallback_build(mut cx: FunctionContext) -> JsResult<JsBoolean> {
192+
#[cfg(feature = "python")]
193+
{
194+
return Ok(JsBoolean::new(&mut cx, false));
195+
}
238196

239-
Ok(promise)
197+
#[allow(unreachable_code)]
198+
Ok(JsBoolean::new(&mut cx, true))
240199
}
241200

242201
#[neon::main]
243202
fn main(mut cx: ModuleContext) -> NeonResult<()> {
244203
cx.export_function("setupLogger", setup_logger)?;
245204
cx.export_function("registerInterface", register_interface)?;
246205
cx.export_function("shutdownInterface", shutdown_interface)?;
206+
cx.export_function("isFallbackBuild", is_fallback_build)?;
247207

248-
cx.export_function("pythonLoadConfig", python_load_config)?;
208+
#[cfg(feature = "python")]
209+
python::python_register_module(cx)?;
249210

250211
Ok(())
251212
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use crate::python::cube_config::CubeConfigPy;
2+
use crate::runtime;
3+
use neon::prelude::*;
4+
use once_cell::sync::OnceCell;
5+
use pyo3::prelude::*;
6+
7+
fn py_runtime<'a, C: Context<'a>>(cx: &mut C) -> NeonResult<&()> {
8+
static PY_RUNTIME: OnceCell<()> = OnceCell::new();
9+
10+
let runtime = runtime(cx)?;
11+
12+
PY_RUNTIME.get_or_try_init(|| {
13+
pyo3::prepare_freethreaded_python();
14+
pyo3_asyncio::tokio::init_with_runtime(runtime).unwrap();
15+
16+
Ok(())
17+
})
18+
}
19+
20+
fn python_load_config(mut cx: FunctionContext) -> JsResult<JsPromise> {
21+
let config_file_content = cx.argument::<JsString>(0)?.value(&mut cx);
22+
23+
let (deferred, promise) = cx.promise();
24+
let channel = cx.channel();
25+
26+
py_runtime(&mut cx)?;
27+
28+
let conf_res = Python::with_gil(|py| -> PyResult<CubeConfigPy> {
29+
let cube_conf_code = include_str!(concat!(
30+
env!("CARGO_MANIFEST_DIR"),
31+
"/python/cube/src/conf/__init__.py"
32+
));
33+
PyModule::from_code(py, cube_conf_code, "__init__.py", "cube.conf")?;
34+
35+
let config_module = PyModule::from_code(py, &config_file_content, "config.py", "")?;
36+
let settings_py = config_module.getattr("settings")?;
37+
38+
let mut cube_conf = CubeConfigPy::new();
39+
40+
for attr_name in cube_conf.get_static_attrs() {
41+
cube_conf.static_from_attr(settings_py, attr_name)?;
42+
}
43+
44+
cube_conf.apply_dynamic_functions(settings_py)?;
45+
46+
Ok(cube_conf)
47+
});
48+
49+
deferred.settle_with(&channel, move |mut cx| match conf_res {
50+
Ok(c) => c.to_object(&mut cx),
51+
Err(err) => cx.throw_error(format!("Python error: {}", err)),
52+
});
53+
54+
Ok(promise)
55+
}
56+
57+
pub fn python_register_module(mut cx: ModuleContext) -> NeonResult<()> {
58+
cx.export_function("pythonLoadConfig", python_load_config)?;
59+
60+
Ok(())
61+
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
mod cross;
2-
mod cube_config;
1+
pub(crate) mod cross;
2+
pub(crate) mod cube_config;
3+
mod entry;
34

4-
pub use cube_config::*;
5+
pub use entry::python_register_module;

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import fs from 'fs/promises';
22
import path from 'path';
33

44
import * as native from '../js';
5-
import * as assert from 'assert';
65

7-
describe('Python', () => {
6+
const suite = native.isFallbackBuild() ? xdescribe : describe;
7+
8+
suite('Python', () => {
89
async function loadConfigurationFile() {
910
const content = await fs.readFile(path.join(process.cwd(), 'test', 'config.py'), 'utf8');
1011
console.log('content', {

packages/cubejs-backend-shared/src/cli.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,13 @@ export const displayCLIError = async (text: string, pkg: string) => {
2222
export const displayCLIWarning = (message: string) => {
2323
console.log(`${color.yellow('Warning.')} ${message}`);
2424
};
25+
26+
const warningOnce = new Map();
27+
28+
export const displayCLIWarningOnce = (key: string, message: string) => {
29+
if (!warningOnce.has(key)) {
30+
warningOnce.set(key, 1);
31+
32+
displayCLIWarning(message);
33+
}
34+
};

packages/cubejs-backend-shared/src/env.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable no-restricted-syntax */
22
import { get } from 'env-var';
33
import { displayCLIWarning } from './cli';
4-
import { detectLibc } from './platform';
4+
import { isNativeSupported } from './platform';
55

66
export class InvalidConfiguration extends Error {
77
public constructor(key: string, value: any, description: string) {
@@ -1543,11 +1543,15 @@ const variables: Record<string, (...args: any) => any> = {
15431543
.asBoolStrict();
15441544

15451545
if (isDevMode) {
1546-
if (process.platform === 'linux' && detectLibc() === 'musl') {
1547-
return undefined;
1548-
}
1546+
if (isNativeSupported()) {
1547+
return 15432;
1548+
} else {
1549+
displayCLIWarning(
1550+
'Native module is not supported on your platform. Please use official docker image as a recommended way'
1551+
);
15491552

1550-
return 15432;
1553+
return false;
1554+
}
15511555
}
15521556

15531557
return undefined;

0 commit comments

Comments
 (0)