1import _thread
2import weakref
3from collections import OrderedDict
4from datetime import timedelta
5
6
7class _TzSingleton(type):
8 def __init__(cls, *args, **kwargs):
9 cls.__instance = None
10 super().__init__(*args, **kwargs)
11
12 def __call__(cls):
13 if cls.__instance is None:
14 cls.__instance = super().__call__()
15 return cls.__instance
16
17
18class _TzFactory(type):
19 def instance(cls, *args, **kwargs):
20 """Alternate constructor that returns a fresh instance"""
21 return type.__call__(cls, *args, **kwargs)
22
23
24class _TzOffsetFactory(_TzFactory):
25 def __init__(cls, *args, **kwargs):
26 cls.__instances = weakref.WeakValueDictionary()
27 cls.__strong_cache = OrderedDict()
28 cls.__strong_cache_size = 8
29
30 cls._cache_lock = _thread.allocate_lock()
31
32 def __call__(cls, name, offset):
33 if isinstance(offset, timedelta):
34 key = (name, offset.total_seconds())
35 else:
36 key = (name, offset)
37
38 instance = cls.__instances.get(key, None)
39 if instance is None:
40 instance = cls.__instances.setdefault(key,
41 cls.instance(name, offset))
42
43 # This lock may not be necessary in Python 3. See GH issue #901
44 with cls._cache_lock:
45 cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance)
46
47 # Remove an item if the strong cache is overpopulated
48 if len(cls.__strong_cache) > cls.__strong_cache_size:
49 cls.__strong_cache.popitem(last=False)
50
51 return instance
52
53
54class _TzStrFactory(_TzFactory):
55 def __init__(cls, *args, **kwargs):
56 cls.__instances = weakref.WeakValueDictionary()
57 cls.__strong_cache = OrderedDict()
58 cls.__strong_cache_size = 8
59
60 cls.__cache_lock = _thread.allocate_lock()
61
62 def __call__(cls, s, posix_offset=False):
63 key = (s, posix_offset)
64 instance = cls.__instances.get(key, None)
65
66 if instance is None:
67 instance = cls.__instances.setdefault(key,
68 cls.instance(s, posix_offset))
69
70 # This lock may not be necessary in Python 3. See GH issue #901
71 with cls.__cache_lock:
72 cls.__strong_cache[key] = cls.__strong_cache.pop(key, instance)
73
74 # Remove an item if the strong cache is overpopulated
75 if len(cls.__strong_cache) > cls.__strong_cache_size:
76 cls.__strong_cache.popitem(last=False)
77
78 return instance