2# +==== BEGIN CatFeeder =================+
5# ...............)..(.')
7# ...............\(__)|
8# Inspired by Joan Stark
9# source https://www.asciiart.eu/
13# FILE: runtime_manager.py
14# CREATION DATE: 22-11-2025
15# LAST Modified: 14:43:14 19-12-2025
17# This is the backend server in charge of making the actual website work.
19# COPYRIGHT: (c) Cat Feeder
20# PURPOSE: Provide an ECS-like lookup dictionary with lazy singleton creation.
22# +==== END CatFeeder =================+
24from __future__
import annotations
25from typing
import Type, TypeVar, Dict, Any, Union, overload, Optional
29from display_tty
import Disp, initialise_logger
31from .final_singleton_class
import FinalSingleton
38 """Flexible runtime service container.
41 Lazy, thread-safe instantiation and retrieval of service classes.
42 Supports plain classes or ``FinalSingleton`` subclasses. First access
43 may supply constructor args; later accesses ignore them.
46 RuntimeError: Named lookup with no registration.
47 TypeError: Constructor argument mismatch (propagated).
48 Exception: Any user exception from constructor or ``async_init``.
51 Failed constructions are not cached; later calls retry.
54 _instances: Dict[str, Any] = {}
55 _classes: Dict[str, Type[Any]] = {}
56 _thread_locks: Dict[str, threading.Lock] = {}
58 _disp: Disp = initialise_logger(__qualname__,
False)
62 cls.
_disp.update_disp_debug(debug)
68 def set(cls, service: Type[T], *args, **kwargs) ->
None:
69 """Register and construct eagerly.
71 No-op if already present.
72 Raises any exception from constructor / async_init.
74 key = service.__name__
85 if issubclass(service, FinalSingleton):
86 setattr(service,
"_allow_create",
True)
87 instance = service(*args, **kwargs)
88 setattr(service,
"_allow_create",
False)
90 instance = service(*args, **kwargs)
93 async_init = getattr(instance,
"async_init",
None)
94 if callable(async_init):
95 maybe_coro = async_init()
96 if asyncio.iscoroutine(maybe_coro):
97 asyncio.run(maybe_coro)
106 def get(cls, service: Type[S], *args,
107 auto_register: bool =
False, **kwargs) -> S: ...
111 def get(cls, service: str, *args,
112 auto_register: bool =
False, **kwargs) -> Any: ...
115 def get(cls, service: Union[str, Type[S]], *args, auto_register: bool =
False, **kwargs) -> Any:
116 """Retrieve (and lazily initialize) a service instance.
119 - ``get(Type[Service]) -> Service``
120 - ``get("ServiceName") -> Any`` (returns registered instance when the concrete type is not statically known)
122 First call may pass constructor args/kwargs; they are ignored on later calls.
123 Async initialization via optional ``async_init`` coroutine is supported.
126 service: Class type or string name of the service.
127 *args: Constructor arguments for first initialization.
128 auto_register: If True, automatically register unregistered classes. If False, raise RuntimeError. Defaults to True.
129 **kwargs: Constructor keyword arguments for first initialization.
132 RuntimeError if named lookup missing or auto_register=False with unregistered class; propagates constructor / async_init exceptions.
134 if isinstance(service, str):
139 f
"Class {key} not registered in RuntimeManager."
142 key = service.__name__
145 if not auto_register:
147 f
"Class {key} not registered in RuntimeManager. Use set() first or pass auto_register=True."
152 if existing
is not None:
158 if existing
is not None:
162 if issubclass(klass, FinalSingleton):
163 setattr(klass,
"_allow_create",
True)
164 instance = klass(*args, **kwargs)
165 setattr(klass,
"_allow_create",
False)
167 instance = klass(*args, **kwargs)
169 async_init = getattr(instance,
"async_init",
None)
170 if callable(async_init):
171 maybe_coro = async_init()
172 if asyncio.iscoroutine(maybe_coro):
173 asyncio.run(maybe_coro)
185 target: Optional[object] =
None) -> Optional[S]: ...
190 target: Optional[object] =
None) -> Optional[Any]: ...
193 def get_if_exists(cls, service: Union[str, Type[S]], target: Optional[object] =
None) -> Optional[Any]:
194 class_name = getattr(service,
"__qualname__",
None)
or getattr(
195 service,
"__name__",
"")
196 cls.
_disp.log_debug(f
"Attempting to retrieve the {class_name} class")
199 f
"{class_name} class exists and is stored in class, returning"
203 cls.
_disp.log_debug(f
"{class_name} class exists, retrieving")
207 f
"{class_name} class does not exist, returning None"
216 def exists(cls, service: Union[str, Type[T]]) -> bool:
217 """Return True if service instance is already initialized."""
218 if isinstance(service, str):
221 if hasattr(service,
"__name__"):
222 key = service.__name__
223 elif hasattr(service,
"__qualname__"):
224 key = service.__qualname__
None update_debug(cls, bool debug=False)
S get(cls, Type[S] service, *args, bool auto_register=False, **kwargs)
Any get(cls, str service, *args, bool auto_register=False, **kwargs)
Any get(cls, Union[str, Type[S]] service, *args, bool auto_register=False, **kwargs)
None set(cls, Type[T] service, *args, **kwargs)
Optional[S] get_if_exists(cls, Type[S] service, Optional[object] target=None)
bool exists(cls, Union[str, Type[T]] service)