refresh-engine 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. refresh_engine/__init__.py +169 -0
  2. refresh_engine/api/__init__.py +43 -0
  3. refresh_engine/api/abc.py +176 -0
  4. refresh_engine/api/engine.py +468 -0
  5. refresh_engine/api/protocols.py +89 -0
  6. refresh_engine/core/models.py +362 -0
  7. refresh_engine/detection/__init__.py +21 -0
  8. refresh_engine/detection/detector.py +54 -0
  9. refresh_engine/detection/fingerprints.py +115 -0
  10. refresh_engine/errors.py +57 -0
  11. refresh_engine/events/__init__.py +6 -0
  12. refresh_engine/events/models.py +42 -0
  13. refresh_engine/events/publisher.py +77 -0
  14. refresh_engine/execution/__init__.py +5 -0
  15. refresh_engine/execution/coordinator.py +128 -0
  16. refresh_engine/execution/executor.py +180 -0
  17. refresh_engine/observability/__init__.py +9 -0
  18. refresh_engine/observability/logging.py +49 -0
  19. refresh_engine/observability/metrics.py +44 -0
  20. refresh_engine/planning/__init__.py +6 -0
  21. refresh_engine/planning/dependency.py +79 -0
  22. refresh_engine/planning/planner.py +86 -0
  23. refresh_engine/py.typed +0 -0
  24. refresh_engine/scheduling/__init__.py +13 -0
  25. refresh_engine/scheduling/async_scheduler.py +126 -0
  26. refresh_engine/scheduling/external.py +17 -0
  27. refresh_engine/stores/__init__.py +11 -0
  28. refresh_engine/stores/memory.py +77 -0
  29. refresh_engine/triggers/__init__.py +16 -0
  30. refresh_engine/triggers/adapters.py +38 -0
  31. refresh_engine/triggers/selectors.py +46 -0
  32. refresh_engine-0.1.0.dist-info/METADATA +95 -0
  33. refresh_engine-0.1.0.dist-info/RECORD +35 -0
  34. refresh_engine-0.1.0.dist-info/WHEEL +4 -0
  35. refresh_engine-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,169 @@
1
+ """Production-grade generic refresh infrastructure."""
2
+
3
+ from refresh_engine.api import (
4
+ AsyncSchedulerABC,
5
+ EventHandler,
6
+ EventPublisherABC,
7
+ FingerprintStrategy,
8
+ FingerprintStrategyABC,
9
+ MetricsSink,
10
+ MetricsSinkABC,
11
+ RefreshOperation,
12
+ RefreshOperationABC,
13
+ ResourceSelector,
14
+ ResourceSelectorABC,
15
+ ResourceSource,
16
+ ResourceSourceABC,
17
+ StateStore,
18
+ StateStoreABC,
19
+ StateTransaction,
20
+ StateTransactionABC,
21
+ )
22
+ from refresh_engine.api.engine import RefreshEngine
23
+ from refresh_engine.core.models import (
24
+ Change,
25
+ ChangeSet,
26
+ ChangeType,
27
+ DiscoveryResult,
28
+ FailurePolicy,
29
+ ImpactSet,
30
+ OverlapPolicy,
31
+ PlanAction,
32
+ RefreshConfig,
33
+ RefreshMode,
34
+ RefreshRequest,
35
+ RefreshResult,
36
+ RefreshStatus,
37
+ Resource,
38
+ ResourceFingerprint,
39
+ ResourceSnapshot,
40
+ ResourceState,
41
+ RetryConfig,
42
+ TimeoutConfig,
43
+ TriggerSource,
44
+ )
45
+ from refresh_engine.detection import (
46
+ ChangeDetector,
47
+ CompositeHash,
48
+ ContentHash,
49
+ ETagFingerprint,
50
+ MetadataHash,
51
+ VersionTokenFingerprint,
52
+ )
53
+ from refresh_engine.errors import (
54
+ CancellationError,
55
+ ChangeDetectionError,
56
+ ConfigurationError,
57
+ DependencyCycleError,
58
+ DependencyError,
59
+ DiscoveryError,
60
+ ExecutionError,
61
+ FingerprintError,
62
+ PlanningError,
63
+ RefreshError,
64
+ SchedulerError,
65
+ SnapshotError,
66
+ StateStoreError,
67
+ )
68
+ from refresh_engine.events import EventKind, EventPublisher, RefreshEvent
69
+ from refresh_engine.observability import (
70
+ InMemoryMetrics,
71
+ StructlogEventHandler,
72
+ default_redactor,
73
+ )
74
+ from refresh_engine.planning import (
75
+ DependencyGraph,
76
+ ImpactAnalyzer,
77
+ RefreshPlanner,
78
+ )
79
+ from refresh_engine.scheduling import (
80
+ AsyncScheduler,
81
+ ExternalSchedulerAdapter,
82
+ )
83
+ from refresh_engine.stores import InMemoryStateStore
84
+ from refresh_engine.triggers import (
85
+ EventTrigger,
86
+ IDSelector,
87
+ PredicateSelector,
88
+ QueryTrigger,
89
+ TagSelector,
90
+ )
91
+
92
+ __version__ = "0.1.0"
93
+
94
+ __all__ = [
95
+ "AsyncScheduler",
96
+ "AsyncSchedulerABC",
97
+ "CancellationError",
98
+ "Change",
99
+ "ChangeDetectionError",
100
+ "ChangeDetector",
101
+ "ChangeSet",
102
+ "ChangeType",
103
+ "CompositeHash",
104
+ "ConfigurationError",
105
+ "ContentHash",
106
+ "DependencyCycleError",
107
+ "DependencyError",
108
+ "DependencyGraph",
109
+ "DiscoveryError",
110
+ "DiscoveryResult",
111
+ "ETagFingerprint",
112
+ "EventHandler",
113
+ "EventKind",
114
+ "EventPublisher",
115
+ "EventPublisherABC",
116
+ "EventTrigger",
117
+ "ExecutionError",
118
+ "ExternalSchedulerAdapter",
119
+ "FailurePolicy",
120
+ "FingerprintError",
121
+ "FingerprintStrategy",
122
+ "FingerprintStrategyABC",
123
+ "IDSelector",
124
+ "ImpactAnalyzer",
125
+ "ImpactSet",
126
+ "InMemoryMetrics",
127
+ "InMemoryStateStore",
128
+ "MetadataHash",
129
+ "MetricsSink",
130
+ "MetricsSinkABC",
131
+ "OverlapPolicy",
132
+ "PlanAction",
133
+ "PlanningError",
134
+ "PredicateSelector",
135
+ "QueryTrigger",
136
+ "RefreshConfig",
137
+ "RefreshEngine",
138
+ "RefreshError",
139
+ "RefreshEvent",
140
+ "RefreshMode",
141
+ "RefreshOperation",
142
+ "RefreshOperationABC",
143
+ "RefreshPlanner",
144
+ "RefreshRequest",
145
+ "RefreshResult",
146
+ "RefreshStatus",
147
+ "Resource",
148
+ "ResourceFingerprint",
149
+ "ResourceSelector",
150
+ "ResourceSelectorABC",
151
+ "ResourceSnapshot",
152
+ "ResourceSource",
153
+ "ResourceSourceABC",
154
+ "ResourceState",
155
+ "RetryConfig",
156
+ "SchedulerError",
157
+ "SnapshotError",
158
+ "StateStore",
159
+ "StateStoreABC",
160
+ "StateStoreError",
161
+ "StateTransaction",
162
+ "StateTransactionABC",
163
+ "StructlogEventHandler",
164
+ "TagSelector",
165
+ "TimeoutConfig",
166
+ "TriggerSource",
167
+ "VersionTokenFingerprint",
168
+ "default_redactor",
169
+ ]
@@ -0,0 +1,43 @@
1
+ """Public extension protocols."""
2
+
3
+ from refresh_engine.api.abc import (
4
+ AsyncSchedulerABC,
5
+ EventPublisherABC,
6
+ FingerprintStrategyABC,
7
+ MetricsSinkABC,
8
+ RefreshOperationABC,
9
+ ResourceSelectorABC,
10
+ ResourceSourceABC,
11
+ StateStoreABC,
12
+ StateTransactionABC,
13
+ )
14
+ from refresh_engine.api.protocols import (
15
+ EventHandler,
16
+ FingerprintStrategy,
17
+ MetricsSink,
18
+ RefreshOperation,
19
+ ResourceSelector,
20
+ ResourceSource,
21
+ StateStore,
22
+ StateTransaction,
23
+ )
24
+
25
+ __all__ = [
26
+ "AsyncSchedulerABC",
27
+ "EventHandler",
28
+ "EventPublisherABC",
29
+ "FingerprintStrategy",
30
+ "FingerprintStrategyABC",
31
+ "MetricsSink",
32
+ "MetricsSinkABC",
33
+ "RefreshOperation",
34
+ "RefreshOperationABC",
35
+ "ResourceSelector",
36
+ "ResourceSelectorABC",
37
+ "ResourceSource",
38
+ "ResourceSourceABC",
39
+ "StateStore",
40
+ "StateStoreABC",
41
+ "StateTransaction",
42
+ "StateTransactionABC",
43
+ ]
@@ -0,0 +1,176 @@
1
+ """Nominal abstract base classes for refresh-engine extension points.
2
+
3
+ The protocol layer supports structural typing. These ABCs are optional convenience
4
+ bases for consumers that prefer inheritance, abstract-method enforcement, and shared
5
+ lifecycle semantics.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from abc import ABC, abstractmethod
11
+ from collections.abc import Mapping
12
+ from typing import TYPE_CHECKING
13
+
14
+ from refresh_engine.api.protocols import EventHandler
15
+ from refresh_engine.core.models import (
16
+ DiscoveryResult,
17
+ PlanAction,
18
+ RefreshRequest,
19
+ Resource,
20
+ ResourceFingerprint,
21
+ ResourceSnapshot,
22
+ ResourceState,
23
+ )
24
+
25
+ if TYPE_CHECKING:
26
+ from refresh_engine.events.models import RefreshEvent
27
+
28
+
29
+ class ResourceSourceABC(ABC):
30
+ """Base class for asynchronous streaming resource sources."""
31
+
32
+ @abstractmethod
33
+ async def discover(self) -> DiscoveryResult:
34
+ """Start a discovery and report whether it is complete."""
35
+
36
+ @abstractmethod
37
+ async def snapshot(self, resource: Resource) -> ResourceSnapshot:
38
+ """Return observable state for one discovered resource."""
39
+
40
+
41
+ class FingerprintStrategyABC(ABC):
42
+ """Base class for deterministic strong fingerprint strategies."""
43
+
44
+ @abstractmethod
45
+ async def fingerprint(self, snapshot: ResourceSnapshot) -> ResourceFingerprint:
46
+ """Create a deterministic fingerprint."""
47
+
48
+
49
+ class RefreshOperationABC(ABC):
50
+ """Base class for application-defined refresh side effects."""
51
+
52
+ @abstractmethod
53
+ async def __call__(
54
+ self,
55
+ resource: Resource | None,
56
+ snapshot: ResourceSnapshot | None,
57
+ action: PlanAction,
58
+ request: RefreshRequest,
59
+ ) -> None:
60
+ """Apply a refresh or deletion operation."""
61
+
62
+
63
+ class StateTransactionABC(ABC):
64
+ """Base class for an isolated state transaction."""
65
+
66
+ @abstractmethod
67
+ async def put(self, state: ResourceState) -> None:
68
+ """Stage a state upsert."""
69
+
70
+ @abstractmethod
71
+ async def delete(self, resource_id: str) -> None:
72
+ """Stage a state deletion."""
73
+
74
+ @abstractmethod
75
+ async def commit(self) -> None:
76
+ """Atomically commit staged changes."""
77
+
78
+ @abstractmethod
79
+ async def rollback(self) -> None:
80
+ """Discard staged changes."""
81
+
82
+ async def __aenter__(self) -> StateTransactionABC:
83
+ return self
84
+
85
+ async def __aexit__(
86
+ self,
87
+ exc_type: type[BaseException] | None,
88
+ exc: BaseException | None,
89
+ traceback: object | None,
90
+ ) -> None:
91
+ if exc_type is None:
92
+ await self.commit()
93
+ else:
94
+ await self.rollback()
95
+
96
+
97
+ class StateStoreABC(ABC):
98
+ """Base class for transactional state persistence."""
99
+
100
+ @abstractmethod
101
+ async def load_all(self) -> Mapping[str, ResourceState]:
102
+ """Load the last successful state."""
103
+
104
+ @abstractmethod
105
+ def transaction(self) -> StateTransactionABC:
106
+ """Create an isolated transaction."""
107
+
108
+
109
+ class ResourceSelectorABC(ABC):
110
+ """Base class for reusable resource selection."""
111
+
112
+ @abstractmethod
113
+ def matches(self, resource: Resource) -> bool:
114
+ """Return whether the resource is selected."""
115
+
116
+
117
+ class MetricsSinkABC(ABC):
118
+ """Base class for provider-neutral metric sinks."""
119
+
120
+ @abstractmethod
121
+ def increment(
122
+ self,
123
+ name: str,
124
+ value: int = 1,
125
+ attributes: Mapping[str, str] | None = None,
126
+ ) -> None:
127
+ """Increment a counter."""
128
+
129
+ @abstractmethod
130
+ def observe(
131
+ self,
132
+ name: str,
133
+ value: float,
134
+ attributes: Mapping[str, str] | None = None,
135
+ ) -> None:
136
+ """Record a numeric observation."""
137
+
138
+
139
+ class EventPublisherABC(ABC):
140
+ """Base class for lifecycle event transports."""
141
+
142
+ @abstractmethod
143
+ def subscribe(self, handler: EventHandler) -> None:
144
+ """Register an event handler."""
145
+
146
+ @abstractmethod
147
+ def unsubscribe(self, handler: EventHandler) -> None:
148
+ """Remove an event handler."""
149
+
150
+ @abstractmethod
151
+ async def publish(self, event: RefreshEvent) -> None:
152
+ """Queue an event for delivery."""
153
+
154
+ @abstractmethod
155
+ async def drain(self) -> None:
156
+ """Wait for queued event delivery."""
157
+
158
+
159
+ class AsyncSchedulerABC(ABC):
160
+ """Base class for scheduler lifecycles owned by an event loop."""
161
+
162
+ @abstractmethod
163
+ async def start(self) -> None:
164
+ """Start accepting scheduled work."""
165
+
166
+ @abstractmethod
167
+ async def pause(self) -> None:
168
+ """Pause future scheduled work."""
169
+
170
+ @abstractmethod
171
+ async def resume(self) -> None:
172
+ """Resume future scheduled work."""
173
+
174
+ @abstractmethod
175
+ async def stop(self) -> None:
176
+ """Stop and release owned tasks."""