Skip to content

cdm_data_loaders.converters.core.inference

Target-independent JSON Schema type inference and decimal scale helpers.

infer_implicit_type

infer_implicit_type(schema: dict[str, Any]) -> str | None

Approximate a type-less schema from its type-specific keywords.

Precedence is object, array, string, number. These keywords constrain values of their own type but do not exclude other JSON types.

Source code in src/cdm_data_loaders/converters/core/inference.py
def infer_implicit_type(schema: dict[str, Any]) -> str | None:
    """Approximate a type-less schema from its type-specific keywords.

    Precedence is object, array, string, number. These keywords constrain
    values of their own type but do not exclude other JSON types.
    """
    if schema.keys() & IMPLICIT_OBJECT_KEYWORDS:
        return "object"
    if schema.keys() & IMPLICIT_ARRAY_KEYWORDS:
        return "array"
    if schema.keys() & IMPLICIT_STRING_KEYWORDS:
        return "string"
    if schema.keys() & IMPLICIT_NUMBER_KEYWORDS:
        return "number"
    return None

json_type_from_enum

json_type_from_enum(
    values: list[Any],
) -> Literal["boolean", "integer", "number", "string"]

Infer a scalar JSON type, using string for empty or heterogeneous enums.

Source code in src/cdm_data_loaders/converters/core/inference.py
def json_type_from_enum(values: list[Any]) -> Literal["boolean", "integer", "number", "string"]:
    """Infer a scalar JSON type, using string for empty or heterogeneous enums."""
    if not values:
        return "string"
    if all(isinstance(value, bool) for value in values):
        return "boolean"
    if all(isinstance(value, int) and not isinstance(value, bool) for value in values):
        return "integer"
    if all(isinstance(value, (int, float)) and not isinstance(value, bool) for value in values):
        return "number"
    return "string"

is_unconstrained_node

is_unconstrained_node(node: TypedNode) -> bool

Identify false or empty JSON schemas that carry no type information.

Parameters:

Name Type Description Default
node TypedNode

the schema node to inspect

required

Returns:

Type Description
bool

True for a never node or an unknown node with no keywords, metadata or combiners

Source code in src/cdm_data_loaders/converters/core/inference.py
def is_unconstrained_node(node: TypedNode) -> bool:
    """Identify false or empty JSON schemas that carry no type information.

    :param node: the schema node to inspect
    :type node: TypedNode
    :return: True for a ``never`` node or an unknown node with no keywords, metadata or combiners
    :rtype: bool
    """
    if node.type == "never":
        return True
    return (
        node.type == "unknown"
        and node.declared_type is None
        and node.inferred_type is None
        and not node.source_keywords
        and not node.constraints
        and not node.annotations
        and not node.extensions
        and node.one_of is None
        and node.any_of is None
        and not node.schema_keywords
        and not node.schema_maps
    )

decimal_places

decimal_places(value: float | Decimal) -> int

Count fractional digits in the decimal representation; non-finite values return zero.

Source code in src/cdm_data_loaders/converters/core/inference.py
def decimal_places(value: float | Decimal) -> int:
    """Count fractional digits in the decimal representation; non-finite values return zero."""
    exponent = Decimal(str(value)).as_tuple().exponent
    if isinstance(exponent, int):
        return max(-exponent, 0)
    return 0

decimal_pattern

decimal_pattern(precision: int, scale: int) -> str

Build the decimal string pattern from integer precision and scale.

Source code in src/cdm_data_loaders/converters/core/inference.py
def decimal_pattern(precision: int, scale: int) -> str:
    """Build the decimal string pattern from integer precision and scale."""
    digits_before = precision - scale
    if scale > 0:
        if digits_before == 0:
            return rf"^-?0(\.\d{{1,{scale}}})?$"
        return rf"^-?\d{{1,{digits_before}}}(\.\d{{1,{scale}}})?$"
    return rf"^-?\d{{1,{digits_before}}}$"