Coverage for src/objectstate/object_state_metadata.py: 84%
77 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-17 07:22 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-17 07:22 +0000
1"""Contracted metadata storage for ObjectState extensions.
3ObjectState.metadata is intentionally outside normal dirty detection, but it is
4inside time-travel snapshots. Extension-owned metadata therefore needs a
5contract: namespaced keys must be registered before they can be written,
6captured, or restored.
7"""
9from __future__ import annotations
11from dataclasses import dataclass
12from typing import Any, ClassVar, Mapping
13import copy
16@dataclass(frozen=True)
17class ObjectStateMetadataContract:
18 """Registered extension-owned ObjectState metadata key."""
20 key: str
21 owner: str
22 description: str
24 def __post_init__(self) -> None:
25 if "." not in self.key:
26 raise ValueError(
27 f"ObjectState metadata extension key must be namespaced: {self.key!r}"
28 )
29 if not self.owner:
30 raise ValueError(f"ObjectState metadata contract {self.key!r} needs an owner")
31 if not self.description:
32 raise ValueError(
33 f"ObjectState metadata contract {self.key!r} needs a description"
34 )
37class ObjectStateMetadataContractRegistry:
38 """Process-local registry for extension-owned ObjectState metadata."""
40 _contracts: ClassVar[dict[str, ObjectStateMetadataContract]] = {}
42 @classmethod
43 def register(cls, contract: ObjectStateMetadataContract) -> ObjectStateMetadataContract:
44 existing = cls._contracts.get(contract.key)
45 if existing is not None and existing != contract:
46 raise ValueError(
47 f"ObjectState metadata key {contract.key!r} is already registered "
48 f"to {existing.owner!r}"
49 )
50 cls._contracts[contract.key] = contract
51 return contract
53 @classmethod
54 def contract_for(cls, key: str) -> ObjectStateMetadataContract:
55 contract = cls._contracts.get(key)
56 if contract is None:
57 raise KeyError(
58 f"ObjectState extension metadata key {key!r} is not registered. "
59 "Register ObjectStateMetadataContract before writing namespaced metadata."
60 )
61 return contract
63 @classmethod
64 def validate_key(cls, key: str) -> None:
65 if "." in key:
66 cls.contract_for(key)
68 @classmethod
69 def validate_mapping(
70 cls,
71 *,
72 scope_id: str | None,
73 metadata: Mapping[str, Any],
74 operation: str,
75 ) -> None:
76 for key in metadata:
77 try:
78 cls.validate_key(key)
79 except KeyError as exc:
80 scope = scope_id if scope_id is not None else "<root>"
81 raise RuntimeError(
82 f"Cannot {operation} ObjectState metadata for {scope}: {exc}"
83 ) from exc
86class ObjectStateMetadataStore(dict):
87 """Dict-compatible metadata store that enforces extension contracts."""
89 def __init__(
90 self,
91 scope_id: str | None,
92 initial: Mapping[str, Any] | None = None,
93 ) -> None:
94 self.scope_id = scope_id
95 super().__init__()
96 if initial:
97 self.update(initial)
99 def _validate_contracts(
100 self,
101 *,
102 operation: str,
103 metadata: Mapping[str, Any] | None = None,
104 ) -> None:
105 ObjectStateMetadataContractRegistry.validate_mapping(
106 scope_id=self.scope_id,
107 metadata=self if metadata is None else metadata,
108 operation=operation,
109 )
111 def __setitem__(self, key: str, value: Any) -> None:
112 ObjectStateMetadataContractRegistry.validate_key(key)
113 super().__setitem__(key, value)
115 def update(self, other: Mapping[str, Any] | None = None, **kwargs: Any) -> None:
116 if other:
117 self._validate_contracts(operation="write", metadata=other)
118 if kwargs:
119 self._validate_contracts(operation="write", metadata=kwargs)
120 super().update(other or {}, **kwargs)
122 def setdefault(self, key: str, default: Any = None) -> Any:
123 ObjectStateMetadataContractRegistry.validate_key(key)
124 return super().setdefault(key, default)
126 def copy_for_snapshot(self) -> dict[str, Any]:
127 self._validate_contracts(operation="snapshot")
128 return copy.deepcopy(dict(self))
130 def __copy__(self) -> "ObjectStateMetadataStore":
131 return ObjectStateMetadataStore(self.scope_id, dict(self))
133 def __deepcopy__(self, memo: dict[int, Any]) -> "ObjectStateMetadataStore":
134 copied = ObjectStateMetadataStore(self.scope_id)
135 memo[id(self)] = copied
136 copied.update(copy.deepcopy(dict(self), memo))
137 return copied
139 @classmethod
140 def from_snapshot(
141 cls,
142 *,
143 scope_id: str | None,
144 metadata: Mapping[str, Any],
145 ) -> "ObjectStateMetadataStore":
146 ObjectStateMetadataContractRegistry.validate_mapping(
147 scope_id=scope_id,
148 metadata=metadata,
149 operation="restore",
150 )
151 return cls(scope_id, copy.deepcopy(dict(metadata)))