Skip to content

Validation

validate_document() checks a document against the epJSON schema: required fields, value types, numeric ranges, enum choices, and reference integrity.

On-demand validation system for IDF documents.

Provides validation against EpJSON schema without requiring eager validation during parsing.

Severity

Bases: Enum

Validation issue severity levels.

Source code in src/idfkit/validation.py
class Severity(Enum):
    """Validation issue severity levels."""

    ERROR = "error"
    WARNING = "warning"
    INFO = "info"

ValidationError dataclass

Represents a validation issue.

Attributes:

Name Type Description
severity Severity

Issue severity (ERROR, WARNING, INFO)

obj_type str

Object type where issue was found

obj_name str

Object name where issue was found

field str | None

Field name where issue was found (if applicable)

message str

Human-readable description

code str

Machine-readable error code

Source code in src/idfkit/validation.py
@dataclass
class ValidationError:
    """
    Represents a validation issue.

    Attributes:
        severity: Issue severity (ERROR, WARNING, INFO)
        obj_type: Object type where issue was found
        obj_name: Object name where issue was found
        field: Field name where issue was found (if applicable)
        message: Human-readable description
        code: Machine-readable error code
    """

    severity: Severity
    obj_type: str
    obj_name: str
    field: str | None
    message: str
    code: str

    def __str__(self) -> str:
        location = f"{self.obj_type}:'{self.obj_name}'"
        if self.field:
            location += f".{self.field}"
        return f"[{self.severity.value.upper()}] {location}: {self.message}"

ValidationResult dataclass

Result of document validation.

Attributes:

Name Type Description
errors list[ValidationError]

List of validation errors

warnings list[ValidationError]

List of validation warnings

info list[ValidationError]

List of informational messages

Source code in src/idfkit/validation.py
@dataclass
class ValidationResult:
    """
    Result of document validation.

    Attributes:
        errors: List of validation errors
        warnings: List of validation warnings
        info: List of informational messages
    """

    errors: list[ValidationError]
    warnings: list[ValidationError]
    info: list[ValidationError]

    @property
    def is_valid(self) -> bool:
        """True if there are no errors."""
        return len(self.errors) == 0

    @property
    def total_issues(self) -> int:
        """Total number of issues found."""
        return len(self.errors) + len(self.warnings) + len(self.info)

    def __str__(self) -> str:
        lines = [f"Validation: {len(self.errors)} errors, {len(self.warnings)} warnings"]
        for err in self.errors[:10]:
            lines.append(f"  {err}")
        if len(self.errors) > 10:
            lines.append(f"  ... and {len(self.errors) - 10} more errors")
        return "\n".join(lines)

    def __bool__(self) -> bool:
        return self.is_valid

is_valid property

True if there are no errors.

total_issues property

Total number of issues found.

validate_document(doc, schema=None, check_references=True, check_required=True, check_types=True, check_ranges=True, object_types=None)

Validate an IDF document against schema.

Parameters:

Name Type Description Default
doc IDFDocument

The document to validate

required
schema EpJSONSchema | None

Schema to validate against (uses doc's schema if not provided)

None
check_references bool

Check reference integrity

True
check_required bool

Check required fields

True
check_types bool

Check field types

True
check_ranges bool

Check numeric ranges

True
object_types list[str] | None

Only validate these types (None = all)

None

Returns:

Type Description
ValidationResult

ValidationResult with all issues found

Examples:

Validate a model before running a simulation:

>>> from idfkit import new_document, validate_document
>>> model = new_document()
>>> model.add("Zone", "Perimeter_ZN_1")
Zone('Perimeter_ZN_1')
>>> result = validate_document(model)
>>> result.is_valid
True
>>> result.total_issues
0

Validate only material and construction definitions:

>>> result = validate_document(model, object_types=["Material", "Construction"])
>>> result.is_valid
True
Source code in src/idfkit/validation.py
def validate_document(  # noqa: C901
    doc: IDFDocument,
    schema: EpJSONSchema | None = None,
    check_references: bool = True,
    check_required: bool = True,
    check_types: bool = True,
    check_ranges: bool = True,
    object_types: list[str] | None = None,
) -> ValidationResult:
    """
    Validate an IDF document against schema.

    Args:
        doc: The document to validate
        schema: Schema to validate against (uses doc's schema if not provided)
        check_references: Check reference integrity
        check_required: Check required fields
        check_types: Check field types
        check_ranges: Check numeric ranges
        object_types: Only validate these types (None = all)

    Returns:
        ValidationResult with all issues found

    Examples:
        Validate a model before running a simulation:

        >>> from idfkit import new_document, validate_document
        >>> model = new_document()
        >>> model.add("Zone", "Perimeter_ZN_1")  # doctest: +ELLIPSIS
        Zone('Perimeter_ZN_1')
        >>> result = validate_document(model)
        >>> result.is_valid
        True
        >>> result.total_issues
        0

        Validate only material and construction definitions:

        >>> result = validate_document(model, object_types=["Material", "Construction"])
        >>> result.is_valid
        True
    """
    schema = schema or doc.schema

    errors: list[ValidationError] = []
    warnings: list[ValidationError] = []
    info: list[ValidationError] = []

    if schema is None:
        warnings.append(
            ValidationError(
                severity=Severity.WARNING,
                obj_type="Document",
                obj_name="",
                field=None,
                message="No schema available - skipping schema validation",
                code="W001",
            )
        )
        return ValidationResult(errors, warnings, info)

    # Determine which object types to validate
    types_to_check = object_types or list(doc.collections.keys())
    logger.debug("Validating %d object type(s)", len(types_to_check))

    for obj_type in types_to_check:
        if obj_type not in doc.collections:
            continue

        for obj in doc[obj_type]:
            obj_errors = _validate_object(
                obj,
                schema,
                check_required=check_required,
                check_types=check_types,
                check_ranges=check_ranges,
            )

            for err in obj_errors:
                if err.severity == Severity.ERROR:
                    errors.append(err)
                elif err.severity == Severity.WARNING:
                    warnings.append(err)
                else:
                    info.append(err)

    # Check reference integrity
    if check_references:
        ref_errors = _validate_references(doc, schema)
        for err in ref_errors:
            if err.severity == Severity.ERROR:
                errors.append(err)
            elif err.severity == Severity.WARNING:
                warnings.append(err)

    result = ValidationResult(errors, warnings, info)
    logger.info(
        "Validation complete: %d error(s), %d warning(s), %d info",
        len(errors),
        len(warnings),
        len(info),
    )
    return result

validate_object(obj, schema, *, check_required=True, check_types=True, check_ranges=True, check_unknown=True)

Validate a single object against schema.

This is a public API for validating individual objects, useful for checking objects at creation time with the validate=True option.

Parameters:

Name Type Description Default
obj IDFObject

The IDFObject to validate

required
schema EpJSONSchema

The EpJSON schema to validate against

required
check_required bool

Check that required fields are present

True
check_types bool

Check that field values match expected types

True
check_ranges bool

Check that numeric values are within bounds

True
check_unknown bool

Check for unknown fields (not in schema)

True

Returns:

Type Description
list[ValidationError]

List of ValidationError objects describing any issues found

Examples:

Check a newly created zone for schema violations:

>>> from idfkit import new_document, validate_object, get_schema, LATEST_VERSION
>>> model = new_document()
>>> zone = model.add("Zone", "Perimeter_ZN_1")
>>> errors = validate_object(zone, get_schema(LATEST_VERSION))
>>> len(errors)
0
Source code in src/idfkit/validation.py
def validate_object(
    obj: IDFObject,
    schema: EpJSONSchema,
    *,
    check_required: bool = True,
    check_types: bool = True,
    check_ranges: bool = True,
    check_unknown: bool = True,
) -> list[ValidationError]:
    """
    Validate a single object against schema.

    This is a public API for validating individual objects, useful for
    checking objects at creation time with the validate=True option.

    Args:
        obj: The IDFObject to validate
        schema: The EpJSON schema to validate against
        check_required: Check that required fields are present
        check_types: Check that field values match expected types
        check_ranges: Check that numeric values are within bounds
        check_unknown: Check for unknown fields (not in schema)

    Returns:
        List of ValidationError objects describing any issues found

    Examples:
        Check a newly created zone for schema violations:

        >>> from idfkit import new_document, validate_object, get_schema, LATEST_VERSION
        >>> model = new_document()
        >>> zone = model.add("Zone", "Perimeter_ZN_1")
        >>> errors = validate_object(zone, get_schema(LATEST_VERSION))
        >>> len(errors)
        0
    """
    return _validate_object(
        obj,
        schema,
        check_required=check_required,
        check_types=check_types,
        check_ranges=check_ranges,
        check_unknown=check_unknown,
    )