blob: b6f9730fdb6b33fc387d71f798c674a1cde56b97 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Public imports and actual Native temporal reads keep Python execution unloaded."""
import subprocess
import sys
import pytest
from pypaimon.execution.native.runtime import native_runtime_available
def test_public_imports_and_shared_validation_do_not_load_either_executor():
script = r'''
import importlib
import importlib.abc
import sys
class RejectExecutors(importlib.abc.MetaPathFinder):
def find_spec(self, fullname, path=None, target=None):
if fullname.startswith(('pypaimon.execution.python.', 'pypaimon.execution.native.')):
raise AssertionError('Shared API loaded execution: ' + fullname)
sys.meta_path.insert(0, RejectExecutors())
for name in (
'pypaimon.multimodal', 'pypaimon.api.rest_api', 'pypaimon.table.file_store_table',
'pypaimon.catalog.table_query_auth', 'pypaimon.filesystem.local_file_io',
'pypaimon.filesystem.pyarrow_file_io', 'pypaimon.ray.merge_condition',
'pypaimon.ray.vector_index_build',
):
importlib.import_module(name)
import pypaimon.globalindex as indexes
for name in indexes.__all__:
getattr(indexes, name)
from pypaimon.schema.data_type_casts import supports_cast
from pypaimon.schema.data_types import AtomicType
assert supports_cast(AtomicType('INT'), AtomicType('BIGINT'))
assert not any(name.startswith(('pypaimon.execution.python.', 'pypaimon.execution.native.'))
for name in sys.modules)
'''
result = subprocess.run(
[sys.executable, '-B', '-c', script], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True, timeout=30)
assert result.returncode == 0, result.stderr
@pytest.mark.skipif(not native_runtime_available(), reason='pypaimon-rust not installed')
def test_native_temporal_reads_never_import_python_execution(tmp_path):
script = r'''
# NATIVE_TEMPORAL_BOUNDARY: the test requires the current SDK in this process.
import importlib.abc
import sys
class RejectPythonExecution(importlib.abc.MetaPathFinder):
def find_spec(self, fullname, path=None, target=None):
if fullname == 'pypaimon.execution.python' or fullname.startswith('pypaimon.execution.python.'):
raise AssertionError('Native temporal loaded Python execution: ' + fullname)
sys.meta_path.insert(0, RejectPythonExecution())
from pypaimon_rust.datafusion import SQLContext
context = SQLContext()
context.register_catalog('paimon', {'warehouse': sys.argv[1]})
context.sql('CREATE SCHEMA paimon.temporal_boundary')
for name, extra, values in (
('anchors', '', '(1, 1), (1, 3)'),
('samples', ', value INT', '(1, 0, 0), (1, 4, 40)'),
):
definition = "CREATE TABLE paimon.temporal_boundary." + name + " (episode INT, event_time BIGINT" + extra
context.sql(definition + """) WITH (
'row-tracking.enabled' = 'true', 'data-evolution.enabled' = 'true')""")
columns = '(episode, event_time)' if name == 'anchors' else '(episode, event_time, value)'
context.sql('INSERT INTO paimon.temporal_boundary.' + name + ' ' + columns + ' VALUES ' + values)
from pypaimon import CatalogFactory
from pypaimon.multimodal import join_asof, interpolate, join_window
from pypaimon.multimodal.query import ScanQuery
catalog = CatalogFactory.create({'warehouse': sys.argv[1]})
options = {'read.native.enabled': 'true', 'scan.native-plan.enabled': 'true'}
anchors = ScanQuery(catalog.get_table('temporal_boundary.anchors').copy(options))
samples = ScanQuery(catalog.get_table('temporal_boundary.samples').copy(options)).select('value')
asof = join_asof(anchors, samples, on='event_time', by='episode')
assert asof.to_arrow()['value'].to_pylist() == [0, 0]
linear = interpolate(anchors, samples, on='event_time', by='episode')
assert linear.to_arrow()['value'].to_pylist() == [10., 30.]
window = join_window(anchors, samples, on='event_time', by='episode',
preceding=1, following=1, aggregations={'mean_value': ('value', 'mean')})
assert window.to_arrow()['mean_value'].to_pylist() == [0., 40.]
reader = asof.to_arrow_batch_reader(batch_size=1)
try:
assert reader.read_next_batch().num_rows == 1
finally:
reader.close()
assert not any(name.startswith('pypaimon.execution.python') for name in sys.modules)
'''
result = subprocess.run(
[sys.executable, '-B', '-c', script, str(tmp_path)], stdout=subprocess.PIPE,
stderr=subprocess.PIPE, universal_newlines=True, timeout=90)
assert result.returncode == 0, result.stderr