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
+884
View File
@@ -0,0 +1,884 @@
import logging
import os
import sys
import time
import numpy as np
import pandas as pd
import pyarrow as pa
import pytest
import ray
from ray.data._internal.block_builder import BlockBuilder
from ray.data._internal.datasource.csv_datasink import CSVDatasink
from ray.data._internal.datasource.csv_datasource import CSVDatasource
from ray.data._internal.datasource.range_datasource import RangeDatasource
from ray.data._internal.execution.interfaces.ref_bundle import (
_ref_bundles_iterator_to_block_refs_list,
)
from ray.data.block import BlockAccessor
from ray.data.dataset import Dataset, MaterializedDataset
from ray.data.datasource.util import (
_validate_head_node_resources_for_local_scheduling,
)
from ray.data.tests.conftest import * # noqa
from ray.data.tests.conftest import (
CoreExecutionMetrics,
assert_core_execution_metrics_equals,
get_initial_core_execution_metrics_snapshot,
)
from ray.data.tests.util import column_udf, extract_values
from ray.tests.conftest import * # noqa
from ray.util.scheduling_strategies import PlacementGroupSchedulingStrategy
def test_schema(ray_start_regular):
last_snapshot = get_initial_core_execution_metrics_snapshot()
ds2 = ray.data.range(10, override_num_blocks=10)
ds3 = ds2.repartition(5)
ds3 = ds3.materialize()
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics(
task_count={
"ReadRange": 10,
"reduce": 5,
}
),
last_snapshot,
)
ds4 = ds3.map(lambda x: {"a": "hi", "b": 1.0}).limit(5).repartition(1)
ds4 = ds4.materialize()
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics(
task_count={
"Map(<lambda>)": lambda count: count <= 5,
"slice_fn": 1,
"reduce": 1,
}
),
last_snapshot,
)
ds2_schema = ds2.schema(fetch_if_missing=False)
assert ds2_schema is not None
assert ds2_schema.names == ["id"]
assert not isinstance(ds2, MaterializedDataset)
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics(task_count={}), last_snapshot
)
ds3_schema = ds3.schema(fetch_if_missing=False)
assert ds3_schema is not None
assert ds3_schema.names == ["id"]
assert isinstance(ds3, MaterializedDataset)
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics(task_count={}), last_snapshot
)
ds4_schema = ds4.schema(fetch_if_missing=False)
assert ds4_schema is not None
assert ds4_schema.names == ["a", "b"]
assert isinstance(ds4, MaterializedDataset)
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics(task_count={}), last_snapshot
)
def test_schema_no_execution(ray_start_regular):
last_snapshot = get_initial_core_execution_metrics_snapshot()
ds = ray.data.range(100, override_num_blocks=10)
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics(task_count={}),
last_snapshot,
)
# We do not kick off the read task by default.
schema = ds.schema()
assert schema.names == ["id"]
# Fetching the schema does not trigger execution, since
# the schema is known beforehand for RangeDatasource.
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics(task_count={}), last_snapshot
)
# Fetching the schema should not trigger execution of extra read tasks.
def test_schema_cached(ray_start_regular):
def check_schema_cached(ds, expected_task_count, last_snapshot):
schema = ds.schema()
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics(expected_task_count), last_snapshot
)
assert schema.names == ["a"]
cached_schema = ds.schema(fetch_if_missing=False)
assert cached_schema is not None
assert schema == cached_schema
last_snapshot = assert_core_execution_metrics_equals(
CoreExecutionMetrics({}), last_snapshot
)
return last_snapshot
last_snapshot = get_initial_core_execution_metrics_snapshot()
ds = ray.data.from_items([{"a": i} for i in range(100)], override_num_blocks=10)
last_snapshot = check_schema_cached(ds, {}, last_snapshot)
# Add a map_batches operator so that we are forced to compute the schema.
ds = ds.map_batches(lambda x: x)
last_snapshot = check_schema_cached(
ds,
{
"MapBatches(<lambda>)": lambda count: count <= 5,
"slice_fn": 1,
},
last_snapshot,
)
def test_avoid_placement_group_capture(shutdown_only):
ray.init(num_cpus=2)
@ray.remote
def run():
ds = ray.data.range(5)
assert sorted(
extract_values("id", ds.map(column_udf("id", lambda x: x + 1)).take())
) == [1, 2, 3, 4, 5]
assert ds.count() == 5
assert sorted(extract_values("id", ds.iter_rows())) == [0, 1, 2, 3, 4]
pg = ray.util.placement_group([{"CPU": 1}])
ray.get(
run.options(
scheduling_strategy=PlacementGroupSchedulingStrategy(
placement_group=pg, placement_group_capture_child_tasks=True
)
).remote()
)
@pytest.fixture
def remove_named_placement_groups():
yield
for info in ray.util.placement_group_table().values():
if info["name"]:
pg = ray.util.get_placement_group(info["name"])
ray.util.remove_placement_group(pg)
def test_ray_remote_args_fn(shutdown_only, remove_named_placement_groups):
ray.init()
pg = ray.util.placement_group([{"CPU": 1}], name="test_pg")
def ray_remote_args_fn():
scheduling_strategy = PlacementGroupSchedulingStrategy(placement_group=pg)
return {"scheduling_strategy": scheduling_strategy}
class ActorClass:
def __call__(self, batch):
assert ray.util.get_current_placement_group() == pg
return batch
ray.data.range(1).map_batches(
ActorClass, concurrency=1, ray_remote_args_fn=ray_remote_args_fn
).take_all()
def test_dataset_lineage_serialization(shutdown_only):
ray.init()
ds = ray.data.range(10)
ds = ds.map(column_udf("id", lambda x: x + 1))
ds = ds.map(column_udf("id", lambda x: x + 1))
ds = ds.random_shuffle()
uuid = ds._get_uuid()
plan_uuid = ds._uuid
serialized_ds = ds.serialize_lineage()
ray.shutdown()
ray.init()
ds = Dataset.deserialize_lineage(serialized_ds)
# Check Dataset state.
assert ds._get_uuid() == uuid
assert ds._uuid == plan_uuid
# Check Dataset content.
assert ds.count() == 10
assert sorted(extract_values("id", ds.take())) == list(range(2, 12))
def test_dataset_lineage_serialization_unsupported(shutdown_only):
ray.init()
# In-memory data sources not supported.
ds = ray.data.from_items(list(range(10)))
ds = ds.map(column_udf("item", lambda x: x + 1))
ds = ds.map(column_udf("item", lambda x: x + 1))
with pytest.raises(ValueError):
ds.serialize_lineage()
# In-memory data source unions not supported.
ds = ray.data.from_items(list(range(10)))
ds1 = ray.data.from_items(list(range(10, 20)))
ds2 = ds.union(ds1)
with pytest.raises(ValueError):
ds2.serialize_lineage()
# Lazy read unions supported.
ds = ray.data.range(10)
ds1 = ray.data.range(20)
ds2 = ds.union(ds1)
serialized_ds = ds2.serialize_lineage()
ds3 = Dataset.deserialize_lineage(serialized_ds)
assert set(extract_values("id", ds3.take(30))) == set(
list(range(10)) + list(range(20))
)
# Zips not supported.
ds = ray.data.from_items(list(range(10)))
ds1 = ray.data.from_items(list(range(10, 20)))
ds2 = ds.zip(ds1)
with pytest.raises(ValueError):
ds2.serialize_lineage()
def test_basic(ray_start_regular_shared):
ds = ray.data.range(5)
assert sorted(
extract_values("id", ds.map(column_udf("id", lambda x: x + 1)).take())
) == [1, 2, 3, 4, 5]
assert ds.count() == 5
assert sorted(extract_values("id", ds.iter_rows())) == [0, 1, 2, 3, 4]
def test_range(ray_start_regular_shared):
ds = ray.data.range(10, override_num_blocks=10)
assert ds._logical_plan.initial_num_blocks() == 10
assert ds.count() == 10
assert ds.take() == [{"id": i} for i in range(10)]
ds = ray.data.range(10, override_num_blocks=2)
assert ds._logical_plan.initial_num_blocks() == 2
assert ds.count() == 10
assert ds.take() == [{"id": i} for i in range(10)]
def test_empty_dataset(ray_start_regular_shared):
ds = ray.data.range(0)
assert ds.count() == 0
assert ds.size_bytes() == 0
assert ds.schema() is None
ds = ray.data.range(1)
ds = ds.filter(lambda x: x["id"] > 1)
ds = ds.materialize()
assert (
str(ds)
== "MaterializedDataset(num_blocks=1, num_rows=0, schema=Unknown schema)"
)
# Test map on empty dataset.
ds = ray.data.from_items([])
ds = ds.map(lambda x: x)
ds = ds.materialize()
assert ds.count() == 0
# Test filter on empty dataset.
ds = ray.data.from_items([])
ds = ds.filter(lambda: True)
ds = ds.materialize()
assert ds.count() == 0
@ray.remote
class Counter:
def __init__(self):
self.value = 0
def increment(self):
self.value += 1
return self.value
def test_cache_dataset(ray_start_regular_shared):
c = Counter.remote()
def inc(x):
ray.get(c.increment.remote())
return x
ds = ray.data.range(1)
ds = ds.map(inc)
assert not isinstance(ds, MaterializedDataset)
ds2 = ds.materialize()
assert isinstance(ds2, MaterializedDataset)
assert not isinstance(ds, MaterializedDataset)
# Tests standard iteration uses the materialized blocks.
for _ in range(10):
ds2.take_all()
assert ray.get(c.increment.remote()) == 2
# Tests streaming iteration uses the materialized blocks.
for _ in range(10):
list(ds2.streaming_split(1)[0].iter_batches())
assert ray.get(c.increment.remote()) == 3
def test_columns(ray_start_regular_shared):
ds = ray.data.range(1)
assert ds.columns() == ds.schema().names
assert ds.columns() == ["id"]
ds = ds.map(lambda x: x)
assert ds.columns(fetch_if_missing=False) is None
def test_schema_repr(ray_start_regular_shared):
ds = ray.data.from_items([{"text": "spam", "number": 0}])
# fmt: off
expected_repr = (
"Column Type\n"
"------ ----\n"
"text string\n"
"number int64"
)
# fmt:on
assert repr(ds.schema()) == expected_repr
ds = ray.data.from_items([{"long_column_name": "spam"}])
# fmt: off
expected_repr = (
"Column Type\n"
"------ ----\n"
"long_column_name string"
)
# fmt: on
assert repr(ds.schema()) == expected_repr
def _check_none_computed(ds):
# In streaming executor, ds.take() will not invoke partial execution
# in LazyBlocklist.
assert not ds._has_computed_output()
def test_lazy_loading_exponential_rampup(ray_start_regular_shared):
ds = ray.data.range(100, override_num_blocks=20)
_check_none_computed(ds)
assert extract_values("id", ds.take(10)) == list(range(10))
_check_none_computed(ds)
assert extract_values("id", ds.take(20)) == list(range(20))
_check_none_computed(ds)
assert extract_values("id", ds.take(30)) == list(range(30))
_check_none_computed(ds)
assert extract_values("id", ds.take(50)) == list(range(50))
_check_none_computed(ds)
assert extract_values("id", ds.take(100)) == list(range(100))
_check_none_computed(ds)
def test_dataset_repr_not_materialized(ray_start_regular_shared, restore_data_context):
ds = ray.data.range(5)
assert repr(ds) == (
"shape: (5, 1)\n"
"╭───────╮\n"
"│ id │\n"
"│ --- │\n"
"│ int64 │\n"
"╰───────╯\n"
"(Dataset isn't materialized)"
)
def test_dataset_repr_materialized(ray_start_regular_shared, restore_data_context):
materialized = ray.data.range(5).materialize()
assert repr(materialized) == (
"shape: (5, 1)\n"
"╭───────╮\n"
"│ id │\n"
"│ --- │\n"
"│ int64 │\n"
"╞═══════╡\n"
"│ 0 │\n"
"│ 1 │\n"
"│ 2 │\n"
"│ 3 │\n"
"│ 4 │\n"
"╰───────╯\n"
"(Showing 5 of 5 rows)"
)
def test_dataset_repr_gap(ray_start_regular_shared, restore_data_context):
ds_with_gap = ray.data.range(20).materialize()
assert repr(ds_with_gap) == (
"shape: (20, 1)\n"
"╭───────╮\n"
"│ id │\n"
"│ --- │\n"
"│ int64 │\n"
"╞═══════╡\n"
"│ 0 │\n"
"│ 1 │\n"
"│ 2 │\n"
"│ 3 │\n"
"│ 4 │\n"
"│ … │\n"
"│ 15 │\n"
"│ 16 │\n"
"│ 17 │\n"
"│ 18 │\n"
"│ 19 │\n"
"╰───────╯\n"
"(Showing 10 of 20 rows)"
)
def test_dataset_explain(ray_start_regular_shared, capsys):
ds = ray.data.range(10, override_num_blocks=10)
ds = ds.map(lambda x: x)
ds.explain()
captured = capsys.readouterr()
assert captured.out.strip() == (
"-------- Logical Plan --------\n"
"MapRows[Map(<lambda>)]\n"
"+- Read[ReadRange]\n"
"\n-------- Logical Plan (Optimized) --------\n"
"MapRows[Map(<lambda>)]\n"
"+- Read[ReadRange]\n"
"\n-------- Physical Plan --------\n"
"TaskPoolMapOperator[Map(<lambda>)]\n"
"+- TaskPoolMapOperator[ReadRange]\n"
" +- InputDataBuffer[Input]\n"
"\n-------- Physical Plan (Optimized) --------\n"
"TaskPoolMapOperator[ReadRange->Map(<lambda>)]\n"
"+- InputDataBuffer[Input]"
)
ds = ds.filter(lambda x: x["id"] > 0)
ds.explain()
captured = capsys.readouterr()
assert captured.out.strip() == (
"-------- Logical Plan --------\n"
"Filter[Filter(<lambda>)]\n"
"+- MapRows[Map(<lambda>)]\n"
" +- Read[ReadRange]\n"
"\n-------- Logical Plan (Optimized) --------\n"
"Filter[Filter(<lambda>)]\n"
"+- MapRows[Map(<lambda>)]\n"
" +- Read[ReadRange]\n"
"\n-------- Physical Plan --------\n"
"TaskPoolMapOperator[Filter(<lambda>)]\n"
"+- TaskPoolMapOperator[Map(<lambda>)]\n"
" +- TaskPoolMapOperator[ReadRange]\n"
" +- InputDataBuffer[Input]\n"
"\n-------- Physical Plan (Optimized) --------\n"
"TaskPoolMapOperator[ReadRange->Map(<lambda>)->Filter(<lambda>)]\n"
"+- InputDataBuffer[Input]"
)
ds = ds.random_shuffle().map(lambda x: x)
ds.explain()
captured = capsys.readouterr()
assert captured.out.strip() == (
"-------- Logical Plan --------\n"
"MapRows[Map(<lambda>)]\n"
"+- RandomShuffle[RandomShuffle]\n"
" +- Filter[Filter(<lambda>)]\n"
" +- MapRows[Map(<lambda>)]\n"
" +- Read[ReadRange]\n"
"\n-------- Logical Plan (Optimized) --------\n"
"MapRows[Map(<lambda>)]\n"
"+- RandomShuffle[RandomShuffle]\n"
" +- Filter[Filter(<lambda>)]\n"
" +- MapRows[Map(<lambda>)]\n"
" +- Read[ReadRange]\n"
"\n-------- Physical Plan --------\n"
"TaskPoolMapOperator[Map(<lambda>)]\n"
"+- AllToAllOperator[RandomShuffle]\n"
" +- TaskPoolMapOperator[Filter(<lambda>)]\n"
" +- TaskPoolMapOperator[Map(<lambda>)]\n"
" +- TaskPoolMapOperator[ReadRange]\n"
" +- InputDataBuffer[Input]\n"
"\n-------- Physical Plan (Optimized) --------\n"
"TaskPoolMapOperator[Map(<lambda>)]\n"
"+- AllToAllOperator[ReadRange->Map(<lambda>)->Filter(<lambda>)->RandomShuffle]\n"
" +- InputDataBuffer[Input]"
)
def test_convert_types(ray_start_regular_shared):
plain_ds = ray.data.range(1)
arrow_ds = plain_ds.map(lambda x: {"a": x["id"]})
assert arrow_ds.take() == [{"a": 0}]
assert "dict" in str(arrow_ds.map(lambda x: {"out": str(type(x))}).take()[0])
arrow_ds = ray.data.range(1)
assert arrow_ds.map(lambda x: {"out": "plain_{}".format(x["id"])}).take() == [
{"out": "plain_0"}
]
assert arrow_ds.map(lambda x: {"a": (x["id"],)}).take() == [{"a": [0]}]
def test_take_batch(ray_start_regular_shared):
ds = ray.data.range(10, override_num_blocks=2)
assert ds.take_batch(3)["id"].tolist() == [0, 1, 2]
assert ds.take_batch(6)["id"].tolist() == [0, 1, 2, 3, 4, 5]
assert ds.take_batch(100)["id"].tolist() == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
assert isinstance(ds.take_batch(3, batch_format="pandas"), pd.DataFrame)
assert isinstance(ds.take_batch(3, batch_format="numpy"), dict)
ds = ray.data.range_tensor(10, override_num_blocks=2)
assert np.all(ds.take_batch(3)["data"] == np.array([[0], [1], [2]]))
assert isinstance(ds.take_batch(3, batch_format="pandas"), pd.DataFrame)
assert isinstance(ds.take_batch(3, batch_format="numpy"), dict)
with pytest.raises(ValueError):
ray.data.range(0).take_batch()
def test_take_all(ray_start_regular_shared):
assert extract_values("id", ray.data.range(5).take_all()) == [0, 1, 2, 3, 4]
with pytest.raises(ValueError):
assert ray.data.range(5).take_all(4)
def test_union(ray_start_regular_shared, restore_data_context):
# Set aggregator CPU to 0 to avoid deadlock in resource-constrained test env.
# Without this, the shuffle task (1 CPU) + aggregator actor (~0.25 CPU) would
# exceed the 1 CPU available in the test cluster, causing scheduler deadlock.
restore_data_context.hash_aggregate_operator_actor_num_cpus_override = 0
ds = ray.data.range(20, override_num_blocks=10).materialize()
# Test lazy union.
ds = ds.union(ds, ds, ds, ds)
assert ds._logical_plan.initial_num_blocks() == 50
assert ds.count() == 100
assert ds.sum() == 950
ds = ds.union(ds)
assert ds.count() == 200
assert ds.sum() == (950 * 2)
# Test materialized union.
ds2 = ray.data.from_items([1, 2, 3, 4, 5])
assert ds2.count() == 5
ds2 = ds2.union(ds2)
assert ds2.count() == 10
ds2 = ds2.union(ds)
assert ds2.count() == 210
def test_block_builder_for_block(ray_start_regular_shared):
# pandas dataframe
builder = BlockBuilder.for_block(pd.DataFrame())
b1 = pd.DataFrame({"A": [1], "B": ["a"]})
builder.add_block(b1)
assert builder.build().equals(b1)
b2 = pd.DataFrame({"A": [2, 3], "B": ["c", "d"]})
builder.add_block(b2)
expected = pd.DataFrame({"A": [1, 2, 3], "B": ["a", "c", "d"]})
assert builder.build().equals(expected)
# pyarrow table
builder = BlockBuilder.for_block(pa.Table.from_arrays(list()))
b1 = pa.Table.from_pydict({"A": [1], "B": ["a"]})
builder.add_block(b1)
builder.build().equals(b1)
b2 = pa.Table.from_pydict({"A": [2, 3], "B": ["c", "d"]})
builder.add_block(b2)
expected = pa.Table.from_pydict({"A": [1, 2, 3], "B": ["a", "c", "d"]})
builder.build().equals(expected)
# wrong type
with pytest.raises(TypeError):
BlockBuilder.for_block(str())
def test_len(ray_start_regular_shared):
ds = ray.data.range(1)
with pytest.raises(AttributeError):
len(ds)
def test_pandas_block_select():
df = pd.DataFrame({"one": [10, 11, 12], "two": [11, 12, 13], "three": [14, 15, 16]})
block_accessor = BlockAccessor.for_block(df)
block = block_accessor.select(["two"])
assert block.equals(df[["two"]])
block = block_accessor.select(["two", "one"])
assert block.equals(df[["two", "one"]])
with pytest.raises(ValueError):
block = block_accessor.select([lambda x: x % 3, "two"])
# NOTE: All tests above share a Ray cluster, while the tests below do not. These
# tests should only be carefully reordered to retain this invariant!
def test_read_write_local_node_ray_client(ray_start_cluster_enabled):
cluster = ray_start_cluster_enabled
cluster.add_node(num_cpus=4)
cluster.head_node._ray_params.ray_client_server_port = "10004"
cluster.head_node.start_ray_client_server()
address = "ray://localhost:10004"
import tempfile
data_path = tempfile.mkdtemp()
df = pd.DataFrame({"one": list(range(0, 10)), "two": list(range(10, 20))})
path = os.path.join(data_path, "test.parquet")
df.to_parquet(path)
# Read/write from Ray Client will result in error.
ray.init(address)
with pytest.raises(ValueError):
ds = ray.data.read_parquet("local://" + path).materialize()
ds = ray.data.from_pandas(df)
with pytest.raises(ValueError):
ds.write_parquet("local://" + data_path).materialize()
@pytest.mark.skipif(
sys.version_info >= (3, 12), reason="No tensorflow for Python 3.12+"
)
def test_read_warning_large_parallelism(
ray_start_regular_shared, propagate_logs, caplog
):
with caplog.at_level(logging.WARNING, logger="ray.data.read_api"):
ray.data.range(5000, override_num_blocks=5000).materialize()
assert (
"The requested number of read blocks of 5000 is "
"more than 4x the number of available CPU slots in the cluster" in caplog.text
), caplog.text
def test_read_write_local_node(ray_start_cluster):
cluster = ray_start_cluster
cluster.add_node(
resources={"bar:1": 100},
num_cpus=10,
_system_config={"max_direct_call_object_size": 0},
)
cluster.add_node(resources={"bar:2": 100}, num_cpus=10)
cluster.add_node(resources={"bar:3": 100}, num_cpus=10)
ray.shutdown()
ray.init(cluster.address)
import os
import tempfile
data_path = tempfile.mkdtemp()
num_files = 5
for idx in range(num_files):
df = pd.DataFrame(
{"one": list(range(idx, idx + 10)), "two": list(range(idx + 10, idx + 20))}
)
path = os.path.join(data_path, f"test{idx}.parquet")
df.to_parquet(path)
ctx = ray.data.context.DataContext.get_current()
ctx.read_write_local_node = True
def check_dataset_is_local(ds):
bundles = ds.iter_internal_ref_bundles()
block_refs = _ref_bundles_iterator_to_block_refs_list(bundles)
ray.wait(block_refs, num_returns=len(block_refs), fetch_local=False)
location_data = ray.experimental.get_object_locations(block_refs)
locations = []
for block in block_refs:
locations.extend(location_data[block]["node_ids"])
assert set(locations) == {ray.get_runtime_context().get_node_id()}
local_path = "local://" + data_path
# Plain read.
ds = ray.data.read_parquet(local_path).materialize()
check_dataset_is_local(ds)
# SPREAD scheduling got overridden when read local scheme.
ds = ray.data.read_parquet(
local_path, ray_remote_args={"scheduling_strategy": "SPREAD"}
).materialize()
check_dataset_is_local(ds)
# With fusion.
ds = (
ray.data.read_parquet(local_path, override_num_blocks=1)
.map(lambda x: x)
.materialize()
)
check_dataset_is_local(ds)
# Write back to local scheme.
output = os.path.join(local_path, "test_read_write_local_node")
ds.write_parquet(output)
assert "1 nodes used" in ds.stats(), ds.stats()
ray.data.read_parquet(output).take_all() == ds.take_all()
# Mixing paths of local and non-local scheme is invalid.
with pytest.raises(ValueError):
ds = ray.data.read_parquet(
[local_path + "/test1.parquet", data_path + "/test2.parquet"]
).materialize()
with pytest.raises(ValueError):
ds = ray.data.read_parquet(
[local_path + "/test1.parquet", "example://iris.parquet"]
).materialize()
with pytest.raises(ValueError):
ds = ray.data.read_parquet(
["example://iris.parquet", local_path + "/test1.parquet"]
).materialize()
def test_validate_head_node_resources_zero_head_cpu(ray_start_cluster):
cluster = ray_start_cluster
cluster.add_node(num_cpus=0)
cluster.wait_for_nodes()
ray.shutdown()
ray.init(address=cluster.address)
with pytest.raises(ValueError, match=r"head node doesn't have enough resources"):
_validate_head_node_resources_for_local_scheduling(
{}, op_description="read local"
)
class FlakyCSVDatasource(CSVDatasource):
def __init__(self, paths, **csv_datasource_kwargs):
super().__init__(paths, **csv_datasource_kwargs)
self.counter = Counter.remote()
def _read_stream(self, f: "pa.NativeFile", path: str):
count = self.counter.increment.remote()
if ray.get(count) == 1:
raise ValueError("oops")
else:
for block in CSVDatasource._read_stream(self, f, path):
yield block
class FlakyCSVDatasink(CSVDatasink):
def __init__(self, path, **csv_datasink_kwargs):
super().__init__(path, **csv_datasink_kwargs)
self.counter = Counter.remote()
def write_block_to_file(self, block: BlockAccessor, file):
count = self.counter.increment.remote()
if ray.get(count) == 1:
raise ValueError("oops")
else:
super().write_block_to_file(block, file)
def test_datasource(ray_start_regular):
source = ray.data.datasource.RandomIntRowDatasource(n=10, num_columns=2)
assert len(ray.data.read_datasource(source).take()) == 10
source = RangeDatasource(n=10)
assert extract_values(
"value",
ray.data.read_datasource(source).take(),
) == list(range(10))
@pytest.mark.skip(reason="")
def test_polars_lazy_import(shutdown_only):
import sys
ctx = ray.data.context.DataContext.get_current()
try:
original_use_polars = ctx.use_polars
ctx.use_polars = True
num_items = 100
parallelism = 4
ray.init(num_cpus=4)
@ray.remote
def f(should_import_polars):
# Sleep to spread the tasks.
time.sleep(1)
polars_imported = "polars" in sys.modules.keys()
return polars_imported == should_import_polars
# We should not use polars for non-Arrow sort.
_ = ray.data.range(num_items, override_num_blocks=parallelism).sort()
assert all(ray.get([f.remote(False) for _ in range(parallelism)]))
a = range(100)
dfs = []
partition_size = num_items // parallelism
for i in range(parallelism):
dfs.append(
pd.DataFrame({"a": a[i * partition_size : (i + 1) * partition_size]})
)
# At least one worker should have imported polars.
_ = (
ray.data.from_pandas(dfs)
.map_batches(lambda t: t, batch_format="pyarrow", batch_size=None)
.sort(key="a")
.materialize()
)
assert any(ray.get([f.remote(True) for _ in range(parallelism)]))
finally:
ctx.use_polars = original_use_polars
def test_batch_formats(shutdown_only):
ds = ray.data.range(100)
assert isinstance(next(iter(ds.iter_batches(batch_format=None))), pa.Table)
assert isinstance(next(iter(ds.iter_batches(batch_format="default"))), dict)
assert isinstance(next(iter(ds.iter_batches(batch_format="pandas"))), pd.DataFrame)
assert isinstance(next(iter(ds.iter_batches(batch_format="pyarrow"))), pa.Table)
assert isinstance(next(iter(ds.iter_batches(batch_format="numpy"))), dict)
ds = ray.data.range_tensor(100)
assert isinstance(next(iter(ds.iter_batches(batch_format=None))), pa.Table)
assert isinstance(next(iter(ds.iter_batches(batch_format="default"))), dict)
assert isinstance(next(iter(ds.iter_batches(batch_format="pandas"))), pd.DataFrame)
assert isinstance(next(iter(ds.iter_batches(batch_format="pyarrow"))), pa.Table)
assert isinstance(next(iter(ds.iter_batches(batch_format="numpy"))), dict)
df = pd.DataFrame({"foo": ["a", "b"], "bar": [0, 1]})
ds = ray.data.from_pandas(df)
assert isinstance(next(iter(ds.iter_batches(batch_format=None))), pd.DataFrame)
assert isinstance(next(iter(ds.iter_batches(batch_format="default"))), dict)
assert isinstance(next(iter(ds.iter_batches(batch_format="pandas"))), pd.DataFrame)
assert isinstance(next(iter(ds.iter_batches(batch_format="pyarrow"))), pa.Table)
assert isinstance(next(iter(ds.iter_batches(batch_format="numpy"))), dict)
def test_dataset_schema_after_read_stats(ray_start_cluster):
cluster = ray_start_cluster
cluster.add_node(num_cpus=1)
ray.init(cluster.address)
cluster.add_node(num_cpus=1, resources={"foo": 1})
ds = ray.data.read_csv(
"example://iris.csv", ray_remote_args={"resources": {"foo": 1}}
)
schema = ds.schema()
ds.stats()
assert schema == ds.schema()
if __name__ == "__main__":
sys.exit(pytest.main(["-v", __file__]))