super-solid-system 1.0.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.
@@ -0,0 +1,303 @@
1
+ from __future__ import annotations
2
+ import inspect
3
+ import threading
4
+ from abc import ABC
5
+ from pydantic import BaseModel
6
+ from typing import Dict, Any, Type, Optional, TypeVar, Generic, List, TYPE_CHECKING
7
+
8
+ from supersolid.core.discovery import DiscoveryStrategy, InternalLib, EntryPoints, PluginDirectory
9
+
10
+ if TYPE_CHECKING:
11
+ from supersolid.core.security import SecurityPolicy
12
+
13
+
14
+ class ComponentMetadata(BaseModel):
15
+ name: str
16
+ namespace: str
17
+ version: Optional[str] = None
18
+ model_config = {
19
+ "extra": "allow"
20
+ }
21
+
22
+
23
+ ComponentType = TypeVar("ComponentType")
24
+
25
+
26
+ class SuperSolidRegistry(Generic[ComponentType], ABC):
27
+ """
28
+ Generic Base Registry managing component classes, plugin discovery,
29
+ type enforcement, instantiation, and active instance caching.
30
+ """
31
+
32
+ def __init__(
33
+ self,
34
+ name: str,
35
+ component_class: Type[ComponentType],
36
+ policy: Optional[SecurityPolicy] = None,
37
+ discovery_strategies: Optional[List[DiscoveryStrategy]] = None,
38
+ root_package: Optional[str] = None,
39
+ ):
40
+ self.name = name.lower()
41
+ self.component_class = component_class
42
+ self.policy = policy
43
+ self._root_package = root_package
44
+ self._registries: Dict[str, Dict[str, Type[ComponentType]]] = {}
45
+ self._instances: Dict[str, ComponentType] = {}
46
+ self._lock = threading.RLock()
47
+ self.discovery_strategies: List[DiscoveryStrategy] = (
48
+ discovery_strategies
49
+ if discovery_strategies is not None
50
+ else [
51
+ InternalLib(),
52
+ EntryPoints(),
53
+ PluginDirectory(),
54
+ ]
55
+ )
56
+
57
+ @property
58
+ def root_package(self) -> str:
59
+ """Dynamically infers or returns the configured root package name."""
60
+ if self._root_package:
61
+ return self._root_package
62
+ parts = self.__class__.__module__.split(".")
63
+ if parts[0] == "__main__":
64
+ return "supersolid"
65
+ return ".".join(parts[:2]) if len(parts) >= 2 and parts[0] == "supersolid" else parts[0]
66
+
67
+ def _cache_key(self, component_namespace: str, component_name: str, kwargs: Optional[Dict[str, Any]] = None) -> str:
68
+ component_namespace = component_namespace.lower()
69
+ component_name = component_name.lower()
70
+ if kwargs:
71
+ kwargs_str = "_".join(
72
+ f"{k}={v}" for k, v in sorted(kwargs.items()) if isinstance(v, (str, int, float, bool))
73
+ )
74
+ if kwargs_str:
75
+ return f"{component_namespace}.{component_name}:{kwargs_str}"
76
+ return f"{component_namespace}.{component_name}"
77
+
78
+ def set_policy(self, policy: SecurityPolicy) -> None:
79
+ """Attaches or updates the SecurityPolicy enforced by this registry."""
80
+ with self._lock:
81
+ self.policy = policy
82
+
83
+ def add_discovery(self, strategy: DiscoveryStrategy) -> None:
84
+ """Injects a new discovery strategy into the registry pipeline."""
85
+ with self._lock:
86
+ self.discovery_strategies.append(strategy)
87
+
88
+ def is_registered(self, component_namespace: str, component_name: str) -> bool:
89
+ """Checks if an item class is currently registered in the registry."""
90
+ component_namespace = component_namespace.lower()
91
+ component_name = component_name.lower()
92
+ with self._lock:
93
+ return component_namespace in self._registries and component_name in self._registries[component_namespace]
94
+
95
+ def register(self, component_namespace: str, component_name: str, component_class: Type[ComponentType]) -> None:
96
+ """Registers a class in the registry with automatic type enforcement."""
97
+ component_namespace = component_namespace.lower()
98
+ component_name = component_name.lower()
99
+
100
+ if not issubclass(component_class, self.component_class):
101
+ raise TypeError(
102
+ f"Cannot register '{component_class.__name__}' in {self.name} registry. "
103
+ f"Expected a subclass of '{self.component_class.__name__}'."
104
+ )
105
+
106
+ with self._lock:
107
+ if component_namespace not in self._registries:
108
+ self._registries[component_namespace] = {}
109
+ self._registries[component_namespace][component_name] = component_class
110
+
111
+ def get(
112
+ self,
113
+ component_namespace: str,
114
+ component_name: str,
115
+ expected_class: Optional[Type[ComponentType]] = None,
116
+ **kwargs: Any
117
+ ) -> Optional[ComponentType]:
118
+ """Gets an active loaded instance from the cache."""
119
+ cache_key = self._cache_key(
120
+ component_namespace, component_name, kwargs)
121
+ with self._lock:
122
+ instance = self._instances.get(cache_key)
123
+
124
+ target_class = expected_class or self.component_class
125
+ if instance is not None and not isinstance(instance, target_class):
126
+ raise TypeError(
127
+ f"Expected instance of {target_class.__name__}, got {type(instance).__name__}!"
128
+ )
129
+ return instance
130
+
131
+ def discover(self, component_namespace: str, component_name: str) -> None:
132
+ """Attempts dynamic plugin discovery across registered discovery strategies."""
133
+ component_namespace = component_namespace.lower()
134
+ component_name = component_name.lower()
135
+ for strategy in self.discovery_strategies:
136
+ if self.policy is not None:
137
+ # 1. InternalLib fallback check
138
+ if not self.policy.allow_internal_lib and isinstance(strategy, InternalLib):
139
+ continue
140
+ if not self.policy.allow_external_entrypoints and isinstance(strategy, EntryPoints):
141
+ continue
142
+ if not self.policy.allow_plugin_discovery and isinstance(strategy, PluginDirectory):
143
+ continue
144
+
145
+ if strategy.discover(self, component_namespace, component_name):
146
+ return
147
+
148
+ def load(
149
+ self,
150
+ component_namespace: str,
151
+ component_name: str,
152
+ config: Any = {},
153
+ allowed_namespaces: Optional[List[str]] = None,
154
+ **kwargs: Any
155
+ ) -> ComponentType:
156
+ """
157
+ Loads, instantiates, and caches an item from the registry.
158
+ Performs dynamic discovery if the item class is not yet registered.
159
+ """
160
+ component_namespace = component_namespace.lower()
161
+ component_name = component_name.lower()
162
+ # 1. Namespace Protection & Policy Check
163
+ if self.policy is not None:
164
+ self.policy.validate_access(
165
+ component_namespace, caller_id=kwargs.get("caller_id"))
166
+ elif allowed_namespaces is not None:
167
+ allowed_set = {c.lower() for c in allowed_namespaces}
168
+ if component_namespace not in allowed_set:
169
+ raise PermissionError(
170
+ f"Namespace '{component_namespace}' is restricted in '{self.name}' registry."
171
+ )
172
+
173
+ cache_key = self._cache_key(
174
+ component_namespace, component_name, kwargs)
175
+
176
+ with self._lock:
177
+ # 1. Return cached instance if already loaded
178
+ if cache_key in self._instances:
179
+ return self._instances[cache_key]
180
+
181
+ # 2. Dynamic Discovery if not yet registered
182
+ if not self.is_registered(component_namespace, component_name):
183
+ self.discover(component_namespace, component_name)
184
+
185
+ if not self.is_registered(component_namespace, component_name):
186
+ raise RuntimeError(
187
+ f"Item '{component_name}' in namespace '{component_namespace}' could not be found in '{self.name}' registry."
188
+ )
189
+
190
+ # 3. Instantiate & initialize class
191
+ cls = self._registries[component_namespace][component_name]
192
+
193
+ # Filter kwargs to match constructor signature
194
+ if cls.__init__ is object.__init__:
195
+ valid_kwargs = {}
196
+ has_config_param = False
197
+ else:
198
+ sig = inspect.signature(cls.__init__)
199
+ has_var_keyword = any(
200
+ p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()
201
+ )
202
+ has_config_param = "config" in sig.parameters
203
+ valid_kwargs = kwargs if has_var_keyword else {
204
+ k: v for k, v in kwargs.items() if k in sig.parameters
205
+ }
206
+
207
+ instance = cls(
208
+ config, **valid_kwargs) if has_config_param else cls(**valid_kwargs)
209
+
210
+ # Call lifecycle hook if present
211
+ if hasattr(instance, "initialize"):
212
+ init_sig = inspect.signature(instance.initialize)
213
+ init_has_var_kw = any(
214
+ p.kind == inspect.Parameter.VAR_KEYWORD for p in init_sig.parameters.values()
215
+ )
216
+ valid_init_kwargs = kwargs if init_has_var_kw else {
217
+ k: v for k, v in kwargs.items() if k in init_sig.parameters
218
+ }
219
+ instance.initialize(**valid_init_kwargs)
220
+
221
+ if hasattr(instance, "boot"):
222
+ boot_sig = inspect.signature(instance.boot)
223
+ boot_has_var_kw = any(
224
+ p.kind == inspect.Parameter.VAR_KEYWORD for p in boot_sig.parameters.values()
225
+ )
226
+ valid_boot_kwargs = kwargs if boot_has_var_kw else {
227
+ k: v for k, v in kwargs.items() if k in boot_sig.parameters
228
+ }
229
+ instance.boot(**valid_boot_kwargs)
230
+
231
+ self._instances[cache_key] = instance
232
+ return instance
233
+
234
+ def list(self, component_namespace: Optional[str] = None) -> Dict[str, Any]:
235
+ """Lists registered component classes."""
236
+ with self._lock:
237
+ if component_namespace:
238
+ return dict(self._registries.get(component_namespace.lower(), {}))
239
+ return {k: dict(v) for k, v in self._registries.items()}
240
+
241
+ def unload(self, component_namespace: str, component_name: str, **kwargs: Any) -> bool:
242
+ """Unloads an active instance and calls its disconnect/stop lifecycle hook."""
243
+ cache_key = self._cache_key(
244
+ component_namespace, component_name, kwargs)
245
+ with self._lock:
246
+ instance = self._instances.pop(cache_key, None)
247
+
248
+ if instance is not None:
249
+ if hasattr(instance, "disconnect"):
250
+ try:
251
+ instance.disconnect(**kwargs)
252
+ except TypeError:
253
+ instance.disconnect()
254
+ elif hasattr(instance, "stop"):
255
+ try:
256
+ instance.stop(**kwargs)
257
+ except TypeError:
258
+ instance.stop()
259
+ return True
260
+ return False
261
+
262
+
263
+ SolidRegistry = SuperSolidRegistry
264
+ OpenRegistry = SuperSolidRegistry
265
+
266
+
267
+ def super_solid_registry(
268
+ name: str,
269
+ component_class: Type[ComponentType],
270
+ policy: Optional[SecurityPolicy] = None,
271
+ discovery_strategies: Optional[List[DiscoveryStrategy]] = None,
272
+ root_package: Optional[str] = None,
273
+ ) -> SuperSolidRegistry:
274
+ return SuperSolidRegistry(
275
+ name=name,
276
+ component_class=component_class,
277
+ policy=policy,
278
+ discovery_strategies=discovery_strategies,
279
+ root_package=root_package,
280
+ )
281
+
282
+
283
+ solid_registry = super_solid_registry
284
+ open_registry = super_solid_registry
285
+
286
+
287
+ def super_solid_component(registry: SuperSolidRegistry, namespace: str, name: str, **metadata: Any):
288
+ """
289
+ Creates a wrapper that will register the decorated class with the registry.
290
+ Args:
291
+ namespace (str): The namespace of the decorated class.
292
+ name (str): The name of the decorated class.
293
+ """
294
+ def super_solid_decorator(cls: Type[ComponentType]):
295
+ cls._metadata = ComponentMetadata(
296
+ name=name, namespace=namespace, **metadata)
297
+ registry.register(namespace, name, cls)
298
+ return cls
299
+ return super_solid_decorator
300
+
301
+
302
+ solid_component = super_solid_component
303
+ open_component = super_solid_component
@@ -0,0 +1,38 @@
1
+ import hashlib
2
+ from typing import Set, Optional, Callable
3
+ from pydantic import BaseModel, Field
4
+
5
+
6
+ class SecurityPolicy(BaseModel):
7
+ """Pydantic security policy enforced on an OpenRegistry instance."""
8
+ allowed_namespaces: Optional[Set[str]] = None
9
+ allowed_callers: Optional[Set[str]] = None
10
+ allow_internal_lib: bool = True
11
+ allow_external_entrypoints: bool = True
12
+ allow_plugin_discovery: bool = True
13
+ verifier: Optional[Callable[[str], bool]] = None
14
+
15
+ model_config = {
16
+ "arbitrary_types_allowed": True
17
+ }
18
+
19
+ def validate_access(self, namespace: str, caller_id: Optional[str] = None) -> None:
20
+ if self.allowed_namespaces is not None and namespace.lower() not in self.allowed_namespaces:
21
+ raise PermissionError(
22
+ f"Access denied: Namespace '{namespace}' is restricted by policy."
23
+ )
24
+ if self.allowed_callers is not None and caller_id and caller_id.lower() not in self.allowed_callers:
25
+ raise PermissionError(
26
+ f"Access denied: Caller '{caller_id}' is not authorized."
27
+ )
28
+
29
+
30
+ class PluginVerifier:
31
+ """Helper for verifying plugin module checksums prior to import."""
32
+ @staticmethod
33
+ def verify_sha256(file_path: str, expected_hash: str) -> bool:
34
+ hasher = hashlib.sha256()
35
+ with open(file_path, "rb") as f:
36
+ for chunk in iter(lambda: f.read(4096), b""):
37
+ hasher.update(chunk)
38
+ return hasher.hexdigest().lower() == expected_hash.lower()
@@ -0,0 +1,97 @@
1
+ import logging
2
+ from abc import ABC
3
+ from typing import Dict, Optional, List
4
+ from graphlib import TopologicalSorter
5
+
6
+ from supersolid.core.engine import SuperSolidEngine
7
+ from supersolid.core.events import (
8
+ SuperSolidEventBus,
9
+ EngineBootingEvent,
10
+ EngineBootedEvent,
11
+ EngineShutdownEvent,
12
+ SystemErrorEvent,
13
+ )
14
+
15
+ logger = logging.getLogger(__name__)
16
+
17
+
18
+ class SuperSolidSystem(ABC):
19
+ """
20
+ Top-Level Orchestrator managing multiple SuperSolidEngines.
21
+ """
22
+ engines: Dict[str, SuperSolidEngine]
23
+ events: SuperSolidEventBus
24
+
25
+ def __init__(
26
+ self,
27
+ engines: Optional[Dict[str, SuperSolidEngine]] = None,
28
+ events: Optional[SuperSolidEventBus] = None
29
+ ):
30
+ self.engines = engines or {}
31
+ self.events = events or SuperSolidEventBus()
32
+
33
+ def add_engine(self, name: str, engine: SuperSolidEngine) -> None:
34
+ self.engines[name.lower()] = engine
35
+
36
+ def get_engine(self, name: str) -> Optional[SuperSolidEngine]:
37
+ return self.engines.get(name.lower())
38
+
39
+ def start(self) -> None:
40
+ """Starts all system engines in topological dependency order, rolling back on failure."""
41
+ order = self._resolve_dependency_order()
42
+ booted: List[str] = []
43
+
44
+ for name in order:
45
+ engine = self.engines.get(name)
46
+ if not engine:
47
+ continue
48
+ try:
49
+ self.events.publish(EngineBootingEvent(engine_name=name))
50
+ engine.boot()
51
+ booted.append(name)
52
+ self.events.publish(EngineBootedEvent(engine_name=name))
53
+ except Exception as e:
54
+ logger.error(
55
+ f"Failed to boot engine '{name}': {e}. Initiating rollback...")
56
+ self.events.publish(SystemErrorEvent(
57
+ context=f"boot:{name}", error=str(e)))
58
+ for booted_name in reversed(booted):
59
+ try:
60
+ if self.engines.get(booted_name):
61
+ self.engines[booted_name].shutdown()
62
+ self.events.publish(
63
+ EngineShutdownEvent(engine_name=booted_name))
64
+ except Exception as rb_err:
65
+ logger.error(
66
+ f"Error during rollback shutdown of '{booted_name}': {rb_err}")
67
+ raise
68
+
69
+ def stop(self) -> None:
70
+ """Shuts down all system engines in reverse topological order, ensuring fault-tolerant cleanup."""
71
+ order = list(reversed(self._resolve_dependency_order()))
72
+ for name in order:
73
+ engine = self.engines.get(name)
74
+ if not engine:
75
+ continue
76
+ try:
77
+ engine.shutdown()
78
+ self.events.publish(EngineShutdownEvent(engine_name=name))
79
+ except Exception as e:
80
+ logger.error(f"Error shutting down engine '{name}': {e}")
81
+ self.events.publish(SystemErrorEvent(
82
+ context=f"shutdown:{name}", error=str(e)))
83
+
84
+ def _resolve_dependency_order(self) -> List[str]:
85
+ sorter = TopologicalSorter()
86
+ for name, engine in self.engines.items():
87
+ # engine.depends_on specifies names of prerequisite engines
88
+ sorter.add(name.lower(), *[dep.lower()
89
+ for dep in engine.depends_on])
90
+ return list(sorter.static_order())
91
+
92
+
93
+ # Aliases
94
+ SolidSystem = SuperSolidSystem
95
+ OpenSystem = SuperSolidSystem
96
+
97
+
supersolid/py.typed ADDED
@@ -0,0 +1 @@
1
+