chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:17:40 +08:00
commit f1825c8ceb
10096 changed files with 2364182 additions and 0 deletions
@@ -0,0 +1,216 @@
"""Unit tests for :class:`ReadFiles`.
Verifies pushdown scaffolding (projection/predicate capability dispatch,
immutable scanner substitution) and schema inference without triggering
physical execution. Each test wires a minimal ``ListFiles`` upstream
op so ``ReadFiles`` (which now has one input dependency) can be
constructed.
"""
import os
from pathlib import Path
from typing import Optional, Union
import pyarrow as pa
import pyarrow.compute as pc
import pyarrow.parquet as pq
import pytest
from ray.data._internal.datasource_v2.listing.file_indexer import (
NonSamplingFileIndexer,
)
from ray.data._internal.datasource_v2.listing.listing_utils import (
sample_files,
)
from ray.data._internal.datasource_v2.parquet_datasource_v2 import (
ParquetDatasourceV2,
)
from ray.data._internal.datasource_v2.scanners.parquet_scanner import (
ParquetScanner,
)
from ray.data._internal.logical.operators import Filter, ListFiles, ReadFiles
from ray.data.datasource.partitioning import Partitioning, PartitionStyle
from ray.data.expressions import Expr, col
def _mk_parquet(path: Path, table: pa.Table) -> None:
pq.write_table(table, str(path))
def _mk_read_files(tmp_path: Path) -> ReadFiles:
f = tmp_path / "data.parquet"
_mk_parquet(f, pa.table({"a": [1, 2, 3], "b": ["x", "y", "z"]}))
datasource = ParquetDatasourceV2([str(f)])
indexer = NonSamplingFileIndexer(ignore_missing_paths=False)
sample = sample_files(indexer, datasource.paths, datasource.filesystem)
schema = datasource.infer_schema(sample)
scanner = datasource.create_scanner(schema=schema)
list_files_op = ListFiles(
paths=list(datasource.paths),
file_indexer=indexer,
filesystem=datasource.filesystem,
source_paths=list(datasource.paths),
file_extensions=datasource.file_extensions,
)
return ReadFiles(
datasource_name=datasource.name,
scanner=scanner,
schema=schema,
parallelism=-1,
input_dependencies=[list_files_op],
)
def _mk_partitioned_read_files(tmp_path: Path) -> ReadFiles:
"""Hive-partitioned dataset with partition column ``country``."""
for country, value in (("US", 1), ("CA", 2)):
d = tmp_path / f"country={country}"
os.makedirs(d, exist_ok=True)
_mk_parquet(d / "data.parquet", pa.table({"a": [value], "b": [str(value)]}))
partitioning = Partitioning(
PartitionStyle.HIVE, base_dir=str(tmp_path), field_names=["country"]
)
datasource = ParquetDatasourceV2([str(tmp_path)], partitioning=partitioning)
indexer = NonSamplingFileIndexer(ignore_missing_paths=False)
sample = sample_files(indexer, datasource.paths, datasource.filesystem)
schema = datasource.infer_schema(sample)
scanner = datasource.create_scanner(schema=schema, partitioning=partitioning)
list_files_op = ListFiles(
paths=list(datasource.paths),
file_indexer=indexer,
filesystem=datasource.filesystem,
source_paths=list(datasource.paths),
file_extensions=datasource.file_extensions,
)
return ReadFiles(
datasource_name=datasource.name,
scanner=scanner,
schema=schema,
parallelism=-1,
input_dependencies=[list_files_op],
)
def test_construction_stores_schema_and_infer_schema_returns_it(tmp_path):
op = _mk_read_files(tmp_path)
assert op.infer_schema().names == ["a", "b"]
def test_input_dependency_is_list_files(tmp_path):
op = _mk_read_files(tmp_path)
assert isinstance(op.input_dependencies[0], ListFiles)
def test_supports_projection_pushdown_true_for_parquet_scanner(tmp_path):
op = _mk_read_files(tmp_path)
assert op.supports_projection_pushdown() is True
def test_apply_projection_returns_new_op_with_pruned_scanner(tmp_path):
op = _mk_read_files(tmp_path)
new_op = op.apply_projection({"a": "a"})
assert new_op is not op
assert isinstance(new_op.scanner, ParquetScanner)
assert new_op.scanner.columns == ("a",)
# Original scanner untouched
assert isinstance(op.scanner, ParquetScanner)
assert op.scanner.columns is None
def test_apply_projection_none_is_noop(tmp_path):
op = _mk_read_files(tmp_path)
assert op.apply_projection(None) is op
def test_supports_predicate_pushdown(tmp_path):
assert _mk_read_files(tmp_path).supports_predicate_pushdown() is True
def _assert_pred_equals(
actual: Optional[Union[Expr, pc.Expression]], expected: Optional[Expr]
) -> None:
if expected is None:
assert actual is None
return
assert actual is not None
# ``ArrowFileScanner.push_filters`` stores the data predicate as a
# ``pc.Expression`` (via ``.to_pyarrow()``), while ``partition_predicate``
# stays a Ray ``Expr``. Dispatch on which we got.
if isinstance(actual, Expr):
assert actual.structurally_equals(expected)
else:
assert actual.equals(expected.to_pyarrow())
@pytest.mark.parametrize(
"partitioned,predicate,expected_data,expected_partition",
[
(False, col("a") > 1, col("a") > 1, None),
(True, col("country") == "US", None, col("country") == "US"),
(
True,
(col("a") > 0) & (col("country") == "US"),
col("a") > 0,
col("country") == "US",
),
],
ids=["data_only", "partition_only", "mixed_and"],
)
def test_apply_predicate_splits_data_and_partition(
tmp_path, partitioned, predicate, expected_data, expected_partition
):
op = (_mk_partitioned_read_files if partitioned else _mk_read_files)(tmp_path)
new_op = op.apply_predicate(predicate)
assert isinstance(new_op, ReadFiles) and new_op is not op
new_scanner = new_op.scanner
assert isinstance(new_scanner, ParquetScanner)
_assert_pred_equals(new_scanner.predicate, expected_data)
_assert_pred_equals(new_scanner.partition_predicate, expected_partition)
# Original scanner untouched.
orig_scanner = op.scanner
assert isinstance(orig_scanner, ParquetScanner)
assert orig_scanner.predicate is None
assert orig_scanner.partition_predicate is None
def test_apply_predicate_mixed_or_keeps_filter_above(tmp_path):
op = _mk_partitioned_read_files(tmp_path)
# Mixed-column ``OR`` can't be safely split — neither bucket is
# populated, so ``apply_predicate`` returns ``self`` and the rule
# leaves the ``Filter`` above ``ReadFiles`` untouched.
result = op.apply_predicate((col("a") > 0) | (col("country") == "US"))
assert result is op
def test_apply_predicate_mixed_and_with_unsplittable_residual(tmp_path):
op = _mk_partitioned_read_files(tmp_path)
# Top-level ``AND`` of one pure-data, one pure-partition, and one
# mixed-OR conjunct: the first two push, the OR stays as a residual
# ``Filter`` so we don't silently drop it.
pure_data = col("a") > 0
pure_partition = col("country") == "US"
mixed_or = (col("a") < 100) | (col("country") == "CA")
result = op.apply_predicate(pure_data & pure_partition & mixed_or)
assert isinstance(result, Filter)
new_read = result.input_dependencies[0]
assert isinstance(new_read, ReadFiles)
new_scanner = new_read.scanner
assert isinstance(new_scanner, ParquetScanner)
_assert_pred_equals(new_scanner.predicate, pure_data)
_assert_pred_equals(new_scanner.partition_predicate, pure_partition)
# The residual carried by the new Filter is exactly the mixed-OR
# conjunct that couldn't be pushed.
_assert_pred_equals(result.predicate_expr, mixed_or)