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