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 handler.validate():
from sloth import MMCIFHandler
handler = MMCIFHandler()
mmcif = handler.read("model.cif")
# Full validation (dictionary schema + wwPDB rules)
report = handler.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.
Relaxed Modeο
Pass relaxed=True to skip the built-in MmcifValidator and run only
user-registered custom rules:
report = handler.validate(mmcif, relaxed=True)
Per-Category Validationο
You can also validate individual categories via the plugin interface:
# Per-category validation (raises ValidationError on failure)
mmcif.data_1ABC._refine.validate()
# Cross-category validation
mmcif.data_1ABC._entity.validate.against(mmcif.data_1ABC._atom_site)
Validator Classesο
SLOTH ships two validator classes in sloth.mmcif.validator, both subclasses
of ValidatorPlugin:
DictionaryValidatorAuto-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
DictionaryValidatorwith 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 when you want explicit control:
from sloth import MMCIFHandler, PluginScope
from sloth.mmcif.validator import DictionaryValidator, MmcifValidator
handler = MMCIFHandler()
# Schema-only (no wwPDB rules)
handler.register("validate", DictionaryValidator(), scope=PluginScope.CATEGORY)
# Full wwPDB + schema
handler.register("validate", MmcifValidator(), scope=PluginScope.CATEGORY)
Multi-Level Validatorsο
For validating entire blocks or containers in one call:
BlockValidatorWraps a
ValidatorPluginand runs all per-category validators + cross- category checkers across every category in aDataBlock. Returns aValidationReport.ContainerValidatorDelegates to
BlockValidatorfor each block in anMMCIFDataContainer.
These are used internally by handler.validate() but can also be used
directly:
from sloth.mmcif.validator import BlockValidator, MmcifValidator
bv = BlockValidator(MmcifValidator())
report = bv.execute(block)
report.raise_on_error()
Validation Reportο
ValidationReport collects all
ValidationError instances:
report = handler.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ο
mmcif.data_1ABC._atom_site.validate()
Cross-Category Validationο
Register a cross-checker by passing a tuple of category names, or use the built-in validators which register cross-checkers automatically:
# The built-in validators already register FK / parent-child / ordering
# cross-checkers. Just chain .against():
mmcif.data_1ABC._entity_src_nat.validate().against(
mmcif.data_1ABC._entity
)
You can also register custom cross-checkers:
from sloth import PluginScope
handler.register(
("_entity", "_atom_site"),
lambda e, a: check_entity_coverage(e, a),
scope=PluginScope.CATEGORY,
)
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"),
)
handler.register("validate", vp, scope=PluginScope.CATEGORY)
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}$"))