PyArrow to LinkML Specification¶
This document specifies how a PyArrow pa.Schema determines:
- The LinkML
SchemaDefinition(and YAML schema string) produced bygenerate_linkml_schema,generate_linkml_yaml, andPyArrowToLinkML, includinglinkml:typesscalar ranges, integer bit-width bounds (minimum_value/maximum_value), fixed-size list cardinalities, synthesized nestedClassDefinitions for Arrowstructandmapcolumns, and collision-free identifier sanitization. - The serialized JSON-compatible row dictionaries produced by
serialize_table_for_linkmlandserialize_record_for_linkml, ready for LinkML instance validation. - The errors raised for Arrow schemas or data types that have no faithful LinkML representation.
Contents: 1. Conversion Rules · 2. Cases · 3. Errors · 4. Fixture Index
1. Conversion Rules¶
1.1 Top-Level Schema Resolution and Envelope¶
- R-SCHEMA-INPUT: The input to
generate_linkml_schemaandgenerate_linkml_yamlmust be a concretepyarrow.Schemainstance; passing a barepa.DataType,pa.Field,pa.Table, or Python mapping raises E1.
Cases: 01_integers_floats_and_booleans, checkm2_quality_report
- R-NON-EMPTY-ROOT: The root
pa.Schemamust contain at least one field; an emptypa.schema([])raises E2.
Cases: 01_integers_floats_and_booleans
- R-SCHEMA-IDENTITY: The candidate name resolves from explicit
schema_namewhen provided, otherwise from non-empty UTF-8schema.metadata[b"title"].SchemaDefinition.nameis the sanitized identifier of that candidate name (defaulting to"schema"when absent), and the rootClassDefinition.nameis the sanitized identifier of that candidate name (defaulting to"Root"when absent).SchemaDefinition.idresolves from explicitschema_idwhen provided, otherwise from non-empty UTF-8schema.metadata[b"$id"], defaulting to"urn:linkml:<SchemaDefinition.name>".
Cases: 01_integers_floats_and_booleans, 04_nullability_and_descriptions, 08_identifier_sanitization_and_collisions, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-IMPORTS-PREFIXES: Every standalone
SchemaDefinitionimports"linkml:types", setsdefault_prefixtoSchemaDefinition.name, and declares two prefixes:<SchemaDefinition.name>: "<id>#"andlinkml: "https://w3id.org/linkml/". Whendestinationis supplied togenerate_linkml_schema, generated classes are appended intodestination.classeswithout overwriting prior definitions.
Cases: 01_integers_floats_and_booleans, 04_nullability_and_descriptions, 08_identifier_sanitization_and_collisions, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
1.2 Slot Naming, Identifier Sanitization, and Collision Disambiguation¶
- R-SLOT-NAME: Every
pa.Field.namemust contain at least one non-whitespace character and is preserved verbatim as itsSlotDefinitionkey underClassDefinition.attributes. An empty or whitespace-only field name (""or" ") raises E9.
Cases: 01_integers_floats_and_booleans, 04_nullability_and_descriptions, checkm2_quality_report
- R-SLOT-ORDER:
ClassDefinition.attributespreserves the exact field declaration order of the sourcepa.Schemaorpa.StructType.
Cases: 01_integers_floats_and_booleans, 04_nullability_and_descriptions, checkm2_quality_report
- R-UNIQUE-FIELD-NAMES: Sibling fields within a
pa.Schemaorpa.StructTypemust have distinct names; duplicate sibling field names raise E3.
Cases: 01_integers_floats_and_booleans
- R-CLASS-IDENTIFIER: LinkML schema and class identifiers replace characters outside
[A-Za-z0-9_]with"_", strip leading and trailing"_", fall back to"schema"when no identifier characters remain, and prepend"_"when the first character is an ASCII digit (0..9).
Cases: 08_identifier_sanitization_and_collisions
- R-CLASS-COLLISION: Derived nested struct class names (
<OwnerClass>_<field_name>) and map entry class names (<OwnerClass>_<field_name>_map) are deduplicated across theSchemaDefinitionby appending_2,_3, ... whenever two sanitized class names collide.
Cases: 08_identifier_sanitization_and_collisions
1.3 Structural Container Shapes¶
- R-SHAPE-STRUCT-CLASS: A nested
pa.struct([...])with at least one child field emits aClassDefinitionnamed<OwnerClass>_<field_name>(sanitized and deduplicated) and sets the parent slot'srangeto that class name. An empty nestedpa.struct([])raises E2.
Cases: 05_nested_structs_and_lists, 06_maps_and_entry_classes, 08_identifier_sanitization_and_collisions, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-SHAPE-LIST-SLOT: Variable-length Arrow list types (
pa.list_,pa.large_list,pa.list_view,pa.large_list_view) emit a singleSlotDefinitionwithmultivalued: trueand therangeof the inner element type (omitting scalarminimum_value/maximum_valueso LinkML's Pydantic codegen does not apply numeric bounds to the outer list container), generating an elementClassDefinitionwhen the element is apa.struct.
Cases: 05_nested_structs_and_lists, 07_dictionaries_and_views, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-SHAPE-FIXED-LIST-CARDINALITY: Fixed-length
pa.list_(item, list_size)(pa.FixedSizeListType) emits a multivaluedSlotDefinition(multivalued: true) withminimum_cardinality: list_sizeandmaximum_cardinality: list_size.
Cases: 05_nested_structs_and_lists, uniref_cluster_entry
- R-SHAPE-MAP-CLASS:
pa.map_(key_type, item_type)emits an entryClassDefinitionnamed<OwnerClass>_<field_name>_mapwith attributeskeyandvalue, and sets the map slot torange: <OwnerClass>_<field_name>_mapandmultivalued: true.
Cases: 06_maps_and_entry_classes, 08_identifier_sanitization_and_collisions, checkm2_quality_report, uniref_cluster_entry
- R-SHAPE-DICTIONARY:
pa.dictionary(index_type, value_type, ordered=...)unwraps to the LinkMLrangeand integer bounds ofvalue_type.
Cases: 07_dictionaries_and_views, checkm2_quality_report, ncbi_assembly_summary
- R-REJECT-NESTED-MULTIVALUED: Because LinkML represents list multiplicity via a flat
multivalued: trueflag on a slot rather than nested array containers, directly nested lists (list<list<...>>) and lists of maps (list<map<...>>) raise E10. Nested collections wrapped inside an intermediatepa.struct(list<struct<...>>) are supported via the struct'sClassDefinition.
Cases: 05_nested_structs_and_lists
1.4 Requiredness and List Element Constraints¶
- R-NULL-REQUIRED: A field with
pa.Field.nullable == Falsesetsrequired: trueon itsSlotDefinition.
Cases: 01_integers_floats_and_booleans, 02_strings_binary_and_decimals, 03_temporal_types, 04_nullability_and_descriptions, 05_nested_structs_and_lists, 06_maps_and_entry_classes, 07_dictionaries_and_views, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-NULL-OPTIONAL: A field with
pa.Field.nullable == Trueomitsrequired(leaving the LinkML slot optional). Standalonepa.null()columns or element types have no value domain and raise E4.
Cases: 02_strings_binary_and_decimals, 03_temporal_types, 04_nullability_and_descriptions, 05_nested_structs_and_lists, 06_maps_and_entry_classes, 07_dictionaries_and_views, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-NULL-MAP-VALUE: Inside a generated
<OwnerClass>_<field_name>_mapentry class,keyalways carriesrequired: true;valuecarriesrequired: truewhenMapType.item_field.nullable == Falseand omitsrequiredwhenMapType.item_field.nullable == True.
Cases: 06_maps_and_entry_classes, checkm2_quality_report, uniref_cluster_entry
- R-LIST-NON-NULL-ITEMS: LinkML multivalued slots represent lists of non-null domain values:
serialize_table_for_linkmlandserialize_record_for_linkmlrejectnullelements inside populated list values withValueErrorwhile permittingnull(None) on optional outer list slots.
Cases: 05_nested_structs_and_lists
1.5 Scalar Ranges and Integer Bounds¶
- R-SCALAR-BOOL:
pa.bool_()maps torange: boolean.
Cases: 01_integers_floats_and_booleans, ncbi_assembly_summary
- R-SCALAR-SIGNED-INT: Single-valued signed integer slots (
int8,int16,int32,int64) map torange: integerwithminimum_value: -2^(w-1)andmaximum_value: 2^(w-1) - 1for bit widthw(multivalued integer list slots omitminimum_value/maximum_value; see R-SHAPE-LIST-SLOT).
Cases: 01_integers_floats_and_booleans, 06_maps_and_entry_classes, checkm2_quality_report, ncbi_assembly_summary
- R-SCALAR-UNSIGNED-INT: Single-valued unsigned integer slots (
uint8,uint16,uint32,uint64) map torange: integerwithminimum_value: 0andmaximum_value: 2^w - 1for bit widthw(multivalued integer list slots omitminimum_value/maximum_value; see R-SHAPE-LIST-SLOT).
Cases: 01_integers_floats_and_booleans, 07_dictionaries_and_views, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-SCALAR-FLOAT: IEEE-754 floating-point types (
float16,float32,float64) map torange: float.
Cases: 01_integers_floats_and_booleans, checkm2_quality_report, uniref_cluster_entry
- R-SCALAR-DECIMAL:
pa.decimal128(p, s)andpa.decimal256(p, s)with0 <= s <= pmap torange: decimal. Decimals withs < 0ors > praise E5.
Cases: 02_strings_binary_and_decimals, 07_dictionaries_and_views, checkm2_quality_report, ncbi_assembly_summary
- R-SCALAR-STRING-BINARY: UTF-8 string types (
string,large_string,string_view) and Base64-serialized binary types (binary,large_binary,binary_view,fixed_size_binary(n)) map torange: string.
Cases: 02_strings_binary_and_decimals, 07_dictionaries_and_views, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-SCALAR-TEMPORAL:
date32anddate64map torange: date;time32andtime64map torange: time; both timezone-aware and timezone-naivepa.timestamptypes map torange: datetime. Relative durations and calendar intervals (pa.duration,pa.month_day_nano_interval) raise E6; Arrow unions raise E7;run_end_encodedandBaseExtensionTypetypes (pa.uuid(), customExtensionType) raise E8.
Cases: 03_temporal_types, ncbi_assembly_summary, uniref_cluster_entry
PyArrow DataType |
Serialized JSON value | LinkML range |
Slot bounds (minimum_value .. maximum_value) |
|---|---|---|---|
pa.bool_() |
bool |
boolean |
— |
pa.int8() |
int |
integer |
-128 .. 127 |
pa.int16() |
int |
integer |
-32768 .. 32767 |
pa.int32() |
int |
integer |
-2147483648 .. 2147483647 |
pa.int64() |
int |
integer |
-9223372036854775808 .. 9223372036854775807 |
pa.uint8() |
int |
integer |
0 .. 255 |
pa.uint16() |
int |
integer |
0 .. 65535 |
pa.uint32() |
int |
integer |
0 .. 4294967295 |
pa.uint64() |
int |
integer |
0 .. 18446744073709551615 |
pa.float16() |
float |
float |
— |
pa.float32() |
float |
float |
— |
pa.float64() |
float |
float |
— |
pa.decimal128(10, 2) |
str (fixed-point decimal) |
decimal |
— |
pa.decimal128(2, 2) |
str (fixed-point decimal) |
decimal |
— |
pa.decimal128(10, 0) |
str (fixed-point integer decimal) |
decimal |
— |
pa.decimal256(50, 10) |
str (fixed-point decimal) |
decimal |
— |
pa.string() |
str |
string |
— |
pa.large_string() |
str |
string |
— |
pa.string_view() |
str |
string |
— |
pa.binary() |
str (Base64) |
string |
— |
pa.large_binary() |
str (Base64) |
string |
— |
pa.binary_view() |
str (Base64) |
string |
— |
pa.binary(16) |
str (Base64) |
string |
— |
pa.date32() |
str (ISO-8601 date) |
date |
— |
pa.date64() |
str (ISO-8601 date) |
date |
— |
pa.time32("s") |
str (ISO-8601 time) |
time |
— |
pa.time32("ms") |
str (ISO-8601 time) |
time |
— |
pa.time64("us") |
str (ISO-8601 time) |
time |
— |
pa.time64("ns") |
str (ISO-8601 time) |
time |
— |
pa.timestamp("s", tz="UTC") |
str (ISO-8601 date-time) |
datetime |
— |
pa.timestamp("us", tz="UTC") |
str (ISO-8601 date-time) |
datetime |
— |
pa.timestamp("ns", tz="America/Los_Angeles") |
str (ISO-8601 date-time) |
datetime |
— |
pa.timestamp("us") |
str (naive ISO-8601 timestamp) |
datetime |
— |
pa.timestamp("ns") |
str (naive ISO-8601 timestamp) |
datetime |
— |
pa.dictionary(pa.int16(), pa.string()) |
str |
string |
— |
pa.dictionary(pa.int8(), pa.int64(), ordered=True) |
int |
integer |
-9223372036854775808 .. 9223372036854775807 |
1.6 Schema and Slot Descriptions¶
- R-META-SCHEMA-DESC: Non-empty UTF-8
schema.metadata[b"comment"]on apa.Schemais emitted asSchemaDefinition.description(and as the rootClassDefinition.descriptionwhen appended into an existing schema viadestination).
Cases: 04_nullability_and_descriptions, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-META-SLOT-DESC: Non-empty UTF-8
pa.Field.metadata[b"comment"]on a root or struct field is emitted asSlotDefinition.description(falling back to inner list elementcommentwhen the outer field has nocomment).
Cases: 04_nullability_and_descriptions, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
1.7 Arrow Table and Record Serialization¶
- R-SER-ROW-ORDER:
serialize_table_for_linkmlandserialize_record_for_linkmlemit one JSON-compatible dictionary per row with keys inpa.Schemadeclaration order; null Arrow scalars on optional slots serialize asnull(None), structs serialize as nested dicts, lists serialize as JSON arrays, and dictionary scalars unwrap to their decoded value.
Cases: 01_integers_floats_and_booleans, 02_strings_binary_and_decimals, 03_temporal_types, 04_nullability_and_descriptions, 05_nested_structs_and_lists, 06_maps_and_entry_classes, 07_dictionaries_and_views, 08_identifier_sanitization_and_collisions, checkm2_quality_report, ncbi_assembly_summary, uniref_cluster_entry
- R-SER-DECIMAL-STR:
decimal128anddecimal256scalars serialize via plain fixed-point formatting (format(value, "f"), never scientific notation) matching LinkML'sdecimalscalar type.
Cases: 02_strings_binary_and_decimals, 07_dictionaries_and_views, checkm2_quality_report, ncbi_assembly_summary
- R-SER-BASE64:
binary,large_binary,binary_view, andfixed_size_binary(n)scalars serialize to ASCII Base64 strings.
Cases: 02_strings_binary_and_decimals, 07_dictionaries_and_views, ncbi_assembly_summary, uniref_cluster_entry
- R-SER-TEMPORAL-ISO:
date32anddate64serialize asYYYY-MM-DD;time32andtime64serialize asHH:MM:SS[.fffffffff]; timezone-aware and timezone-naivetimestampscalars serialize via ISO-8601isoformat().
Cases: 03_temporal_types, ncbi_assembly_summary, uniref_cluster_entry
- R-SER-MAP-ENTRIES:
pa.map_scalars serialize as[{"key": <serialized key>, "value": <serialized value>}, ...]matching the generated<OwnerClass>_<field_name>_mapentry class attributes.
Cases: 06_maps_and_entry_classes, 08_identifier_sanitization_and_collisions, checkm2_quality_report, uniref_cluster_entry
- R-SER-FINITE-FLOAT: Finite
float16,float32, andfloat64scalars serialize as JSONfloatnumbers; non-finiteNaNor±Infinityvalues raiseValueError.
Cases: 01_integers_floats_and_booleans
2. Cases¶
Booleans, Signed and Unsigned Integers, and IEEE-754 Floats (spec/01_integers_floats_and_booleans)¶
Rules: R-SCHEMA-INPUT, R-NON-EMPTY-ROOT, R-SCHEMA-IDENTITY, R-IMPORTS-PREFIXES, R-SLOT-NAME, R-SLOT-ORDER, R-UNIQUE-FIELD-NAMES, R-NULL-REQUIRED, R-SCALAR-BOOL, R-SCALAR-SIGNED-INT, R-SCALAR-UNSIGNED-INT, R-SCALAR-FLOAT, R-SER-ROW-ORDER, R-SER-FINITE-FLOAT
Exercises default unnamed schema identity (name: schema, id: urn:linkml:schema, root class Root),
two's-complement minimum_value/maximum_value bounds on int8..int64, unsigned 0..2^w - 1 bounds on
uint8..uint64, boolean, and float ranges across float16/float32/float64.
Schema (schema.py)¶
"""PyArrow schema for 01_integers_floats_and_booleans."""
import pyarrow as pa
SCHEMA = pa.schema(
[
pa.field("flag", pa.bool_(), nullable=False),
pa.field("i8", pa.int8(), nullable=False),
pa.field("i16", pa.int16(), nullable=False),
pa.field("i32", pa.int32(), nullable=False),
pa.field("i64", pa.int64(), nullable=False),
pa.field("u8", pa.uint8(), nullable=False),
pa.field("u16", pa.uint16(), nullable=False),
pa.field("u32", pa.uint32(), nullable=False),
pa.field("u64", pa.uint64(), nullable=False),
pa.field("f16", pa.float16(), nullable=False),
pa.field("f32", pa.float32(), nullable=False),
pa.field("f64", pa.float64(), nullable=False),
]
)
Input (table.py)¶
"""Sample PyArrow table for 01_integers_floats_and_booleans."""
import numpy as np
import pyarrow as pa
from schema import SCHEMA
TABLE = pa.Table.from_pylist(
[
{
"flag": True,
"i8": -128,
"i16": -32768,
"i32": -2147483648,
"i64": -9223372036854775808,
"u8": 0,
"u16": 0,
"u32": 0,
"u64": 0,
"f16": np.float16(-1.5),
"f32": -0.25,
"f64": -123456.7890125,
},
{
"flag": False,
"i8": 127,
"i16": 32767,
"i32": 2147483647,
"i64": 9223372036854775807,
"u8": 255,
"u16": 65535,
"u32": 4294967295,
"u64": 18446744073709551615,
"f16": np.float16(3.5),
"f32": 2.5,
"f64": 0.0,
},
],
schema=SCHEMA,
)
Serialized records: run boundary_values¶
[
{
"flag": true,
"i8": -128,
"i16": -32768,
"i32": -2147483648,
"i64": -9223372036854775808,
"u8": 0,
"u16": 0,
"u32": 0,
"u64": 0,
"f16": -1.5,
"f32": -0.25,
"f64": -123456.7890125
},
{
"flag": false,
"i8": 127,
"i16": 32767,
"i32": 2147483647,
"i64": 9223372036854775807,
"u8": 255,
"u16": 65535,
"u32": 4294967295,
"u64": 18446744073709551615,
"f16": 3.5,
"f32": 2.5,
"f64": 0.0
}
]
LinkML schema (expected_schema.yaml)¶
name: schema
id: urn:linkml:schema
imports:
- linkml:types
prefixes:
schema:
prefix_prefix: schema
prefix_reference: urn:linkml:schema#
linkml:
prefix_prefix: linkml
prefix_reference: https://w3id.org/linkml/
default_prefix: schema
classes:
Root:
name: Root
attributes:
flag:
name: flag
range: boolean
required: true
i8:
name: i8
range: integer
required: true
minimum_value: -128
maximum_value: 127
i16:
name: i16
range: integer
required: true
minimum_value: -32768
maximum_value: 32767
i32:
name: i32
range: integer
required: true
minimum_value: -2147483648
maximum_value: 2147483647
i64:
name: i64
range: integer
required: true
minimum_value: -9223372036854775808
maximum_value: 9223372036854775807
u8:
name: u8
range: integer
required: true
minimum_value: 0
maximum_value: 255
u16:
name: u16
range: integer
required: true
minimum_value: 0
maximum_value: 65535
u32:
name: u32
range: integer
required: true
minimum_value: 0
maximum_value: 4294967295
u64:
name: u64
range: integer
required: true
minimum_value: 0
maximum_value: 18446744073709551615
f16:
name: f16
range: float
required: true
f32:
name: f32
range: float
required: true
f64:
name: f64
range: float
required: true
Strings, Base64 Binary Columns, and Fixed-Point Decimals (spec/02_strings_binary_and_decimals)¶
Rules: R-SCALAR-STRING-BINARY, R-SCALAR-DECIMAL, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-SER-ROW-ORDER, R-SER-DECIMAL-STR, R-SER-BASE64
Verifies that string, large_string, binary, large_binary, and fixed_size_binary map to range: string
while decimal128 and decimal256 map to range: decimal with plain fixed-point string serialization.
Schema (schema.py)¶
"""PyArrow schema for 02_strings_binary_and_decimals."""
import pyarrow as pa
SCHEMA = pa.schema(
[
pa.field("utf8_str", pa.string(), nullable=False),
pa.field("large_str", pa.large_string(), nullable=False),
pa.field("raw_bin", pa.binary(), nullable=False),
pa.field("large_bin", pa.large_binary(), nullable=False),
pa.field("fixed_bin_4", pa.binary(4), nullable=False),
pa.field("dec_standard", pa.decimal128(10, 2), nullable=False),
pa.field("dec_integer_only", pa.decimal128(8, 0), nullable=False),
pa.field("dec256_high_prec", pa.decimal256(50, 10), nullable=True),
]
)
Input (table.py)¶
"""Sample PyArrow table for 02_strings_binary_and_decimals."""
from decimal import Decimal
import pyarrow as pa
from schema import SCHEMA
TABLE = pa.Table.from_pylist(
[
{
"utf8_str": "contig_0001",
"large_str": "ATGCNATGCNATGCN",
"raw_bin": b"\x00\x01\x02\xff",
"large_bin": b"kbase-large-payload",
"fixed_bin_4": b"\xde\xad\xbe\xef",
"dec_standard": Decimal("-12345678.95"),
"dec_integer_only": Decimal(98765432),
"dec256_high_prec": Decimal("0.0000000001"),
},
{
"utf8_str": "",
"large_str": "second-large-string",
"raw_bin": b"",
"large_bin": b"\x89PNG",
"fixed_bin_4": b"\x00\x00\x00\x01",
"dec_standard": Decimal("0.00"),
"dec_integer_only": Decimal(-42),
"dec256_high_prec": None,
},
],
schema=SCHEMA,
)
Serialized records: run default¶
[
{
"utf8_str": "contig_0001",
"large_str": "ATGCNATGCNATGCN",
"raw_bin": "AAEC/w==",
"large_bin": "a2Jhc2UtbGFyZ2UtcGF5bG9hZA==",
"fixed_bin_4": "3q2+7w==",
"dec_standard": "-12345678.95",
"dec_integer_only": "98765432",
"dec256_high_prec": "0.0000000001"
},
{
"utf8_str": "",
"large_str": "second-large-string",
"raw_bin": "",
"large_bin": "iVBORw==",
"fixed_bin_4": "AAAAAQ==",
"dec_standard": "0.00",
"dec_integer_only": "-42",
"dec256_high_prec": null
}
]
LinkML schema (expected_schema.yaml)¶
name: schema
id: urn:linkml:schema
imports:
- linkml:types
prefixes:
schema:
prefix_prefix: schema
prefix_reference: urn:linkml:schema#
linkml:
prefix_prefix: linkml
prefix_reference: https://w3id.org/linkml/
default_prefix: schema
classes:
Root:
name: Root
attributes:
utf8_str:
name: utf8_str
range: string
required: true
large_str:
name: large_str
range: string
required: true
raw_bin:
name: raw_bin
range: string
required: true
large_bin:
name: large_bin
range: string
required: true
fixed_bin_4:
name: fixed_bin_4
range: string
required: true
dec_standard:
name: dec_standard
range: decimal
required: true
dec_integer_only:
name: dec_integer_only
range: decimal
required: true
dec256_high_prec:
name: dec256_high_prec
range: decimal
Dates, Times, and Timezone-Aware and Timezone-Naive Timestamps (spec/03_temporal_types)¶
Rules: R-SCALAR-TEMPORAL, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-SER-ROW-ORDER, R-SER-TEMPORAL-ISO
Maps date32/date64 to range: date, time32/time64 to range: time, and both timezone-aware and
timezone-naive timestamp types across units (s, ms, us, ns) to range: datetime.
Schema (schema.py)¶
"""PyArrow schema for 03_temporal_types."""
import pyarrow as pa
SCHEMA = pa.schema(
[
pa.field("d32", pa.date32(), nullable=False),
pa.field("d64", pa.date64(), nullable=False),
pa.field("t32_s", pa.time32("s"), nullable=False),
pa.field("t32_ms", pa.time32("ms"), nullable=False),
pa.field("t64_us", pa.time64("us"), nullable=False),
pa.field("t64_ns", pa.time64("ns"), nullable=False),
pa.field("ts_s_utc", pa.timestamp("s", tz="UTC"), nullable=False),
pa.field("ts_us_la", pa.timestamp("us", tz="America/Los_Angeles"), nullable=False),
pa.field("ts_ns_utc", pa.timestamp("ns", tz="UTC"), nullable=False),
pa.field("ts_naive_us", pa.timestamp("us"), nullable=False),
pa.field("ts_naive_ns_opt", pa.timestamp("ns"), nullable=True),
]
)
Input (table.py)¶
"""Sample PyArrow table for 03_temporal_types."""
import datetime as dt
import pyarrow as pa
from schema import SCHEMA
TABLE = pa.table(
{
"d32": pa.array([dt.date(2026, 4, 10), dt.date(1970, 1, 1)], type=pa.date32()),
"d64": pa.array([dt.date(2026, 4, 11), dt.date(2000, 2, 29)], type=pa.date64()),
"t32_s": pa.array([dt.time(8, 30, 0), dt.time(23, 59, 59)], type=pa.time32("s")),
"t32_ms": pa.array([dt.time(8, 30, 0, 250000), dt.time(0, 0, 0)], type=pa.time32("ms")),
"t64_us": pa.array([dt.time(14, 15, 30, 123456), dt.time(12, 0, 0)], type=pa.time64("us")),
"t64_ns": pa.array([52_200_123_456_000, 0], type=pa.time64("ns")),
"ts_s_utc": pa.array(
[
dt.datetime(2026, 4, 10, 14, 30, 0, tzinfo=dt.UTC),
dt.datetime(2025, 1, 1, 0, 0, 0, tzinfo=dt.UTC),
],
type=pa.timestamp("s", tz="UTC"),
),
"ts_us_la": pa.array(
[
dt.datetime(2026, 4, 10, 14, 30, 0, 125000, tzinfo=dt.UTC),
dt.datetime(2025, 1, 15, 18, 0, 0, tzinfo=dt.UTC),
],
type=pa.timestamp("us", tz="America/Los_Angeles"),
),
"ts_ns_utc": pa.array(
[
dt.datetime(2026, 4, 10, 14, 30, 0, 750000, tzinfo=dt.UTC),
dt.datetime(2025, 6, 1, 6, 0, 0, tzinfo=dt.UTC),
],
type=pa.timestamp("ns", tz="UTC"),
),
"ts_naive_us": pa.array(
[
dt.datetime(2026, 4, 10, 9, 45, 0, tzinfo=dt.UTC).replace(tzinfo=None),
dt.datetime(2025, 12, 31, 23, 59, 59, tzinfo=dt.UTC).replace(tzinfo=None),
],
type=pa.timestamp("us"),
),
"ts_naive_ns_opt": pa.array(
[
dt.datetime(2026, 4, 10, 9, 45, 0, 500000, tzinfo=dt.UTC).replace(tzinfo=None),
None,
],
type=pa.timestamp("ns"),
),
},
schema=SCHEMA,
)
Serialized records: run default¶
[
{
"d32": "2026-04-10",
"d64": "2026-04-11",
"t32_s": "08:30:00",
"t32_ms": "08:30:00.250000",
"t64_us": "14:15:30.123456",
"t64_ns": "14:30:00.123456",
"ts_s_utc": "2026-04-10T14:30:00+00:00",
"ts_us_la": "2026-04-10T07:30:00.125000-07:00",
"ts_ns_utc": "2026-04-10T14:30:00.750000+00:00",
"ts_naive_us": "2026-04-10T09:45:00",
"ts_naive_ns_opt": "2026-04-10T09:45:00.500000"
},
{
"d32": "1970-01-01",
"d64": "2000-02-29",
"t32_s": "23:59:59",
"t32_ms": "00:00:00",
"t64_us": "12:00:00",
"t64_ns": "00:00:00",
"ts_s_utc": "2025-01-01T00:00:00+00:00",
"ts_us_la": "2025-01-15T10:00:00-08:00",
"ts_ns_utc": "2025-06-01T06:00:00+00:00",
"ts_naive_us": "2025-12-31T23:59:59",
"ts_naive_ns_opt": null
}
]
LinkML schema (expected_schema.yaml)¶
name: schema
id: urn:linkml:schema
imports:
- linkml:types
prefixes:
schema:
prefix_prefix: schema
prefix_reference: urn:linkml:schema#
linkml:
prefix_prefix: linkml
prefix_reference: https://w3id.org/linkml/
default_prefix: schema
classes:
Root:
name: Root
attributes:
d32:
name: d32
range: date
required: true
d64:
name: d64
range: date
required: true
t32_s:
name: t32_s
range: time
required: true
t32_ms:
name: t32_ms
range: time
required: true
t64_us:
name: t64_us
range: time
required: true
t64_ns:
name: t64_ns
range: time
required: true
ts_s_utc:
name: ts_s_utc
range: datetime
required: true
ts_us_la:
name: ts_us_la
range: datetime
required: true
ts_ns_utc:
name: ts_ns_utc
range: datetime
required: true
ts_naive_us:
name: ts_naive_us
range: datetime
required: true
ts_naive_ns_opt:
name: ts_naive_ns_opt
range: datetime
Nullability, Schema Identity Metadata, and Slot Descriptions (spec/04_nullability_and_descriptions)¶
Rules: R-SCHEMA-IDENTITY, R-IMPORTS-PREFIXES, R-SLOT-NAME, R-SLOT-ORDER, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-META-SCHEMA-DESC, R-META-SLOT-DESC, R-SER-ROW-ORDER
Demonstrates resolution of SchemaDefinition.name, root ClassDefinition.name, id, prefixes, and
description from pa.Schema.metadata (title, $id, comment), alongside required vs optional slots and
slot-level description extraction (including fallback to list item comment).
Schema (schema.py)¶
"""PyArrow schema for 04_nullability_and_descriptions."""
import pyarrow as pa
SCHEMA = pa.schema(
[
pa.field("assay_id", pa.string(), nullable=False, metadata={"comment": "Primary assay identifier."}),
pa.field("od600", pa.float64(), nullable=True, metadata={"comment": "Optical density at 600 nm."}),
pa.field("replicate_num", pa.int16(), nullable=False),
pa.field("notes", pa.string(), nullable=True),
pa.field(
"reagents",
pa.list_(pa.field("item", pa.string(), nullable=False, metadata={"comment": "Reagent catalog code."})),
nullable=True,
),
],
metadata={
"title": "AssayMeasurement",
"$id": "https://kbase.us/linkml/assay_measurement",
"comment": "High-throughput growth assay measurement records.",
},
)
Input (table.py)¶
"""Sample PyArrow table for 04_nullability_and_descriptions."""
import pyarrow as pa
from schema import SCHEMA
TABLE = pa.Table.from_pylist(
[
{
"assay_id": "ASY-1001",
"od600": 0.825,
"replicate_num": 1,
"notes": "Exponential phase",
"reagents": ["R-10", "R-22"],
},
{
"assay_id": "ASY-1002",
"od600": None,
"replicate_num": 2,
"notes": None,
"reagents": None,
},
],
schema=SCHEMA,
)
Serialized records: run default¶
[
{
"assay_id": "ASY-1001",
"od600": 0.825,
"replicate_num": 1,
"notes": "Exponential phase",
"reagents": [
"R-10",
"R-22"
]
},
{
"assay_id": "ASY-1002",
"od600": null,
"replicate_num": 2,
"notes": null,
"reagents": null
}
]
LinkML schema (expected_schema.yaml)¶
name: AssayMeasurement
description: High-throughput growth assay measurement records.
id: https://kbase.us/linkml/assay_measurement
imports:
- linkml:types
prefixes:
AssayMeasurement:
prefix_prefix: AssayMeasurement
prefix_reference: https://kbase.us/linkml/assay_measurement#
linkml:
prefix_prefix: linkml
prefix_reference: https://w3id.org/linkml/
default_prefix: AssayMeasurement
classes:
AssayMeasurement:
name: AssayMeasurement
attributes:
assay_id:
name: assay_id
description: Primary assay identifier.
range: string
required: true
od600:
name: od600
description: Optical density at 600 nm.
range: float
replicate_num:
name: replicate_num
range: integer
required: true
minimum_value: -32768
maximum_value: 32767
notes:
name: notes
range: string
reagents:
name: reagents
range: string
multivalued: true
Nested Struct Classes, Multivalued Lists, and Fixed-Size List Cardinality (spec/05_nested_structs_and_lists)¶
Rules: R-SHAPE-STRUCT-CLASS, R-SHAPE-LIST-SLOT, R-SHAPE-FIXED-LIST-CARDINALITY, R-REJECT-NESTED-MULTIVALUED, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-LIST-NON-NULL-ITEMS, R-SER-ROW-ORDER
Demonstrates derived <OwnerClass>_<field_name> classes for nested pa.struct columns and struct-typed list
items (including a nested list<string> inside a struct element class), omission of scalar numeric bounds on
multivalued list slots for LinkML Pydantic compatibility, and minimum_cardinality / maximum_cardinality
on pa.FixedSizeListType.
Schema (schema.py)¶
"""PyArrow schema for 05_nested_structs_and_lists."""
import pyarrow as pa
DOMAIN_HIT_STRUCT = pa.struct(
[
pa.field("domain_acc", pa.string(), nullable=False),
pa.field("start_aa", pa.uint32(), nullable=False),
pa.field("end_aa", pa.uint32(), nullable=False),
pa.field("evidence_codes", pa.list_(pa.field("item", pa.string(), nullable=False)), nullable=False),
]
)
SCHEMA = pa.schema(
[
pa.field("protein_id", pa.string(), nullable=False),
pa.field(
"source_locus",
pa.struct(
[
pa.field("contig", pa.string(), nullable=False),
pa.field("strand", pa.string(), nullable=True),
]
),
nullable=False,
),
pa.field("ec_numbers", pa.list_(pa.field("item", pa.string(), nullable=False)), nullable=False),
pa.field("quality_flags", pa.large_list(pa.field("item", pa.uint8(), nullable=False)), nullable=True),
pa.field("coords_3d", pa.list_(pa.field("item", pa.float32(), nullable=False), 3), nullable=False),
pa.field("domain_hits", pa.list_(pa.field("item", DOMAIN_HIT_STRUCT, nullable=False)), nullable=False),
],
metadata={"title": "ProteinAnnotation"},
)
Input (table.py)¶
"""Sample PyArrow table for 05_nested_structs_and_lists."""
import pyarrow as pa
from schema import SCHEMA
TABLE = pa.Table.from_pylist(
[
{
"protein_id": "WP_000001.1",
"source_locus": {"contig": "NC_000913.3", "strand": "+"},
"ec_numbers": ["2.7.7.7", "3.1.11.2"],
"quality_flags": [1, 4, 255],
"coords_3d": [1.25, -0.5, 4.0],
"domain_hits": [
{
"domain_acc": "PF00476",
"start_aa": 12,
"end_aa": 298,
"evidence_codes": ["ECO:0000259", "ECO:0000313"],
}
],
},
{
"protein_id": "WP_000002.1",
"source_locus": {"contig": "NC_000913.3", "strand": None},
"ec_numbers": [],
"quality_flags": None,
"coords_3d": [0.0, 0.0, 0.0],
"domain_hits": [],
},
],
schema=SCHEMA,
)
Serialized records: run default¶
[
{
"protein_id": "WP_000001.1",
"source_locus": {
"contig": "NC_000913.3",
"strand": "+"
},
"ec_numbers": [
"2.7.7.7",
"3.1.11.2"
],
"quality_flags": [
1,
4,
255
],
"coords_3d": [
1.25,
-0.5,
4.0
],
"domain_hits": [
{
"domain_acc": "PF00476",
"start_aa": 12,
"end_aa": 298,
"evidence_codes": [
"ECO:0000259",
"ECO:0000313"
]
}
]
},
{
"protein_id": "WP_000002.1",
"source_locus": {
"contig": "NC_000913.3",
"strand": null
},
"ec_numbers": [],
"quality_flags": null,
"coords_3d": [
0.0,
0.0,
0.0
],
"domain_hits": []
}
]
LinkML schema (expected_schema.yaml)¶
name: ProteinAnnotation
id: urn:linkml:ProteinAnnotation
imports:
- linkml:types
prefixes:
ProteinAnnotation:
prefix_prefix: ProteinAnnotation
prefix_reference: urn:linkml:ProteinAnnotation#
linkml:
prefix_prefix: linkml
prefix_reference: https://w3id.org/linkml/
default_prefix: ProteinAnnotation
classes:
ProteinAnnotation_source_locus:
name: ProteinAnnotation_source_locus
attributes:
contig:
name: contig
range: string
required: true
strand:
name: strand
range: string
ProteinAnnotation_domain_hits:
name: ProteinAnnotation_domain_hits
attributes:
domain_acc:
name: domain_acc
range: string
required: true
start_aa:
name: start_aa
range: integer
required: true
minimum_value: 0
maximum_value: 4294967295
end_aa:
name: end_aa
range: integer
required: true
minimum_value: 0
maximum_value: 4294967295
evidence_codes:
name: evidence_codes
range: string
required: true
multivalued: true
ProteinAnnotation:
name: ProteinAnnotation
attributes:
protein_id:
name: protein_id
range: string
required: true
source_locus:
name: source_locus
range: ProteinAnnotation_source_locus
required: true
ec_numbers:
name: ec_numbers
range: string
required: true
multivalued: true
quality_flags:
name: quality_flags
range: integer
multivalued: true
coords_3d:
name: coords_3d
range: float
required: true
multivalued: true
minimum_cardinality: 3
maximum_cardinality: 3
domain_hits:
name: domain_hits
range: ProteinAnnotation_domain_hits
required: true
multivalued: true
Map Entry Classes with Required vs Optional Values and Non-String Keys (spec/06_maps_and_entry_classes)¶
Rules: R-SHAPE-MAP-CLASS, R-SHAPE-STRUCT-CLASS, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-NULL-MAP-VALUE, R-SCALAR-SIGNED-INT, R-SER-ROW-ORDER, R-SER-MAP-ENTRIES
Verifies that pa.map_ synthesizes <OwnerClass>_<field_name>_map entry classes where key is always
required: true, value carries required: true iff item_field.nullable == False, and struct map values
produce a nested <OwnerClass>_<field_name>_map_value class.
Schema (schema.py)¶
"""PyArrow schema for 06_maps_and_entry_classes."""
import pyarrow as pa
HIT_DETAIL_STRUCT = pa.struct(
[
pa.field("score", pa.float64(), nullable=False),
pa.field("note", pa.string(), nullable=True),
]
)
SCHEMA = pa.schema(
[
pa.field("sample_id", pa.string(), nullable=False),
pa.field(
"read_counts",
pa.map_(pa.string(), pa.field("value", pa.int64(), nullable=False)),
nullable=False,
),
pa.field(
"taxid_hits",
pa.map_(pa.int32(), pa.field("value", HIT_DETAIL_STRUCT, nullable=True), keys_sorted=True),
nullable=True,
),
],
metadata={"title": "SampleReadMap"},
)
Input (table.py)¶
"""Sample PyArrow table for 06_maps_and_entry_classes."""
import pyarrow as pa
from schema import SCHEMA
TABLE = pa.Table.from_pylist(
[
{
"sample_id": "SMP-01",
"read_counts": [("raw", 150000), ("trimmed", 142500)],
"taxid_hits": [
(562, {"score": 99.5, "note": "Escherichia coli"}),
(1280, None),
],
},
{
"sample_id": "SMP-02",
"read_counts": [],
"taxid_hits": None,
},
],
schema=SCHEMA,
)
Serialized records: run default¶
[
{
"sample_id": "SMP-01",
"read_counts": [
{
"key": "raw",
"value": 150000
},
{
"key": "trimmed",
"value": 142500
}
],
"taxid_hits": [
{
"key": 562,
"value": {
"score": 99.5,
"note": "Escherichia coli"
}
},
{
"key": 1280,
"value": null
}
]
},
{
"sample_id": "SMP-02",
"read_counts": [],
"taxid_hits": null
}
]
LinkML schema (expected_schema.yaml)¶
name: SampleReadMap
id: urn:linkml:SampleReadMap
imports:
- linkml:types
prefixes:
SampleReadMap:
prefix_prefix: SampleReadMap
prefix_reference: urn:linkml:SampleReadMap#
linkml:
prefix_prefix: linkml
prefix_reference: https://w3id.org/linkml/
default_prefix: SampleReadMap
classes:
SampleReadMap_read_counts_map:
name: SampleReadMap_read_counts_map
attributes:
key:
name: key
range: string
required: true
value:
name: value
range: integer
required: true
minimum_value: -9223372036854775808
maximum_value: 9223372036854775807
SampleReadMap_taxid_hits_map_value:
name: SampleReadMap_taxid_hits_map_value
attributes:
score:
name: score
range: float
required: true
note:
name: note
range: string
SampleReadMap_taxid_hits_map:
name: SampleReadMap_taxid_hits_map
attributes:
key:
name: key
range: integer
required: true
minimum_value: -2147483648
maximum_value: 2147483647
value:
name: value
range: SampleReadMap_taxid_hits_map_value
SampleReadMap:
name: SampleReadMap
attributes:
sample_id:
name: sample_id
range: string
required: true
read_counts:
name: read_counts
range: SampleReadMap_read_counts_map
required: true
multivalued: true
taxid_hits:
name: taxid_hits
range: SampleReadMap_taxid_hits_map
multivalued: true
Dictionary-Encoded Columns and String/Binary/List View Variants (spec/07_dictionaries_and_views)¶
Rules: R-SHAPE-DICTIONARY, R-SHAPE-LIST-SLOT, R-SCALAR-UNSIGNED-INT, R-SCALAR-DECIMAL, R-SCALAR-STRING-BINARY, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-SER-ROW-ORDER, R-SER-DECIMAL-STR, R-SER-BASE64
Verifies that pa.dictionary unwraps to the underlying value range and integer bounds (uint16 ->
0..65535), and that Arrow view types (string_view, binary_view, list_view, large_list_view) map to
standard LinkML string and multivalued slots.
Schema (schema.py)¶
"""PyArrow schema for 07_dictionaries_and_views."""
import pyarrow as pa
SCHEMA = pa.schema(
[
pa.field("id", pa.string_view(), nullable=False),
pa.field("payload_view", pa.binary_view(), nullable=False),
pa.field("category", pa.dictionary(pa.int16(), pa.string()), nullable=False),
pa.field("rank_code", pa.dictionary(pa.int8(), pa.uint16(), ordered=True), nullable=True),
pa.field("weight_tier", pa.dictionary(pa.int8(), pa.decimal128(6, 2)), nullable=False),
pa.field("tags_view", pa.list_view(pa.field("item", pa.string(), nullable=False)), nullable=False),
pa.field("scores_large_view", pa.large_list_view(pa.field("item", pa.uint8(), nullable=False)), nullable=True),
],
metadata={"title": "CatalogViewRecord"},
)
Input (table.py)¶
"""Sample PyArrow table for 07_dictionaries_and_views."""
from decimal import Decimal
import pyarrow as pa
from schema import SCHEMA
TABLE = pa.Table.from_arrays(
[
pa.array(["VIEW-01", "VIEW-02"], type=pa.string_view()),
pa.array([b"\xca\xfe\xba\xbe", b"plain-bytes"], type=pa.binary_view()),
pa.DictionaryArray.from_arrays(
pa.array([0, 1], type=pa.int16()),
pa.array(["metagenome", "isolate"], type=pa.string()),
),
pa.DictionaryArray.from_arrays(
pa.array([0, None], type=pa.int8()),
pa.array([1024], type=pa.uint16()),
ordered=True,
),
pa.DictionaryArray.from_arrays(
pa.array([0, 1], type=pa.int8()),
pa.array([Decimal("15.25"), Decimal("1.00")], type=pa.decimal128(6, 2)),
),
pa.array([["marine", "pelagic"], []], type=pa.list_view(pa.field("item", pa.string(), nullable=False))),
pa.array([[10, 200], None], type=pa.large_list_view(pa.field("item", pa.uint8(), nullable=False))),
],
schema=SCHEMA,
)
Serialized records: run default¶
[
{
"id": "VIEW-01",
"payload_view": "yv66vg==",
"category": "metagenome",
"rank_code": 1024,
"weight_tier": "15.25",
"tags_view": [
"marine",
"pelagic"
],
"scores_large_view": [
10,
200
]
},
{
"id": "VIEW-02",
"payload_view": "cGxhaW4tYnl0ZXM=",
"category": "isolate",
"rank_code": null,
"weight_tier": "1.00",
"tags_view": [],
"scores_large_view": null
}
]
LinkML schema (expected_schema.yaml)¶
name: CatalogViewRecord
id: urn:linkml:CatalogViewRecord
imports:
- linkml:types
prefixes:
CatalogViewRecord:
prefix_prefix: CatalogViewRecord
prefix_reference: urn:linkml:CatalogViewRecord#
linkml:
prefix_prefix: linkml
prefix_reference: https://w3id.org/linkml/
default_prefix: CatalogViewRecord
classes:
CatalogViewRecord:
name: CatalogViewRecord
attributes:
id:
name: id
range: string
required: true
payload_view:
name: payload_view
range: string
required: true
category:
name: category
range: string
required: true
rank_code:
name: rank_code
range: integer
minimum_value: 0
maximum_value: 65535
weight_tier:
name: weight_tier
range: decimal
required: true
tags_view:
name: tags_view
range: string
required: true
multivalued: true
scores_large_view:
name: scores_large_view
range: integer
multivalued: true
Identifier Sanitization, Explicit Overrides, and Class Name Collision Disambiguation (spec/08_identifier_sanitization_and_collisions)¶
Rules: R-SCHEMA-IDENTITY, R-IMPORTS-PREFIXES, R-CLASS-IDENTIFIER, R-CLASS-COLLISION, R-SHAPE-STRUCT-CLASS, R-SHAPE-MAP-CLASS, R-SER-ROW-ORDER, R-SER-MAP-ENTRIES
Exercises explicit schema_name and schema_id overrides with punctuation sanitization
("--genome-report.v1--" -> genome_report_v1), plus deterministic _2 suffix disambiguation when
<OwnerClass>_meta_info collides with <OwnerClass>_meta's child info struct.
Schema (schema.py)¶
"""PyArrow schema for 08_identifier_sanitization_and_collisions."""
import pyarrow as pa
SCHEMA = pa.schema(
[
pa.field(
"meta_info",
pa.struct([pa.field("source", pa.string(), nullable=False)]),
nullable=False,
),
pa.field(
"meta",
pa.struct(
[
pa.field(
"info",
pa.struct([pa.field("version", pa.int32(), nullable=False)]),
nullable=False,
)
]
),
nullable=False,
),
pa.field(
"labels",
pa.map_(pa.string(), pa.field("value", pa.string(), nullable=False)),
nullable=False,
),
pa.field(
"labels_map",
pa.struct([pa.field("note", pa.string(), nullable=True)]),
nullable=True,
),
],
metadata={
"title": "OverriddenTitle",
"$id": "urn:overridden",
},
)
Input (table.py)¶
"""Sample PyArrow table for 08_identifier_sanitization_and_collisions."""
import pyarrow as pa
from schema import SCHEMA
TABLE = pa.Table.from_pylist(
[
{
"meta_info": {"source": "IMG/M"},
"meta": {"info": {"version": 4}},
"labels": [("env", "soil")],
"labels_map": {"note": "collides with labels map class"},
},
{
"meta_info": {"source": "GTDB"},
"meta": {"info": {"version": 220}},
"labels": [],
"labels_map": None,
},
],
schema=SCHEMA,
)
Serialized records: run sanitized_override (schema_name=--genome-report.v1--, schema_id=https://kbase.us/linkml/genome-report-v1)¶
[
{
"meta_info": {
"source": "IMG/M"
},
"meta": {
"info": {
"version": 4
}
},
"labels": [
{
"key": "env",
"value": "soil"
}
],
"labels_map": {
"note": "collides with labels map class"
}
},
{
"meta_info": {
"source": "GTDB"
},
"meta": {
"info": {
"version": 220
}
},
"labels": [],
"labels_map": null
}
]
LinkML schema (expected_schema.yaml)¶
name: genome_report_v1
id: https://kbase.us/linkml/genome-report-v1
imports:
- linkml:types
prefixes:
genome_report_v1:
prefix_prefix: genome_report_v1
prefix_reference: https://kbase.us/linkml/genome-report-v1#
linkml:
prefix_prefix: linkml
prefix_reference: https://w3id.org/linkml/
default_prefix: genome_report_v1
classes:
genome_report_v1_meta_info:
name: genome_report_v1_meta_info
attributes:
source:
name: source
range: string
required: true
genome_report_v1_meta_info_2:
name: genome_report_v1_meta_info_2
attributes:
version:
name: version
range: integer
required: true
minimum_value: -2147483648
maximum_value: 2147483647
genome_report_v1_meta:
name: genome_report_v1_meta
attributes:
info:
name: info
range: genome_report_v1_meta_info_2
required: true
genome_report_v1_labels_map:
name: genome_report_v1_labels_map
attributes:
key:
name: key
range: string
required: true
value:
name: value
range: string
required: true
genome_report_v1_labels_map_2:
name: genome_report_v1_labels_map_2
attributes:
note:
name: note
range: string
genome_report_v1:
name: genome_report_v1
attributes:
meta_info:
name: meta_info
range: genome_report_v1_meta_info
required: true
meta:
name: meta
range: genome_report_v1_meta
required: true
labels:
name: labels
range: genome_report_v1_labels_map
required: true
multivalued: true
labels_map:
name: labels_map
range: genome_report_v1_labels_map_2
CheckM2 Genome Quality and Contig Statistics Table (examples/checkm2_quality_report)¶
Rules: R-SCHEMA-INPUT, R-SCHEMA-IDENTITY, R-IMPORTS-PREFIXES, R-SLOT-NAME, R-SLOT-ORDER, R-SHAPE-STRUCT-CLASS, R-SHAPE-LIST-SLOT, R-SHAPE-MAP-CLASS, R-SHAPE-DICTIONARY, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-NULL-MAP-VALUE, R-SCALAR-SIGNED-INT, R-SCALAR-UNSIGNED-INT, R-SCALAR-FLOAT, R-SCALAR-DECIMAL, R-SCALAR-STRING-BINARY, R-META-SCHEMA-DESC, R-META-SLOT-DESC, R-SER-ROW-ORDER, R-SER-DECIMAL-STR, R-SER-MAP-ENTRIES
Schema: specs/schema_converters/pyarrow_to_linkml/examples/checkm2_quality_report/schema.py
- Run
default:table.py,expected.json
Expected LinkML schema: expected_schema.yaml
NCBI Datasets Assembly Summary Table (examples/ncbi_assembly_summary)¶
Rules: R-SCHEMA-IDENTITY, R-IMPORTS-PREFIXES, R-SHAPE-STRUCT-CLASS, R-SHAPE-LIST-SLOT, R-SHAPE-DICTIONARY, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-SCALAR-BOOL, R-SCALAR-SIGNED-INT, R-SCALAR-UNSIGNED-INT, R-SCALAR-DECIMAL, R-SCALAR-STRING-BINARY, R-SCALAR-TEMPORAL, R-META-SCHEMA-DESC, R-META-SLOT-DESC, R-SER-ROW-ORDER, R-SER-DECIMAL-STR, R-SER-BASE64, R-SER-TEMPORAL-ISO
Schema: specs/schema_converters/pyarrow_to_linkml/examples/ncbi_assembly_summary/schema.py
- Run
default:table.py,expected.json
Expected LinkML schema: expected_schema.yaml
UniRef Sequence Cluster Inventory Table (examples/uniref_cluster_entry)¶
Rules: R-SCHEMA-IDENTITY, R-IMPORTS-PREFIXES, R-SHAPE-STRUCT-CLASS, R-SHAPE-LIST-SLOT, R-SHAPE-FIXED-LIST-CARDINALITY, R-SHAPE-MAP-CLASS, R-NULL-REQUIRED, R-NULL-OPTIONAL, R-NULL-MAP-VALUE, R-SCALAR-UNSIGNED-INT, R-SCALAR-FLOAT, R-SCALAR-STRING-BINARY, R-SCALAR-TEMPORAL, R-META-SCHEMA-DESC, R-META-SLOT-DESC, R-SER-ROW-ORDER, R-SER-BASE64, R-SER-TEMPORAL-ISO, R-SER-MAP-ENTRIES
Schema: specs/schema_converters/pyarrow_to_linkml/examples/uniref_cluster_entry/schema.py
- Run
default:table.py,expected.json
Expected LinkML schema: expected_schema.yaml
3. Errors (Unsupported / Invalid Input)¶
Schema generation raises a subclass of PyArrowToLinkMLError (itself a ValueError).
| Code | Exception | Condition | Reason | Fixtures |
|---|---|---|---|---|
| E1 | InvalidPyArrowSchemaError |
Input is not a pa.Schema instance |
Schema generation requires a concrete PyArrow pa.Schema object; bare pa.DataType, pa.Field, or Python mappings are not table schemas. |
e01_non_schema_input.py: Input is a pa.Field rather than a pa.Schema. |
| E2 | EmptyStructError |
Empty root schema or nested pa.struct([]) (zero fields) |
Arrow tables and LinkML record classes generated from Arrow structs require at least one named attribute. | e02a_empty_root_schema.py: Root pa.schema([]) declares zero fields.e02b_empty_nested_struct.py: Nested pa.struct([]) column declares zero child fields. |
| E3 | DuplicateFieldNameError |
Duplicate field name in schema or pa.struct |
LinkML class attributes is keyed by slot name and cannot represent duplicate sibling field names. |
e03a_duplicate_root_field.py: Root pa.Schema declares two fields named 'id'.e03b_duplicate_struct_field.py: Nested pa.struct declares duplicate child fields named 'tag'. |
| E4 | NullOnlyTypeError |
Standalone pa.null() field or element type |
A column or container element typed solely as pa.null() carries no value domain; nullable columns must wrap a concrete Arrow value type with nullable=True. |
e04_null_only_field.py: Column uses standalone pa.null() with no value type. |
| E5 | InvalidDecimalTypeError |
Decimal type with negative scale or scale > precision |
Fixed-point decimal representations require 0 <= scale <= precision; negative scales or scale > precision have no plain decimal slot representation. |
e05_negative_scale_decimal.py: Column uses pa.decimal128(6, -2) with negative scale. |
| E6 | UnsupportedTemporalIntervalError |
Duration or calendar interval type (pa.duration, pa.month_day_nano_interval) |
Relative durations and month_day_nano intervals have no lossless built-in linkml:types scalar range. |
e06a_duration_field.py: Column uses pa.duration("us").e06b_interval_field.py: Column uses pa.month_day_nano_interval(). |
| E7 | UnsupportedUnionTypeError |
Arrow union type (pa.sparse_union or pa.dense_union) |
Arrow type-code unions cannot be represented as typed LinkML slots without class/union redesign. | e07a_sparse_union_field.py: Column uses pa.sparse_union(...).e07b_dense_union_field.py: Column uses pa.dense_union(...). |
| E8 | UnsupportedArrowTypeError |
Unsupported Arrow physical or extension type (run_end_encoded, ExtensionType, etc.) |
Specialized physical encodings such as pa.run_end_encoded and custom pa.ExtensionType subclasses have no general LinkML mapping. |
e08a_run_end_encoded_field.py: Column uses pa.run_end_encoded(pa.int32(), pa.string()).e08b_extension_type_field.py: Column uses Arrow extension type pa.uuid(). |
| E9 | EmptyFieldNameError |
Blank or empty field name | LinkML SlotDefinition identifiers require a non-empty attribute name; blank Arrow field names cannot form valid LinkML slots. |
e09a_empty_field_name.py: Column declares an empty field name "".e09b_whitespace_field_name.py: Column declares a whitespace-only field name " ". |
| E10 | NestedMultivaluedSlotError |
Directly nested list or map inside a list (list<list<...>> or list<map<...>>) |
LinkML slots use a flat multivalued: true flag on a single range and cannot represent multidimensional lists or lists of map-entry lists without intermediate wrapper classes. |
e10a_nested_list_in_list.py: Column uses a 2D nested list pa.list_(pa.list_(pa.int64())).e10b_nested_map_in_list.py: Column uses a list of maps pa.list_(pa.map_(pa.string(), pa.int64())). |
4. Fixture Index¶
Every case directory contains a case.yaml manifest; the test suite and this document are both driven by it.
| Case | Schema | Runs |
|---|---|---|
spec/01_integers_floats_and_booleans |
schema.py |
boundary_values |
spec/02_strings_binary_and_decimals |
schema.py |
default |
spec/03_temporal_types |
schema.py |
default |
spec/04_nullability_and_descriptions |
schema.py |
default |
spec/05_nested_structs_and_lists |
schema.py |
default |
spec/06_maps_and_entry_classes |
schema.py |
default |
spec/07_dictionaries_and_views |
schema.py |
default |
spec/08_identifier_sanitization_and_collisions |
schema.py |
sanitized_override |
examples/checkm2_quality_report |
schema.py |
default |
examples/ncbi_assembly_summary |
schema.py |
default |
examples/uniref_cluster_entry |
schema.py |
default |