graflo.architecture.evolution.equivalence¶
Equivalence clusters over merge-time vertex/relation mappings.
A :class:~graflo.architecture.evolution.ops.VertexEquivalence (or
:class:~graflo.architecture.evolution.ops.RelationEquivalence) declares one
n-ary cluster directly: left / right name one or more members on each
side, collapsing onto one merged name. :func:index_clusters is the
consistency check over the declared clusters of one
:class:~graflo.architecture.evolution.ops.MergeManifestsOp — there is no
connected-component search left to do (one declaration is one cluster); it
validates that the declarations do not overlap or collapse into each other by
accident:
- no
(side, name)may be claimed by two declarations — that is the author's job to state as one cluster, not two; - two declarations must not share one merged name — sharing one collapses them into one merged class, which must be spelled as one n-ary cluster so it is visible to review, not left implicit;
- a merged name that already exists as a different, non-member class on a side must not be silently merged into — add it to the cluster explicitly. The one exception is a name that another declaration renames away: the lowered map applies in one step, so the side lands on a vacated name whether it is one member or a merge.
Members and merged names are resolved before indexing — into may be
omitted and a member may be spelled by its canonical name — by
:func:~graflo.architecture.evolution.canonical.resolve_clusters, which hands
the resolved shapes in as :class:ClusterSpec s. Nodes are (side, name)
pairs so a class named Org on the left is never confused with Org on
the right.
Attributes¶
DeclarationT = TypeVar('DeclarationT', VertexEquivalence, RelationEquivalence)
module-attribute
¶
Kind = Literal['vertex', 'relation']
module-attribute
¶
RelationCluster = Cluster[RelationEquivalence]
module-attribute
¶
Side = Literal['left', 'right']
module-attribute
¶
SubjectScope = Literal['left', 'right', 'merged', 'canonical']
module-attribute
¶
Classes¶
Cluster
dataclass
¶
Bases: Generic[DeclarationT]
One n-ary equivalence cluster, over vertices or relations, in resolved names.
Source code in graflo/architecture/evolution/equivalence.py
Attributes¶
aliases = field(default_factory=dict)
class-attribute
instance-attribute
¶
declaration
instance-attribute
¶
declared_into = None
class-attribute
instance-attribute
¶
into
instance-attribute
¶
left
instance-attribute
¶
right
instance-attribute
¶
synthesized = False
class-attribute
instance-attribute
¶
Methods:¶
__init__(left, right, into, declaration, aliases=dict(), declared_into=None, synthesized=False)
¶
members(side)
¶
property_maps(side)
¶
The declaration's per-member attribute maps, keyed by resolved member name.
Source code in graflo/architecture/evolution/equivalence.py
resolved(side, declared)
¶
The member a spelling names on side: its own name, or its canonical one.
ClusterConflictError
¶
Bases: Refusal
Two or more equivalence declarations conflict over cluster membership.
check names the rule that refused and subjects the names it is
about, as :func:subject ids -- see :class:.Refusal.
Source code in graflo/architecture/evolution/equivalence.py
ClusterIndex
dataclass
¶
Every declared cluster of one merge op, validated for consistency.
Source code in graflo/architecture/evolution/equivalence.py
Attributes¶
declared_intos
property
¶
Every merged name as the author spelled it, before translation.
labels
property
¶
The merged names of every vertex cluster.
relation_labels
property
¶
The merged names of every relation cluster.
relations
instance-attribute
¶
vertices
instance-attribute
¶
Methods:¶
__init__(vertices, relations)
¶
cluster_for_label(into)
¶
The vertex cluster collapsing onto into, or None.
relation_members(side)
¶
ClusterSpec
dataclass
¶
One declaration's resolved shape: members in the manifests' own names, and its merged name.
aliases records, per side, every other name a member answers to —
the canonical name it was declared by, or the one the canonical map gives
it — so the per-member maps (property equivalences,
SideIdentity.members, identity-alignment member keys) may be keyed by
either the member's own name or its canonical one. declared_into is
the merged name as the author spelled it, before any canonical map
translated it; synthesized marks a cluster merge created itself for
a same-name pair under name_conflict="union_right".
Source code in graflo/architecture/evolution/equivalence.py
Attributes¶
aliases = field(default_factory=dict)
class-attribute
instance-attribute
¶
declared_into = None
class-attribute
instance-attribute
¶
into
instance-attribute
¶
left
instance-attribute
¶
right
instance-attribute
¶
synthesized = False
class-attribute
instance-attribute
¶
Methods:¶
__init__(left, right, into, aliases=dict(), declared_into=None, synthesized=False)
¶
UnknownMemberError
¶
Bases: Refusal
An equivalence names a member the manifest on that side does not declare.
Its own type because it is the one refusal here that is nearly always a
typo rather than a disagreement between two declarations, and a caller
classifying refusals cannot key on a bare ValueError.
Derives check and subjects rather than taking them from the caller:
there is only one rule it can be an instance of, and only one name it can
be about.
Source code in graflo/architecture/evolution/equivalence.py
Functions:¶
check_member_existence(vertex_clusters, relation_clusters, *, left_vertex_names, right_vertex_names, left_relation_names, right_relation_names)
¶
Every member must exist on its side; a near-miss spelling is named.
Raises:
| Type | Description |
|---|---|
UnknownMemberError
|
A member is absent from its side. A subclass of
|
Source code in graflo/architecture/evolution/equivalence.py
declared_spec(declaration)
¶
The shape a declaration states outright, with no canonical map to consult.
Source code in graflo/architecture/evolution/equivalence.py
did_you_mean(name, candidates)
¶
A suffix naming a candidate that denotes the same concept, if any.
Authoring an equivalence in the wrong convention is the likeliest mistake at this boundary, and "not in left manifest" alone is a dead end when the vertex is right there under another spelling.
Source code in graflo/architecture/evolution/equivalence.py
index_clusters(op, *, left_vertices=(), right_vertices=(), left_relations=(), right_relations=(), vertex_specs=None, relation_specs=None)
¶
Validate and index the declared clusters of op.
vertex_specs / relation_specs are the resolved shapes, aligned with the
op's declaration lists; omitted, each declaration is taken as written
(which requires into). The name collections are what a merged name
may collide with on each side.
Raises :class:ClusterConflictError on an overlapping declaration, two
declarations sharing one merged name, or a merged name that would
silently occupy an existing non-member class on a side.
Source code in graflo/architecture/evolution/equivalence.py
subject(scope, name, attr=None)
¶
A stable id for the class or attribute a refusal is about.
Every refusal at this boundary names the declarations it refuses, and a caller that wants to point at them — a preview, a diagram, an editor — needs those names as data rather than parsed back out of prose.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
scope
|
SubjectScope
|
Which vocabulary the name lives in. |
required |
name
|
str
|
The class or relation name. |
required |
attr
|
str | None
|
An attribute of it, when the subject is narrower than a class. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
|