Coverage for /home/agp/Documents/me/code/gutools/gutools/uobjects.py : 0%

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):
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)
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)
31 if not isinstance(uattr, str):
32 uattr = uattr(uobject)
33 setattr(uobject, uattr, to_U_func(value))
35 for uattr, func in post.items():
36 if hasattr(uobject, uattr):
37 setattr(uobject, uattr, func(getattr(uobject, uattr)))
39 return uobject
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()
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)
53 if not isinstance(atrr, str):
54 atrr = atrr(obj)
55 setattr(obj, atrr, func(value))
57 if not isinstance(uattr, str):
58 uattr = uattr(uobject)
59 setattr(uobject, uattr, from_U_func(value))
61 for atrr, func in post.items():
62 setattr(obj, atrr, func(getattr(obj, atrr)))
64 return obj
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 }
75 # same as to_uobject
76 uobject = cls.to_uobject(item, **post)
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
88 return uobject, changes
91 __args__ = tuple([])
92 __kwargs__ = {}
93 __slots__ = __args__ + tuple(__kwargs__.keys())
94 __alias__ = {}
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])
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)
110 kw2 = dict(self.__kwargs__)
111 kw = list(self.__args__)
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
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)
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)
138 self.__pos_init__()
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
148 def astuple(self):
149 return tuple(self.aslist())
151 def aslist(self):
152 return [getattr(self, k) for k in self.__slots__]
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)
163 # return dict([(k, getattr(self, k)) for k in self.__slots__])
164 return kw
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]
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))
178 def __repr__(self):
179 return str(self)
181 def __eq__(self, other):
182 if isinstance(other, UObject):
183 return self.astuple() == other.astuple()
184 return False
186 def __getitem__(self, key):
187 key = self.__alias__.get(key, key)
188 return getattr(self, key)
190 def __setitem__(self, key, value):
191 key = self.__alias__.get(key, key)
192 return setattr(self, key, value)
194 def update(self, **kw):
195 for k in set(self.__slots__).intersection(kw):
196 self.setattr(k, kw[k])
199def I(a):
200 return a
202def E(item, name):
203 return getattr(item, name)
205# TODO: hash uid to permId and viceversa. Need map persistence
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
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]
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
231# --------------------------------------------------------------------
232# UUnit
233# --------------------------------------------------------------------
234class UUnit(UObject):
235 __args__ = ('value', )
236 __kwargs__ = {'unit': '', }
237 __slots__ = __args__ + tuple(set(__kwargs__).difference(__args__))
239 conversion = {}
241 def __long__(self):
242 return self.value
244 def __float__(self):
245 return self.value
247 def __str__(self):
248 return f"{self.value} {self.unit}"
250 def __repr__(self):
251 return str(self)
253 def __bool__(self):
254 return bool(self.value)
256 def __eq__(self, other):
257 if isinstance(other, UUnit):
258 return self.aslist() == other.aslist()
259 return self.value == other
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}'")
272 else:
273 f = 1.0
275 return UUnit(self.value + f * other.value, self.unit)
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
289 def __radd__(self, other):
290 res = self + other
291 if self.unit != "EUR":
292 res.changeTo("EUR")
293 return res
299# --------------------------------------------------------------------
300# Smart Answer
301# --------------------------------------------------------------------
303class SmartContainer(dict):
304 _key_attribute = dict()
305 _includes = dict()
306 _excludes = dict()
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)
320 @classmethod
321 def register_key(cls, klass, attr):
322 cls._key_attribute[klass] = attr
324 @classmethod
325 def includes(cls, klass, attr, filter_):
326 ex = cls._excludes.setdefault(klass, dict()).setdefault(attr, list())
327 ex.append(filter_)
329 @classmethod
330 def excludes(cls, klass, attr, filter_):
331 ex = cls._excludes.setdefault(klass, dict()).setdefault(attr, list())
332 ex.append(filter_)
334 def add(self, item):
335 if isinstance(item, tuple):
336 if len(item) == 1:
337 item = item[0]
339 if self._check_filters(item):
340 return # discard insertion
342 key = self._get_key_attribute(item)
343 self[key] = item
344 return item
346 def aslist(self):
347 keys = list(self.keys())
348 keys.sort()
349 return [self[k] for k in keys]
351 def sortitems(self):
352 keys = list(self.keys())
353 keys.sort()
354 for k in keys:
355 yield k, self[k]
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
367 def walk(self):
368 yield from walk(self, includes=self._includes, excludes=self._excludes)
370class Answer(SmartContainer):
371 pass
373# --------------------------------------------------------------------
374# Converters
375# --------------------------------------------------------------------
376class UConverter(object):
377 """Base class for Converters.
378 """
379 def __init__(self):
380 pass
382 def convert(self, container):
383 raise RuntimeError('Must be overriden')
385 def convert_item(self, item):
386 raise RuntimeError('Must be overriden')
388def test_walk():
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 ]
405 for container in containers:
406 # footprint = [d for d in walk(container)]
407 container2 = rebuild(walk(container))
409 assert container == container2
410 assert id(container) != id(container2) or isinstance(container, tuple)
414 foo = 1
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', }
423 foo = UFoo()
424 x = foo.foo
425 foo.foo = 1
426 assert foo.a == foo.foo
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 ]
436 info2 = unflattern(info, [-1, ])
437 info3 = unflattern(info, [-1, 0])
439 foo = 1
441def test_uunit():
443 a = UUnit(1, "EUR")
444 b = UUnit(2, "EUR")
445 u = UUnit(5, "USD")
447 if a:
448 foo = 1
449 else:
450 foo = 2
452 assert foo == 1
454 z = a + b
456 assert z == 3
458 UUnit.conversion[('EUR', 'USD')] = 1.13
459 x = u + b
460 y = b + u
463 foo = 1
465if __name__ == '__main__':
467 test_uunit()
468 test_unflattern()
469 test_alias()
470 test_walk()