Skip to content

type_bridge.validation

validation

Validation utilities for TypeBridge.

This module provides validation functions to ensure type names, attribute names, and role names don't conflict with TypeQL reserved words/keywords.

ValidationError

Bases: ValueError

Base class for validation errors in TypeBridge.

ReservedWordError

ReservedWordError(word, context, suggestion=None)

Bases: ValidationError

Raised when a type name conflicts with a TypeQL reserved word.

This error is raised when attempting to use a TypeQL keyword as a: - Entity type name - Relation type name - Attribute type name - Role name

Initialize ReservedWordError.

Parameters:

Name Type Description Default
word str

The reserved word that was used

required
context Literal['entity', 'relation', 'attribute', 'role']

What kind of name was being validated

required
suggestion str | None

Optional suggested alternative name

None
Source code in type_bridge/validation.py
def __init__(
    self,
    word: str,
    context: Literal["entity", "relation", "attribute", "role"],
    suggestion: str | None = None,
):
    """Initialize ReservedWordError.

    Args:
        word: The reserved word that was used
        context: What kind of name was being validated
        suggestion: Optional suggested alternative name
    """
    self.word = word
    self.context = context
    self.suggestion = suggestion

    message = self._build_message()
    super().__init__(message)

validate_type_name

validate_type_name(name, context)

Validate that a type name doesn't conflict with TypeQL reserved words.

Parameters:

Name Type Description Default
name str

The type name to validate

required
context Literal['entity', 'relation', 'attribute', 'role']

What kind of name is being validated

required

Raises:

Type Description
ReservedWordError

If the name is a TypeQL reserved word

ValidationError

If the name is invalid for other reasons

Source code in type_bridge/validation.py
def validate_type_name(
    name: str, context: Literal["entity", "relation", "attribute", "role"]
) -> None:
    """Validate that a type name doesn't conflict with TypeQL reserved words.

    Args:
        name: The type name to validate
        context: What kind of name is being validated

    Raises:
        ReservedWordError: If the name is a TypeQL reserved word
        ValidationError: If the name is invalid for other reasons
    """
    logger.debug(f"Validating {context} name: {name}")

    # Try Rust validation engine first
    if _rust_validator is not None:
        try:
            _rust_validator.validate_type_name(name, context)
            return
        except ValueError as exc:
            msg = str(exc)
            # Map Rust errors back to the correct Python exception types
            if "reserved word" in msg.lower():
                raise ReservedWordError(name, context) from exc
            raise ValidationError(msg) from exc

    # Python fallback
    if not name:
        logger.warning(f"Empty {context} name attempted")
        raise ValidationError(f"Empty {context} name is not allowed")

    # Check for reserved words (case-insensitive to be safe)
    if is_reserved_word(name):
        logger.warning(f"Reserved word used as {context} name: {name}")
        raise ReservedWordError(name, context)

    # TypeQL 3.8.0 uses XID_START and XID_CONTINUE for identifier validation
    # XID_START: Unicode letters (Lu, Ll, Lt, Lm, Lo, Nl) and underscore
    # XID_CONTINUE: XID_START + combining marks (Mn, Mc), digits (Nd), connector (Pc), hyphen
    if not _is_xid_start(name[0]):
        logger.warning(
            f"{context.capitalize()} name '{name}' does not start with a valid character"
        )
        raise ValidationError(
            f"{context.capitalize()} name '{name}' must start with a letter or underscore"
        )

    # Check remaining characters
    for char in name[1:]:
        if not _is_xid_continue(char):
            logger.warning(
                f"{context.capitalize()} name '{name}' contains invalid character: {char}"
            )
            raise ValidationError(
                f"{context.capitalize()} name '{name}' contains invalid character '{char}'. "
                f"Only letters, numbers, underscores, hyphens, and combining marks are allowed."
            )

validate_query_against_schema

validate_query_against_schema(clauses, schema, *, strict=False)

Validate parsed query clauses against a TypeSchema.

This performs semantic validation: ownership checks, role validation, value type compatibility, abstract type instantiation, and cardinality hints.

Parameters:

Name Type Description Default
clauses list[dict[str, Any]]

Parsed clauses (from QueryCompiler().parse() or manual construction).

required
schema Any

A TypeSchema instance from type_bridge_core.

required
strict bool

If True, raise ImportError when the Rust core is unavailable and treat warnings as errors.

False

Returns:

Type Description
dict[str, Any]

Dict with is_valid (bool) and errors (list of error dicts).

dict[str, Any]

Each error dict has code, message, path, severity keys.

Source code in type_bridge/validation.py
def validate_query_against_schema(
    clauses: list[dict[str, Any]],
    schema: Any,
    *,
    strict: bool = False,
) -> dict[str, Any]:
    """Validate parsed query clauses against a TypeSchema.

    This performs semantic validation: ownership checks, role validation,
    value type compatibility, abstract type instantiation, and cardinality hints.

    Args:
        clauses: Parsed clauses (from ``QueryCompiler().parse()`` or manual construction).
        schema: A ``TypeSchema`` instance from ``type_bridge_core``.
        strict: If ``True``, raise ``ImportError`` when the Rust core is unavailable
            and treat warnings as errors.

    Returns:
        Dict with ``is_valid`` (bool) and ``errors`` (list of error dicts).
        Each error dict has ``code``, ``message``, ``path``, ``severity`` keys.
    """
    try:
        from type_bridge_core import (
            ValidationEngine as _RustEngine,  # type: ignore[import-not-found]
        )

        engine = _RustEngine()
        result: dict[str, Any] = engine.validate_query(clauses, schema)

        if strict:
            has_issues = any(
                e.get("severity") in ("Error", "Warning") for e in result.get("errors", [])
            )
            result["is_valid"] = not has_issues

        return result

    except ImportError:
        if strict:
            raise ImportError(
                "Schema-aware query validation requires the type-bridge-core Rust extension"
            ) from None
        # Graceful skip: return valid with no errors.
        return {"is_valid": True, "errors": []}

validate_entity_data

validate_entity_data(entity_data, rules_json, schema=None)

Validate entity data against custom validation rules.

Uses the Rust ValidationEngine to evaluate rules defined in the portable JSON DSL (see :class:~type_bridge.rules.RuleBuilder).

Parameters:

Name Type Description Default
entity_data dict[str, Any]

Dict with __type__ key and attribute values.

required
rules_json str

JSON string of validation rules.

required
schema Any

Optional TypeSchema instance for ownership checks.

None

Returns:

Type Description
dict[str, Any]

Dict with is_valid (bool) and errors (list of error dicts).

Source code in type_bridge/validation.py
def validate_entity_data(
    entity_data: dict[str, Any],
    rules_json: str,
    schema: Any = None,
) -> dict[str, Any]:
    """Validate entity data against custom validation rules.

    Uses the Rust ``ValidationEngine`` to evaluate rules defined in the
    portable JSON DSL (see :class:`~type_bridge.rules.RuleBuilder`).

    Args:
        entity_data: Dict with ``__type__`` key and attribute values.
        rules_json: JSON string of validation rules.
        schema: Optional ``TypeSchema`` instance for ownership checks.

    Returns:
        Dict with ``is_valid`` (bool) and ``errors`` (list of error dicts).
    """
    try:
        from type_bridge_core import (
            ValidationEngine as _RustEngine,  # type: ignore[import-not-found]
        )

        engine = _RustEngine()
        engine.load_rules(rules_json)
        result: dict[str, Any] = engine.validate_entity(entity_data, schema)
        return result

    except ImportError:
        # Graceful skip: return valid with no errors.
        return {"is_valid": True, "errors": []}