jpayne@68: from threading import RLock jpayne@68: try: jpayne@68: from collections.abc import Mapping as DictMixin jpayne@68: except ImportError: # Python < 3.3 jpayne@68: try: jpayne@68: from UserDict import DictMixin # Python 2 jpayne@68: except ImportError: # Python 3.0-3.3 jpayne@68: from collections import Mapping as DictMixin jpayne@68: jpayne@68: jpayne@68: # With lazy loading, we might end up with multiple threads triggering jpayne@68: # it at the same time. We need a lock. jpayne@68: _fill_lock = RLock() jpayne@68: jpayne@68: jpayne@68: class LazyDict(DictMixin): jpayne@68: """Dictionary populated on first use.""" jpayne@68: data = None jpayne@68: jpayne@68: def __getitem__(self, key): jpayne@68: if self.data is None: jpayne@68: _fill_lock.acquire() jpayne@68: try: jpayne@68: if self.data is None: jpayne@68: self._fill() jpayne@68: finally: jpayne@68: _fill_lock.release() jpayne@68: return self.data[key.upper()] jpayne@68: jpayne@68: def __contains__(self, key): jpayne@68: if self.data is None: jpayne@68: _fill_lock.acquire() jpayne@68: try: jpayne@68: if self.data is None: jpayne@68: self._fill() jpayne@68: finally: jpayne@68: _fill_lock.release() jpayne@68: return key in self.data jpayne@68: jpayne@68: def __iter__(self): jpayne@68: if self.data is None: jpayne@68: _fill_lock.acquire() jpayne@68: try: jpayne@68: if self.data is None: jpayne@68: self._fill() jpayne@68: finally: jpayne@68: _fill_lock.release() jpayne@68: return iter(self.data) jpayne@68: jpayne@68: def __len__(self): jpayne@68: if self.data is None: jpayne@68: _fill_lock.acquire() jpayne@68: try: jpayne@68: if self.data is None: jpayne@68: self._fill() jpayne@68: finally: jpayne@68: _fill_lock.release() jpayne@68: return len(self.data) jpayne@68: jpayne@68: def keys(self): jpayne@68: if self.data is None: jpayne@68: _fill_lock.acquire() jpayne@68: try: jpayne@68: if self.data is None: jpayne@68: self._fill() jpayne@68: finally: jpayne@68: _fill_lock.release() jpayne@68: return self.data.keys() jpayne@68: jpayne@68: jpayne@68: class LazyList(list): jpayne@68: """List populated on first use.""" jpayne@68: jpayne@68: _props = [ jpayne@68: '__str__', '__repr__', '__unicode__', jpayne@68: '__hash__', '__sizeof__', '__cmp__', jpayne@68: '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', jpayne@68: 'append', 'count', 'index', 'extend', 'insert', 'pop', 'remove', jpayne@68: 'reverse', 'sort', '__add__', '__radd__', '__iadd__', '__mul__', jpayne@68: '__rmul__', '__imul__', '__contains__', '__len__', '__nonzero__', jpayne@68: '__getitem__', '__setitem__', '__delitem__', '__iter__', jpayne@68: '__reversed__', '__getslice__', '__setslice__', '__delslice__'] jpayne@68: jpayne@68: def __new__(cls, fill_iter=None): jpayne@68: jpayne@68: if fill_iter is None: jpayne@68: return list() jpayne@68: jpayne@68: # We need a new class as we will be dynamically messing with its jpayne@68: # methods. jpayne@68: class LazyList(list): jpayne@68: pass jpayne@68: jpayne@68: fill_iter = [fill_iter] jpayne@68: jpayne@68: def lazy(name): jpayne@68: def _lazy(self, *args, **kw): jpayne@68: _fill_lock.acquire() jpayne@68: try: jpayne@68: if len(fill_iter) > 0: jpayne@68: list.extend(self, fill_iter.pop()) jpayne@68: for method_name in cls._props: jpayne@68: delattr(LazyList, method_name) jpayne@68: finally: jpayne@68: _fill_lock.release() jpayne@68: return getattr(list, name)(self, *args, **kw) jpayne@68: return _lazy jpayne@68: jpayne@68: for name in cls._props: jpayne@68: setattr(LazyList, name, lazy(name)) jpayne@68: jpayne@68: new_list = LazyList() jpayne@68: return new_list jpayne@68: jpayne@68: # Not all versions of Python declare the same magic methods. jpayne@68: # Filter out properties that don't exist in this version of Python jpayne@68: # from the list. jpayne@68: LazyList._props = [prop for prop in LazyList._props if hasattr(list, prop)] jpayne@68: jpayne@68: jpayne@68: class LazySet(set): jpayne@68: """Set populated on first use.""" jpayne@68: jpayne@68: _props = ( jpayne@68: '__str__', '__repr__', '__unicode__', jpayne@68: '__hash__', '__sizeof__', '__cmp__', jpayne@68: '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', jpayne@68: '__contains__', '__len__', '__nonzero__', jpayne@68: '__getitem__', '__setitem__', '__delitem__', '__iter__', jpayne@68: '__sub__', '__and__', '__xor__', '__or__', jpayne@68: '__rsub__', '__rand__', '__rxor__', '__ror__', jpayne@68: '__isub__', '__iand__', '__ixor__', '__ior__', jpayne@68: 'add', 'clear', 'copy', 'difference', 'difference_update', jpayne@68: 'discard', 'intersection', 'intersection_update', 'isdisjoint', jpayne@68: 'issubset', 'issuperset', 'pop', 'remove', jpayne@68: 'symmetric_difference', 'symmetric_difference_update', jpayne@68: 'union', 'update') jpayne@68: jpayne@68: def __new__(cls, fill_iter=None): jpayne@68: jpayne@68: if fill_iter is None: jpayne@68: return set() jpayne@68: jpayne@68: class LazySet(set): jpayne@68: pass jpayne@68: jpayne@68: fill_iter = [fill_iter] jpayne@68: jpayne@68: def lazy(name): jpayne@68: def _lazy(self, *args, **kw): jpayne@68: _fill_lock.acquire() jpayne@68: try: jpayne@68: if len(fill_iter) > 0: jpayne@68: for i in fill_iter.pop(): jpayne@68: set.add(self, i) jpayne@68: for method_name in cls._props: jpayne@68: delattr(LazySet, method_name) jpayne@68: finally: jpayne@68: _fill_lock.release() jpayne@68: return getattr(set, name)(self, *args, **kw) jpayne@68: return _lazy jpayne@68: jpayne@68: for name in cls._props: jpayne@68: setattr(LazySet, name, lazy(name)) jpayne@68: jpayne@68: new_set = LazySet() jpayne@68: return new_set jpayne@68: jpayne@68: # Not all versions of Python declare the same magic methods. jpayne@68: # Filter out properties that don't exist in this version of Python jpayne@68: # from the list. jpayne@68: LazySet._props = [prop for prop in LazySet._props if hasattr(set, prop)]