Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import re 

2from gutools.tools import flatten, BASIC_TYPES, walk, rebuild 

3from gutools.colors import * 

4# -------------------------------------------------------------------- 

5# UObjects 

6# -------------------------------------------------------------------- 

7class UObject(object): 

8 

9 converter_to_U = dict() 

10 converter_from_U = dict() 

11 @classmethod 

12 def register_converter(cls, uklass, klass, params): 

13 """params is a dict 

14 params[(attr, uatrt)] = (to_U_func, from_U_func) 

15 """ 

16 # keep the same params objects instead creating new ones 

17 cls.converter_to_U[klass] = (uklass, params) 

18 cls.converter_from_U[uklass] = (klass, params) 

19 

20 @classmethod 

21 def to_uobject(cls, item, **post): 

22 if item is None: 

23 return item 

24 uklass, info = cls.converter_to_U[item.__class__] 

25 uobject = uklass() 

26 for uattr, attr, to_U_func, from_U_func in info: 

27 if not isinstance(attr, str): 

28 attr = attr(item) 

29 value = getattr(item, attr, None) 

30 

31 if not isinstance(uattr, str): 

32 uattr = uattr(uobject) 

33 setattr(uobject, uattr, to_U_func(value)) 

34 

35 for uattr, func in post.items(): 

36 if hasattr(uobject, uattr): 

37 setattr(uobject, uattr, func(getattr(uobject, uattr))) 

38 

39 return uobject 

40 

41 @classmethod 

42 def from_uobject(cls, item, **post): 

43 if item is None: 

44 return item 

45 klass, info = cls.converter_from_U[item.__class__] 

46 obj = klass() 

47 

48 for uattr, attr, to_U_func, from_U_func in info: 

49 if not isinstance(uattr, str): 

50 uattr = uattr(item) 

51 value = getattr(item, uattr) 

52 

53 if not isinstance(atrr, str): 

54 atrr = atrr(obj) 

55 setattr(obj, atrr, func(value)) 

56 

57 if not isinstance(uattr, str): 

58 uattr = uattr(uobject) 

59 setattr(uobject, uattr, from_U_func(value)) 

60 

61 for atrr, func in post.items(): 

62 setattr(obj, atrr, func(getattr(obj, atrr))) 

63 

64 return obj 

65 

66 @classmethod 

67 def change_to_uobject(cls, item, changes, **post): 

68 # TODO: move to register_converter pattern 

69 aliases = { 

70 'lmtPrice': 'price', 

71 'auxPrice': 'price', 

72 # 'trailStopPrice': 'price', 

73 } 

74 

75 # same as to_uobject 

76 uobject = cls.to_uobject(item, **post) 

77 

78 _, info = cls.converter_to_U[item.__class__] 

79 for attr, uattr in aliases.items(): 

80 change = changes.pop(attr, None) 

81 if change is not None: 

82 for _, (uattr2, converter) in info.items(): 

83 if uattr == uattr2: 

84 change = [converter(v) for v in change] 

85 changes[uattr] = change 

86 break 

87 

88 return uobject, changes 

89 

90 

91 __args__ = tuple([]) 

92 __kwargs__ = {} 

93 __slots__ = __args__ + tuple(__kwargs__.keys()) 

94 __alias__ = {} 

95 

96 def __init__(self, *args, **kwargs): 

97 # translate alias to slots members 

98 for k in set(self.__alias__).intersection(kwargs): 

99 kwargs.setdefault(self.__alias__[k], kwargs[k]) 

100 

101 if len(args) == 1 and hasattr(args[0], 'aslist'): # isinstance(args[0], UObject): 

102 args = args[0].aslist() 

103 # remove all overriden kwargs 

104 idx = [k for k in kwargs if k in self.__args__] 

105 idx = [self.__args__.index(k) for k in idx] 

106 idx.sort(reverse=True) 

107 for i in idx: 

108 args.pop(i) 

109 

110 kw2 = dict(self.__kwargs__) 

111 kw = list(self.__args__) 

112 

113 # set the named args 

114 for k, v in kwargs.items(): 

115 if k in kw: 

116 setattr(self, k, v) 

117 kw.remove(k) 

118 kw2.pop(k, None) 

119 elif k in kw2: 

120 setattr(self, k, v) 

121 kw2.pop(k, None) 

122 else: 

123 foo = 1 

124 

125 # set the unnamed args 

126 kw.extend(kw2.keys()) 

127 for i in range(min(len(kw), len(args))): 

128 k = kw.pop(0) 

129 setattr(self, k, args[i]) 

130 kw2.pop(k, None) 

131 

132 # set default values 

133 for k in kw: 

134 kw2.setdefault(k, None) 

135 for k, v in kw2.items(): 

136 setattr(self, k, v) 

137 

138 self.__pos_init__() 

139 

140 def __pos_init__(self): 

141 """Try to replace an attribute when value is 'callable'""" 

142 for k in self.__slots__: 

143 try: 

144 setattr(self, k, getattr(self, k)()) 

145 except TypeError: 

146 pass 

147 

148 def astuple(self): 

149 return tuple(self.aslist()) 

150 

151 def aslist(self): 

152 return [getattr(self, k) for k in self.__slots__] 

153 

154 def asdict(self, skip_nones=False, **kw): 

155 for k in self.__slots__: 

156 v = getattr(self, k) 

157 if skip_nones and v is None: 

158 continue 

159 kw.setdefault(k, v) 

160 for k in set(kw).difference(self.__slots__): 

161 kw.pop(k, None) 

162 

163 # return dict([(k, getattr(self, k)) for k in self.__slots__]) 

164 return kw 

165 

166 def argsval(self, mask=r'arg\d'): 

167 regexp = re.compile(mask) 

168 keys = [k for k in self.__slots__ if regexp.match(k)] 

169 keys.sort() 

170 return [getattr(self, k) for k in keys] 

171 

172 def __str__(self): 

173 # args = list(self.__args__) 

174 # args.extend(set(self.__kwargs__).difference(args)) 

175 data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__ if getattr(self, k) is not None] 

176 return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data)) 

177 

178 def __repr__(self): 

179 return str(self) 

180 

181 def __eq__(self, other): 

182 if isinstance(other, UObject): 

183 return self.astuple() == other.astuple() 

184 return False 

185 

186 def __getitem__(self, key): 

187 key = self.__alias__.get(key, key) 

188 return getattr(self, key) 

189 

190 def __setitem__(self, key, value): 

191 key = self.__alias__.get(key, key) 

192 return setattr(self, key, value) 

193 

194 def update(self, **kw): 

195 for k in set(self.__slots__).intersection(kw): 

196 self.setattr(k, kw[k]) 

197 

198 

199def I(a): 

200 return a 

201 

202def E(item, name): 

203 return getattr(item, name) 

204 

205# TODO: hash uid to permId and viceversa. Need map persistence 

206 

207def iterasdict(iterator): 

208 r = list() 

209 for item in iterator: 

210 if item is None: 

211 r.append(item) 

212 else: 

213 r.append(item.asdict()) 

214 return r 

215 

216def kget(container, *keys): 

217 """Get a value from a container trying multiples keys in order.""" 

218 for key in keys: 

219 if key in container: 

220 return container[key] 

221 

222def vget(container, value, *keys): 

223 """TBD""" 

224 for name, item in container.items(): 

225 for key in keys: 

226 if getattr(item, key, None) == value: 

227 return name, item 

228 return None, None 

229 

230 

231# -------------------------------------------------------------------- 

232# UUnit 

233# -------------------------------------------------------------------- 

234class UUnit(UObject): 

235 __args__ = ('value', ) 

236 __kwargs__ = {'unit': '', } 

237 __slots__ = __args__ + tuple(set(__kwargs__).difference(__args__)) 

238 

239 conversion = {} 

240 

241 def __long__(self): 

242 return self.value 

243 

244 def __float__(self): 

245 return self.value 

246 

247 def __str__(self): 

248 return f"{self.value} {self.unit}" 

249 

250 def __repr__(self): 

251 return str(self) 

252 

253 def __bool__(self): 

254 return bool(self.value) 

255 

256 def __eq__(self, other): 

257 if isinstance(other, UUnit): 

258 return self.aslist() == other.aslist() 

259 return self.value == other 

260 

261 def __add__(self, other): 

262 """ 

263 Defines the '+' operator. 

264 """ 

265 assert isinstance(other, UUnit) 

266 if other.unit != self.unit: 

267 fact = UUnit.conversion.get 

268 f = fact((other.unit, self.unit)) or 1.0 / fact((self.unit, other.unit)) 

269 if not f: 

270 raise RuntimeError(f"Unable to convert '{other.unit}' to '{self.unit}'") 

271 

272 else: 

273 f = 1.0 

274 

275 return UUnit(self.value + f * other.value, self.unit) 

276 

277 

278 def __iadd__(self, other): 

279 """ 

280 Similar to __add__ 

281 """ 

282 if type(other) == int or type(other) == float: 

283 x = (other * Ccy.currencies[self.unit]) 

284 else: 

285 x = (other.value / Ccy.currencies[other.unit] * Ccy.currencies[self.unit]) 

286 self.value += x 

287 return self 

288 

289 def __radd__(self, other): 

290 res = self + other 

291 if self.unit != "EUR": 

292 res.changeTo("EUR") 

293 return res 

294 

295 

296 

297 

298 

299# -------------------------------------------------------------------- 

300# Smart Answer 

301# -------------------------------------------------------------------- 

302 

303class SmartContainer(dict): 

304 _key_attribute = dict() 

305 _includes = dict() 

306 _excludes = dict() 

307 

308 def _get_key_attribute(self, item): 

309 keys = self._key_attribute.get(item.__class__) 

310 if keys: 

311 for attr in keys.split('.'): 

312 item = getattr(item, attr, None) 

313 if item is None: 

314 return 

315 return item 

316 else: 

317 return len(self) 

318 

319 

320 @classmethod 

321 def register_key(cls, klass, attr): 

322 cls._key_attribute[klass] = attr 

323 

324 @classmethod 

325 def includes(cls, klass, attr, filter_): 

326 ex = cls._excludes.setdefault(klass, dict()).setdefault(attr, list()) 

327 ex.append(filter_) 

328 

329 @classmethod 

330 def excludes(cls, klass, attr, filter_): 

331 ex = cls._excludes.setdefault(klass, dict()).setdefault(attr, list()) 

332 ex.append(filter_) 

333 

334 def add(self, item): 

335 if isinstance(item, tuple): 

336 if len(item) == 1: 

337 item = item[0] 

338 

339 if self._check_filters(item): 

340 return # discard insertion 

341 

342 key = self._get_key_attribute(item) 

343 self[key] = item 

344 return item 

345 

346 def aslist(self): 

347 keys = list(self.keys()) 

348 keys.sort() 

349 return [self[k] for k in keys] 

350 

351 def sortitems(self): 

352 keys = list(self.keys()) 

353 keys.sort() 

354 for k in keys: 

355 yield k, self[k] 

356 

357 def _check_filters(self, item) -> bool: 

358 for item in flatten(item): 

359 ex_info = self._excludes.get(item.__class__, {}) 

360 for attr, filters in ex_info.items(): 

361 value = getattr(item, attr) 

362 for filter_ in filters: 

363 if filter_(value): 

364 return True 

365 return False 

366 

367 def walk(self): 

368 yield from walk(self, includes=self._includes, excludes=self._excludes) 

369 

370class Answer(SmartContainer): 

371 pass 

372 

373# -------------------------------------------------------------------- 

374# Converters 

375# -------------------------------------------------------------------- 

376class UConverter(object): 

377 """Base class for Converters. 

378 """ 

379 def __init__(self): 

380 pass 

381 

382 def convert(self, container): 

383 raise RuntimeError('Must be overriden') 

384 

385 def convert_item(self, item): 

386 raise RuntimeError('Must be overriden') 

387 

388def test_walk(): 

389 

390 containers = [ 

391 {}, 

392 [], 

393 tuple([]), 

394 { 

395 'foo': 3, 

396 'bar': [1, 2, 3], 

397 'buzz': { 

398 'a': 'A', 

399 'b': 'B', 

400 'c': (9, 8, 7), 

401 }, 

402 }, 

403 ] 

404 

405 for container in containers: 

406 # footprint = [d for d in walk(container)] 

407 container2 = rebuild(walk(container)) 

408 

409 assert container == container2 

410 assert id(container) != id(container2) or isinstance(container, tuple) 

411 

412 

413 

414 foo = 1 

415 

416def test_alias(): 

417 class UFoo(UObject): 

418 __args__ = ('a', 'b', 'c', ) 

419 __kwargs__ = {'a': 'uno', } 

420 __slots__ = __args__ + tuple(set(__kwargs__).difference(__args__)) 

421 __alias__ = {'foo': 'a', } 

422 

423 foo = UFoo() 

424 x = foo.foo 

425 foo.foo = 1 

426 assert foo.a == foo.foo 

427 

428def test_unflattern(): 

429 info = [ 

430 [1, 2, 3, 'uno'], 

431 [4, 5, 6, 'dos'], 

432 [7, 8, 9, 'tres'], 

433 [10, 11, 12, 'cuatro'], 

434 ] 

435 

436 info2 = unflattern(info, [-1, ]) 

437 info3 = unflattern(info, [-1, 0]) 

438 

439 foo = 1 

440 

441def test_uunit(): 

442 

443 a = UUnit(1, "EUR") 

444 b = UUnit(2, "EUR") 

445 u = UUnit(5, "USD") 

446 

447 if a: 

448 foo = 1 

449 else: 

450 foo = 2 

451 

452 assert foo == 1 

453 

454 z = a + b 

455 

456 assert z == 3 

457 

458 UUnit.conversion[('EUR', 'USD')] = 1.13 

459 x = u + b 

460 y = b + u 

461 

462 

463 foo = 1 

464 

465if __name__ == '__main__': 

466 

467 test_uunit() 

468 test_unflattern() 

469 test_alias() 

470 test_walk()