1717__license__ = "SPDX-License-Identifier: MIT"
1818
1919from functools import lru_cache
20- from typing import TYPE_CHECKING
20+ from typing import TYPE_CHECKING , Any , ClassVar , Self
2121
2222if TYPE_CHECKING :
2323 from collections .abc import Sequence
@@ -30,19 +30,50 @@ class FitterRegistry:
3030 """
3131 Registry that stores fitter descriptors and selects the best match.
3232
33+ This class is a singleton: every call to ``FitterRegistry()`` returns the
34+ same instance. Use ``FitterRegistry._reset()`` in tests to clear state.
35+
3336 Examples
3437 --------
3538 >>> registry = FitterRegistry()
3639 >>> registry.register(some_descriptor)
3740 >>> desc = registry.find("cdf", ["pdf"], required_tags={"continuous", "univariate"})
3841 """
3942
43+ _instance : ClassVar [FitterRegistry | None ] = None
44+
45+ def __new__ (cls ) -> Self :
46+ if cls ._instance is None :
47+ cls ._instance = super ().__new__ (cls )
48+ return cls ._instance # type: ignore[return-value]
49+
4050 def __init__ (self ) -> None :
51+ if getattr (self , "_initialized" , False ):
52+ return
4153 self ._by_key : dict [
4254 tuple [GenericCharacteristicName , tuple [GenericCharacteristicName , ...]],
4355 list [FitterDescriptor ],
4456 ] = {}
4557 self ._all : list [FitterDescriptor ] = []
58+ self ._initialized = True
59+
60+ def __copy__ (self ) -> Self :
61+ """Singleton copy returns the same instance."""
62+ return self
63+
64+ def __deepcopy__ (self , memo : dict [Any , Any ]) -> Self :
65+ """Singleton deepcopy returns the same instance."""
66+ return self
67+
68+ @classmethod
69+ def _reset (cls ) -> None :
70+ """
71+ Clear the singleton instance (for testing purposes only).
72+
73+ Resets all registered descriptors and allows the next ``FitterRegistry()``
74+ call to create a fresh instance.
75+ """
76+ cls ._instance = None
4677
4778 def register (self , descriptor : FitterDescriptor ) -> None :
4879 """
@@ -153,8 +184,9 @@ def fitter_registry() -> FitterRegistry:
153184 -----
154185 - Descriptors are **not** created at import time; they are built here on
155186 first access, keeping module-level side-effects to a minimum.
156- - Users who need a custom registry should instantiate ``FitterRegistry()``
157- directly and populate it themselves.
187+ - ``FitterRegistry`` is a singleton; ``FitterRegistry() is fitter_registry()``
188+ is always ``True`` after the first call.
189+ - To reset the singleton (e.g. in tests), call ``reset_fitter_registry()``.
158190 """
159191 from pysatl_core .distributions .computations .continuous import (
160192 _build_continuous_descriptors ,
@@ -172,6 +204,7 @@ def fitter_registry() -> FitterRegistry:
172204def reset_fitter_registry () -> None :
173205 """Reset the cached fitter registry (useful in tests)."""
174206 fitter_registry .cache_clear ()
207+ FitterRegistry ._reset ()
175208
176209
177210__all__ = [
0 commit comments