Validationο
SLOTH provides a layered validation system built on the plugin system. Two ready-to-use validator classes and a library of composable rule factories cover everything from mmCIF dictionary conformance to wwPDB deposition business rules.
All validation code lives in sloth.mmcif.validator.
Quick Startο
The simplest way to validate is with MMCIFValidator:
from sloth import MMCIFHandler, MMCIFValidator
handler = MMCIFHandler()
mmcif = handler.read("model.cif")
# Full validation (dictionary schema + wwPDB rules)
vp = MMCIFValidator()
report = vp.validate(mmcif)
print(report.is_valid) # True / False
print(len(report.errors)) # number of ERROR-level issues
print(len(report.warnings)) # number of WARNING-level issues
# Raise on first error
report.raise_on_error()
validate() is polymorphic β it works on a single
Category, a
DataBlock, or an entire
MMCIFDataContainer.
Per-Category Validationο
Register a validator on a model to get dot-notation access:
from sloth import MMCIFValidator
vp = MMCIFValidator()
block = mmcif.data_1ABC
# Register on a category
block._refine.register("validate", vp)
block._refine.validate()
# Cross-category validation
block._entity.register("validate", vp)
block._entity.validate().against(block._atom_site)
Validator Classesο
SLOTH ships two validator classes in sloth.mmcif.validator, both subclasses
of ValidatorPlugin:
SchemaValidatorAuto-generated from the bundled
mmcif_pdbx_v50.dic(or any mmCIF dictionary). Covers mandatory items, enumerations, type-regex patterns, foreign keys, composite keys, and parent/child category presence β all extracted viaDictionaryParser.MMCIFValidatorExtends
SchemaValidatorwith wwPDB deposition business rules expressed as declarative class-level data tables. Adding a new rule is as simple as appending a tuple.
Use them directly:
from sloth.mmcif.validator import SchemaValidator, MMCIFValidator
# Schema-only (no wwPDB rules)
schema_vp = SchemaValidator()
report = schema_vp.validate(mmcif)
# Full wwPDB + schema
full_vp = MMCIFValidator()
report = full_vp.validate(mmcif)
Multi-Level Validatorsο
For validating entire blocks or containers via the plugin interface (dot-notation):
DataBlockValidatorWraps a
ValidatorPluginand runs all per-category validators + cross- category checkers across every category in aDataBlock. Returns aValidationReport.ContainerValidatorDelegates to
DataBlockValidatorfor each block in anMMCIFDataContainer.
Register them on models for dot-notation access:
from sloth.mmcif.validator import (
MMCIFValidator, DataBlockValidator, ContainerValidator,
)
vp = MMCIFValidator()
bv = DataBlockValidator(vp)
cv = ContainerValidator(bv)
# Register on a container for one-call validation
mmcif.register("validate", cv)
wrapper = mmcif.validate()
wrapper.report.raise_on_error()
# Or register on a block
block.register("validate", bv)
wrapper = block.validate()
print(wrapper.report.is_valid)
Validation Reportο
ValidationReport collects all
ValidationError instances:
report = vp.validate(container)
report.is_valid # True if no ERROR-level issues
report.errors # list of ERROR-level ValidationError
report.warnings # list of WARNING-level ValidationError
report.all_issues # everything (ERROR + WARNING + INFO)
report.raise_on_error() # raises the first ERROR, or does nothing
Single-Category Validationο
# Register validator, then use dot-notation
block._atom_site.register("validate", vp)
block._atom_site.validate()
Cross-Category Validationο
The built-in validators register cross-checkers automatically for FK,
parent/child, and ordering constraints. Chain .against() to run them:
block._entity_src_nat.register("validate", vp)
block._entity_src_nat.validate().against(block._entity)
You can also register custom cross-checkers on a ValidatorPlugin:
vp = ValidatorPlugin()
vp.register_cross_checker(
("_entity", "_atom_site"),
lambda e, a: check_entity_coverage(e, a),
)
Custom Rules with Factoriesο
The sloth.mmcif.validator module exports 18 composable factory functions
that return validator callables. Use them to build a custom
ValidatorPlugin:
from sloth.mmcif.validator import ValidatorPlugin
from sloth.mmcif.validator import (
mandatory_items,
value_length,
ordering_check,
foreign_key,
)
vp = ValidatorPlugin()
# Category-level rules
vp.register_validator("_struct", mandatory_items(["title"]))
vp.register_validator("_struct", value_length("title", min_len=10))
# Cross-category rule
vp.register_cross_checker(
("_atom_site", "_entity"),
foreign_key("label_entity_id", "id"),
)
# Validate directly
report = vp.validate(mmcif)
# Or register on a model for dot-notation
block._struct.register("validate", vp)
block._struct.validate()
Single-category factories:
mandatory_items()β items must be non-nullone_of_following()β at least one item must be setvalue_length()β string length boundsvalue_range()β numeric boundsconditional_mandatory()β items required when a trigger has specific valuesregex_check()β values must match a regexordering_check()β numeric ordering between two items in the same categoryallowed_pairs()β restrict value combinationsmin_rows()β minimum row countenumeration_check()β values must be in an allowed settype_check()β values must match a type regex
Cross-category factories:
foreign_key()β FK integrity across categoriesparent_child()β parent must exist when child doescomposite_key()β multi-column FK integrityoper_expression()β validateoper_expressionreferences against_pdbx_struct_oper_listcross_mandatory()β items required in cat B when cat A is presentcross_ordering()β numeric ordering across two categories
Validation Severityο
Every rule factory accepts a severity parameter:
from sloth import ValidationError, ValidationSeverity
from sloth.mmcif.validator import value_range
# ERROR β prevents processing (default for most factories)
# WARNING β flags potential issues
# INFO β informational notices
check = value_range("defocus", min_val=0, max_val=200,
severity=ValidationSeverity.WARNING)
Extending MMCIFValidatorο
To add wwPDB rules, subclass MMCIFValidator and
extend the declarative tables:
from sloth.mmcif.validator import MMCIFValidator
class MyValidator(MMCIFValidator):
# Add mandatory items for a custom category
_MANDATORY = MMCIFValidator._MANDATORY + [
("_my_category", ["required_field_a", "required_field_b"]),
]
Or add rules at runtime after instantiation:
from sloth.mmcif.validator import MMCIFValidator, regex_check
v = MMCIFValidator()
v.register_validator("_my_category", regex_check("code", r"^[A-Z]{3}$"))