__init__.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import functools
  2. import threading
  3. import collections
  4. import contextlib
  5. import six
  6. def map0(func, value):
  7. return func(value) if value is not None else value
  8. def single(x):
  9. if len(x) != 1:
  10. raise Exception('Length of {} is not equal to 1'.format(x))
  11. return x[0]
  12. class _Result(object):
  13. pass
  14. def lazy(func):
  15. result = _Result()
  16. lock = threading.Lock()
  17. @functools.wraps(func)
  18. def wrapper(*args):
  19. try:
  20. return result.result
  21. except AttributeError:
  22. with lock:
  23. try:
  24. return result.result
  25. except AttributeError:
  26. result.result = func(*args)
  27. return result.result
  28. return wrapper
  29. def lazy_property(fn):
  30. attr_name = '_lazy_' + fn.__name__
  31. lock = threading.Lock()
  32. @property
  33. def _lazy_property(self):
  34. if hasattr(self, attr_name):
  35. return getattr(self, attr_name)
  36. with lock:
  37. if not hasattr(self, attr_name):
  38. setattr(self, attr_name, fn(self))
  39. return getattr(self, attr_name)
  40. return _lazy_property
  41. class classproperty(object):
  42. def __init__(self, func):
  43. self.func = func
  44. def __get__(self, _, owner):
  45. return self.func(owner)
  46. class lazy_classproperty(object):
  47. def __init__(self, func):
  48. self.func = func
  49. def __get__(self, _, owner):
  50. attr_name = '_lazy_' + self.func.__name__
  51. if not hasattr(owner, attr_name):
  52. setattr(owner, attr_name, self.func(owner))
  53. return getattr(owner, attr_name)
  54. class nullcontext(object):
  55. def __enter__(self):
  56. pass
  57. def __exit__(self, exc_type, exc_value, traceback):
  58. pass
  59. def memoize(limit=0, thread_local=False, thread_safe=True):
  60. assert limit >= 0
  61. assert limit <= 0 or thread_safe, 'memoize() it not thread safe enough to work in limiting and non-thread safe mode'
  62. def decorator(func):
  63. memory = {}
  64. if six.PY3:
  65. lock = contextlib.nullcontext()
  66. else:
  67. lock = nullcontext()
  68. lock = threading.Lock() if thread_safe else lock
  69. if limit:
  70. keys = collections.deque()
  71. def get(args):
  72. if args not in memory:
  73. with lock:
  74. if args not in memory:
  75. fargs = args[-1]
  76. memory[args] = func(*fargs)
  77. keys.append(args)
  78. if len(keys) > limit:
  79. del memory[keys.popleft()]
  80. return memory[args]
  81. else:
  82. def get(args):
  83. if args not in memory:
  84. with lock:
  85. if args not in memory:
  86. fargs = args[-1]
  87. memory.setdefault(args, func(*fargs))
  88. return memory[args]
  89. if thread_local:
  90. @functools.wraps(func)
  91. def wrapper(*args):
  92. th = threading.current_thread()
  93. return get((th.ident, th.name, args))
  94. else:
  95. @functools.wraps(func)
  96. def wrapper(*args):
  97. return get(('', '', args))
  98. return wrapper
  99. return decorator
  100. # XXX: add test
  101. def compose(*functions):
  102. def compose2(f, g):
  103. return lambda x: f(g(x))
  104. return functools.reduce(compose2, functions, lambda x: x)
  105. class Singleton(type):
  106. _instances = {}
  107. def __call__(cls, *args, **kwargs):
  108. if cls not in cls._instances:
  109. cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
  110. return cls._instances[cls]
  111. def stable_uniq(it):
  112. seen = set()
  113. res = []
  114. for e in it:
  115. if e not in seen:
  116. res.append(e)
  117. seen.add(e)
  118. return res
  119. def first(it):
  120. for d in it:
  121. if d:
  122. return d
  123. def split(data, func):
  124. l, r = [], []
  125. for e in data:
  126. if func(e):
  127. l.append(e)
  128. else:
  129. r.append(e)
  130. return l, r
  131. def flatten_dict(dd, separator='.', prefix=''):
  132. return (
  133. {
  134. prefix + separator + k if prefix else k: v
  135. for kk, vv in dd.items()
  136. for k, v in flatten_dict(vv, separator, kk).items()
  137. }
  138. if isinstance(dd, dict)
  139. else {prefix: dd}
  140. )