graflo.architecture.evolution.compare¶
Two versions of a schema, side by side.
:func:compare_schemas lines up the vertex types, relations and fields of two
manifests as rows -- one row per element, renamed or not -- with each side's
values. Given their common ancestor, rows are anchored on it and each side's
recorded renames (:class:RenameHints, ancestor → side) say where an ancestor
element went; without one, rows pair by name, then by canonical name.
Every row carries the slots :func:merge_three_way addresses it by, so
:meth:SchemaComparison.locate finds the row a merge conflict is about.
A derived view of the schema, not part of the contract: nothing here is hashed or stored.
Attributes¶
CompareStatus = Literal['unchanged', 'added', 'removed', 'renamed', 'changed']
module-attribute
¶
A row read left → right: added is only on the right, removed only on the left.
EdgeKey = tuple[str, str, str | None]
module-attribute
¶
Side = Literal['left', 'right']
module-attribute
¶
__all__ = ['CompareFocus', 'CompareStatus', 'FieldRow', 'FieldSide', 'RelationRow', 'RelationSide', 'SchemaComparison', 'VertexRow', 'VertexSide', 'compare_schemas']
module-attribute
¶
Classes¶
CompareFocus
¶
Bases: ConfigBaseModel
Where a merge slot lands among the rows.
Source code in graflo/architecture/evolution/compare.py
FieldRow
¶
Bases: ConfigBaseModel
A field (vertex property or edge property) on both sides.
Source code in graflo/architecture/evolution/compare.py
Attributes¶
aspects = PydanticField(default_factory=list, description='What differs between the sides: name, type, description, identity.')
class-attribute
instance-attribute
¶
changed_on = PydanticField(default_factory=list, description='The sides that changed it since the common ancestor.')
class-attribute
instance-attribute
¶
id
instance-attribute
¶
left = None
class-attribute
instance-attribute
¶
right = None
class-attribute
instance-attribute
¶
slots = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
status
instance-attribute
¶
FieldSide
¶
Bases: ConfigBaseModel
One side of a field row.
Source code in graflo/architecture/evolution/compare.py
Attributes¶
description = None
class-attribute
instance-attribute
¶
in_identity = PydanticField(default=False, description='Whether the field is part of the primary key.')
class-attribute
instance-attribute
¶
name
instance-attribute
¶
type = PydanticField(default=None, description='``"INT"``, ``"LIST[STRING]"``; None when untyped.')
class-attribute
instance-attribute
¶
RelationRow
¶
Bases: ConfigBaseModel
An edge on both sides, with its properties.
Rows are keyed by their endpoint rows, so an edge whose endpoint was renamed stays on one row; an edge moved to other endpoints is a removed and an added row.
Source code in graflo/architecture/evolution/compare.py
Attributes¶
aspects = PydanticField(default_factory=list, description='What differs: name, description, directed, identity, fields.')
class-attribute
instance-attribute
¶
changed_on = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
fields = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
id
instance-attribute
¶
left = None
class-attribute
instance-attribute
¶
right = None
class-attribute
instance-attribute
¶
slots = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
status
instance-attribute
¶
RelationSide
¶
Bases: ConfigBaseModel
One side of a relation row: one edge.
Source code in graflo/architecture/evolution/compare.py
Attributes¶
description = None
class-attribute
instance-attribute
¶
directed = True
class-attribute
instance-attribute
¶
identities = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
relation = None
class-attribute
instance-attribute
¶
source
instance-attribute
¶
source_row = PydanticField(description="The id of the source's vertex row.")
class-attribute
instance-attribute
¶
target
instance-attribute
¶
target_row = PydanticField(description="The id of the target's vertex row.")
class-attribute
instance-attribute
¶
SchemaComparison
¶
Bases: ConfigBaseModel
Two schemas as aligned rows.
Source code in graflo/architecture/evolution/compare.py
Attributes¶
aligned_on_base = PydanticField(description='Rows are anchored on the common ancestor (else paired by name).')
class-attribute
instance-attribute
¶
base_label = None
class-attribute
instance-attribute
¶
counts = PydanticField(default_factory=dict, description='Vertex and relation rows per status.')
class-attribute
instance-attribute
¶
left_label = None
class-attribute
instance-attribute
¶
relations = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
right_label = None
class-attribute
instance-attribute
¶
vertices = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
warnings = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
Methods:¶
locate(slot)
¶
The row (and field row) a merge slot addresses, or None.
The deepest row slot that covers slot wins; what slot says beyond it
is the aspect (("vertex", "company", "description") → the Company row,
aspect description). Match on the slot, never on its label: a
canonical name may itself contain /.
Source code in graflo/architecture/evolution/compare.py
VertexRow
¶
Bases: ConfigBaseModel
A vertex type on both sides, with its fields.
Source code in graflo/architecture/evolution/compare.py
Attributes¶
aspects = PydanticField(default_factory=list, description='What differs: name, description, identity, secondary, fields.')
class-attribute
instance-attribute
¶
changed_on = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
fields = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
id
instance-attribute
¶
left = None
class-attribute
instance-attribute
¶
right = None
class-attribute
instance-attribute
¶
slots = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
status
instance-attribute
¶
VertexSide
¶
Bases: ConfigBaseModel
One side of a vertex row.
Source code in graflo/architecture/evolution/compare.py
Attributes¶
description = None
class-attribute
instance-attribute
¶
identity = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
identity_mode = PydanticField(description='natural, hash, funnel, assigned or blank.')
class-attribute
instance-attribute
¶
name
instance-attribute
¶
secondary_identities = PydanticField(default_factory=list)
class-attribute
instance-attribute
¶
Functions:¶
compare_schemas(left, right, *, base=None, left_hints=None, right_hints=None, hints=None, left_label=None, right_label=None, base_label=None)
¶
Align the schemas of left and right into rows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
left
|
GraphManifest
|
One version. |
required |
right
|
GraphManifest
|
The other version. |
required |
base
|
GraphManifest | None
|
Their common ancestor. Rows are anchored on it, and each row's
|
None
|
left_hints
|
RenameHints | None
|
Renames from base to left, as :func: |
None
|
right_hints
|
RenameHints | None
|
Renames from base to right. |
None
|
hints
|
RenameHints | None
|
Renames from left to right; read only without base. |
None
|
left_label
|
str | None
|
The left side's name (a version), echoed back. |
None
|
right_label
|
str | None
|
The right side's name. |
None
|
base_label
|
str | None
|
The ancestor's name. |
None
|
Returns:
| Type | Description |
|---|---|
SchemaComparison
|
The rows in declaration order: the ancestor's elements first, then what |
SchemaComparison
|
the left side added, then what only the right side has. |