Coverage for functions \ flipdare \ core \ singleton.py: 83%

30 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-05-08 12:22 +1000

1#!/usr/bin/env python 

2# Copyright (c) 2026 Flipdare Pty Ltd. All rights reserved. 

3# 

4# This file is part of Flipdare's proprietary software and contains 

5# confidential and copyrighted material. Unauthorised copying, 

6# modification, distribution, or use of this file is strictly 

7# prohibited without prior written permission from Flipdare Pty Ltd. 

8# 

9# This software includes third-party components licensed under MIT, 

10# BSD, and Apache 2.0 licences. See THIRD_PARTY_NOTICES for details. 

11# 

12 

13from __future__ import annotations 

14from typing import Any, ClassVar, Self, TypeVar, cast 

15 

16T = TypeVar("T", bound="Singleton") 

17 

18 

19class Singleton: 

20 """ 

21 Base class for singleton pattern. 

22 

23 Usage: 

24 class MyService(Singleton): 

25 def __init__(self, config: str = "default"): 

26 super().__init__() 

27 self.config = config 

28 

29 # First call with arguments 

30 service = MyService.instance(config="production") 

31 

32 # Subsequent calls ignore arguments and return existing instance 

33 same_service = MyService.instance(config="ignored") 

34 

35 # For resetting in dev/test environments: 

36 MyService.reset_instance() 

37 """ 

38 

39 # Note: Each subclass needs its own _instance, so we use a dict keyed by class 

40 _instances: ClassVar[dict[type, Singleton]] = {} 

41 

42 def __new__(cls, *args: Any, **kwargs: Any) -> Self: # noqa: ARG004 

43 if cls not in cls._instances: 

44 instance = super().__new__(cls) 

45 cls._instances[cls] = instance 

46 

47 return cast("Self", cls._instances[cls]) 

48 

49 def __init__(self, *args: Any, **kwargs: Any) -> None: # noqa: ARG002 

50 """ 

51 Override this in subclasses to accept initialization arguments. 

52 Always call super().__init__() first. 

53 Use the _initialized guard to prevent re-initialization. 

54 """ 

55 if hasattr(self, "_initialized"): 

56 return 

57 

58 self._initialized = True 

59 

60 @classmethod 

61 def instance(cls, *args: Any, **kwargs: Any) -> Self: 

62 """ 

63 Get or create the singleton instance. 

64 

65 Arguments are only used on first instantiation. 

66 Subsequent calls return the existing instance regardless of arguments. 

67 """ 

68 if cls not in cls._instances: 

69 cls._instances[cls] = cls(*args, **kwargs) 

70 

71 return cls._instances[cls] # type: ignore[return-value] 

72 

73 @classmethod 

74 def reset_instance(cls) -> None: 

75 """Reset the singleton instance. Only allowed in dev/test environments.""" 

76 from flipdare.app_env import get_app_environment 

77 

78 if get_app_environment().in_cloud: 

79 from flipdare.services import get_app_logger 

80 from flipdare.generated.shared.app_error_code import AppErrorCode 

81 

82 # we dont want to throw a runtime error, because that may affect 

83 # users instead log a message and return .. 

84 msg = f"Attempt made to reset {cls.__name__} in production" 

85 get_app_logger().system_error( 

86 error_code=AppErrorCode.SERVER, 

87 message=msg, 

88 notify_admin=True, 

89 ) 

90 return 

91 

92 if cls in cls._instances: 

93 # LOG().info(f"Resetting singleton instance for {cls.__name__}") 

94 del cls._instances[cls]