Skip to content

Why file definitions are separate from loaded data

DataFile describes an input, DataStore registers definitions relative to a root, and DataReader loads and processes the selected input. A definition is not the loaded data and registration is not proof that every file can be read.

A file root must exist when constructing a store. Reader errors occur when the input is read; processing and component validation are later boundaries. The separation lets a parser request only the inputs it needs.

>>> from pathlib import Path
>>> from tempfile import TemporaryDirectory
>>> from r2x_core import DataFile, DataStore, TabularProcessing
>>> with TemporaryDirectory() as directory:
... root = Path(directory)
... _ = (root / "generators.csv").write_text("name,capacity_mw,status\nAlpha,100,active\nRetired,25,retired\n", encoding="utf-8")
... store = DataStore(root)
... store.add_data([DataFile(name="generators", relative_fpath="generators.csv", proc_spec=TabularProcessing(filter_by={"status": "active"}))])
... names = store.list_data()
... records = store.read_data("generators").collect().to_dicts()
>>> # Expected: registration exposes a logical name, while reading applies the filter.
>>> names
['generators']
>>> records
[{'name': 'Alpha', 'capacity_mw': 100, 'status': 'active'}]

The CSV result is a Polars lazy frame; collect() materializes its rows. TabularProcessing describes operations such as filtering and reshaping, and ReaderConfig holds reader-specific arguments. Keeping these apart avoids mixing file-format options with transformations. The processor reference specifies operation order and constraints; process file data shows how to configure them.

A valid configuration can still refer to unavailable or malformed input. Diagnose the root path, registered logical name, physical input and processed schema in that order. The troubleshooting guide provides checks for each boundary. For a complete data-to-artifact journey, use the CSV capacity scenario.