jpayne@68
|
1 from threading import RLock
|
jpayne@68
|
2 try:
|
jpayne@68
|
3 from collections.abc import Mapping as DictMixin
|
jpayne@68
|
4 except ImportError: # Python < 3.3
|
jpayne@68
|
5 try:
|
jpayne@68
|
6 from UserDict import DictMixin # Python 2
|
jpayne@68
|
7 except ImportError: # Python 3.0-3.3
|
jpayne@68
|
8 from collections import Mapping as DictMixin
|
jpayne@68
|
9
|
jpayne@68
|
10
|
jpayne@68
|
11 # With lazy loading, we might end up with multiple threads triggering
|
jpayne@68
|
12 # it at the same time. We need a lock.
|
jpayne@68
|
13 _fill_lock = RLock()
|
jpayne@68
|
14
|
jpayne@68
|
15
|
jpayne@68
|
16 class LazyDict(DictMixin):
|
jpayne@68
|
17 """Dictionary populated on first use."""
|
jpayne@68
|
18 data = None
|
jpayne@68
|
19
|
jpayne@68
|
20 def __getitem__(self, key):
|
jpayne@68
|
21 if self.data is None:
|
jpayne@68
|
22 _fill_lock.acquire()
|
jpayne@68
|
23 try:
|
jpayne@68
|
24 if self.data is None:
|
jpayne@68
|
25 self._fill()
|
jpayne@68
|
26 finally:
|
jpayne@68
|
27 _fill_lock.release()
|
jpayne@68
|
28 return self.data[key.upper()]
|
jpayne@68
|
29
|
jpayne@68
|
30 def __contains__(self, key):
|
jpayne@68
|
31 if self.data is None:
|
jpayne@68
|
32 _fill_lock.acquire()
|
jpayne@68
|
33 try:
|
jpayne@68
|
34 if self.data is None:
|
jpayne@68
|
35 self._fill()
|
jpayne@68
|
36 finally:
|
jpayne@68
|
37 _fill_lock.release()
|
jpayne@68
|
38 return key in self.data
|
jpayne@68
|
39
|
jpayne@68
|
40 def __iter__(self):
|
jpayne@68
|
41 if self.data is None:
|
jpayne@68
|
42 _fill_lock.acquire()
|
jpayne@68
|
43 try:
|
jpayne@68
|
44 if self.data is None:
|
jpayne@68
|
45 self._fill()
|
jpayne@68
|
46 finally:
|
jpayne@68
|
47 _fill_lock.release()
|
jpayne@68
|
48 return iter(self.data)
|
jpayne@68
|
49
|
jpayne@68
|
50 def __len__(self):
|
jpayne@68
|
51 if self.data is None:
|
jpayne@68
|
52 _fill_lock.acquire()
|
jpayne@68
|
53 try:
|
jpayne@68
|
54 if self.data is None:
|
jpayne@68
|
55 self._fill()
|
jpayne@68
|
56 finally:
|
jpayne@68
|
57 _fill_lock.release()
|
jpayne@68
|
58 return len(self.data)
|
jpayne@68
|
59
|
jpayne@68
|
60 def keys(self):
|
jpayne@68
|
61 if self.data is None:
|
jpayne@68
|
62 _fill_lock.acquire()
|
jpayne@68
|
63 try:
|
jpayne@68
|
64 if self.data is None:
|
jpayne@68
|
65 self._fill()
|
jpayne@68
|
66 finally:
|
jpayne@68
|
67 _fill_lock.release()
|
jpayne@68
|
68 return self.data.keys()
|
jpayne@68
|
69
|
jpayne@68
|
70
|
jpayne@68
|
71 class LazyList(list):
|
jpayne@68
|
72 """List populated on first use."""
|
jpayne@68
|
73
|
jpayne@68
|
74 _props = [
|
jpayne@68
|
75 '__str__', '__repr__', '__unicode__',
|
jpayne@68
|
76 '__hash__', '__sizeof__', '__cmp__',
|
jpayne@68
|
77 '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__',
|
jpayne@68
|
78 'append', 'count', 'index', 'extend', 'insert', 'pop', 'remove',
|
jpayne@68
|
79 'reverse', 'sort', '__add__', '__radd__', '__iadd__', '__mul__',
|
jpayne@68
|
80 '__rmul__', '__imul__', '__contains__', '__len__', '__nonzero__',
|
jpayne@68
|
81 '__getitem__', '__setitem__', '__delitem__', '__iter__',
|
jpayne@68
|
82 '__reversed__', '__getslice__', '__setslice__', '__delslice__']
|
jpayne@68
|
83
|
jpayne@68
|
84 def __new__(cls, fill_iter=None):
|
jpayne@68
|
85
|
jpayne@68
|
86 if fill_iter is None:
|
jpayne@68
|
87 return list()
|
jpayne@68
|
88
|
jpayne@68
|
89 # We need a new class as we will be dynamically messing with its
|
jpayne@68
|
90 # methods.
|
jpayne@68
|
91 class LazyList(list):
|
jpayne@68
|
92 pass
|
jpayne@68
|
93
|
jpayne@68
|
94 fill_iter = [fill_iter]
|
jpayne@68
|
95
|
jpayne@68
|
96 def lazy(name):
|
jpayne@68
|
97 def _lazy(self, *args, **kw):
|
jpayne@68
|
98 _fill_lock.acquire()
|
jpayne@68
|
99 try:
|
jpayne@68
|
100 if len(fill_iter) > 0:
|
jpayne@68
|
101 list.extend(self, fill_iter.pop())
|
jpayne@68
|
102 for method_name in cls._props:
|
jpayne@68
|
103 delattr(LazyList, method_name)
|
jpayne@68
|
104 finally:
|
jpayne@68
|
105 _fill_lock.release()
|
jpayne@68
|
106 return getattr(list, name)(self, *args, **kw)
|
jpayne@68
|
107 return _lazy
|
jpayne@68
|
108
|
jpayne@68
|
109 for name in cls._props:
|
jpayne@68
|
110 setattr(LazyList, name, lazy(name))
|
jpayne@68
|
111
|
jpayne@68
|
112 new_list = LazyList()
|
jpayne@68
|
113 return new_list
|
jpayne@68
|
114
|
jpayne@68
|
115 # Not all versions of Python declare the same magic methods.
|
jpayne@68
|
116 # Filter out properties that don't exist in this version of Python
|
jpayne@68
|
117 # from the list.
|
jpayne@68
|
118 LazyList._props = [prop for prop in LazyList._props if hasattr(list, prop)]
|
jpayne@68
|
119
|
jpayne@68
|
120
|
jpayne@68
|
121 class LazySet(set):
|
jpayne@68
|
122 """Set populated on first use."""
|
jpayne@68
|
123
|
jpayne@68
|
124 _props = (
|
jpayne@68
|
125 '__str__', '__repr__', '__unicode__',
|
jpayne@68
|
126 '__hash__', '__sizeof__', '__cmp__',
|
jpayne@68
|
127 '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__',
|
jpayne@68
|
128 '__contains__', '__len__', '__nonzero__',
|
jpayne@68
|
129 '__getitem__', '__setitem__', '__delitem__', '__iter__',
|
jpayne@68
|
130 '__sub__', '__and__', '__xor__', '__or__',
|
jpayne@68
|
131 '__rsub__', '__rand__', '__rxor__', '__ror__',
|
jpayne@68
|
132 '__isub__', '__iand__', '__ixor__', '__ior__',
|
jpayne@68
|
133 'add', 'clear', 'copy', 'difference', 'difference_update',
|
jpayne@68
|
134 'discard', 'intersection', 'intersection_update', 'isdisjoint',
|
jpayne@68
|
135 'issubset', 'issuperset', 'pop', 'remove',
|
jpayne@68
|
136 'symmetric_difference', 'symmetric_difference_update',
|
jpayne@68
|
137 'union', 'update')
|
jpayne@68
|
138
|
jpayne@68
|
139 def __new__(cls, fill_iter=None):
|
jpayne@68
|
140
|
jpayne@68
|
141 if fill_iter is None:
|
jpayne@68
|
142 return set()
|
jpayne@68
|
143
|
jpayne@68
|
144 class LazySet(set):
|
jpayne@68
|
145 pass
|
jpayne@68
|
146
|
jpayne@68
|
147 fill_iter = [fill_iter]
|
jpayne@68
|
148
|
jpayne@68
|
149 def lazy(name):
|
jpayne@68
|
150 def _lazy(self, *args, **kw):
|
jpayne@68
|
151 _fill_lock.acquire()
|
jpayne@68
|
152 try:
|
jpayne@68
|
153 if len(fill_iter) > 0:
|
jpayne@68
|
154 for i in fill_iter.pop():
|
jpayne@68
|
155 set.add(self, i)
|
jpayne@68
|
156 for method_name in cls._props:
|
jpayne@68
|
157 delattr(LazySet, method_name)
|
jpayne@68
|
158 finally:
|
jpayne@68
|
159 _fill_lock.release()
|
jpayne@68
|
160 return getattr(set, name)(self, *args, **kw)
|
jpayne@68
|
161 return _lazy
|
jpayne@68
|
162
|
jpayne@68
|
163 for name in cls._props:
|
jpayne@68
|
164 setattr(LazySet, name, lazy(name))
|
jpayne@68
|
165
|
jpayne@68
|
166 new_set = LazySet()
|
jpayne@68
|
167 return new_set
|
jpayne@68
|
168
|
jpayne@68
|
169 # Not all versions of Python declare the same magic methods.
|
jpayne@68
|
170 # Filter out properties that don't exist in this version of Python
|
jpayne@68
|
171 # from the list.
|
jpayne@68
|
172 LazySet._props = [prop for prop in LazySet._props if hasattr(set, prop)]
|