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