0
# Singleton Decorator
1
2
A testable singleton decorator that allows easily creating singleton objects by adding a decorator to class definitions while maintaining testability. Unlike traditional singleton implementations that make direct class access impossible in unit tests, this decorator uses a wrapper object that stores the original class in a `__wrapped__` attribute, allowing developers to access the decorated class directly for isolated testing.
3
4
## Package Information
5
6
- **Package Name**: singleton-decorator
7
- **Language**: Python
8
- **Installation**: `pip install singleton-decorator`
9
10
## Core Imports
11
12
```python
13
from singleton_decorator import singleton
14
```
15
16
Version information:
17
18
```python
19
from singleton_decorator import __version__
20
```
21
22
## Basic Usage
23
24
```python
25
from singleton_decorator import singleton
26
27
@singleton
28
class DatabaseConnection:
29
def __init__(self, host, port):
30
self.host = host
31
self.port = port
32
print(f"Connecting to {host}:{port}")
33
34
def query(self, sql):
35
return f"Executing: {sql}"
36
37
# First call creates the instance
38
db1 = DatabaseConnection("localhost", 5432)
39
# Subsequent calls return the same instance (constructor args ignored)
40
db2 = DatabaseConnection("ignored", 9999)
41
db3 = DatabaseConnection("also_ignored", 1111)
42
43
# All variables reference the same instance
44
assert db1 is db2 is db3
45
print(db1.host) # "localhost"
46
print(db2.host) # "localhost" (not "ignored")
47
```
48
49
## Capabilities
50
51
### Singleton Decorator
52
53
Creates a singleton wrapper for any class, ensuring only one instance exists while maintaining testability through the `__wrapped__` attribute.
54
55
```python { .api }
56
def singleton(cls):
57
"""
58
A singleton decorator that returns a wrapper object. A call on that object
59
returns a single instance object of decorated class. Use the __wrapped__
60
attribute to access decorated class directly in unit tests.
61
62
Args:
63
cls: The class to decorate as a singleton
64
65
Returns:
66
_SingletonWrapper: Wrapper object that manages singleton instance
67
"""
68
```
69
70
### Testing Access
71
72
Access the original class for isolated unit testing without singleton behavior.
73
74
```python { .api }
75
# Access pattern for testing
76
decorated_class.__wrapped__ # Original class for direct method calls
77
```
78
79
**Usage in Tests:**
80
81
```python
82
from unittest import TestCase, mock
83
from singleton_decorator import singleton
84
85
@singleton
86
class MyService:
87
def __init__(self, config):
88
self.config = config
89
90
def process_data(self, data):
91
return f"Processing {data} with {self.config}"
92
93
class TestMyService(TestCase):
94
def test_process_data(self):
95
# Use __wrapped__ to test method in isolation
96
mock_self = mock.MagicMock()
97
mock_self.config = "test_config"
98
99
result = MyService.__wrapped__.process_data(mock_self, "test_data")
100
101
assert result == "Processing test_data with test_config"
102
```
103
104
## Types and Implementation Details
105
106
```python { .api }
107
class _SingletonWrapper:
108
"""
109
Internal wrapper class created for each decorated class.
110
Manages singleton instance lifecycle.
111
"""
112
113
def __init__(self, cls):
114
"""
115
Initialize wrapper with the decorated class.
116
117
Args:
118
cls: The class to wrap
119
"""
120
121
def __call__(self, *args, **kwargs):
122
"""
123
Returns singleton instance of wrapped class.
124
Creates instance on first call, returns existing instance thereafter.
125
126
Args:
127
*args: Constructor arguments (only used on first call)
128
**kwargs: Constructor keyword arguments (only used on first call)
129
130
Returns:
131
object: Single instance of the wrapped class
132
"""
133
134
__wrapped__: type # Original decorated class for testing access
135
_instance: object # Singleton instance (None until first call)
136
137
__version__: str # Package version constant ("1.0.0")
138
```
139
140
## Important Notes
141
142
### Constructor Behavior
143
144
- The `__init__` method is called **only once** with arguments from the first instantiation
145
- All subsequent instantiation attempts ignore their arguments and return the existing instance
146
- This behavior ensures true singleton semantics but requires careful consideration of constructor arguments
147
148
### Testing Guidelines
149
150
- Use `ClassName.__wrapped__` to access the original class in unit tests
151
- Pass mock objects as `self` to test methods in complete isolation
152
- This approach avoids constructor dependencies and maintains test isolation
153
- The `__wrapped__` attribute provides direct access to all class methods and attributes
154
155
### Thread Safety
156
157
This implementation does not provide thread-safety guarantees. In multi-threaded environments, consider additional synchronization mechanisms if concurrent access to the singleton creation is possible.