JSONLines (JSON Schema) Pipeline Specification¶
This document specifies how an ENTITY_SCHEMAS registry of JSON Schema documents and the
JSONLines JSON Schema validation pipeline
(cdm_data_loaders.pipelines.jsonlines.extract_jsonschema_validate_pipeline and JsonlJsonschemaIngestSettings)
determine:
- Entity schema registration, JSON Schema dialect validation, and PyArrow schema generation (
JsonlJsonschemaIngestSettings,SchemaRegistry,build_output_schema_registry). - Per-line JSON parsing, JSON Schema validation with
FormatChecker, and dead-letter routing (_rejected) (iter_file_jsonl_records,_make_resolve_error,make_page_router). - Per-entity directory discovery,
.gzdecompression, MD5 sidecar pre-processing, table transforms, and pipeline reporting (build_entity_resource,run_jsonlines_pipeline). - Preset subclass behavior (
ncbi_datasets/NcbiDatasetsIngestSettings). - Errors (
JsonlJsonschemaErrorsubclasses) raised for invalidENTITY_SCHEMASregistries or invalid JSON Schemas.
| Property | Value |
|---|---|
| Base Pipeline Family | jsonlines_jsonschema |
| Base CLI Entry Point | jsonlines_jsonschema |
| Base Settings Class | cdm_data_loaders.pipelines.jsonlines.settings.JsonlJsonschemaIngestSettings |
| Pipeline Runner | cdm_data_loaders.pipelines.jsonlines.extract_jsonschema_validate_pipeline.run_jsonlines_pipeline |
| Preset Subclasses Covered | ncbi_datasets |
| Specification Directory | specs/pipelines/jsonlines_jsonschema/ |
Settings Class Hierarchy & Preset Subclasses¶
The generic jsonlines_jsonschema pipeline is configured by JsonlJsonschemaIngestSettings and executed by run_jsonlines_pipeline. Domain preset pipelines subclass the base settings class to lock schema, table, and transform parameters as ClassVar constants or narrowed defaults while delegating execution to the same pipeline runner.
classDiagram
JsonlJsonschemaIngestSettings <|-- NcbiDatasetsSettings
Preset Subclass: ncbi_datasets (NcbiDatasetsSettings)¶
- CLI Command:
ncbi_datasets - MRO Chain:
NcbiDatasetsSettings->JsonlJsonschemaIngestSettings - Shared Runner:
cdm_data_loaders.pipelines.jsonlines.extract_jsonschema_validate_pipeline.run_jsonlines_pipeline
| Setting / Class Attribute | Override Kind | Base (JsonlJsonschemaIngestSettings) |
Preset (NcbiDatasetsSettings) |
|---|---|---|---|
transforms_module |
Locked ClassVar (CLI flag removed) |
None |
'cdm_data_loaders.schemas.ncbi_datasets.ncbi_openapi' |
output_schemas_module |
Locked ClassVar (CLI flag removed) |
None |
'cdm_data_loaders.schemas.ncbi_datasets.ncbi_openapi' |
dataset_name |
Locked ClassVar (CLI flag removed) |
(required) | 'ncbi_datasets' |
entity_schemas |
Locked ClassVar (CLI flag removed) |
(required) | 'cdm_data_loaders.schemas.ncbi_datasets.ncbi_openapi' |
Running Smoke Tests Across Environments¶
# Run all base and preset smoke tests for jsonlines_jsonschema across built-in environments
uv run pytest tests/cdm_data_loaders/pipelines/test_pipeline_smoke_spec.py --smoke-pipeline jsonlines_jsonschema
# Run only the ncbi_datasets preset subclass smoke tests
uv run pytest tests/cdm_data_loaders/pipelines/test_pipeline_smoke_spec.py --smoke-pipeline ncbi_datasets
# Run against a custom environment directory (config.toml, secrets.toml, settings.toml)
uv run pytest tests/cdm_data_loaders/pipelines/test_pipeline_smoke_spec.py \
--smoke-pipeline jsonlines_jsonschema \
--dlt-config-dir /path/to/env_dlt_config \
--settings-file /path/to/env_dlt_config/settings.toml
1. Conversion & Pipeline Rules¶
1.1 Entity Schema Registry, Dialects, and PyArrow Schema Generation¶
- R-JSONL-ENTITY-REGISTRY:
JsonlJsonschemaIngestSettingsimportsENTITY_SCHEMAS: dict[str, dict[str, Any]]from the module named byentity_schemasviaload_registry_dict(E1InvalidEntitySchemasRegistryErrorwhen the module cannot be imported,ENTITY_SCHEMASis missing/empty/non-dict, or uses the reserved_rejectedkey).
Cases: spec/01_scalars_nested_types_and_extra_fields, spec/04_multi_entity_gzip_and_subdirs, presets/ncbi_datasets/01_valid_and_filtered, presets/ncbi_datasets/02_rejected_records
- R-JSONL-SCHEMA-CONVERT: Each entry in
ENTITY_SCHEMASis validated byValidatedSchema, requiring an explicit$schemakeyword for a supported draft (E2UnsupportedJsonlSchemaDialectError) and passingvalidator.check_schema(E3InvalidJsonlEntitySchemaError). For each entity not overridden byTABLE_TRANSFORMSorOUTPUT_SCHEMAS,convert_schema_to_pyarrowdereferences local$refpointers andallOfbranches viadereference_schema(E4UnresolvableJsonlSchemaRefError) and converts the dereferenced Draft 2020-12objectschema to apa.SchemaviaJSONSchemaToPyArrow(E5UnsupportedJsonlArrowSchemaError).
Cases: spec/01_scalars_nested_types_and_extra_fields, spec/02_defs_ref_allof_and_draft_dialects
- R-JSONL-SCHEMA-SKIP: When
transforms_moduledefines aRecordTransformFnfor an entity inTABLE_TRANSFORMS(or provides an explicitpa.SchemainOUTPUT_SCHEMAS),build_output_schema_registryskips automaticconvert_schema_to_pyarrowconversion for that entity and usesOUTPUT_SCHEMASfor target table coercion.
Cases: spec/06_table_transforms_and_output_schemas
Declared $schema URI |
Validator Class | Format Checking |
|---|---|---|
https://json-schema.org/draft/2020-12/schema |
Draft202012Validator |
FormatChecker() enabled |
https://json-schema.org/draft/2019-09/schema |
Draft201909Validator |
FormatChecker() enabled |
http://json-schema.org/draft-07/schema# |
Draft7Validator |
FormatChecker() enabled |
http://json-schema.org/draft-06/schema# |
Draft6Validator |
FormatChecker() enabled |
http://json-schema.org/draft-04/schema# |
Draft4Validator |
FormatChecker() enabled |
https://kbase.github.io/cdm-data-loaders/schemas/xsv_config/0.1.0/schema.json |
Validator |
FormatChecker() enabled |
1.2 Line Streaming, JSON Schema Validation, and PyArrow Coercion¶
- R-JSONL-DIALECT-FORMAT:
_make_resolve_errorbuilds ajsonschemavalidator for each entity's raw schema usingvalidator_for(raw_schema)(raw_schema, format_checker=FormatChecker()), enforcing stringformatconstraints (such asdate,date-time,email, anduuid) during record validation whilecoerce_record_for_pyarrowparsesformat: "date"strings intopa.date32()(datetime.date) and keeps other formatted strings aspa.string().
Cases: spec/02_defs_ref_allof_and_draft_dialects, spec/03_blank_lines_parse_errors_and_format_rejections
- R-JSONL-BLANK-AND-REJECT:
iter_file_jsonl_recordsreads each file line-by-line with 1-based line numbering and skips blank or whitespace-only lines. Lines that failjson.loads(parse_error = str(e)) or failvalidator.iter_errors(error_detail = json.dumps([error.message for error in errors]), sorted bystr(error)) are routed bymake_page_routerto_rejected(REJECTED_PYARROW_SCHEMA) withtable_name,source_file(relative path<entity_type>/<file>),line_no,raw_record, anderror_detail.
Cases: spec/03_blank_lines_parse_errors_and_format_rejections, presets/ncbi_datasets/02_rejected_records
- R-JSONL-COERCE: Valid records are passed through
route_and_coerce_recordintoDictBuffer(max_items=settings.buffer_size, schema_map=settings.schema_map)to emitpa.Tablepages. Undeclared extra fields permitted by the JSON Schema are dropped by PyArrow schema projection, and omitted optional fields are populated withnull.
Cases: spec/01_scalars_nested_types_and_extra_fields
1.3 Per-Entity Discovery, Pre-processors, Dead-Letter Routing, and Transforms¶
- R-JSONL-MULTI-ENTITY-GLOB: For each
table_nameinENTITY_SCHEMAS,build_entity_resourcescans<input_dir>/<table_name>/<file_glob>(default"*.jsonl*"). Entities without matching files or subdirectories produce no rows; matching plain.jsonland gzip-compressed.jsonl.gzfiles across nested subdirectories are transparently decompressed and streamed.
Cases: spec/04_multi_entity_gzip_and_subdirs
- R-JSONL-PREPROCESS-MD5: When
preprocessors_moduleconfiguresFILE_PREPROCESSORS,preprocess_filevalidates each discoveredFileItemDictbefore reading lines. If a pre-processor raises an exception (such as a mismatched or malformed<file>.md5sidecar), the file is skipped and a row is emitted to_rejectedwithtable_name,source_file(relative path),line_no=None,raw_record=None, anderror_detail.
Cases: spec/05_md5_sidecar_preprocessing
- R-JSONL-TRANSFORMS: When
transforms_moduledefinesTABLE_TRANSFORMS, validated record dicts for that entity are passed to itsRecordTransformFn, which may drop records ({}), compute derived fields, or fan out records across multiple destination tables coerced withOUTPUT_SCHEMAS.
Cases: spec/06_table_transforms_and_output_schemas, presets/ncbi_datasets/01_valid_and_filtered
- R-JSONL-REPORT: Every pipeline execution writes one audit row to
_load_infoand a YAML summary to<output_dir>/report.txtrecordingstatus,ingested_files(relative paths<entity_type>/<file>),load_errors, andpipeline_load_info_errs.
Cases: spec/03_blank_lines_parse_errors_and_format_rejections, spec/06_table_transforms_and_output_schemas, presets/ncbi_datasets/01_valid_and_filtered, presets/ncbi_datasets/02_rejected_records
| Column | PyArrow DataType |
Field.nullable |
Description |
|---|---|---|---|
table_name |
pa.string() |
True |
Target entity table name (None for level-mode file pre-processor errors) |
source_file |
pa.string() |
True |
Input file path relative to input_dir |
line_no |
pa.int64() |
True |
1-based physical line number in source_file (None for file-level errors) |
raw_record |
pa.string() |
True |
Stripped raw input line (None for file-level errors) |
error_detail |
pa.string() |
True |
Parser error message, JSON-encoded list of schema errors, or pre-processor error |
1.4 NCBI Datasets Preset Subclass (NcbiDatasetsIngestSettings)¶
- R-NCBI-PRESET:
NcbiDatasetsIngestSettings(ncbi_datasetsCLI) subclassesJsonlJsonschemaIngestSettings, lockingdataset_name="ncbi_datasets",entity_schemas="cdm_data_loaders.parsers.ncbi_datasets",transforms_module="cdm_data_loaders.parsers.ncbi_datasets",output_schemas_module="cdm_data_loaders.parsers.ncbi_datasets", and precomputedSCHEMASforannotation_reportanddataset_report.
Cases: presets/ncbi_datasets/01_valid_and_filtered, presets/ncbi_datasets/02_rejected_records
- R-NCBI-FILTER-EMPTY:
filter_annotation_reportincdm_data_loaders.parsers.ncbi_datasetsdropsannotation_reportrecords whosereportslist is missing or empty (such as{"total_count": 0}) before buffering, retaining only records with a non-emptyreportslist.
Cases: presets/ncbi_datasets/01_valid_and_filtered, presets/ncbi_datasets/02_rejected_records
2. Case Coverage Matrix¶
| Suite | Target CLI | Settings Class | Case | Title | Rules | Expected Table Counts |
|---|---|---|---|---|---|---|
spec |
jsonlines_jsonschema |
JsonlJsonschemaIngestSettings |
01_scalars_nested_types_and_extra_fields |
Scalar Types, Nested Objects, Arrays, and Undeclared Extra Fields | R-JSONL-ENTITY-REGISTRY, R-JSONL-SCHEMA-CONVERT, R-JSONL-COERCE |
default: {'genomes': 2} |
spec |
jsonlines_jsonschema |
JsonlJsonschemaIngestSettings |
02_defs_ref_allof_and_draft_dialects |
Local $defs / $ref Dereferencing, allOf Composition, and Format Validation |
R-JSONL-SCHEMA-CONVERT, R-JSONL-DIALECT-FORMAT |
default: {'submissions': 2} |
spec |
jsonlines_jsonschema |
JsonlJsonschemaIngestSettings |
03_blank_lines_parse_errors_and_format_rejections |
Blank Lines, JSON Parse Errors, Schema Violations, and FormatChecker Rejections |
R-JSONL-BLANK-AND-REJECT, R-JSONL-DIALECT-FORMAT, R-JSONL-REPORT |
partial_rejections: {'contacts': 2, '_rejected': 4}; total_rejection: {'_rejected': 2} |
spec |
jsonlines_jsonschema |
JsonlJsonschemaIngestSettings |
04_multi_entity_gzip_and_subdirs |
Multi-Entity Subdirectory Discovery, Gzip Decompression, and Missing Entity Directories | R-JSONL-MULTI-ENTITY-GLOB, R-JSONL-ENTITY-REGISTRY |
default: {'projects': 2, 'experiments': 1} |
spec |
jsonlines_jsonschema |
JsonlJsonschemaIngestSettings |
05_md5_sidecar_preprocessing |
MD5 Sidecar Pre-processing and File-Level _rejected Routing |
R-JSONL-PREPROCESS-MD5 |
partial_md5_failure: {'articles': 1, '_rejected': 1}; total_md5_failure: {'_rejected': 1} |
spec |
jsonlines_jsonschema |
JsonlJsonschemaIngestSettings |
06_table_transforms_and_output_schemas |
Table Transforms, Schema Conversion Skip, and Multi-Table Fan-Out | R-JSONL-SCHEMA-SKIP, R-JSONL-TRANSFORMS, R-JSONL-REPORT |
default: {'samples': 2, 'sample_measurements': 2} |
presets/ncbi_datasets |
ncbi_datasets |
NcbiDatasetsSettings |
01_valid_and_filtered |
Valid Records and Empty Annotation Report Filtering | R-JSONL-ENTITY-REGISTRY, R-JSONL-TRANSFORMS, R-JSONL-REPORT, R-NCBI-PRESET, R-NCBI-FILTER-EMPTY |
default: {'annotation_report': 1, 'dataset_report': 1} |
presets/ncbi_datasets |
ncbi_datasets |
NcbiDatasetsSettings |
02_rejected_records |
Malformed JSON and Schema-Invalid Record Rejection (_rejected) |
R-JSONL-ENTITY-REGISTRY, R-JSONL-BLANK-AND-REJECT, R-JSONL-REPORT, R-NCBI-PRESET, R-NCBI-FILTER-EMPTY |
mixed_valid_and_invalid: {'annotation_report': 1, 'dataset_report': 1, '_rejected': 2} |
3. Base Pipeline Specification Cases (jsonlines_jsonschema / spec/)¶
Shared Fixture Module (specs/pipelines/jsonlines_jsonschema/sample_registry.py)
"""Entity schema registry for JSONLines (JSON Schema) base pipeline specification cases."""
import json
from pathlib import Path
from typing import Any
SPEC_DIR = Path(__file__).parent / "spec"
def _load_schema(rel_path: str) -> dict[str, Any]:
return json.loads((SPEC_DIR / rel_path).read_text(encoding="utf-8"))
ENTITY_SCHEMAS: dict[str, dict[str, Any]] = {
"genomes": _load_schema("01_scalars_nested_types_and_extra_fields/schema.json"),
"submissions": _load_schema("02_defs_ref_allof_and_draft_dialects/schema.json"),
"contacts": _load_schema("03_blank_lines_parse_errors_and_format_rejections/schema.json"),
"projects": _load_schema("04_multi_entity_gzip_and_subdirs/schema.json"),
"experiments": _load_schema("04_multi_entity_gzip_and_subdirs/experiment_schema.json"),
"missing_entity": _load_schema("04_multi_entity_gzip_and_subdirs/schema.json"),
"articles": _load_schema("05_md5_sidecar_preprocessing/schema.json"),
}
Shared Fixture Module (specs/pipelines/jsonlines_jsonschema/transforms.py)
"""Entity schema registries, file pre-processors, table transforms, and output schemas for JSONLines spec cases."""
import json
from pathlib import Path
from typing import Any, Final
import pyarrow as pa
from cdm_data_loaders.core.preprocessors import validate_md5_sidecar
from cdm_data_loaders.core.registry import FilePreprocessFn, RecordTransformFn
SPEC_DIR = Path(__file__).parent / "spec"
NEUTRAL_PH: Final[float] = 7.0
FILE_PREPROCESSORS: tuple[FilePreprocessFn, ...] = (validate_md5_sidecar,)
SAMPLE_SUMMARY_SCHEMA = pa.schema(
[
pa.field("sample_id", pa.string(), nullable=False),
pa.field("ph", pa.float64(), nullable=False),
pa.field("ph_bucket", pa.string(), nullable=False),
]
)
SAMPLE_MEASUREMENT_SCHEMA = pa.schema(
[
pa.field("sample_id", pa.string(), nullable=False),
pa.field("metric", pa.string(), nullable=False),
]
)
def transform_sample(row: dict[str, Any]) -> dict[str, list[dict[str, Any]]]:
"""Drop excluded samples, bucket pH values, and fan out metrics to ``sample_measurements``."""
if row.get("exclude") is True:
return {}
sample_id = str(row["sample_id"]).upper()
ph = float(row["ph"])
ph_bucket = "acidic" if ph < NEUTRAL_PH else "neutral_or_basic"
routed: dict[str, list[dict[str, Any]]] = {
"samples": [
{
"sample_id": sample_id,
"ph": ph,
"ph_bucket": ph_bucket,
}
]
}
metrics = row.get("metrics") or []
if metrics:
routed["sample_measurements"] = [{"sample_id": sample_id, "metric": str(m)} for m in metrics]
return routed
ENTITY_SCHEMAS: dict[str, dict[str, Any]] = {
"samples": json.loads((SPEC_DIR / "06_table_transforms_and_output_schemas" / "schema.json").read_text("utf-8")),
}
TABLE_TRANSFORMS: dict[str, RecordTransformFn] = {
"samples": transform_sample,
}
OUTPUT_SCHEMAS: dict[str, pa.Schema] = {
"samples": SAMPLE_SUMMARY_SCHEMA,
"sample_measurements": SAMPLE_MEASUREMENT_SCHEMA,
}
Scalar Types, Nested Objects, Arrays, and Undeclared Extra Fields (spec/01_scalars_nested_types_and_extra_fields)¶
Rules: R-JSONL-ENTITY-REGISTRY, R-JSONL-SCHEMA-CONVERT, R-JSONL-COERCE
ENTITY_SCHEMAS["genomes"]mapsstring,integer,number,boolean, nullable["string", "null"],array(pa.list_), and nestedobject(pa.struct) properties toEXPECTED_SCHEMA.- Undeclared extra fields in input records (
extra_note) are dropped during PyArrow table coercion, and omitted optional fields (tags,assembly_stats) are populated withnull.
Schema (schema.json)¶
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "GenomeRecord",
"type": "object",
"required": ["genome_id", "contig_count", "gc_content", "is_complete"],
"properties": {
"genome_id": {
"type": "string"
},
"contig_count": {
"type": "integer"
},
"gc_content": {
"type": "number"
},
"is_complete": {
"type": "boolean"
},
"strain": {
"type": ["string", "null"]
},
"tags": {
"type": "array",
"items": {
"type": "string"
}
},
"assembly_stats": {
"type": "object",
"required": ["n50"],
"properties": {
"n50": {
"type": "integer"
},
"source_lab": {
"type": "string"
}
}
}
}
}
Input (data.jsonl)¶
{"genome_id": "GCF_0001", "contig_count": 1, "gc_content": 50.8, "is_complete": true, "strain": "K-12", "tags": ["reference", "gut"], "assembly_stats": {"n50": 4641652, "source_lab": "NCBI"}, "extra_note": "dropped by PyArrow schema"}
{"genome_id": "GCF_0002", "contig_count": 42, "gc_content": 38.2, "is_complete": false, "strain": null}
Converted rows: run default (entity_type=genomes)¶
[
{
"assembly_stats": {
"n50": 4641652,
"source_lab": "NCBI"
},
"contig_count": 1,
"gc_content": 50.8,
"genome_id": "GCF_0001",
"is_complete": true,
"strain": "K-12",
"tags": [
"reference",
"gut"
]
},
{
"assembly_stats": null,
"contig_count": 42,
"gc_content": 38.2,
"genome_id": "GCF_0002",
"is_complete": false,
"strain": null,
"tags": null
}
]
PyArrow schema (expected_schema.py)¶
"""Expected PyArrow schema for 01_scalars_nested_types_and_extra_fields."""
import pyarrow as pa
EXPECTED_SCHEMA = pa.schema(
[
pa.field("genome_id", pa.string(), nullable=False, metadata={"jsonschema_type": "string"}),
pa.field("contig_count", pa.int64(), nullable=False, metadata={"jsonschema_type": "integer"}),
pa.field("gc_content", pa.float64(), nullable=False, metadata={"jsonschema_type": "number"}),
pa.field("is_complete", pa.bool_(), nullable=False, metadata={"jsonschema_type": "boolean"}),
pa.field("strain", pa.string(), nullable=True, metadata={"jsonschema_type": "string"}),
pa.field(
"tags",
pa.list_(pa.field("item", pa.string(), nullable=False, metadata={"jsonschema_type": "string"})),
nullable=True,
),
pa.field(
"assembly_stats",
pa.struct(
[
pa.field("n50", pa.int64(), nullable=False, metadata={"jsonschema_type": "integer"}),
pa.field("source_lab", pa.string(), nullable=True, metadata={"jsonschema_type": "string"}),
]
),
nullable=True,
),
]
)
Local $defs / $ref Dereferencing, allOf Composition, and Format Validation (spec/02_defs_ref_allof_and_draft_dialects)¶
Rules: R-JSONL-SCHEMA-CONVERT, R-JSONL-DIALECT-FORMAT
schema.jsondeclares local$defsdefinitions (#/$defs/base_id,#/$defs/contact_info) and top-levelallOfcomposition.dereference_schemamerges theallOfbranches and resolves$refpointers beforeJSONSchemaToPyArrowbuildsEXPECTED_SCHEMA._make_resolve_errorvalidatessubmitted_date(format: "date") andcontact.email(format: "email") withFormatChecker(), whilecoerce_record_for_pyarrowparsessubmitted_dateintopa.date32()(datetime.date) and keepscontact.emailaspa.string().
Schema (schema.json)¶
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "SampleSubmission",
"$defs": {
"base_id": {
"type": "object",
"required": ["submission_id"],
"properties": {
"submission_id": {
"type": "string"
}
}
},
"contact_info": {
"type": "object",
"required": ["email"],
"properties": {
"email": {
"type": "string",
"format": "email"
},
"orcid": {
"type": "string"
}
}
}
},
"allOf": [
{
"$ref": "#/$defs/base_id"
},
{
"type": "object",
"required": ["submitted_date", "contact"],
"properties": {
"submitted_date": {
"type": "string",
"format": "date"
},
"contact": {
"$ref": "#/$defs/contact_info"
}
}
}
]
}
Input (data.jsonl)¶
{"submission_id": "SUB-01", "submitted_date": "2025-03-15", "contact": {"email": "pi@university.edu", "orcid": "0000-0001-2345-6789"}}
{"submission_id": "SUB-02", "submitted_date": "2025-03-16", "contact": {"email": "curator@institute.org"}}
Converted rows: run default (entity_type=submissions)¶
[
{
"contact": {
"email": "pi@university.edu",
"orcid": "0000-0001-2345-6789"
},
"submission_id": "SUB-01",
"submitted_date": "2025-03-15"
},
{
"contact": {
"email": "curator@institute.org",
"orcid": null
},
"submission_id": "SUB-02",
"submitted_date": "2025-03-16"
}
]
PyArrow schema (expected_schema.py)¶
"""Expected PyArrow schema for 02_defs_ref_allof_and_draft_dialects."""
import pyarrow as pa
EXPECTED_SCHEMA = pa.schema(
[
pa.field("submission_id", pa.string(), nullable=False, metadata={"jsonschema_type": "string"}),
pa.field("submitted_date", pa.date32(), nullable=False, metadata={"jsonschema_type": "string (date)"}),
pa.field(
"contact",
pa.struct(
[
pa.field("email", pa.string(), nullable=False, metadata={"jsonschema_type": "string (email)"}),
pa.field("orcid", pa.string(), nullable=True, metadata={"jsonschema_type": "string"}),
]
),
nullable=False,
),
]
)
Blank Lines, JSON Parse Errors, Schema Violations, and FormatChecker Rejections (spec/03_blank_lines_parse_errors_and_format_rejections)¶
Rules: R-JSONL-BLANK-AND-REJECT, R-JSONL-DIALECT-FORMAT, R-JSONL-REPORT
iter_file_jsonl_recordsskips blank/whitespace-only lines (line 2 ofdata.jsonl) while preserving 1-based physical line numbers (line_no).- Malformed JSON lines (line 3), non-object JSON arrays (line 4), type mismatches (line 5), and
format: "email"violations (line 6) are routed to_rejected(REJECTED_PYARROW_SCHEMA). - Run
total_rejectionverifies that when all lines in a file fail validation, only_rejectedis created.
Schema (schema.json)¶
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ContactRecord",
"type": "object",
"required": ["id", "count", "contact_email"],
"properties": {
"id": {
"type": "string"
},
"count": {
"type": "integer"
},
"contact_email": {
"type": "string",
"format": "email"
}
}
}
Input (data.jsonl)¶
{"id": "rec-1", "count": 5, "contact_email": "alice@example.org"}
{bad json
[1, 2, 3]
{"id": "rec-2", "count": "not-an-int", "contact_email": "bob@example.org"}
{"id": "rec-3", "count": 10, "contact_email": "not-an-email"}
{"id": "rec-4", "count": 12, "contact_email": "carol@example.org"}
Converted rows: run partial_rejections (entity_type=contacts)¶
[
{
"contact_email": "alice@example.org",
"count": 5,
"id": "rec-1"
},
{
"contact_email": "carol@example.org",
"count": 12,
"id": "rec-4"
}
]
Rejected rows (_rejected): run partial_rejections (entity_type=contacts)¶
[
{
"error_detail": "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
"line_no": 3,
"raw_record": "{bad json",
"source_file": "contacts/data.jsonl",
"table_name": "contacts"
},
{
"error_detail": "[\"[1, 2, 3] is not of type 'object'\"]",
"line_no": 4,
"raw_record": "[1, 2, 3]",
"source_file": "contacts/data.jsonl",
"table_name": "contacts"
},
{
"error_detail": "[\"'not-an-int' is not of type 'integer'\"]",
"line_no": 5,
"raw_record": "{\"id\": \"rec-2\", \"count\": \"not-an-int\", \"contact_email\": \"bob@example.org\"}",
"source_file": "contacts/data.jsonl",
"table_name": "contacts"
},
{
"error_detail": "[\"'not-an-email' is not a 'email'\"]",
"line_no": 6,
"raw_record": "{\"id\": \"rec-3\", \"count\": 10, \"contact_email\": \"not-an-email\"}",
"source_file": "contacts/data.jsonl",
"table_name": "contacts"
}
]
Input (data_all_invalid.jsonl)¶
Converted rows: run total_rejection (entity_type=contacts)¶
Rejected rows (_rejected): run total_rejection (entity_type=contacts)¶
[
{
"error_detail": "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
"line_no": 1,
"raw_record": "{unclosed",
"source_file": "contacts/data_all_invalid.jsonl",
"table_name": "contacts"
},
{
"error_detail": "[\"'bad' is not of type 'integer'\"]",
"line_no": 2,
"raw_record": "{\"id\": \"rec-9\", \"count\": \"bad\", \"contact_email\": \"valid@example.org\"}",
"source_file": "contacts/data_all_invalid.jsonl",
"table_name": "contacts"
}
]
PyArrow schema (expected_schema.py)¶
"""Expected PyArrow schema for 03_blank_lines_parse_errors_and_format_rejections."""
import pyarrow as pa
EXPECTED_SCHEMA = pa.schema(
[
pa.field("id", pa.string(), nullable=False, metadata={"jsonschema_type": "string"}),
pa.field("count", pa.int64(), nullable=False, metadata={"jsonschema_type": "integer"}),
pa.field("contact_email", pa.string(), nullable=False, metadata={"jsonschema_type": "string (email)"}),
]
)
Multi-Entity Subdirectory Discovery, Gzip Decompression, and Missing Entity Directories (spec/04_multi_entity_gzip_and_subdirs)¶
Rules: R-JSONL-MULTI-ENTITY-GLOB, R-JSONL-ENTITY-REGISTRY
ENTITY_SCHEMASregisters three entities:projects,experiments, andmissing_entity.data/containsprojects/part1.jsonl,projects/nested/part2.jsonl.gz(recursively matched byfile_glob="**/*.jsonl*"), andexperiments/exp1.jsonl, whilemissing_entity/is absent and skipped without error.
Schema (schema.json)¶
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ProjectRecord",
"type": "object",
"required": ["project_id", "name"],
"properties": {
"project_id": {
"type": "string"
},
"name": {
"type": "string"
}
}
}
Input (data/experiments/exp1.jsonl)¶
Input (data/projects/nested/part2.jsonl.gz)¶
Input (data/projects/part1.jsonl)¶
Converted rows: run default (entity_type=projects, file_glob=**/*.jsonl*, extra_entities={'experiments': 'experiment_schema.json', 'missing_entity': 'schema.json'})¶
[
{
"name": "Marine Virome",
"project_id": "PRJ-2"
},
{
"name": "Soil Metagenome",
"project_id": "PRJ-1"
}
]
Converted rows (experiments): run default (entity_type=projects, file_glob=**/*.jsonl*, extra_entities={'experiments': 'experiment_schema.json', 'missing_entity': 'schema.json'})¶
PyArrow schema (expected_schema.py)¶
"""Expected PyArrow schemas for 04_multi_entity_gzip_and_subdirs."""
import pyarrow as pa
PROJECTS_SCHEMA = pa.schema(
[
pa.field("project_id", pa.string(), nullable=False, metadata={"jsonschema_type": "string"}),
pa.field("name", pa.string(), nullable=False, metadata={"jsonschema_type": "string"}),
]
)
EXPERIMENTS_SCHEMA = pa.schema(
[
pa.field("exp_id", pa.string(), nullable=False, metadata={"jsonschema_type": "string"}),
pa.field("project_id", pa.string(), nullable=False, metadata={"jsonschema_type": "string"}),
pa.field("reads", pa.int64(), nullable=False, metadata={"jsonschema_type": "integer"}),
]
)
EXPECTED_SCHEMA = PROJECTS_SCHEMA
EXPECTED_SCHEMAS = {
"projects": PROJECTS_SCHEMA,
"experiments": EXPERIMENTS_SCHEMA,
"missing_entity": PROJECTS_SCHEMA,
}
MD5 Sidecar Pre-processing and File-Level _rejected Routing (spec/05_md5_sidecar_preprocessing)¶
Rules: R-JSONL-PREPROCESS-MD5
preprocessors_module="specs.pipelines.jsonlines_jsonschema.transforms"registersvalidate_md5_sidecar.- Run
partial_md5_failureingestsarticles/good.jsonland rejectsarticles/bad.jsonldue to a digest mismatch inarticles/bad.jsonl.md5, emitting a file-level row (line_no=None,raw_record=None) to_rejected. - Run
total_md5_failurerejectsarticles/malformed.jsonldue to invalid sidecar syntax inarticles/malformed.jsonl.md5, leavingarticlesuncreated.
Schema (schema.json)¶
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "ArticleRecord",
"type": "object",
"required": ["pmid", "title"],
"properties": {
"pmid": {
"type": "integer"
},
"title": {
"type": "string"
}
}
}
Input (data_partial/articles/bad.jsonl)¶
Input (data_partial/articles/bad.jsonl.md5)¶
Input (data_partial/articles/good.jsonl)¶
Input (data_partial/articles/good.jsonl.md5)¶
Converted rows: run partial_md5_failure (entity_type=articles, file_glob=*.jsonl, preprocessors_module=specs.pipelines.jsonlines_jsonschema.transforms)¶
Rejected rows (_rejected): run partial_md5_failure (entity_type=articles, file_glob=*.jsonl, preprocessors_module=specs.pipelines.jsonlines_jsonschema.transforms)¶
[
{
"error_detail": "MD5 mismatch for articles/bad.jsonl: expected 00000000000000000000000000000000, got d7400ffc422f33a13f8edec2834cf6e1",
"line_no": null,
"raw_record": null,
"source_file": "articles/bad.jsonl",
"table_name": "articles"
}
]
Input (data_total_failure/articles/malformed.jsonl)¶
Input (data_total_failure/articles/malformed.jsonl.md5)¶
Converted rows: run total_md5_failure (entity_type=articles, file_glob=*.jsonl, preprocessors_module=specs.pipelines.jsonlines_jsonschema.transforms)¶
Rejected rows (_rejected): run total_md5_failure (entity_type=articles, file_glob=*.jsonl, preprocessors_module=specs.pipelines.jsonlines_jsonschema.transforms)¶
[
{
"error_detail": "Invalid MD5 sidecar content for articles/malformed.jsonl: 'not-a-valid-md5-line'",
"line_no": null,
"raw_record": null,
"source_file": "articles/malformed.jsonl",
"table_name": "articles"
}
]
PyArrow schema (expected_schema.py)¶
"""Expected PyArrow schema for 05_md5_sidecar_preprocessing."""
import pyarrow as pa
EXPECTED_SCHEMA = pa.schema(
[
pa.field("pmid", pa.int64(), nullable=False, metadata={"jsonschema_type": "integer"}),
pa.field("title", pa.string(), nullable=False, metadata={"jsonschema_type": "string"}),
]
)
Table Transforms, Schema Conversion Skip, and Multi-Table Fan-Out (spec/06_table_transforms_and_output_schemas)¶
Rules: R-JSONL-SCHEMA-SKIP, R-JSONL-TRANSFORMS, R-JSONL-REPORT
transforms_module="specs.pipelines.jsonlines_jsonschema.transforms"definesTABLE_TRANSFORMS["samples"] = transform_sampleandOUTPUT_SCHEMASforsamplesandsample_measurements.build_output_schema_registryskipsconvert_schema_to_pyarrowforsamples(allowing input-onlyanyOffields such asraw_payloadinschema.jsonto validate input lines without raisingE5).transform_sampledropsexclude=Truerows, uppercasessample_id, computesph_bucket, and fans outmetricstosample_measurements.
Schema (schema.json)¶
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "SampleRecord",
"type": "object",
"required": ["sample_id", "ph"],
"properties": {
"sample_id": {
"type": "string"
},
"ph": {
"type": "number"
},
"exclude": {
"type": "boolean"
},
"metrics": {
"type": "array",
"items": {
"type": "string"
}
},
"raw_payload": {
"anyOf": [
{"type": "string"},
{"type": "integer"}
]
}
}
}
Input (data.jsonl)¶
{"sample_id": "smp-1", "ph": 6.4, "metrics": ["moisture", "salinity"], "raw_payload": "note"}
{"sample_id": "smp-2", "ph": 7.1, "exclude": true, "metrics": ["ignored"], "raw_payload": 42}
{"sample_id": "smp-3", "ph": 7.8}
Converted rows: run default (entity_type=samples, entity_schemas=specs.pipelines.jsonlines_jsonschema.transforms, transforms_module=specs.pipelines.jsonlines_jsonschema.transforms, output_schemas_module=specs.pipelines.jsonlines_jsonschema.transforms)¶
[
{
"ph": 6.4,
"ph_bucket": "acidic",
"sample_id": "SMP-1"
},
{
"ph": 7.8,
"ph_bucket": "neutral_or_basic",
"sample_id": "SMP-3"
}
]
Converted rows (sample_measurements): run default (entity_type=samples, entity_schemas=specs.pipelines.jsonlines_jsonschema.transforms, transforms_module=specs.pipelines.jsonlines_jsonschema.transforms, output_schemas_module=specs.pipelines.jsonlines_jsonschema.transforms)¶
PyArrow schema (expected_schema.py)¶
"""Expected PyArrow schemas for 06_table_transforms_and_output_schemas."""
import pyarrow as pa
SAMPLE_SUMMARY_SCHEMA = pa.schema(
[
pa.field("sample_id", pa.string(), nullable=False),
pa.field("ph", pa.float64(), nullable=False),
pa.field("ph_bucket", pa.string(), nullable=False),
]
)
SAMPLE_MEASUREMENT_SCHEMA = pa.schema(
[
pa.field("sample_id", pa.string(), nullable=False),
pa.field("metric", pa.string(), nullable=False),
]
)
EXPECTED_SCHEMA = SAMPLE_SUMMARY_SCHEMA
EXPECTED_SCHEMAS = {
"samples": SAMPLE_SUMMARY_SCHEMA,
"sample_measurements": SAMPLE_MEASUREMENT_SCHEMA,
}
4. Preset Subclass Specification Cases (presets/)¶
Valid Records and Empty Annotation Report Filtering (presets/ncbi_datasets/01_valid_and_filtered)¶
Suite: presets/ncbi_datasets · Target CLI: ncbi_datasets · Settings Class: NcbiDatasetsSettings
Directory: specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/01_valid_and_filtered/
Rules: R-JSONL-ENTITY-REGISTRY, R-JSONL-TRANSFORMS, R-JSONL-REPORT, R-NCBI-PRESET, R-NCBI-FILTER-EMPTY
Loads annotation_report/annotations.jsonl (one empty response {"total_count": 0} dropped by
filter_annotation_report, and one populated annotation report retained) alongside
dataset_report/dataset_reports.jsonl (one assembly dataset report).
Settings (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/01_valid_and_filtered/settings.toml)
Input annotation_report/annotations.jsonl (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/01_valid_and_filtered/input/annotation_report/annotations.jsonl)
{"total_count":0}
{"reports":[{"annotation":{"annotations":[{"assembly_accession":"GCA_000234645.4"}],"gene_type":"protein-coding","genomic_regions":[{"gene_range":{"accession_version":"JF734911.1","range":[{"begin":"562","end":"1713","orientation":"plus"}]}}],"name":"integrase/recombinase","proteins":[{"accession_version":"AET85156.1","length":383,"name":"integrase/recombinase"}],"symbol":"int"},"row_id":"1"}],"total_count":1}
Input dataset_report/dataset_reports.jsonl (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/01_valid_and_filtered/input/dataset_report/dataset_reports.jsonl)
{"accession":"GCA_018350175.1","current_accession":"GCA_018350175.1","paired_accession":"GCF_018350175.1","source_database":"SOURCE_DATABASE_GENBANK","organism":{"tax_id":9685,"organism_name":"Felis catus","common_name":"domestic cat","infraspecific_names":{"isolate":"Fca126","sex":"female"}},"assembly_info":{"assembly_level":"Chromosome","assembly_status":"current","assembly_name":"F.catus_Fca126_mat1.0","assembly_type":"haploid","bioproject_accession":"PRJNA684600","release_date":"2021-05-13","submitter":"Texas A&M University","refseq_category":"reference genome","sequencing_tech":"PacBio Sequel 2","assembly_method":"NextDenovo v. 2.2-beta.0"},"assembly_stats":{"total_number_of_chromosomes":19,"total_sequence_length":"2425730029","number_of_contigs":109,"contig_n50":90731473,"contig_l50":10,"gc_percent":42.0},"wgs_info":{"wgs_project_accession":"JAFEKA01"}}
Expected report.txt Summary & Row Counts ({'annotation_report': 1, 'dataset_report': 1})
status: completed
ingested_files:
- annotation_report/annotations.jsonl
- dataset_report/dataset_reports.jsonl
load_errors: []
pipeline_load_info_errs: null
Expected PyArrow Tables (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/01_valid_and_filtered/expected_tables.py — EXPECTED_TABLES)
"""Expected PyArrow tables for ncbi_datasets 01_valid_and_filtered."""
import pyarrow as pa
from cdm_data_loaders.schemas.ncbi_datasets.schema import (
ANNOTATION_REPORT_SCHEMA,
DATASET_REPORT_SCHEMA,
)
EXPECTED_TABLES: dict[str, pa.Table] = {
"annotation_report": pa.Table.from_pylist(
[
{
"reports": [
{
"annotation": {
"annotations": [{"assembly_accession": "GCA_000234645.4"}],
"gene_type": "protein-coding",
"genomic_regions": [
{
"gene_range": {
"accession_version": "JF734911.1",
"range": [{"begin": "562", "end": "1713", "orientation": "plus"}],
}
}
],
"name": "integrase/recombinase",
"proteins": [
{
"accession_version": "AET85156.1",
"length": 383,
"name": "integrase/recombinase",
}
],
"symbol": "int",
},
"row_id": "1",
}
],
"total_count": 1,
}
],
schema=ANNOTATION_REPORT_SCHEMA,
),
"dataset_report": pa.Table.from_pylist(
[
{
"accession": "GCA_018350175.1",
"current_accession": "GCA_018350175.1",
"paired_accession": "GCF_018350175.1",
"source_database": "SOURCE_DATABASE_GENBANK",
"organism": {
"tax_id": 9685,
"organism_name": "Felis catus",
"common_name": "domestic cat",
"infraspecific_names": {"isolate": "Fca126", "sex": "female"},
},
"assembly_info": {
"assembly_level": "Chromosome",
"assembly_status": "current",
"assembly_name": "F.catus_Fca126_mat1.0",
"assembly_type": "haploid",
"bioproject_accession": "PRJNA684600",
"release_date": "2021-05-13",
"submitter": "Texas A&M University",
"refseq_category": "reference genome",
"sequencing_tech": "PacBio Sequel 2",
"assembly_method": "NextDenovo v. 2.2-beta.0",
},
"assembly_stats": {
"total_number_of_chromosomes": 19,
"total_sequence_length": "2425730029",
"number_of_contigs": 109,
"contig_n50": 90731473,
"contig_l50": 10,
"gc_percent": 42.0,
},
"wgs_info": {"wgs_project_accession": "JAFEKA01"},
}
],
schema=DATASET_REPORT_SCHEMA,
),
}
Expected Canonical Table Rows (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/01_valid_and_filtered/expected.json)
{
"annotation_report": [
{
"reports": [
{
"annotation": {
"annotations": [
{
"assembly_accession": "GCA_000234645.4"
}
],
"gene_type": "protein-coding",
"genomic_regions": [
{
"gene_range": {
"accession_version": "JF734911.1",
"range": [
{
"begin": "562",
"end": "1713",
"orientation": "plus"
}
]
}
}
],
"name": "integrase/recombinase",
"proteins": [
{
"accession_version": "AET85156.1",
"length": 383,
"name": "integrase/recombinase"
}
],
"symbol": "int"
},
"row_id": "1"
}
],
"total_count": 1
}
],
"dataset_report": [
{
"accession": "GCA_018350175.1",
"assembly_info": {
"assembly_level": "Chromosome",
"assembly_method": "NextDenovo v. 2.2-beta.0",
"assembly_name": "F.catus_Fca126_mat1.0",
"assembly_status": "current",
"assembly_type": "haploid",
"bioproject_accession": "PRJNA684600",
"refseq_category": "reference genome",
"release_date": "2021-05-13",
"sequencing_tech": "PacBio Sequel 2",
"submitter": "Texas A&M University"
},
"assembly_stats": {
"contig_l50": 10,
"contig_n50": 90731473,
"gc_percent": 42.0,
"number_of_contigs": 109,
"total_number_of_chromosomes": 19,
"total_sequence_length": "2425730029"
},
"current_accession": "GCA_018350175.1",
"organism": {
"common_name": "domestic cat",
"infraspecific_names": {
"isolate": "Fca126",
"sex": "female"
},
"organism_name": "Felis catus",
"tax_id": 9685
},
"paired_accession": "GCF_018350175.1",
"source_database": "SOURCE_DATABASE_GENBANK",
"wgs_info": {
"wgs_project_accession": "JAFEKA01"
}
}
]
}
Malformed JSON and Schema-Invalid Record Rejection (_rejected) (presets/ncbi_datasets/02_rejected_records)¶
Suite: presets/ncbi_datasets · Target CLI: ncbi_datasets · Settings Class: NcbiDatasetsSettings
Directory: specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/02_rejected_records/
Rules: R-JSONL-ENTITY-REGISTRY, R-JSONL-BLANK-AND-REJECT, R-JSONL-REPORT, R-NCBI-PRESET, R-NCBI-FILTER-EMPTY
Mixed valid, filtered, schema-invalid, and malformed JSON lines across annotation_report/batch.jsonl
and dataset_report/batch.jsonl. Valid records load into their target tables, empty reports rows are
discarded, and invalid lines route to _rejected with line-level provenance.
Settings (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/02_rejected_records/settings.toml)
Input annotation_report/batch.jsonl (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/02_rejected_records/input/annotation_report/batch.jsonl)
{"total_count":0}
{"reports":[{"annotation":{"name":"integrase/recombinase","symbol":"int"},"row_id":"1"}],"total_count":1}
{"total_count":"not-an-int"}
Input dataset_report/batch.jsonl (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/02_rejected_records/input/dataset_report/batch.jsonl)
Expected report.txt Summary & Row Counts ({'annotation_report': 1, 'dataset_report': 1, '_rejected': 2})
status: completed
ingested_files:
- annotation_report/batch.jsonl
- dataset_report/batch.jsonl
load_errors: []
pipeline_load_info_errs: null
Expected PyArrow Tables (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/02_rejected_records/expected_tables.py — EXPECTED_TABLES)
"""Expected PyArrow tables for ncbi_datasets 02_rejected_records."""
import pyarrow as pa
from cdm_data_loaders.schemas.ncbi_datasets.schema import (
ANNOTATION_REPORT_SCHEMA,
DATASET_REPORT_SCHEMA,
)
from cdm_data_loaders.utils.buffer import REJECTED_PYARROW_SCHEMA
EXPECTED_TABLES: dict[str, pa.Table] = {
"annotation_report": pa.Table.from_pylist(
[
{
"reports": [
{
"annotation": {"name": "integrase/recombinase", "symbol": "int"},
"row_id": "1",
}
],
"total_count": 1,
}
],
schema=ANNOTATION_REPORT_SCHEMA,
),
"dataset_report": pa.Table.from_pylist(
[
{
"accession": "GCF_000005845.2",
"source_database": "SOURCE_DATABASE_REFSEQ",
}
],
schema=DATASET_REPORT_SCHEMA,
),
"_rejected": pa.Table.from_pylist(
[
{
"table_name": "annotation_report",
"source_file": "annotation_report/batch.jsonl",
"line_no": 3,
"raw_record": '{"total_count":"not-an-int"}',
"error_detail": "[\"'not-an-int' is not of type 'integer'\"]",
},
{
"table_name": "dataset_report",
"source_file": "dataset_report/batch.jsonl",
"line_no": 2,
"raw_record": "{malformed json",
"error_detail": "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
},
],
schema=REJECTED_PYARROW_SCHEMA,
),
}
Expected Canonical Table Rows (specs/pipelines/jsonlines_jsonschema/presets/ncbi_datasets/02_rejected_records/expected.json)
{
"_rejected": [
{
"error_detail": "[\"'not-an-int' is not of type 'integer'\"]",
"line_no": 3,
"raw_record": "{\"total_count\":\"not-an-int\"}",
"source_file": "annotation_report/batch.jsonl",
"table_name": "annotation_report"
},
{
"error_detail": "Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
"line_no": 2,
"raw_record": "{malformed json",
"source_file": "dataset_report/batch.jsonl",
"table_name": "dataset_report"
}
],
"annotation_report": [
{
"reports": [
{
"annotation": {
"name": "integrase/recombinase",
"symbol": "int"
},
"row_id": "1"
}
],
"total_count": 1
}
],
"dataset_report": [
{
"accession": "GCF_000005845.2",
"source_database": "SOURCE_DATABASE_REFSEQ"
}
]
}
5. Errors (Unsupported / Invalid Input)¶
Schema generation raises a subclass of JsonlJsonschemaError (itself a RuntimeError).
| Code | Exception | Condition | Reason | Fixtures |
|---|---|---|---|---|
| E1 | InvalidEntitySchemasRegistryError |
Invalid or empty ENTITY_SCHEMAS registry | The entity_schemas module must define a non-empty ENTITY_SCHEMAS dictionary and cannot use the reserved _rejected table name. |
01_empty_entity_schemas.json: ENTITY_SCHEMAS is an empty dictionary.02_reserved_rejected_table_name.json: ENTITY_SCHEMAS uses the reserved _rejected table name. |
| E2 | UnsupportedJsonlSchemaDialectError |
Missing or unsupported JSON Schema $schema dialect | Every entity JSON Schema must declare a supported $schema draft URI (Draft 4, 6, 7, 2019-09, 2020-12, or KBase xsv_config). |
03_missing_schema_keyword.json: Entity JSON Schema omits the required $schema keyword.04_unsupported_schema_draft.json: Entity JSON Schema declares an unrecognized $schema URI. |
| E3 | InvalidJsonlEntitySchemaError |
Entity JSON Schema fails draft metaschema validation | Every entity JSON Schema must be a valid schema document under its declared $schema metaschema. |
05_invalid_property_type_keyword.json: Property schema uses an invalid type value rejected by the Draft 2020-12 metaschema.06_non_list_required_keyword.json: required is a string instead of an array of property names. |
| E4 | UnresolvableJsonlSchemaRefError |
Unresolvable or circular $ref in entity JSON Schema | All $ref pointers and allOf branches in an entity JSON Schema must resolve without cycles before PyArrow conversion. |
07_dangling_local_ref.json: Local $ref pointer targets a non-existent definition in $defs.08_circular_ref.json: Self-referential $ref cycle in $defs fails dereference_schema. |
| E5 | UnsupportedJsonlArrowSchemaError |
Entity JSON Schema cannot be converted to a PyArrow table schema | Each non-transformed, non-overridden entity JSON Schema must have type: "object" with at least one property and only supported PyArrow column constructs. |
09_non_object_root_type.json: Root entity schema has type: "array" instead of type: "object".10_unsupported_anyof_union.json: Incompatible anyOf union branches (array and string) cannot be converted to a PyArrow column type without a table transform or OUTPUT_SCHEMAS override. |
6. Fixture Index¶
Every case directory contains a case.yaml manifest; the test suites and this document are both driven by it.
| Case | Target CLI | Manifest | Runs |
|---|---|---|---|
spec/01_scalars_nested_types_and_extra_fields |
jsonlines_jsonschema |
case.yaml |
default |
spec/02_defs_ref_allof_and_draft_dialects |
jsonlines_jsonschema |
case.yaml |
default |
spec/03_blank_lines_parse_errors_and_format_rejections |
jsonlines_jsonschema |
case.yaml |
partial_rejections, total_rejection |
spec/04_multi_entity_gzip_and_subdirs |
jsonlines_jsonschema |
case.yaml |
default |
spec/05_md5_sidecar_preprocessing |
jsonlines_jsonschema |
case.yaml |
partial_md5_failure, total_md5_failure |
spec/06_table_transforms_and_output_schemas |
jsonlines_jsonschema |
case.yaml |
default |
presets/ncbi_datasets/01_valid_and_filtered |
ncbi_datasets |
case.yaml |
default |
presets/ncbi_datasets/02_rejected_records |
ncbi_datasets |
case.yaml |
mixed_valid_and_invalid |