import copy
from typing import TYPE_CHECKING, Dict, List, Literal, Optional, Union
from ray.actor import ActorHandle
from ray.util.annotations import DeveloperAPI, PublicAPI
if TYPE_CHECKING:
from ray.data import DataIterator, Dataset, ExecutionOptions, NodeIdStr
@PublicAPI(stability="stable")
class DataConfig:
"""Class responsible for configuring Train dataset preprocessing.
For advanced use cases, this class can be subclassed and the `configure()` method
overridden for custom data preprocessing.
"""
[docs]
def __init__(
self,
datasets_to_split: Union[Literal["all"], List[str]] = "all",
execution_options: Optional[
Union["ExecutionOptions", Dict[str, "ExecutionOptions"]]
] = None,
enable_shard_locality: bool = True,
):
"""Construct a DataConfig.
Args:
datasets_to_split: Specifies which datasets should be split among workers.
Can be set to "all" or a list of dataset names. Defaults to "all",
i.e. split all datasets.
execution_options: Optional Ray Data execution options. When set, they are
applied to dataset shards. When ``None`` (the default), Train applies
:meth:`default_ingest_options` to each dataset shard. Can be either:
1. A single ExecutionOptions object applied to all datasets.
2. A dict mapping dataset names to ExecutionOptions for per-dataset
overrides. Datasets not present in the dict use
:meth:`default_ingest_options`.
NOTE: For exclude_resources and resource_limits, those options only affect
Ray Data *after* train performs its cluster resource reservation.
So if you specify exclude_resources, it will exclude the resources
from data's reservation, *not* train's reservation.
enable_shard_locality: If true, dataset sharding across Train workers will
consider locality to minimize cross-node data transfer. Enabled by default.
"""
if isinstance(datasets_to_split, list) or datasets_to_split == "all":
self._datasets_to_split = datasets_to_split
else:
raise TypeError(
"`datasets_to_split` should be a 'all' or a list of strings of "
"dataset names. Received "
f"{type(datasets_to_split).__name__} with value {datasets_to_split}."
)
self._user_execution_options = execution_options
self._enable_shard_locality = enable_shard_locality
def _get_user_execution_options(
self, dataset_name: str
) -> Optional["ExecutionOptions"]:
"""Return user-provided execution options for a dataset, if any."""
if self._user_execution_options is None:
return None
if isinstance(self._user_execution_options, dict):
if dataset_name not in self._user_execution_options:
return None
return self._user_execution_options[dataset_name]
return self._user_execution_options
def _resolve_execution_options(self, dataset_name: str) -> "ExecutionOptions":
"""Return a deep copy of the effective execution options for a dataset shard.
Returns a deep copy so callers (including subclasses that override
``configure``) can mutate the result without aliasing the driver
``DataContext`` or the user-supplied ``ExecutionOptions`` object.
"""
return copy.deepcopy(
self._get_user_execution_options(dataset_name)
or self.default_ingest_options()
)
[docs]
@staticmethod
def default_ingest_options() -> "ExecutionOptions":
"""The default Ray Data options used for data ingest.
By default, configurations are carried over from what is already set
in DataContext.
"""
from ray.data import ExecutionOptions
from ray.data.context import DataContext
ctx = DataContext.get_current()
return ExecutionOptions(
resource_limits=ctx.execution_options.resource_limits,
exclude_resources=ctx.execution_options.exclude_resources,
preserve_order=ctx.execution_options.preserve_order,
verbose_progress=ctx.execution_options.verbose_progress,
)