-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsingleton_decorator_test.py
More file actions
64 lines (43 loc) · 1.56 KB
/
singleton_decorator_test.py
File metadata and controls
64 lines (43 loc) · 1.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""Singleton pattern example using a decorator.
See also https://realpython.com/primer-on-python-decorators/#creating-singletons
"""
import functools
from typing import Any, Callable, ClassVar, Dict, Optional, Tuple, Type, TypeVar
T = TypeVar("T")
def singleton(class_: Type[T]) -> Callable[..., T]:
"""Class decorator to enforce singleton."""
# Called once for every class that is decorated.
instance: Optional[T] = None
@functools.wraps(class_)
def wrapper_singleton(*args: Tuple[Any], **kwargs: Dict[str, Any]) -> T:
# Called once every time the class is instantiated (i.e., `SomeClass()`)
nonlocal instance
if instance is None:
instance = class_(*args, **kwargs)
return instance
return wrapper_singleton
@singleton
class DataSource:
"""Singleton representing a data source."""
called: ClassVar[int] = 0
def __init__(self) -> None:
DataSource.called += 1
@singleton
class Resource:
"""Singleton representing a resource."""
called: ClassVar[int] = 0
def __init__(self) -> None:
Resource.called += 1
def test_singleton_datasource() -> None:
"""Verify that 1 and only 1 instance is created."""
data_source_1 = DataSource()
data_source_2 = DataSource()
assert DataSource.called == 1
assert data_source_1 is data_source_2
resource_1 = Resource()
resource_2 = Resource()
assert Resource.called == 1
assert resource_1 is resource_2
data_source_3 = DataSource()
assert DataSource.called == 1
assert data_source_3 is data_source_1