graflo.architecture.evolution.naming_graph¶
One naming graph over the original names of both sides of a merge.
The nodes are the classes and relations each manifest declares, under their own names, never erased. Three declarations add edges:
- the vocabulary (
canonical_maps) — a class's default name in the union; several classes a side's vocabulary sends to one name are merged by it, which itsallow_mergesacknowledges; - an equivalence — its members are one class, across the two sides;
renames— a class or relation no group holds, called something else.
A group is a connected component over the merging edges (vocabulary
merges, equivalence membership, and the pairs union_right unions), so an
equivalence naming one class a vocabulary merges with others takes all of
them. A group is named by its equivalences' into (a name in the union,
never translated), else by the vocabulary, else by the one spelling its
members share. Everything not in a group keeps its rename, else its
vocabulary name, else its own.
The merge is valid when every union name is reached by exactly one group or
one ungrouped class, every declared name exists, and every group has one name
and one identity. :func:build_naming reports every violation as a
:class:NamingFinding instead of raising, so a caller sees all of them at
once; :func:resolve_naming raises them together as one
:class:MergeNamingError.
Per-member maps stay keyed by the members' own names, and
:func:~graflo.architecture.evolution.merge.merge_manifests reads each side
as handed in, before the relabel. So class-specific identity sources, property
maps and the when guards derived from how a resource produces a member
survive the merge. A member a vocabulary joins whose producing resources the
group's identity does not key is given its own key behind a tag
(side:Class): it belongs to the class, but its records never fuse with
another member's.
Attributes¶
NodeKey = tuple[Kind, Side, str]
module-attribute
¶
Severity = Literal['refusal', 'incomplete', 'note']
module-attribute
¶
Via = Literal['equivalence', 'vocabulary', 'rename', 'union_right', 'own name']
module-attribute
¶
__all__ = ['MergeNamingError', 'MergeNamingIncompleteError', 'NamingEdge', 'NamingFinding', 'NamingGraph', 'NamingGroup', 'NamingResult', 'apply_repair', 'build_naming', 'naming_table', 'resolve_naming', 'suggest_merge_op']
module-attribute
¶
logger = logging.getLogger(__name__)
module-attribute
¶
Classes¶
MergeNamingError
¶
Bases: MergeCanonicalConflictError
Every naming problem of one merge op, raised together.
findings lists them; check and completion are the first one's,
and subjects names every node any of them is about.
Source code in graflo/architecture/evolution/naming_graph.py
Attributes¶
completion = blocking[0].repairs[0] if blocking[0].repairs else None
instance-attribute
¶
findings = blocking
instance-attribute
¶
Methods:¶
__init__(findings)
¶
Source code in graflo/architecture/evolution/naming_graph.py
MergeNamingIncompleteError
¶
Bases: MergeNamingError, MergeIncompleteError
Every naming problem is one an addition settles; completion says which.
Source code in graflo/architecture/evolution/naming_graph.py
NamingEdge
dataclass
¶
One thing a declaration says about where a name goes.
Source code in graflo/architecture/evolution/naming_graph.py
NamingFinding
dataclass
¶
One violation of the naming rules, or one acknowledged default.
kind is the rule, subjects the names it is about as
:func:~graflo.architecture.evolution.equivalence.subject ids, and
repairs the declarations that would settle it, safest first.
Source code in graflo/architecture/evolution/naming_graph.py
Attributes¶
blocking
property
¶
Whether this finding stops the merge.
check = ''
class-attribute
instance-attribute
¶
kind
instance-attribute
¶
message
instance-attribute
¶
repairs = ()
class-attribute
instance-attribute
¶
severity
instance-attribute
¶
subjects = ()
class-attribute
instance-attribute
¶
Methods:¶
__init__(kind, severity, message, subjects=(), repairs=(), check='')
¶
NamingGraph
dataclass
¶
Every node's union name and provenance, the groups, and the edges that put them there.
Source code in graflo/architecture/evolution/naming_graph.py
Attributes¶
edges
instance-attribute
¶
groups
instance-attribute
¶
targets
instance-attribute
¶
via
instance-attribute
¶
Methods:¶
__init__(targets, via, groups, edges)
¶
rows(kind='vertex')
¶
(union name, left names, right names, via) per union name of kind, sorted.
One row per provenance, so a group joined partly through a vocabulary shows the vocabulary-joined members on their own row.
Source code in graflo/architecture/evolution/naming_graph.py
NamingGroup
dataclass
¶
One group: its union name, and each member with where its name came from.
Source code in graflo/architecture/evolution/naming_graph.py
NamingResult
dataclass
¶
What a merge would apply, and everything wrong with its declarations.
Source code in graflo/architecture/evolution/naming_graph.py
Attributes¶
blocking
property
¶
findings
instance-attribute
¶
graph
instance-attribute
¶
notes
property
¶
resolution
instance-attribute
¶
Methods:¶
__init__(resolution, graph, findings)
¶
raise_if_blocking()
¶
Raise every blocking finding as one :class:MergeNamingError.
Source code in graflo/architecture/evolution/naming_graph.py
Functions:¶
apply_repair(op, repair)
¶
op with repair applied, or None when an edit of the op cannot express it.
add_key_source carries an identity entry to fill in by hand, and a
repair of a synthesized group has no declaration to replace.
Source code in graflo/architecture/evolution/naming_graph.py
build_naming(op, *, left, right, canonical_maps=())
¶
Resolve op against left and right in one pass, refusing nothing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
op
|
MergeManifestsOp
|
The merge op: equivalences, vocabulary, renames, policy. |
required |
left
|
GraphManifest
|
The left manifest, as handed to merge. |
required |
right
|
GraphManifest
|
The right manifest. |
required |
canonical_maps
|
Sequence[tuple[Side, CanonicalMap]]
|
Extra |
()
|
Returns:
| Type | Description |
|---|---|
NamingResult
|
The resolution merge would apply (groups as clusters over their closed |
NamingResult
|
member sets, one relabel per side), the naming graph, and every |
NamingResult
|
finding. Lowered best-effort when a finding blocks it. |
Source code in graflo/architecture/evolution/naming_graph.py
naming_table(graph, kind='vertex', *, findings=(), quiet=False)
¶
The naming graph as text: one row per union name and provenance.
A row whose union name a blocking finding is about ends in
<- conflict. With quiet, a table where every class keeps its own
name and nothing conflicts is left out.
Source code in graflo/architecture/evolution/naming_graph.py
resolve_naming(op, *, left, right, canonical_maps=())
¶
:func:build_naming, raising every blocking finding as one :class:MergeNamingError.
Source code in graflo/architecture/evolution/naming_graph.py
suggest_merge_op(left, right, op=None, *, canonical_maps=(), max_steps=32)
¶
A merge op that settles what can be settled without guessing, for review.
Without op, a scaffold: an equivalence for every name both sides share.
With one, op with the first repair of each blocking finding applied —
a rename away before a new into, and either before fusing entities —
until no finding has one left, and each automatic own key written out
where the identity is declared. Two spellings of one concept are never
unioned: that is a guess, left to the author. Nothing is applied to a
manifest; the result is a value to read, edit and save.