Skip to content

Validator

The Validator class is the primary interface for data validation.

Overview

from pycharter import Validator

# Create from contract file
validator = Validator.from_file("user_contract.yaml")

# Validate data
result = validator.validate({"name": "Alice", "age": 30})

if result.is_valid:
    print(f"Valid: {result.data}")
else:
    print(f"Errors: {result.errors}")

API Reference

Validator

Validator(contract_dir: str | None = None, contract_file: str | None = None, contract_dict: dict[str, Any] | DataContract | None = None, contract_metadata: ContractMetadata | None = None, store: ContractStoreClient | None = None, contract_name: str | None = None, contract_version: str | None = None)

Generic Validator that performs validation from contract artifacts.

Instantiate with contract files, a directory, or a contract store, then call validate() or validate_batch() on data dicts.

Provide exactly one source: contract_dir, contract_file, contract_dict (dict or DataContract), contract_metadata, or the tuple (store, contract_name, contract_version).

Source code in src/pycharter/runtime_validator/_engine.py
def __init__(
    self,
    contract_dir: str | None = None,
    contract_file: str | None = None,
    contract_dict: dict[str, Any] | DataContract | None = None,
    contract_metadata: ContractMetadata | None = None,
    store: ContractStoreClient | None = None,
    contract_name: str | None = None,
    contract_version: str | None = None,
):
    """Initialize the validator with contract artifacts.

    Provide exactly one source: *contract_dir*, *contract_file*,
    *contract_dict* (dict or DataContract), *contract_metadata*, or the tuple
    (*store*, *contract_name*, *contract_version*).
    """
    if isinstance(contract_dict, DataContract):
        contract_dict = contract_dict.to_dict()
    self.schema: dict[str, Any] | None = None
    self.coercion_rules: dict[str, Any] = {}
    self.validation_rules: dict[str, Any] = {}
    self.model: type[BaseModel] | None = None
    self._schema_from_store: bool = False

    # Builder configuration options
    self._strict_mode: bool = False
    self._include_quality: bool = False
    self._quality_thresholds: dict[str, float] | None = None
    self._metrics_collector: MetricsCollector | None = None
    self._schema_name: str | None = None
    self._schema_version: str = "1.0.0"

    self._dispatch_load(
        contract_dir=contract_dir,
        contract_file=contract_file,
        contract_dict=contract_dict,
        contract_metadata=contract_metadata,
        store=store,
        contract_name=contract_name,
        contract_version=contract_version,
    )
    self._generate_model()

from_file classmethod

from_file(path: str) -> Validator

Create validator from a single contract file.

Source code in src/pycharter/runtime_validator/_engine.py
@classmethod
def from_file(cls, path: str) -> Validator:
    """Create validator from a single contract file."""
    return cls(contract_file=path)

from_files classmethod

from_files(schema: str, coercion_rules: str | None = None, validation_rules: str | None = None) -> Validator

Create validator from explicit file paths.

Source code in src/pycharter/runtime_validator/_engine.py
@classmethod
def from_files(
    cls,
    schema: str,
    coercion_rules: str | None = None,
    validation_rules: str | None = None,
) -> Validator:
    """Create validator from explicit file paths."""
    contract = load_from_explicit_files(schema, coercion_rules, validation_rules)
    return cls(contract_dict=contract)

from_dir classmethod

from_dir(directory: str) -> Validator

Create validator from a directory containing config files.

Source code in src/pycharter/runtime_validator/_engine.py
@classmethod
def from_dir(cls, directory: str) -> Validator:
    """Create validator from a directory containing config files."""
    return cls(contract_dir=directory)

from_dict classmethod

from_dict(schema: dict[str, Any], coercion_rules: dict[str, Any] | None = None, validation_rules: dict[str, Any] | None = None) -> Validator

Create validator from dictionaries.

Source code in src/pycharter/runtime_validator/_engine.py
@classmethod
def from_dict(
    cls,
    schema: dict[str, Any],
    coercion_rules: dict[str, Any] | None = None,
    validation_rules: dict[str, Any] | None = None,
) -> Validator:
    """Create validator from dictionaries."""
    return cls(
        contract_dict={
            "schema": schema,
            "coercion_rules": coercion_rules or {},
            "validation_rules": validation_rules or {},
        }
    )

validate

validate(data: dict[str, Any], strict: bool | None = None, include_quality: bool | None = None) -> ValidationResult

Validate a single data record against the contract.

Parameters:

Name Type Description Default
data dict[str, Any]

Data dictionary to validate.

required
strict bool | None

If True, raise exceptions on validation errors. Defaults to builder setting or False.

None
include_quality bool | None

If True, include quality metrics in result. Defaults to builder setting or False.

None

Returns:

Type Description
ValidationResult

ValidationResult object.

Source code in src/pycharter/runtime_validator/_engine.py
def validate(
    self,
    data: dict[str, Any],
    strict: bool | None = None,
    include_quality: bool | None = None,
) -> ValidationResult:
    """Validate a single data record against the contract.

    Args:
        data: Data dictionary to validate.
        strict: If True, raise exceptions on validation errors.
            Defaults to builder setting or False.
        include_quality: If True, include quality metrics in result.
            Defaults to builder setting or False.

    Returns:
        ValidationResult object.
    """
    if not self.model:
        raise ValueError("Model not initialized. Check contract loading.")

    use_strict = strict if strict is not None else self._strict_mode
    use_quality = (
        include_quality if include_quality is not None else self._include_quality
    )

    start_time = time.perf_counter()
    result = validate(self.model, data, strict=use_strict)
    duration_ms = (time.perf_counter() - start_time) * 1000

    if use_quality:
        result.quality = compute_quality_metrics([data], [result])

    if self._metrics_collector:
        record_metrics(
            self._metrics_collector,
            result,
            duration_ms,
            self._schema_name or "unknown",
            self._schema_version,
        )

    return result

validate_batch

validate_batch(data_list: list[dict[str, Any]], strict: bool | None = None, include_quality: bool | None = None) -> list[ValidationResult]

Validate a batch of data records against the contract.

Parameters:

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

List of data dictionaries to validate.

required
strict bool | None

If True, stop on first validation error. Defaults to builder setting or False.

None
include_quality bool | None

If True, include quality metrics in results. Defaults to builder setting or False.

None

Returns:

Type Description
list[ValidationResult]

List of ValidationResult objects.

Source code in src/pycharter/runtime_validator/_engine.py
def validate_batch(
    self,
    data_list: list[dict[str, Any]],
    strict: bool | None = None,
    include_quality: bool | None = None,
) -> list[ValidationResult]:
    """Validate a batch of data records against the contract.

    Args:
        data_list: List of data dictionaries to validate.
        strict: If True, stop on first validation error.
            Defaults to builder setting or False.
        include_quality: If True, include quality metrics in results.
            Defaults to builder setting or False.

    Returns:
        List of ValidationResult objects.
    """
    if not self.model:
        raise ValueError("Model not initialized. Check contract loading.")

    use_strict = strict if strict is not None else self._strict_mode
    use_quality = (
        include_quality if include_quality is not None else self._include_quality
    )

    start_time = time.perf_counter()
    results = validate_batch(self.model, data_list, strict=use_strict)
    duration_ms = (time.perf_counter() - start_time) * 1000

    if use_quality:
        quality = compute_quality_metrics(data_list, results)
        if results:
            results[-1].quality = quality

    if self._metrics_collector:
        record_batch_metrics(
            self._metrics_collector,
            results,
            duration_ms,
            self._schema_name or "unknown",
            self._schema_version,
        )

    return results

get_model

get_model() -> type[BaseModel]

Get the generated Pydantic model.

Returns:

Type Description
type[BaseModel]

Pydantic model class.

Raises:

Type Description
ValueError

If model not initialized.

Source code in src/pycharter/runtime_validator/_engine.py
def get_model(self) -> type[BaseModel]:
    """Get the generated Pydantic model.

    Returns:
        Pydantic model class.

    Raises:
        ValueError: If model not initialized.
    """
    if not self.model:
        raise ValueError("Model not initialized. Check contract loading.")
    return self.model

Factory Methods

from_file

Create validator from a single contract file:

validator = Validator.from_file("contracts/user.yaml")

from_files

Create from separate files:

validator = Validator.from_files(
    schema="schemas/user.yaml",
    coercion_rules="rules/coercion.yaml",
    validation_rules="rules/validation.yaml"
)

from_dir

Create from a directory containing schema.yaml, coercion_rules.yaml, validation_rules.yaml:

validator = Validator.from_dir("contracts/user/")

from_dict

Create from dictionaries:

validator = Validator.from_dict(
    schema={"type": "object", "properties": {...}},
    coercion_rules={"rules": {...}},
    validation_rules={"rules": {...}}
)

From Contract Store

Create a validator from a contract stored by name and version:

from pycharter import Validator, PostgresContractStore

store = PostgresContractStore("postgresql://...")
store.connect()

validator = Validator(store=store, contract_name="user", contract_version="1.0.0")
result = validator.validate(data)
store.disconnect()

From DataContract

You can pass a DataContract (or dict) directly:

from pycharter import Validator, build_contract_from_store

contract = build_contract_from_store(store, "user", "1.0.0")
validator = Validator(contract_dict=contract)  # accepts DataContract or dict
result = validator.validate(data)

ValidationResult

ValidationResult

ValidationResult(is_valid: bool, data: BaseModel | None = None, errors: list[dict[str, Any]] | None = None, quality: QualityMetrics | None = None, state: str | None = None, trigger: str | None = None, entity_id: str | None = None, contract_name: str | None = None)

Result of a validation operation.

Attributes:

Name Type Description
is_valid

Whether validation passed

data

Validated data (Pydantic model instance) if valid

errors

List of validation errors if invalid

quality

Optional quality metrics

Source code in src/pycharter/runtime_validator/validator_core.py
def __init__(
    self,
    is_valid: bool,
    data: BaseModel | None = None,
    errors: list[dict[str, Any]] | None = None,
    quality: QualityMetrics | None = None,
    # Lifecycle context — set by caller (e.g. bridge code), not by validator
    state: str | None = None,
    trigger: str | None = None,
    entity_id: str | None = None,
    contract_name: str | None = None,
):
    self.is_valid = is_valid
    self.data = data
    self.errors = errors or []
    self.quality = quality
    self.state = state
    self.trigger = trigger
    self.entity_id = entity_id
    self.contract_name = contract_name

is_valid instance-attribute

is_valid = is_valid

data instance-attribute

data = data

errors instance-attribute

errors = errors or []

Examples

Single Validation

result = validator.validate({
    "name": "Alice",
    "email": "alice@example.com",
    "age": 30
})

if result.is_valid:
    user = result.data  # Pydantic model instance
    print(user.name)
else:
    for error in result.errors:
        print(f"{error['loc']}: {error['msg']}")

Batch Validation

records = [
    {"name": "Alice", "email": "alice@example.com"},
    {"name": "", "email": "invalid"},  # Invalid
    {"name": "Charlie", "email": "charlie@example.com"},
]

results = validator.validate_batch(records)

valid_records = [r.data for r in results if r.is_valid]
print(f"Valid: {len(valid_records)}/{len(records)}")

Strict Mode

from pydantic import ValidationError

try:
    result = validator.validate(data, strict=True)
    # If we get here, data is valid
except ValidationError as e:
    print(f"Validation failed: {e}")

Getting the Model

# Access the generated Pydantic model
UserModel = validator.get_model()

# Use it directly
user = UserModel(name="Alice", email="alice@example.com")

# Export schema
print(UserModel.model_json_schema())

Convenience Functions

validate_with_contract

Quick validation without creating a Validator instance. Accepts a contract file path, a DataContract, or a dict:

from pycharter import validate_with_contract

result = validate_with_contract("contract.yaml", data)
result = validate_with_contract(contract_dict, data)  # DataContract or dict

validate_with_store

Validate using a contract from the contract store (by contract name and version):

from pycharter import validate_with_store

result = validate_with_store(store, "user", "1.0.0", data)

See Also