torchcell.datamodels.schema.ProvenanceGapMixin

class ProvenanceGapMixin(*, provenance_gaps: list[~torchcell.verification.sourced.ProvenanceGap] = <factory>)[source]

Bases: ModelStrict

Mixin giving a model a provenance_gaps list of typed field-absences.

Shared by Phenotype, Environment, and Compound so a field the source does not carry – a phenotype n_samples a secondary curation layer dropped, an environment temperature YeastPhenome never recorded, a compound inchikey no resolver could map – is a documented, typed ABSENCE (with a reason + looked_in) rather than a guess or a silent None. Two invariants keep it honest and machine-checkable: (1) each gap.field names a real field on the concrete model (checked against model_fields, so inherited fields resolve too); (2) a gapped field must be None – you cannot both store a value and declare it missing. provenance_gaps itself cannot be gapped.

#753 adds the PER-KEY form of invariant (2). A dict-valued field that is sourced for some keys and unsourced for others (Rapp 2026’s target_metabolite_ids: 1,077 features with a sourced identity, 244 merged isobaric features with 2 to 11 candidates each and no sourced pick) cannot be None, so the whole-field rule refused the gap and the 244 keys had to live outside the record. A gap that names keys is admitted beside a populated mapping, on the strictly stronger condition that the mapping does NOT carry any of those keys: storing a value and declaring it missing is still refused, now key by key.

validate_provenance_gaps() → ProvenanceGapMixin[source]

Each ProvenanceGap must name a real field on this model, and that field must be None (you cannot both store a value and declare it missing).

gapped_fields() → set[str][source]

Names of the fields this record declares as typed absences.

model_config: ClassVar[ConfigDict] = {'extra': 'forbid', 'frozen': True}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].