Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/cdm_data_loaders/pipelines/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,8 @@

pipeline = dlt.pipeline(**pipeline_kwargs)

logger.info("Prepping pipeline...")

Check warning on line 293 in src/cdm_data_loaders/pipelines/core.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/core.py#L293

Added line #L293 was not covered by tests

try:
pipeline.destination = dlt.destination(settings.use_destination, **(destination_kwargs or {}))
except Exception as e:
Expand All @@ -305,7 +307,9 @@
load_info: LoadInfo | None = None
err_msg: str | None = None
try:
logger.info("Running pipeline!")

Check warning on line 310 in src/cdm_data_loaders/pipelines/core.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/core.py#L310

Added line #L310 was not covered by tests
load_info = pipeline.run(resource, **(pipeline_run_kwargs or {}))
logger.info("Pipeline complete!")

Check warning on line 312 in src/cdm_data_loaders/pipelines/core.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/core.py#L312

Added line #L312 was not covered by tests
except PipelineStepFailed as e:
# errors raise PipelineStepFailed
err_msg = f"Pipeline failed: {e!s}"
Expand Down
18 changes: 16 additions & 2 deletions src/cdm_data_loaders/pipelines/jsonlines/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from typing import TYPE_CHECKING, Any, Final

import dlt
import pyarrow as pa

Check warning on line 16 in src/cdm_data_loaders/pipelines/jsonlines/base.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/base.py#L16

Added line #L16 was not covered by tests
from dlt.common.pipeline import LoadInfo
from dlt.common.schema.typing import TSchemaContractDict
from dlt.sources.filesystem import filesystem
Expand All @@ -23,7 +24,7 @@
run_pipeline,
)
from cdm_data_loaders.readers.jsonlines import stream_jsonl_lines
from cdm_data_loaders.utils.buffer import ListBuffer
from cdm_data_loaders.utils.buffer import ArrowListBuffer, ListBuffer

Check warning on line 27 in src/cdm_data_loaders/pipelines/jsonlines/base.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/base.py#L27

Added line #L27 was not covered by tests

if TYPE_CHECKING:
from dlt.extract.resource import DltResource
Expand Down Expand Up @@ -67,6 +68,7 @@
rejected_suffix: str = DEFAULT_REJECTED_SUFFIX,
include_record: bool = False,
transform_valid: Callable[[Any], Any] | None = None,
pyarrow_schema: pa.Schema | None = None,
) -> Callable[[list[dict[str, Any]]], Generator[Any, Any, Any]]:
"""Build a function that routes pages of line records to valid and rejected tables.

Expand All @@ -76,6 +78,8 @@
through `transform_valid` (identity when None) into `table_name`; failed
records yield a provenance row into ``{table_name}{rejected_suffix}``. With
`include_record`, the rejected row also carries the parsed `record` value.
When `pyarrow_schema` is supplied, valid records are accumulated in an
`ArrowListBuffer` and yielded as `pyarrow.Table` pages.

:param table_name: table name for valid records
:type table_name: str
Expand All @@ -89,13 +93,19 @@
:type include_record: bool
:param transform_valid: function applied to each valid record before yielding
:type transform_valid: Callable[[Any], Any] | None
:param pyarrow_schema: optional PyArrow schema for buffering valid records as Arrow tables
:type pyarrow_schema: pa.Schema | None
:return: function taking one page of line records and yielding routed pages
:rtype : Callable[[list[dict[str, Any]]], Generator[Any, Any, Any]]
"""
rejected_table = f"{table_name}{rejected_suffix}"

def _route(page: list[dict[str, Any]]) -> Generator[Any, Any, Any]:
valid_buffer = ListBuffer(table_name=table_name, max_items=buffer_size)
valid_buffer: ListBuffer | ArrowListBuffer = (

Check warning on line 104 in src/cdm_data_loaders/pipelines/jsonlines/base.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/base.py#L104

Added line #L104 was not covered by tests
ArrowListBuffer(table_to_schema_map={table_name: pyarrow_schema}, max_items=buffer_size)
if pyarrow_schema is not None
else ListBuffer(table_name=table_name, max_items=buffer_size)
)
rejected_buffer = ListBuffer(table_name=rejected_table, max_items=buffer_size)
for item in page:
error_detail = item["parse_error"]
Expand Down Expand Up @@ -126,6 +136,7 @@
rejected_suffix: str = DEFAULT_REJECTED_SUFFIX,
include_record: bool = False,
transform_valid: Callable[[Any], Any] | None = None,
pyarrow_schema: pa.Schema | None = None,
) -> "DltResource":
"""Read plain or gzip JSONL files for one entity and route validated pages.

Expand All @@ -144,6 +155,8 @@
:type include_record: bool
:param transform_valid: function applied to each valid record before yielding
:type transform_valid: Callable[[Any], Any] | None
:param pyarrow_schema: optional PyArrow schema for buffering valid records as Arrow tables
:type pyarrow_schema: pa.Schema | None
:return: resource yielding validated and rejected records for this entity
:rtype: DltResource
"""
Expand All @@ -170,6 +183,7 @@
rejected_suffix=rejected_suffix,
include_record=include_record,
transform_valid=transform_valid,
pyarrow_schema=pyarrow_schema,
),
data_from=raw,
name=f"{table_name}_validated",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@

The module selected by ``schema_files_module`` must define ``ENTITY_SCHEMAS``:
a mapping of table names to JSON Schema dictionaries, each declaring ``$schema``.
Files are read from one input subdirectory per table. Valid records are loaded
unchanged; parsing and validation failures go to ``<table>_rejected`` with
source_file, line_no, raw_record, and error_detail. Format checks are enabled.
Each validated JSON Schema is dereferenced and converted to a ``pyarrow.Schema``
via ``JSONSchemaToPyArrow``. Files are read from one input subdirectory per
table. Valid records are coerced to the target ``pyarrow.Schema`` and buffered
into ``pyarrow.Table`` pages via ``ArrowListBuffer``; parsing and validation
failures go to ``<table>_rejected`` with source_file, line_no, raw_record, and
error_detail. Format checks are enabled.
"""

import json
from collections.abc import Mapping
from datetime import UTC, date, datetime, time
from decimal import Decimal

Check warning on line 16 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L14-L16

Added lines #L14 - L16 were not covered by tests
from logging import Logger, getLogger
from typing import Any

import pyarrow as pa

Check warning on line 20 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L20

Added line #L20 was not covered by tests
from dlt.common.pipeline import LoadInfo
from dlt.extract.resource import DltResource
from jsonschema import FormatChecker
from jsonschema.exceptions import SchemaError
from jsonschema.validators import validator_for

from cdm_data_loaders.converters.jsonschema_to_pyarrow import JSONSchemaToPyArrow, JSONSchemaToPyArrowError

Check warning on line 27 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L27

Added line #L27 was not covered by tests
from cdm_data_loaders.pipelines.core import PipelineCli
from cdm_data_loaders.pipelines.jsonlines.base import (
ParseErrorResolver,
Expand All @@ -27,6 +35,7 @@
build_entity_resource as build_shared_entity_resource,
)
from cdm_data_loaders.pipelines.jsonlines.settings import JSONSCHEMA_PIPELINE_NAME, JsonlJsonschemaIngestSettings
from cdm_data_loaders.utils.jsonschema.dereferencer import DereferencingError, dereference_schema

Check warning on line 38 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L38

Added line #L38 was not covered by tests

logger: Logger = getLogger(__name__)

Expand Down Expand Up @@ -79,6 +88,118 @@
return registry


def convert_schema_to_pyarrow(schema: dict[str, Any]) -> pa.Schema:

Check warning on line 91 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L91

Added line #L91 was not covered by tests
"""Dereference a validated JSON Schema and convert it to a PyArrow schema.

:param schema: validated JSON Schema dictionary declaring ``$schema``
:type schema: dict[str, Any]
:return: target PyArrow schema for the entity table
:rtype: pa.Schema
:raises DereferencingError: if ``$ref`` or ``allOf`` cannot be resolved
:raises JSONSchemaToPyArrowError: if the dereferenced schema cannot be converted to a PyArrow schema
"""
dereferenced = dereference_schema(schema)
if "$schema" not in dereferenced and "$schema" in schema:
dereferenced = {"$schema": schema["$schema"], **dereferenced}
return JSONSchemaToPyArrow().convert(dereferenced)

Check warning on line 104 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L101-L104

Added lines #L101 - L104 were not covered by tests


def build_pyarrow_schemas(table_to_schema: dict[str, dict[str, Any]]) -> dict[str, pa.Schema]:

Check warning on line 107 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L107

Added line #L107 was not covered by tests
"""Convert all entity JSON Schemas to PyArrow schemas up front.

Collects any dereferencing or conversion errors across all tables and raises
a single ``RuntimeError`` listing every failing table.

:param table_to_schema: mapping of table name to validated JSON Schema dictionary
:type table_to_schema: dict[str, dict[str, Any]]
:return: mapping of table name to PyArrow schema
:rtype: dict[str, pa.Schema]
:raises RuntimeError: if one or more schemas cannot be converted to PyArrow schemas
"""
pyarrow_schemas: dict[str, pa.Schema] = {}
errors: dict[str, str] = {}
for table_name, schema in table_to_schema.items():
try:
pyarrow_schemas[table_name] = convert_schema_to_pyarrow(schema)
except (DereferencingError, JSONSchemaToPyArrowError) as error:
logger.exception("Error converting JSON Schema to PyArrow for %s", table_name)
errors[table_name] = str(error)

Check warning on line 126 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L119-L126

Added lines #L119 - L126 were not covered by tests

if errors:
details = "".join(f"{table_name}: {error}\n" for table_name, error in errors.items())
err_msg = f"The following errors were found when converting schemas to PyArrow:\n{details}"
raise RuntimeError(err_msg)
return pyarrow_schemas

Check warning on line 132 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L128-L132

Added lines #L128 - L132 were not covered by tests


def _parse_iso_timestamp(value: str, data_type: pa.TimestampType) -> datetime:

Check warning on line 135 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L135

Added line #L135 was not covered by tests
"""Parse an ISO-8601 timestamp string, attaching UTC when target type is tz-aware."""
parsed_dt = datetime.fromisoformat(value)
if parsed_dt.tzinfo is None and data_type.tz is not None:
return parsed_dt.replace(tzinfo=UTC)
return parsed_dt

Check warning on line 140 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L137-L140

Added lines #L137 - L140 were not covered by tests


def _coerce_scalar_for_pyarrow(value: Any, data_type: pa.DataType) -> Any: # noqa: ANN401

Check warning on line 143 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L143

Added line #L143 was not covered by tests
"""Coerce a JSON-parsed value to match a PyArrow scalar type."""
coerced = value
if pa.types.is_date(data_type) and isinstance(value, str):
coerced = date.fromisoformat(value)
elif pa.types.is_timestamp(data_type) and isinstance(value, str):
coerced = _parse_iso_timestamp(value, data_type)
elif pa.types.is_time(data_type) and isinstance(value, str):
coerced = time.fromisoformat(value)
elif pa.types.is_decimal(data_type) and isinstance(value, int | float | str) and not isinstance(value, bool):
coerced = Decimal(str(value))
elif pa.types.is_floating(data_type) and isinstance(value, int) and not isinstance(value, bool):
coerced = float(value)
elif pa.types.is_integer(data_type) and isinstance(value, float) and value.is_integer():
coerced = int(value)
elif (pa.types.is_string(data_type) or pa.types.is_large_string(data_type)) and not isinstance(value, str):
coerced = json.dumps(value) if isinstance(value, Mapping | list) else str(value)
return coerced

Check warning on line 160 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L145-L160

Added lines #L145 - L160 were not covered by tests


def _coerce_value_for_pyarrow(value: Any, data_type: pa.DataType) -> Any: # noqa: ANN401

Check warning on line 163 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L163

Added line #L163 was not covered by tests
"""Recursively coerce a JSON-parsed value to match the target PyArrow DataType."""
if value is None:
return None
if pa.types.is_struct(data_type) and isinstance(value, Mapping):
return {

Check warning on line 168 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L165-L168

Added lines #L165 - L168 were not covered by tests
field.name: _coerce_value_for_pyarrow(value[field.name], field.type)
for field in data_type
if field.name in value
}
if (pa.types.is_list(data_type) or pa.types.is_large_list(data_type)) and isinstance(value, list):
return [_coerce_value_for_pyarrow(item, data_type.value_type) for item in value]
if pa.types.is_map(data_type) and isinstance(value, Mapping):
return {

Check warning on line 176 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L173-L176

Added lines #L173 - L176 were not covered by tests
_coerce_value_for_pyarrow(k, data_type.key_type): _coerce_value_for_pyarrow(v, data_type.item_type)
for k, v in value.items()
}
return _coerce_scalar_for_pyarrow(value, data_type)

Check warning on line 180 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L180

Added line #L180 was not covered by tests


def coerce_record_for_pyarrow(record: dict[str, Any], schema: pa.Schema) -> dict[str, Any]:

Check warning on line 183 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L183

Added line #L183 was not covered by tests
"""Coerce a JSON-validated record dictionary to match a PyArrow schema.

Only fields declared in ``schema`` are retained and coerced recursively so
``pyarrow.Table.from_pylist`` can construct a table matching ``schema``.

:param record: JSON-parsed and schema-validated record dictionary
:type record: dict[str, Any]
:param schema: target PyArrow schema for the record's table
:type schema: pa.Schema
:return: coerced record dictionary ready for ``ArrowListBuffer``
:rtype: dict[str, Any]
"""
return {

Check warning on line 196 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L196

Added line #L196 was not covered by tests
field.name: _coerce_value_for_pyarrow(record[field.name], field.type)
for field in schema
if field.name in record
}


def _make_resolve_error(schema: dict[str, Any]) -> ParseErrorResolver:
"""Build the per-record JSON Schema validation closure.

Expand All @@ -96,39 +217,61 @@


def build_entity_resource(
table_name: str, schema: dict[str, Any], settings: JsonlJsonschemaIngestSettings
table_name: str,
schema: dict[str, Any],
settings: JsonlJsonschemaIngestSettings,
*,
pyarrow_schema: pa.Schema | None = None,
) -> DltResource:
"""Read plain or gzip JSONL files for one entity and route validated pages.

A missing entity directory produces no rows. Nested values are preserved
rather than normalized into child tables.
A missing entity directory produces no rows. Validated records are coerced
to the target PyArrow schema and buffered into ``pyarrow.Table`` pages via
``ArrowListBuffer``. Nested values are preserved rather than normalized into
child tables.

:param table_name: table name
:type table_name: str
:param schema: JSONSchema for the table
:type schema: dict[str, Any]
:param settings: pipeline configuration
:type settings: JsonlJsonschemaIngestSettings
:param pyarrow_schema: precomputed PyArrow schema, or None to convert ``schema``
:type pyarrow_schema: pa.Schema | None
:return: resource yielding validated and rejected records for this entity
:rtype: DltResource
"""
return build_shared_entity_resource(table_name, settings, resolve_error=_make_resolve_error(schema))
resolved_pa_schema = pyarrow_schema if pyarrow_schema is not None else convert_schema_to_pyarrow(schema)
return build_shared_entity_resource(

Check warning on line 245 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L244-L245

Added lines #L244 - L245 were not covered by tests
table_name,
settings,
resolve_error=_make_resolve_error(schema),
transform_valid=lambda record: coerce_record_for_pyarrow(record, resolved_pa_schema),
pyarrow_schema=resolved_pa_schema,
)


def run_jsonlines_pipeline(settings: JsonlJsonschemaIngestSettings) -> LoadInfo | None:
"""Validate the registry and load requested tables using the shared runner.
"""Validate the registry, build PyArrow schemas, and load requested tables.

:param settings: pipeline configuration
:type settings: JsonlJsonschemaIngestSettings
:raises ValueError: if settings.table_names includes a name not in ENTITY_SCHEMAS
:raises RuntimeError: if any schema fails JSON Schema validation or PyArrow conversion
:return: load information for the pipeline
:rtype: LoadInfo | None
"""
table_to_schema = load_entity_schemas(settings.schema_files_module)
pyarrow_schemas = build_pyarrow_schemas(table_to_schema)

Check warning on line 265 in src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py

View check run for this annotation

Codecov / codecov/patch

src/cdm_data_loaders/pipelines/jsonlines/extract_jsonschema_validate_pipeline.py#L265

Added line #L265 was not covered by tests
return run_registry_ingest(
settings,
table_to_schema,
build_resource=build_entity_resource,
build_resource=lambda table_name, schema, cfg: build_entity_resource(
table_name,
schema,
cfg,
pyarrow_schema=pyarrow_schemas[table_name],
),
pipeline_name=JSONSCHEMA_PIPELINE_NAME,
pipeline_run_kwargs={"loader_file_format": str(settings.loader_file_format)},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
from pathlib import Path
from typing import Any

import pyarrow as pa
import pyarrow.parquet as pq
import pytest

from cdm_data_loaders.core.fields import PARQUET
from cdm_data_loaders.pipelines.jsonlines.settings import JSONSCHEMA_PIPELINE_NAME, JsonlJsonschemaIngestSettings
from tests.cdm_data_loaders.pipelines.framework import PipelineTestRunner
from tests.cdm_data_loaders.pipelines.helpers import (
Expand All @@ -23,6 +26,20 @@

WIDGET_RECORD = {"widget_id": "a", "count": 0, "nested": {"tags": ["one", "two"]}}
GADGET_RECORD = {"widget_id": "g", "count": 3}
EXPECTED_WIDGET_ROW = {
"widget_id": "a",
"count": 0,
"collected": None,
"label": None,
"nested": {"tags": ["one", "two"]},
}
EXPECTED_GADGET_ROW = {
"widget_id": "g",
"count": 3,
"collected": None,
"label": None,
"nested": None,
}
TYPE_ERROR_RAW = '{"widget_id":"bad", "count":"2"}'
FORMAT_ERROR_RAW = '{"widget_id":"bad","count":1,"collected":"2023-02-29"}'
MALFORMED_RAW = "{broken"
Expand Down Expand Up @@ -73,9 +90,17 @@ def test_run_jsonlines_pipeline_pass_persists_valid_and_rejected_rows(
tables = read_data_tables(settings, load_info.dataset_name)
assert set(tables) == {"widget", "widget_rejected", "gadget"}
assert [{key: parse_json_string(value) for key, value in row.items()} for row in tables["widget"]] == [
WIDGET_RECORD
EXPECTED_WIDGET_ROW
] * 3
assert tables["gadget"] == [GADGET_RECORD]
assert tables["gadget"] == [EXPECTED_GADGET_ROW]
if loader_file_format == PARQUET:
widget_parquet = next((Path(settings.output_dir) / load_info.dataset_name / "widget").glob("*.parquet"))
parquet_schema = pq.read_schema(widget_parquet)
assert parquet_schema.field("widget_id").type == pa.string()
assert parquet_schema.field("count").type == pa.int64()
assert parquet_schema.field("collected").type == pa.date32()
assert parquet_schema.field("label").type == pa.string()
assert parquet_schema.field("nested").type == pa.struct([pa.field("tags", pa.list_(pa.string()))])
assert sorted(tables["widget_rejected"], key=lambda row: (row["source_file"], row["line_no"])) == [
{
"source_file": "compressed.jsonl.gz",
Expand Down Expand Up @@ -151,4 +176,4 @@ def test_cli_pass_loads_selected_entity(
)
assert load_info.dataset_name == "schema_cli"
assert_pipeline_report_written(Path(settings.output_dir), load_info, metadata_moved=True)
assert read_data_tables(settings, load_info.dataset_name) == {"gadget": [GADGET_RECORD]}
assert read_data_tables(settings, load_info.dataset_name) == {"gadget": [EXPECTED_GADGET_ROW]}
Loading
Loading