gabriel / muse public
set_ops.py python
95 lines 2.9 KB
Raw
sha256:37af4ab271a64f11d7bf14b4871ebaaf5dd68fb0247521007c6b034d35aa01be Merge branch 'fix/hub-user-read-update-path' into dev Human 9 days ago
1 """Hash-set algebra diff for unordered collections.
2
3 Computes the symmetric difference between two ``frozenset[str]`` collections
4 of content IDs. The result is a ``StructuredDelta`` containing:
5
6 - ``InsertOp`` entries for content IDs present in *target* but not *base*.
7 - ``DeleteOp`` entries for content IDs present in *base* but not *target*.
8
9 No ``MoveOp`` or ``ReplaceOp`` is ever produced: unordered sets have no
10 positional semantics, so every element is either added or removed.
11
12 This is the algorithm the MIDI plugin uses for file-level diffing (which set
13 of POSIX paths changed). Plugins with a ``SetSchema`` in their ``DomainSchema``
14 get this algorithm for free via :func:`~muse.core.diff_algorithms.diff_by_schema`.
15
16 Public API
17 ----------
18 - :func:`diff` — ``frozenset[str]`` × ``frozenset[str]`` → ``StructuredDelta``.
19 """
20
21 import logging
22
23 from muse.core.schema import SetSchema
24 from muse.domain import DeleteOp, DomainOp, InsertOp, StructuredDelta
25
26 logger = logging.getLogger(__name__)
27
28 def diff(
29 schema: SetSchema,
30 base: frozenset[str],
31 target: frozenset[str],
32 *,
33 domain: str,
34 address: str = "",
35 ) -> StructuredDelta:
36 """Diff two unordered sets of content IDs under the given ``SetSchema``.
37
38 All insertions and deletions have ``position=None`` because the collection
39 is unordered — position has no meaning for set elements.
40
41 Args:
42 schema: The ``SetSchema`` declaring element type and identity.
43 base: Base (ancestor) set of content IDs.
44 target: Target (newer) set of content IDs.
45 domain: Domain tag for the returned ``StructuredDelta``.
46 address: Address prefix for generated op entries.
47
48 Returns:
49 A ``StructuredDelta`` with ``InsertOp`` and ``DeleteOp`` entries.
50 """
51 added = sorted(target - base)
52 removed = sorted(base - target)
53 elem = schema["element_type"]
54
55 ops: list[DomainOp] = []
56
57 for cid in removed:
58 ops.append(
59 DeleteOp(
60 op="delete",
61 address=address,
62 position=None,
63 content_id=cid,
64 content_summary=f"{elem} removed: {cid}",
65 )
66 )
67
68 for cid in added:
69 ops.append(
70 InsertOp(
71 op="insert",
72 address=address,
73 position=None,
74 content_id=cid,
75 content_summary=f"{elem} added: {cid}",
76 )
77 )
78
79 n_add = len(added)
80 n_del = len(removed)
81 parts: list[str] = []
82 if n_add:
83 parts.append(f"{n_add} {elem}{'s' if n_add != 1 else ''} added")
84 if n_del:
85 parts.append(f"{n_del} {elem}{'s' if n_del != 1 else ''} removed")
86 summary = ", ".join(parts) if parts else f"no {elem} changes"
87
88 logger.debug(
89 "set_ops.diff %r: +%d -%d elements",
90 address,
91 n_add,
92 n_del,
93 )
94
95 return StructuredDelta(domain=domain, ops=ops, summary=summary)
File History 11 commits
sha256:37af4ab271a64f11d7bf14b4871ebaaf5dd68fb0247521007c6b034d35aa01be Merge branch 'fix/hub-user-read-update-path' into dev Human 9 days ago
sha256:b7be56ec091919a612cffe7f3c8b600209d5155517443fdac0e16954c8c7d81f Merge 'task/git-export-ignored-file-deletion' into 'dev' — … Human 12 days ago
sha256:8de4334a98c945aace420969d389ad678aa926d4ab4e886b2ac4c4241cb3bf2b revert: keep pyproject.toml in canonical PEP 440 form Sonnet 4.6 patch 15 days ago
sha256:50bb615c573aa4b928fca75b60f82ba2a659910066507cec6a95c412ae22ccb9 docs: add issue docs for push have-negotiation bug (#55) an… Sonnet 4.6 18 days ago
sha256:d90d175cded68aae1d4ffcf4858917854195d0cd8ce1fe73cee4dbc02541cb74 chore: trigger push to surface null-OID paths Sonnet 4.6 24 days ago
sha256:e452ad9a6ace6ccc6d875a35e06caf9da5576a970c1c36133b69a891ce5fefa8 chore: prebuild timing test Sonnet 4.6 34 days ago
sha256:0008ab6695e3e064b3e236b24fd19e538fef6a588eb0d211622f4466d919c0b1 merge: pull staging/dev — advance to 0.2.0rc12 Sonnet 4.6 patch 36 days ago
sha256:9c33d61749fff814c5226d5386aa2af7064c2c02788594a25fdd709358132eea fix: _PROPOSAL_PREFIX_RESOLVE_LIMIT 200 → 100 to match hub … Sonnet 4.6 47 days ago
sha256:36c3cb3e76619d4c30a6d9bf81b5ec4ff148e30dcfed913e3114ca7b43b81c7e fix: rename objects→blobs in push client and all stale test… Sonnet 4.6 patch 50 days ago
sha256:c06a9b9b9fee26c68ea725b44d54b2c0a171301ce9de746d5b656617b4463a9a fix: repair four test failures from post-migration audit Sonnet 4.6 patch 56 days ago
sha256:1900655993c83c4107067375548a7be823e471d2515830842f1a12cba4bd3cdf fix: unified object store migration — idempotent writes, JS… Sonnet 4.6 minor 57 days ago