| # 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. | |
| """Execution packages hold paired Paimon backends, not common adapters.""" | |
| from pathlib import Path | |
| import subprocess | |
| import sys | |
| import pytest | |
| def test_execution_package_contains_only_backend_implementations(): | |
| root = Path(__file__).resolve().parents[1] / 'execution' | |
| modules = {path.name for path in root.iterdir() if path.is_file() and path.suffix == '.py'} | |
| packages = {path.name for path in root.iterdir() | |
| if path.is_dir() and any(path.glob('*.py'))} | |
| assert modules == {'__init__.py'} | |
| assert packages == {'python', 'native'} | |
| @pytest.mark.skipif(sys.version_info < (3, 7), reason='Python 3.6 retains eager package exports') | |
| def test_common_adapters_do_not_load_either_execution_backend(): | |
| source = r""" | |
| import importlib | |
| import importlib.abc | |
| import sys | |
| class RejectExecution(importlib.abc.MetaPathFinder): | |
| def find_spec(self, fullname, path=None, target=None): | |
| if fullname == 'pypaimon.execution' or fullname.startswith('pypaimon.execution.'): | |
| raise AssertionError('A common adapter imported an execution backend: ' + fullname) | |
| sys.meta_path.insert(0, RejectExecution()) | |
| modules = [ | |
| 'pypaimon.read.read_dispatch', 'pypaimon.read.read_support', | |
| 'pypaimon.read.read_explain', 'pypaimon.table.source.search_dispatch', | |
| 'pypaimon.write.backend_dispatch', 'pypaimon.write.write_dispatch', | |
| 'pypaimon.write.update_dispatch', 'pypaimon.write.input_adapter', | |
| 'pypaimon.write.merge_condition', 'pypaimon.write.merge_transform', | |
| 'pypaimon.globalindex.index_dispatch', 'pypaimon.globalindex.vindex_input', | |
| 'pypaimon.table.system.system_table_read', | |
| 'pypaimon.table.source.deletion_file', 'pypaimon.index.deletion_vector_meta', | |
| 'pypaimon.index.index_file_meta', | |
| 'pypaimon.index.dynamic_bucket', 'pypaimon.data.map_shared_shredding', | |
| 'pypaimon.data.variant_shredding', 'pypaimon.data.variant_path', | |
| 'pypaimon.schema.arrow_schema', 'pypaimon.schema.data_type_casts', | |
| 'pypaimon.schema.schema_manager', 'pypaimon.utils.file_store_path_factory', | |
| 'pypaimon.table.system.files_table', 'pypaimon.common.predicate_json_parser', | |
| 'pypaimon.common.transform_schema', 'pypaimon.common.arrow_predicate', | |
| 'pypaimon.common.external_path_provider', | |
| 'pypaimon.common.uri_reader', 'pypaimon.common.blob_index_cache', | |
| 'pypaimon.common.delta_varint_compressor', 'pypaimon.common.file_io', | |
| ] | |
| for name in modules: | |
| importlib.import_module(name) | |
| from pypaimon.utils.file_store_path_factory import FileStorePathFactory | |
| factory = FileStorePathFactory( | |
| '/warehouse', ['p'], '__DEFAULT_PARTITION__', 'parquet', 'data-', 'changelog-', | |
| False, False, 'zstd', external_paths=['/external'], external_path_strategy='round-robin') | |
| provider = factory.create_external_path_provider(('x',), 0, canonical_partition=True) | |
| assert provider.get_next_external_data_path('data.parquet') == '/external/p=x/bucket-0/data.parquet' | |
| assert not any(name == 'pypaimon.execution' or name.startswith('pypaimon.execution.') | |
| for name in sys.modules) | |
| """ | |
| result = subprocess.run([sys.executable, '-c', source], | |
| stdout=subprocess.PIPE, stderr=subprocess.PIPE, | |
| universal_newlines=True, timeout=30) | |
| assert result.returncode == 0, result.stdout + result.stderr |