grain.sources module#
APIs for reading data from various file formats.
List of Members#
- class grain.sources.RandomAccessDataSource(*args, **kwargs)[source]#
Interface for datasets where storage supports efficient random access.
This Protocol defines the contract for any custom data source injected into the PyGrain pipeline. Implementations do not need to inherit from this class directly; they only need to implement the required structural methods (__len__ and __getitem__).
Notes: Checkpointing: If used with DataLoader, __repr__ has to be additionally implemented to support checkpointing.
Multiprocessing: If used with multiprocessing, the instance must be fully picklable.
Example
Implementing a minimal, checkpoint-safe custom data source:
from grain.sources import RandomAccessDataSource class MyInMemorySource: def __init__(self, data: list): self._data = data def __len__(self) -> int: return len(self._data) def __getitem__(self, index: int): return self._data[index] def __repr__(self) -> str: # Required for PyGrain checkpointing with DataLoader return f"MyInMemorySource(size={len(self)})" source = MyInMemorySource(["a", "b", "c"]) # source satisfies the RandomAccessDataSource protocol. assert isinstance(source, RandomAccessDataSource)
- __getitem__(index)[source]#
Returns the value for the given index.
This method must be thread-safe and deterministic.
Note that a number of sources take SupportsIndex instead of int for index. Such sources will still support int index and pass the isinstance check with this protocol, but all new source implementations should use int directly.
- Parameters:
index (int) – An integer in [0, len(self)-1].
- Returns:
The corresponding record. File data sources often return the raw bytes but records can be any Python object.
- Return type:
T
- class grain.sources.ArrayRecordDataSource(*args, **kwargs)[source]#
Data source for ArrayRecord files.
Reads serialized byte records from one or more ArrayRecord files or file instructions with random access indexing support.
Example
Writing to and reading records from an ArrayRecord file using MapDataset:
from array_record.python import array_record.python.array_record_data_source as array_record_module import grain # Write records to an ArrayRecord file. writer = array_record_module.ArrayRecordWriter( "/tmp/data.array_record", "group_size:1" ) writer.write(b"example_byte_record_1") writer.write(b"example_byte_record_2") writer.close() # Load the ArrayRecord file using ArrayRecordDataSource. source = grain.sources.ArrayRecordDataSource( ["/tmp/data.array_record"] ) parent_ds = grain.MapDataset.source(source) print(len(parent_ds)) # 2 print(parent_ds[0]) # b'example_byte_record_1'
- Parameters:
paths (array_record.python.array_record_data_source.PathLikeOrFileInstruction | Sequence[array_record.python.array_record_data_source.PathLikeOrFileInstruction])
reader_options (dict[str, str] | None)
- __init__(paths, reader_options=None)[source]#
Creates a new ArrayRecordDataSource object.
See array_record.ArrayRecordDataSource for more details.
- Parameters:
paths (array_record.python.array_record_data_source.PathLikeOrFileInstruction | Sequence[array_record.python.array_record_data_source.PathLikeOrFileInstruction]) – A single path/FileInstruction or list of paths/FileInstructions.
reader_options (dict[str, str] | None) – a dict[str, str] to be passed when creating a reader. For example, {index_storage_option:”in_memory”} stores the reader indices in memory versus {index_storage_option:”offloaded”} stores the indices on disk to save memory usage.
- Raises:
ValueError – If reader_options is provided but not supported by the underlying ArrayRecord reader version.
Simple in-memory data source for sequences that is sharable among multiple processes.
Note
This constrains storable values to only the int, float, bool, str (less than 10M bytes each), bytes (less than 10M bytes each), and None built-in data types. It also notably differs from the built-in list type in that these lists can not change their overall length (i.e. no append, insert, etc.)
Example
Sharing an in-memory sequence across multiple worker processes without duplicating memory:
import grain # Store the sequence in OS shared memory. Unlike a standard Python list # that is copied into every worker process when pickled, only a # lightweight shared memory reference is sent to each worker. data = [10, 20, 30, 40] source = grain.sources.SharedMemoryDataSource(data) # Read and transform elements across 2 worker processes. ds = ( grain.MapDataset.source(source) .map(lambda x: x * 2) .to_iter_dataset() ) print(list(ds)) # [20, 40, 60, 80] # Clean up the shared memory block when done. source.close() source.unlink()
- Parameters:
elements (Sequence[Any] | None)
name (str | None)
Creates a new InMemoryDataSource object.
- Parameters:
elements (Sequence[Any] | None) – The elements for the sharable list.
name (str | None) – The name of the datasource.
- Raises:
ValueError – If neither elements nor name is provided.
- class grain.sources.RangeDataSource(start, stop, step)[source]#
Range data source, similar to python range() function.
Produces a sequence of integers from start to stop with a given step, supporting efficient indexing and length lookup without loading data into memory.
Example
Creating a range data source and accessing elements:
import grain # Create a range data source source = grain.sources.RangeDataSource(start=0, stop=10, step=2) # Create a MapDataset from the source ds = grain.MapDataset.source(source) # Print the length of the dataset print(len(ds)) # 5 # Print all elements in the dataset print(list(ds)) # [0, 2, 4, 6, 8]
- Parameters:
start (int)
stop (int)
step (int)
- __init__(start, stop, step)[source]#
Initializes the RangeDataSource.
- Parameters:
start (int) – The start value of the range sequence.
stop (int) – The stop boundary of the range sequence (exclusive).
step (int) – The step increment between consecutive elements. Must not be 0.
- Raises:
AssertionError – If step is 0 or if computed length is negative.