199 lines
5.9 KiB
Python
199 lines
5.9 KiB
Python
import pandas as pd
|
|
import pytest
|
|
|
|
import ray
|
|
from ray._common.test_utils import run_string_as_driver
|
|
from ray.data.block import BlockMetadata
|
|
from ray.data.context import DataContext
|
|
from ray.data.datasource import Datasource, ReadTask
|
|
from ray.tests.conftest import * # noqa
|
|
|
|
# Auto-use `restore_data_context` for each test.
|
|
pytestmark = pytest.mark.usefixtures("restore_data_context")
|
|
|
|
|
|
def test_context_saved_when_dataset_created(
|
|
ray_start_regular_shared,
|
|
):
|
|
ctx = DataContext.get_current()
|
|
ctx.set_config("foo", 1)
|
|
d1 = ray.data.range(10)
|
|
d2 = ray.data.range(10)
|
|
assert ctx.get_config("foo") == 1
|
|
assert d1.context.get_config("foo") == 1
|
|
assert d2.context.get_config("foo") == 1
|
|
|
|
# Changing `d1.context` should not affect `d2.context` or the global context.
|
|
d1.context.set_config("foo", 2)
|
|
assert d1.context.get_config("foo") == 2
|
|
assert d2.context.get_config("foo") == 1
|
|
assert ctx.get_config("foo") == 1
|
|
|
|
# Changed value can be propagated to remote tasks.
|
|
@ray.remote(num_cpus=0)
|
|
def check(d1, d2):
|
|
assert d1.context.get_config("foo") == 2
|
|
assert d2.context.get_config("foo") == 1
|
|
|
|
ray.get(check.remote(d1, d2))
|
|
|
|
# Changing the global context should not affect `d1.context` or `d2.context`.
|
|
ctx.set_config("foo", 3)
|
|
assert ctx.get_config("foo") == 3
|
|
assert d1.context.get_config("foo") == 2
|
|
assert d2.context.get_config("foo") == 1
|
|
|
|
|
|
def test_context_inheritance(ray_start_regular_shared):
|
|
ds = ray.data.range(10)
|
|
ds.context.set_config("foo", 1)
|
|
assert DataContext.get_current().get_config("foo", None) is None
|
|
|
|
# Test that applying a new operator to an existing dataset
|
|
# inherits the context.
|
|
ds2 = ds.map_batches(lambda batch: batch)
|
|
assert ds2.context.get_config("foo") == 1
|
|
|
|
# Test that materializing a dataset also inherits the context.
|
|
mds = ds.materialize()
|
|
assert mds.context.get_config("foo") == 1
|
|
|
|
# Test that the iterator also inherits the context.
|
|
iter = ds.iterator()
|
|
assert iter.get_context().get_config("foo") == 1
|
|
assert iter.materialize().context.get_config("foo") == 1
|
|
|
|
|
|
def _test_updating_context_after_dataset_creation(gen_ds):
|
|
context = DataContext.get_current()
|
|
context.set_config("foo", 1)
|
|
ds = gen_ds()
|
|
assert ds.take_all()[0]["id"] == 1
|
|
# DataContext is supposed to be sealed when a Dataset is created.
|
|
# Test that updating the current DataContext doesn't affect existing Datasets.
|
|
context.set_config("foo", 2)
|
|
assert ds.take_all()[0]["id"] == 1
|
|
|
|
|
|
def test_read(
|
|
ray_start_regular_shared,
|
|
):
|
|
class CustomDatasource(Datasource):
|
|
def prepare_read(self, parallelism: int):
|
|
def read_fn():
|
|
value = DataContext.get_current().get_config("foo")
|
|
return [pd.DataFrame({"id": [value]})]
|
|
|
|
meta = BlockMetadata(
|
|
num_rows=1, size_bytes=8, input_files=None, exec_stats=None
|
|
)
|
|
return [ReadTask(read_fn, meta)]
|
|
|
|
_test_updating_context_after_dataset_creation(
|
|
lambda: ray.data.read_datasource(CustomDatasource()),
|
|
)
|
|
|
|
|
|
def test_map(
|
|
ray_start_regular_shared,
|
|
):
|
|
_test_updating_context_after_dataset_creation(
|
|
lambda: ray.data.range(1).map(
|
|
lambda _: {"id": DataContext.get_current().get_config("foo")}
|
|
)
|
|
)
|
|
|
|
|
|
def test_flat_map(
|
|
ray_start_regular_shared,
|
|
):
|
|
_test_updating_context_after_dataset_creation(
|
|
lambda: ray.data.range(1).flat_map(
|
|
lambda _: [{"id": DataContext.get_current().get_config("foo")}]
|
|
)
|
|
)
|
|
|
|
|
|
def test_map_batches(
|
|
ray_start_regular_shared,
|
|
):
|
|
_test_updating_context_after_dataset_creation(
|
|
lambda: ray.data.range(1).map_batches(
|
|
lambda x: {"id": [DataContext.get_current().get_config("foo")]}
|
|
)
|
|
)
|
|
|
|
|
|
def test_filter(
|
|
ray_start_regular_shared,
|
|
):
|
|
_test_updating_context_after_dataset_creation(
|
|
lambda: ray.data.from_items([1])
|
|
.filter(lambda x: x["item"] == DataContext.get_current().get_config("foo"))
|
|
.rename_columns({"item": "id"})
|
|
)
|
|
|
|
|
|
def test_streaming_split(
|
|
ray_start_regular_shared,
|
|
):
|
|
# Tests that custom DataContext can be properly propagated
|
|
# when using `streaming_split()`.
|
|
block_size = 123 * 1024 * 1024
|
|
data_context = DataContext.get_current()
|
|
data_context.target_max_block_size = block_size
|
|
data_context.set_config("foo", "bar")
|
|
|
|
def f(x):
|
|
assert DataContext.get_current().target_max_block_size == block_size
|
|
assert DataContext.get_current().get_config("foo") == "bar"
|
|
return x
|
|
|
|
num_splits = 2
|
|
splits = (
|
|
ray.data.range(10, override_num_blocks=10).map(f).streaming_split(num_splits)
|
|
)
|
|
|
|
@ray.remote(num_cpus=0)
|
|
def consume(split):
|
|
for _ in split.iter_rows():
|
|
pass
|
|
|
|
assert ray.get([consume.remote(split) for split in splits]) == [None] * num_splits
|
|
|
|
|
|
def test_context_placement_group():
|
|
driver_code = """
|
|
import ray
|
|
from ray.data.context import DataContext
|
|
from ray.util.scheduling_strategies import PlacementGroupSchedulingStrategy
|
|
from ray._private.test_utils import placement_group_assert_no_leak
|
|
|
|
ray.init(num_cpus=1)
|
|
|
|
context = DataContext.get_current()
|
|
# This placement group will take up all cores of the local cluster.
|
|
placement_group = ray.util.placement_group(
|
|
name="core_hog",
|
|
strategy="SPREAD",
|
|
bundles=[
|
|
{"CPU": 1},
|
|
],
|
|
)
|
|
ray.get(placement_group.ready())
|
|
context.scheduling_strategy = PlacementGroupSchedulingStrategy(placement_group)
|
|
ds = ray.data.range(100, override_num_blocks=2).map(lambda x: {"id": x["id"] + 1})
|
|
assert ds.take_all() == [{"id": x} for x in range(1, 101)]
|
|
placement_group_assert_no_leak([placement_group])
|
|
ray.shutdown()
|
|
"""
|
|
|
|
# Successful exit is sufficient to verify this test.
|
|
run_string_as_driver(driver_code)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
|
|
sys.exit(pytest.main(["-v", __file__]))
|