Moved in MergeDicts. master
[Modular.git] / Modular.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#!/usr/bin/python3
 
'''
Created on Apr 26, 2013
 
@author: andy
'''
 
__all__ = ['Component', 'ModularType', 'Modular', 'Node']
 
from _tools import merge_dicts
from sys import modules
from collections import OrderedDict
 
class ModularType(type):
        subclasses = {}
        DerivedValues = {}
        filter = {}
        @classmethod
        def __prepare__(mcls, name, bases):
                return {'__classname__': name}
        
        def __new__(cls, name, bases, classDict):
                newClass = type.__new__(cls, name, bases, classDict)
                if 'moduleDepends' in classDict:
                        for module in classDict['moduleDepends']:
                                if module not in cls.DerivedValues:
                                        cls.DerivedValues[module] = {}
                                cls.DerivedValues[module] = merge_dicts(cls.DerivedValues[module], {newClass: classDict['moduleDepends']})
                if 'filter' in classDict:
                        cls.filter = merge_dicts(cls.filter, classDict['filter'])
                if name not in cls.subclasses:
                        cls.subclasses[name] = newClass
                _all = modules[newClass.__module__].__dict__.setdefault('__all__', [])
                if name not in _all:
                        _all.append(name)
                return newClass
        
        @classmethod
        def getSubclass(cls, idx):
                if idx in cls.subclasses:
                        return cls.subclasses[idx]
                raise KeyError("{0}: No such ModularType class.".format(repr(idx)))
 
class Modular(metaclass=ModularType):
        def __init__(self):
                self._components = OrderedDict()
        
        def addComponent(self, comp):
                if isinstance(comp, Modular):
                        o = comp
                elif isinstance(comp, type):
                        o = comp()
                else:
                        raise TypeError("Got '{0}', expected Component object or class.".format(comp.__class__.__name__))
                component_name = o.__class__.__name__
                if component_name in self._components:
                        return self._components[component_name]
                
                self._components[component_name] = o
                o._container = self
                
                if o.__class__ in self.__class__.DerivedValues:
                        for mod, depends in self.__class__.DerivedValues[o.__class__].items():
                                dependsMet = True
                                for depend in depends:
                                        if depend.__name__ not in self._components:
                                                dependsMet = False
                                                break
                                if dependsMet:
                                        c = mod()
                                        if isinstance(c, mod):
                                                self.addComponent(mod)
                
                return o
                        
        
        def getComponent(self):
                return self
        
        def __getattribute__(self, *args):
                ex = True
                rval = None
                try:
                        rval = object.__getattribute__(self, *args)
                        ex = False
                except AttributeError as e:
                        for component in self._components.values():
                                try:
                                        rval = object.__getattribute__(component, *args)
                                        ex = False
                                except AttributeError:
                                        pass
                                except Exception as f:
                                        e = f
                        if ex:
                                raise e
                
                return rval
 
class Node(Modular):
        id_count = 0
        @classmethod
        def next_id(cls):
                cls.id_count += 1
                return cls.id_count
        
        def __new__(cls, *args, **kwargs):
                o = super().__new__(cls)
                o._ID = cls.next_id()
                
                return o
        
        def __init__(self):
                super().__init__()
                self.__setattr__("get{0}Node".format(self.__class__.__name__), self.getComponent)
                self._Parent = None
                self._Children = {}
        
        @classmethod
        def _ChangeParent(cls, nod, new_parent):
                if nod._Parent is not None:
                        nod._Parent._RemoveChildByIndex(nod)
                new_parent._AddChildByIndex(nod)
        
        def _AddChildByIndex(self, child, idx=None):
                if idx is not None:
                        index = idx
                else:
                        index = child._ID
                if child._ID in self._Children:
                        return False
                if child._Parent is not None:
                        child._Parent._RemoveChildByIndex(idx)
                child._Parent = self
                self._Children[index] = child
                
                return True
        
        def _RemoveChildByIndex(self, Idx):
                if Idx in self._Children:
                        child = self._Children[Idx]
                        child._Parent = None
                        del self._Children[Idx]
                        return child
                return False
 
class Component(Node):
        HumanName = "ComponentName"
        HumanUnits = "units"
        def __init__(self):
                super().__init__()
                self._identifier = self.__classname__
                self._value = 0
        
        def getTareValue(self):
                return self._value
        
        def setValue(self, value):
                self._value = value
        
        def getValue(self):
                s = self._value
                for child in self._container._Children.values():
                        if self.__class__.__name__ in child._components:
                                childCompNode = child._components[self.__class__.__name__]
                                s += childCompNode.getValue()
                return s
        
        def __str__(self):
                _val = self.getValue()
                try:
                        _val = "{0:.6g}".format(_val)
                except:
                        pass
                return "{0}: {1} {2}".format(self.HumanName, _val, self.HumanUnits)
 
class MultiValueComponent(Component):
        def __init__(self):
                super().__init__()
                self._value = {}
        
        def getValueByKey(self, key):
                return self._value[key]
        
        def getValue(self):
                s = self._value
                for child in self._container._Children.values():
                        if self.__classname__ in child._components:
                                s = merge_dicts(s, child._components[self.__classname__].getValue())
                return s
        
        def __str__(self):
                s = []
                for key, value in self.getValue().items():
                        try:
                                value = "{0:.6g}".format(value)
                        except:
                                pass
                        units = self.HumanUnits
                        if key in self.keyNames:
                                key = self.keyNames[key]
                        if key in self.keyUnits:
                                units = self.keyUnits[key]
                        s.append("{0} ({1}): {2} {3}".format(self.HumanName, key, value, units))
                return "\n{0: <{w}}".format('', w=self._container._depth * 4).join(s)
        
        keyNames = {}
        keyUnits = {}