forked from autotest/virt-test
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathversionable_class.py
267 lines (211 loc) · 8.19 KB
/
versionable_class.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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
"""
VersionableClass provides class hierarchy which automatically select the right
version of a class. Class manipulation is used for this reason.
"""
class VersionableClass(object):
"""
VersionableClass provides class hierarchy which automatically select right
version of class. Class manipulation is used for this reason.
By this reason is:
Advantage) Only one version is working in one process. Class is changed in
whole process.
Disadvantage) Only one version is working in one process.
Example of usage (in base_utils_unittest):
class FooC(object):
pass
#Not implemented get_version -> not used for versioning.
class VCP(FooC, VersionableClass):
def __new__(cls, *args, **kargs):
VCP.master_class = VCP
return super(VCP, cls).__new__(cls, *args, **kargs)
def foo(self):
pass
class VC2(VCP, VersionableClass):
@staticmethod
def get_version():
return "get_version_from_system"
@classmethod
def is_right_version(cls, version):
if version is not None:
if "version is satisfied":
return True
return False
def func1(self):
print "func1"
def func2(self):
print "func2"
# get_version could be inherited.
class VC3(VC2, VersionableClass):
@classmethod
def is_right_version(cls, version):
if version is not None:
if "version+1 is satisfied":
return True
return False
def func2(self):
print "func2_2"
class M(VCP):
pass
m = M() # <- When class is constructed the right version is
# automatically selected. In this case VC3 is selected.
m.func2() # call VC3.func2(m)
m.func1() # call VC2.func1(m)
m.foo() # call VC1.foo(m)
# When controlled "program" version is changed then is necessary call
check_repair_versions or recreate object.
m.check_repair_versions()
# priority of class. (change place where is method searched first in group
# of verisonable class.)
class PP(VersionableClass):
def __new__(cls, *args, **kargs):
PP.master_class = PP
return super(PP, cls).__new__(cls, *args, **kargs)
class PP2(PP, VersionableClass):
@staticmethod
def get_version():
return "get_version_from_system"
@classmethod
def is_right_version(cls, version):
if version is not None:
if "version is satisfied":
return True
return False
def func1(self):
print "PP func1"
class N(VCP, PP):
pass
n = N()
n.func1() # -> "func2"
n.set_priority_class(PP, [VCP, PP])
n.func1() # -> "PP func1"
Necessary for using:
1) Subclass of versionable class must have implemented class methods
get_version and is_right_version. These two methods are necessary
for correct version section. Class without this method will be never
chosen like suitable class.
2) Every class derived from master_class have to add to class definition
inheritance from VersionableClass. Direct inheritance from Versionable
Class is use like a mark for manipulation with VersionableClass.
3) Master of VersionableClass have to defined class variable
cls.master_class.
"""
def __new__(cls, *args, **kargs):
cls.check_repair_versions()
return super(VersionableClass, cls).__new__(cls, *args, **kargs)
#VersionableClass class management class.
@classmethod
def check_repair_versions(cls, master_classes=None):
"""
Check version of versionable class and if version not
match repair version to correct version.
@param master_classes: Check and repair only master_class.
@type master_classes: list.
"""
if master_classes is None:
master_classes = cls._find_versionable_baseclass()
for base in master_classes:
cls._check_repair_version_class(base)
@classmethod
def set_priority_class(cls, prioritized_class, group_classes):
"""
Set class priority. Limited only for change bases class priority inside
one subclass.__bases__ after that continue to another class.
"""
def change_position(ccls):
if not VersionableClass in ccls.__bases__:
bases = list(ccls.__bases__)
index = None
remove_variant = None
for i, item in enumerate(ccls.__bases__):
if (VersionableClass in item.__bases__ and
item.master_class in group_classes):
if index is None:
index = i
if item.master_class is prioritized_class:
remove_variant = item
bases.remove(item)
break
else:
return
bases.insert(index, remove_variant)
ccls.__bases__ = tuple(bases)
def find_cls(ccls):
change_position(ccls)
for base in ccls.__bases__:
find_cls(base)
find_cls(cls)
@classmethod
def _check_repair_version_class(cls, master_class):
version = None
for class_version in master_class._find_versionable_subclass():
try:
version = class_version.get_version()
if class_version.is_right_version(version):
cls._switch_by_class(class_version)
break
except NotImplementedError:
continue
else:
cls._switch_by_class(class_version)
@classmethod
def _find_versionable_baseclass(cls):
"""
Find versionable class in master class.
"""
ver_class = []
for superclass in cls.mro():
if VersionableClass in superclass.__bases__:
ver_class.append(superclass.master_class)
return set(ver_class)
@classmethod
def _find_versionable_subclass(cls):
"""
Find versionable subclasses which subclass.master_class == cls
"""
subclasses = [cls]
for sub in cls.__subclasses__():
if VersionableClass in list(sub.__bases__):
subclasses.extend(sub._find_versionable_subclass())
return subclasses
@classmethod
def _switch_by_class(cls, new_class):
"""
Finds all class with same master_class as new_class in class tree
and replaces them by new_class.
@param new_class: Class for replacing.
"""
def find_replace_class(bases):
for base in bases:
if (VersionableClass in base.__bases__ and
base.master_class == new_class.master_class):
bnew = list(bases)
bnew[bnew.index(base)] = new_class
return tuple(bnew)
else:
bnew = find_replace_class(base.__bases__)
if bnew:
base.__bases__ = bnew
bnew = find_replace_class(cls.__bases__)
if bnew:
cls.__bases__ = bnew
# Method defined in part below must be defined in
# verisonable class subclass.
@classmethod
def get_version(cls):
"""
Get version of installed OpenVSwtich.
Must be re-implemented for in child class.
@return: Version or None when get_version is unsuccessful.
"""
raise NotImplementedError("Method 'get_verison' must be"
" implemented in child class")
@classmethod
def is_right_version(cls, version):
"""
Check condition for select control class.
Function must be re-implemented in new OpenVSwitchControl class.
Must be re-implemented for in child class.
@param version: version of OpenVSwtich
"""
raise NotImplementedError("Method 'is_right_version' must be"
" implemented in child class")