graflo.architecture.evolution.inverse¶
Inverses for the subset of contract operations that have one.
Several ops are lossy — merge_vertices discards which source each property
came from, change_field_types discards the previous type when narrowing,
sanitize and project_manifest drop material outright. There is no
information anywhere from which to reconstruct the prior state, so a generic
downgrade() is not achievable and pretending otherwise would produce a
manifest that merely looks restored.
What is achievable is an inverse for the reversible subset, computed against the
pre-state manifest: inverting remove_vertices requires the removed
:class:Vertex models, and they exist only before the op runs.
:func:invert_op returns None for an irreversible op. Callers decide what
that means; :mod:~graflo.architecture.evolution.revision prefers replaying
from a base, which is always correct, and only falls back to inverses when no
base is available.
Attributes¶
IRREVERSIBLE = {'merge_vertices': 'merging discards which source each property and identity came from', 'merge_edges': "merging discards the source relations' individual definitions", 'change_field_types': 'the previous field type is not recoverable once overwritten', 'sanitize': 'renames are flavor-driven and not recorded per element', 'project_manifest': 'projection drops elements outright', 'merge_manifests': 'merge is binary; there is no single prior manifest', 'add_resource_transforms': 'appended pipeline steps are not tracked per-op; there is no remove_resource_transforms op', 'ensure_extracted_fields': 'widened projections are not tracked per-op; there is no narrowing op'}
module-attribute
¶
__all__ = ['IRREVERSIBLE', 'invert_op', 'invert_ops', 'irreversible_reason', 'is_reversible']
module-attribute
¶
logger = logging.getLogger(__name__)
module-attribute
¶
Classes¶
Functions:¶
invert_op(op, *, manifest)
¶
The op undoing op, computed against the pre-state manifest.
Returns None when op is irreversible, and when it is reversible in
general but not from this manifest. manifest must be the manifest as it
was before op was applied — that is where the information an inverse
needs still exists.
An inverse is exact or absent. The handlers derive a candidate from the op's payload, and the payload says what the op named, not what it did: a removal names a property the type never had and is skipped forward, a relation-addressed op meets edges that disagree, a removal cascades over elements no single op restores. So the candidate is replayed, and one that does not land back on manifest is not offered.
Source code in graflo/architecture/evolution/inverse.py
invert_ops(ops, *, manifest)
¶
Inverses for ops in reverse order, plus reasons for any that lack one.
Each inverse is computed against the state before its own op, so the ops are replayed forward to reconstruct those intermediate states.
Source code in graflo/architecture/evolution/inverse.py
irreversible_reason(op)
¶
Why op cannot be inverted, or None when it can.
canonicalize is reversible exactly when it only renames: a group of
more than one class or relation is a merge, which discards where each
property and identity came from.