Coverage for src/configuraptor/singleton.py: 100%
15 statements
« prev ^ index » next coverage.py v7.2.7, created at 2026-05-01 17:14 +0200
« prev ^ index » next coverage.py v7.2.7, created at 2026-05-01 17:14 +0200
1"""
2Singleton mixin/metaclass.
3"""
5import typing
7T = typing.TypeVar("T")
10class SingletonMeta(type):
11 """
12 Every instance of a singleton shares the same object underneath.
14 Can be used as a metaclass:
15 Example:
16 class AbstractConfig(metaclass=Singleton):
18 Source: https://stackoverflow.com/questions/6760685/creating-a-singleton-in-python
19 """
21 _instances: typing.ClassVar[dict[type[typing.Any], typing.Any]] = {}
23 def __call__(self: type[T], *args: typing.Any, **kwargs: typing.Any) -> T:
24 """
25 When a class is instantiated (e.g. `AbstractConfig()`), __call__ is called. This overrides the default behavior.
26 """
27 if self not in SingletonMeta._instances:
28 SingletonMeta._instances[self] = type.__call__(self, *args, **kwargs)
30 return typing.cast(T, SingletonMeta._instances[self])
32 def clear(cls, instance: "Singleton | type[Singleton] | None" = None) -> None:
33 """
34 Use to remove old instances.
36 (otherwise e.g. pytest will crash)
37 """
38 if instance:
39 # Singleton.clear(SomeSingleton) or Singleton.clear(some_instance)
40 SingletonMeta._instances.pop(instance if isinstance(instance, SingletonMeta) else instance.__class__, None)
41 elif isinstance(cls, SingletonMeta) and issubclass(cls, Singleton) and cls is not Singleton:
42 # SomeSingleton.clear()
43 SingletonMeta._instances.pop(cls, None)
44 else:
45 # Singleton.clear()
46 SingletonMeta._instances.clear()
49class Singleton(metaclass=SingletonMeta):
50 """
51 Mixin to make a class a singleton.
52 """