itertoolsext.py (7829B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\brennivin\itertoolsext.py 2 import datetime as _datetime 3 import copy 4 from itertools import * 5 import random as _random 6 from . import compat as _compat 7 from .dochelpers import identity as _identity, default as _unsupplied 8 if _compat.PY3K: 9 ifilter = filter 10 izip = zip 11 izip_longest = zip_longest 12 13 class Bundle(dict): 14 15 def __init__(self, seq = None, **kwargs): 16 dict.__init__(self, (seq or ()), **kwargs) 17 18 def __getattr__(self, item): 19 try: 20 return self[item] 21 except KeyError: 22 raise AttributeError(item) 23 24 def __setattr__(self, key, value): 25 self[key] = value 26 27 def __str__(self): 28 clsn = self.__class__.__name__ 29 return '%s(%s)' % (clsn, dict.__repr__(self)) 30 31 __repr__ = __str__ 32 33 def __deepcopy__(self, memo = None, _nil = []): 34 return Bundle(copy.deepcopy(dict(self))) 35 36 def __hash__(self): 37 return hash((tuple(self.keys()), tuple(self.values()))) 38 39 40 class FrozenDict(dict): 41 42 def __raise(self, *args, **kwargs): 43 raise TypeError() 44 45 clear = pop = popitem = setdefault = update = __raise 46 __delitem__ = __setitem__ = __raise 47 48 def __hash__(self): 49 if not hasattr(self, '_hash'): 50 self._hash = 0 51 for pair in self.items(): 52 self._hash ^= hash(pair) 53 54 return self._hash 55 56 57 def all(seq, predicate = bool): 58 for item in seq: 59 if not predicate(item): 60 return False 61 62 return True 63 64 65 def any(seq, predicate = bool): 66 for item in seq: 67 if predicate(item): 68 return True 69 70 return False 71 72 73 def bucket(seq, keyprojection = _identity, valueprojection = _identity): 74 result = {} 75 for item in seq: 76 thisbucket = result.setdefault(keyprojection(item), []) 77 thisbucket.append(valueprojection(item)) 78 79 return result 80 81 82 def count(seq, predicate = None): 83 i = 0 84 if predicate is None: 85 try: 86 return len(seq) 87 except TypeError: 88 pass 89 90 for _ in seq: 91 i += 1 92 93 else: 94 for item in seq: 95 if predicate(item): 96 i += 1 97 98 return i 99 100 101 def datespan(startdate, enddate, delta = _datetime.timedelta(days=1)): 102 if startdate < enddate: 103 compare = lambda current, end: current < end 104 increment = lambda current: current + delta 105 else: 106 compare = lambda current, end: current > end 107 increment = lambda current: current - delta 108 currentdate = startdate 109 while compare(currentdate, enddate): 110 yield currentdate 111 currentdate = increment(currentdate) 112 113 114 def first(seq, predicate = None): 115 if predicate is None: 116 return next(islice(seq, 1)) 117 filtered = ifilter(predicate, seq) 118 return next(filtered) 119 120 121 def first_or_default(seq, predicate = None, default = None): 122 try: 123 return first(seq, predicate) 124 except StopIteration: 125 return default 126 127 128 def flatmap(function, sequence): 129 for item in sequence: 130 subseq = function(item) 131 for subitem in subseq: 132 yield subitem 133 134 135 def groupby2(seq, keyfunc): 136 sseq = sorted(seq, key=keyfunc) 137 for key, group in groupby(sseq, keyfunc): 138 yield (key, list(group)) 139 140 141 def groups_of_n(seq, n): 142 evenlyDivisible = len(seq) % n == 0 143 if not evenlyDivisible: 144 msg = 'Sequence length %s not divisible by %s' % (len(seq), n) 145 raise ArithmeticError(msg) 146 return [ seq[i:i + n] for i in range(0, len(seq), n) ] 147 148 149 def last(seq, predicate = None): 150 lastitem = last_or_default(seq, predicate, _unsupplied) 151 if lastitem is _unsupplied: 152 raise StopIteration() 153 return lastitem 154 155 156 def last_or_default(seq, predicate = None, default = None): 157 lastitem = default 158 for item in seq: 159 if predicate: 160 if predicate(item): 161 lastitem = item 162 else: 163 lastitem = item 164 165 return lastitem 166 167 168 def single(seq): 169 iterator = iter(seq) 170 try: 171 result = next(iterator) 172 except StopIteration: 173 raise 174 175 try: 176 next(iterator) 177 except StopIteration: 178 return result 179 180 raise StopIteration('Sequence has more than one item.') 181 182 183 def skip(sequence, number): 184 cnt = 0 185 for item in sequence: 186 if cnt >= number: 187 yield item 188 else: 189 cnt += 1 190 191 192 def take(seq, number, predicate = None): 193 if not isinstance(number, (int, float, _compat.long)): 194 raise TypeError('number arg must be a number type.') 195 yieldedcount = 0 196 for item in seq: 197 if yieldedcount >= number: 198 break 199 if predicate is None or predicate(item): 200 yield item 201 yieldedcount += 1 202 203 204 def unique(seq, transform = _identity): 205 seen = set() 206 for item in seq: 207 marker = transform(item) 208 if marker in seen: 209 continue 210 seen.add(marker) 211 yield item 212 213 214 def treeyield_depthfirst(node, getchildren, getchild = None, yieldnode = False): 215 if yieldnode: 216 yield node 217 childEnumerator = getchildren(node) 218 if getchild: 219 childEnumerator = range(childEnumerator) 220 for child in childEnumerator: 221 if getchild: 222 child = getchild(node, child) 223 for grandkid in treeyield_depthfirst(child, getchildren, getchild=getchild, yieldnode=True): 224 yield grandkid 225 226 227 def treeyield_breadthfirst(node, getchildren, getchild = None, yieldnode = False): 228 if yieldnode: 229 yield node 230 childEnumerator = getchildren(node) 231 if getchild: 232 childEnumerator = range(childEnumerator) 233 children = [] 234 for child in childEnumerator: 235 if getchild: 236 child = getchild(node, child) 237 yield child 238 children.append(child) 239 240 for child in children: 241 for grandkid in treeyield_breadthfirst(child, getchildren, getchild=getchild): 242 yield grandkid 243 244 245 def get_compound_item(collection, *indices): 246 result = collection 247 for ind in indices: 248 result = result[ind] 249 250 return result 251 252 253 def set_compound_item(collection, value, *indices): 254 operative = collection 255 for ind in indices[:-1]: 256 operative = operative[ind] 257 258 operative[indices[-1]] = value 259 260 261 def del_compound_item(collection, *indices): 262 f = get_compound_item(collection, *indices[:-1]) 263 del f[indices[-1]] 264 265 266 def _getattr_from_compound_element(obj, e): 267 if isinstance(e, int): 268 attr = obj[e] 269 else: 270 attr = getattr(obj, e) 271 return attr 272 273 274 def get_compound_attr(obj, *namesandindices): 275 currentattr = obj 276 for e in namesandindices: 277 currentattr = _getattr_from_compound_element(currentattr, e) 278 279 return currentattr 280 281 282 def set_compound_attr(obj, value, *namesandindices): 283 currentattr = obj 284 for e in namesandindices[:-1]: 285 currentattr = _getattr_from_compound_element(currentattr, e) 286 287 setattr(currentattr, namesandindices[-1], value) 288 289 290 def dict_add(alpha, beta, adder_function = None): 291 if adder_function is None: 292 adder_function = lambda value_a, value_b: value_a + value_b 293 for key in beta: 294 if key in alpha: 295 alpha[key] = adder_function(alpha[key], beta[key]) 296 else: 297 alpha[key] = beta[key] 298 299 300 def shuffle(col, maxattempts = 5): 301 ret = list(col) 302 if not ret or len(ret) == 1: 303 return ret 304 attempts = 0 305 while True: 306 _random.shuffle(ret) 307 if col != ret: 308 return ret 309 attempts += 1 310 if attempts > maxattempts: 311 raise AssertionError('Could not get a shuffle in %s attempts.' % maxattempts) 312 313 314 def remove_duplicates_and_preserve_order(seq): 315 seen = set() 316 seen_add = seen.add 317 return [ x for x in seq if not (x in seen or seen_add(x)) ]