baldertest 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- _balder/__init__.py +12 -0
- _balder/_version.py +34 -0
- _balder/balder_plugin.py +73 -0
- _balder/balder_session.py +341 -0
- _balder/balder_settings.py +15 -0
- _balder/cnnrelations/__init__.py +7 -0
- _balder/cnnrelations/and_connection_relation.py +176 -0
- _balder/cnnrelations/base_connection_relation.py +270 -0
- _balder/cnnrelations/or_connection_relation.py +65 -0
- _balder/collector.py +874 -0
- _balder/connection.py +863 -0
- _balder/connection_metadata.py +255 -0
- _balder/console/__init__.py +0 -0
- _balder/console/balder.py +58 -0
- _balder/controllers/__init__.py +12 -0
- _balder/controllers/base_device_controller.py +72 -0
- _balder/controllers/controller.py +29 -0
- _balder/controllers/device_controller.py +446 -0
- _balder/controllers/feature_controller.py +715 -0
- _balder/controllers/normal_scenario_setup_controller.py +402 -0
- _balder/controllers/scenario_controller.py +524 -0
- _balder/controllers/setup_controller.py +134 -0
- _balder/controllers/vdevice_controller.py +95 -0
- _balder/decorator_connect.py +104 -0
- _balder/decorator_covered_by.py +74 -0
- _balder/decorator_fixture.py +29 -0
- _balder/decorator_for_vdevice.py +118 -0
- _balder/decorator_gateway.py +34 -0
- _balder/decorator_insert_into_tree.py +52 -0
- _balder/decorator_parametrize.py +31 -0
- _balder/decorator_parametrize_by_feature.py +36 -0
- _balder/device.py +18 -0
- _balder/exceptions.py +182 -0
- _balder/executor/__init__.py +0 -0
- _balder/executor/basic_executable_executor.py +133 -0
- _balder/executor/basic_executor.py +205 -0
- _balder/executor/executor_tree.py +217 -0
- _balder/executor/parametrized_testcase_executor.py +52 -0
- _balder/executor/scenario_executor.py +169 -0
- _balder/executor/setup_executor.py +163 -0
- _balder/executor/testcase_executor.py +203 -0
- _balder/executor/unresolved_parametrized_testcase_executor.py +184 -0
- _balder/executor/variation_executor.py +882 -0
- _balder/exit_code.py +19 -0
- _balder/feature.py +74 -0
- _balder/feature_replacement_mapping.py +107 -0
- _balder/feature_vdevice_mapping.py +88 -0
- _balder/fixture_definition_scope.py +19 -0
- _balder/fixture_execution_level.py +22 -0
- _balder/fixture_manager.py +483 -0
- _balder/fixture_metadata.py +26 -0
- _balder/node_gateway.py +103 -0
- _balder/objects/__init__.py +0 -0
- _balder/objects/connections/__init__.py +0 -0
- _balder/objects/connections/osi_1_physical.py +116 -0
- _balder/objects/connections/osi_2_datalink.py +35 -0
- _balder/objects/connections/osi_3_network.py +47 -0
- _balder/objects/connections/osi_4_transport.py +40 -0
- _balder/objects/connections/osi_5_session.py +13 -0
- _balder/objects/connections/osi_6_presentation.py +13 -0
- _balder/objects/connections/osi_7_application.py +83 -0
- _balder/objects/devices/__init__.py +0 -0
- _balder/objects/devices/this_device.py +12 -0
- _balder/parametrization.py +75 -0
- _balder/plugin_manager.py +138 -0
- _balder/previous_executor_mark.py +23 -0
- _balder/routing_path.py +335 -0
- _balder/scenario.py +20 -0
- _balder/setup.py +18 -0
- _balder/solver.py +246 -0
- _balder/testresult.py +163 -0
- _balder/unmapped_vdevice.py +13 -0
- _balder/utils/__init__.py +0 -0
- _balder/utils/functions.py +103 -0
- _balder/utils/inner_device_managing_metaclass.py +14 -0
- _balder/utils/mixin_can_be_covered_by_executor.py +24 -0
- _balder/utils/typings.py +4 -0
- _balder/vdevice.py +9 -0
- balder/__init__.py +56 -0
- balder/connections.py +43 -0
- balder/devices.py +9 -0
- balder/exceptions.py +44 -0
- balder/parametrization.py +8 -0
- baldertest-0.1.0.dist-info/METADATA +356 -0
- baldertest-0.1.0.dist-info/RECORD +89 -0
- baldertest-0.1.0.dist-info/WHEEL +5 -0
- baldertest-0.1.0.dist-info/entry_points.txt +2 -0
- baldertest-0.1.0.dist-info/licenses/LICENSE +21 -0
- baldertest-0.1.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
from typing import Type, Dict, Union, List, Callable, Tuple
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
import inspect
|
|
8
|
+
from _balder.cnnrelations import OrConnectionRelation
|
|
9
|
+
from _balder.device import Device
|
|
10
|
+
from _balder.vdevice import VDevice
|
|
11
|
+
from _balder.feature import Feature
|
|
12
|
+
from _balder.controllers import Controller
|
|
13
|
+
from _balder.controllers.vdevice_controller import VDeviceController
|
|
14
|
+
from _balder.connection import Connection
|
|
15
|
+
from _balder.exceptions import UnclearMethodVariationError, MultiInheritanceError, VDeviceOverwritingError, \
|
|
16
|
+
VDeviceResolvingError, FeatureOverwritingError, IllegalVDeviceMappingError
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__file__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FeatureController(Controller):
|
|
22
|
+
"""
|
|
23
|
+
This is the controller class for :class:`Feature` items.
|
|
24
|
+
"""
|
|
25
|
+
# helper property to disable manual constructor creation
|
|
26
|
+
__priv_instantiate_key = object()
|
|
27
|
+
|
|
28
|
+
#: contains all existing feature and its corresponding controller object
|
|
29
|
+
_items: Dict[Type[Feature], FeatureController] = {}
|
|
30
|
+
|
|
31
|
+
def __init__(self, related_cls, _priv_instantiate_key):
|
|
32
|
+
|
|
33
|
+
# this helps to make this constructor only possible inside the controller object
|
|
34
|
+
if _priv_instantiate_key != FeatureController.__priv_instantiate_key:
|
|
35
|
+
raise RuntimeError('it is not allowed to instantiate a controller manually -> use the static method '
|
|
36
|
+
'`FeatureController.get_for()` for it')
|
|
37
|
+
|
|
38
|
+
if not isinstance(related_cls, type):
|
|
39
|
+
raise TypeError('the attribute `related_cls` has to be a type (no object)')
|
|
40
|
+
if not issubclass(related_cls, Feature):
|
|
41
|
+
raise TypeError(f'the attribute `related_cls` has to be a sub-type of `{Feature.__name__}`')
|
|
42
|
+
if related_cls == Feature:
|
|
43
|
+
raise TypeError(f'the attribute `related_cls` is `{Feature.__name__}` - controllers for native type are '
|
|
44
|
+
f'forbidden')
|
|
45
|
+
# contains a reference to the related class this controller instance belongs to
|
|
46
|
+
self._related_cls = related_cls
|
|
47
|
+
|
|
48
|
+
#: holds the defined **Class-Based-Binding** for the related feature class sorted by VDevice types
|
|
49
|
+
self._cls_for_vdevice: Dict[Type[VDevice], Connection] = {}
|
|
50
|
+
|
|
51
|
+
#: holds the absolute calculated **Class-Based-Binding** for the related feature class sorted by VDevice types
|
|
52
|
+
#: (will be calculated by :meth:`FeatureController.determine_absolute_class_based_for_vdevice`, which will be
|
|
53
|
+
#: called during collecting)
|
|
54
|
+
self._abs_cls_for_vdevice: Union[Dict[Type[VDevice], Connection], None] = None
|
|
55
|
+
|
|
56
|
+
#: contains the **Method-Based-Binding** information for the current feature type (will be automatically set by
|
|
57
|
+
#: executor)
|
|
58
|
+
self._for_vdevice: Union[Dict[str, Dict[Callable, Dict[Type[VDevice], Connection]]], None] = None
|
|
59
|
+
|
|
60
|
+
#: contains the original defined :class:`VDevice` objects for this feature (will be automatically set by
|
|
61
|
+
#: :class:`Collector`)
|
|
62
|
+
self._original_vdevice_definitions: Union[Dict[str, Type[VDevice]], None] = None
|
|
63
|
+
|
|
64
|
+
#: contains the current active method variations for the related feature class - for every key (method name str)
|
|
65
|
+
#: a tuple with the VDevice type, the valid connection and the callable itself will be returned
|
|
66
|
+
self._current_active_method_variation: Dict[str, Tuple[Type[VDevice], Connection, Callable]] = {}
|
|
67
|
+
|
|
68
|
+
# ---------------------------------- STATIC METHODS ----------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def get_for(related_cls: Type[Feature]) -> FeatureController:
|
|
72
|
+
"""
|
|
73
|
+
This class returns the current existing controller instance for the given item. If the instance does not exist
|
|
74
|
+
yet, it will automatically create it and saves the instance in an internal dictionary.
|
|
75
|
+
"""
|
|
76
|
+
if FeatureController._items.get(related_cls) is None:
|
|
77
|
+
item = FeatureController(related_cls, _priv_instantiate_key=FeatureController.__priv_instantiate_key)
|
|
78
|
+
FeatureController._items[related_cls] = item
|
|
79
|
+
|
|
80
|
+
return FeatureController._items.get(related_cls)
|
|
81
|
+
|
|
82
|
+
# ---------------------------------- CLASS METHODS -----------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
# ---------------------------------- PROPERTIES --------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def related_cls(self) -> Type[Feature]:
|
|
88
|
+
return self._related_cls
|
|
89
|
+
|
|
90
|
+
# ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
|
|
91
|
+
|
|
92
|
+
def _validate_vdevice_reference_used_in_for_vdevice_decorators(self):
|
|
93
|
+
# now check if a definition for this class exists
|
|
94
|
+
all_vdevices = self.get_abs_inner_vdevice_classes()
|
|
95
|
+
# check the class based @for_vdevice and check the used vDevice classes here
|
|
96
|
+
for cur_decorated_vdevice in self.get_class_based_for_vdevice().keys():
|
|
97
|
+
if cur_decorated_vdevice not in all_vdevices:
|
|
98
|
+
raise VDeviceResolvingError(
|
|
99
|
+
f"you assign a vDevice to the class based decorator `@for_vdevice()` of the feature class "
|
|
100
|
+
f"`{self.related_cls.__name__}` which is no direct member of this feature - note that you have "
|
|
101
|
+
f"to define the vDevice in your feature before using it in the decorator - if necessary "
|
|
102
|
+
f"overwrite it")
|
|
103
|
+
# check the method based @for_vdevice and check the used vDevice classes here
|
|
104
|
+
if self.get_method_based_for_vdevice() is not None:
|
|
105
|
+
for cur_method_name, cur_method_data in self.get_method_based_for_vdevice().items():
|
|
106
|
+
for _, vdevice_dict in cur_method_data.items():
|
|
107
|
+
for cur_vdevice in vdevice_dict.keys():
|
|
108
|
+
if cur_vdevice not in all_vdevices:
|
|
109
|
+
raise VDeviceResolvingError(
|
|
110
|
+
f"you assign a vDevice to the method variation `{cur_method_name}` of the feature "
|
|
111
|
+
f"class `{self.related_cls.__name__}` which is no direct member of this feature - note "
|
|
112
|
+
f"that you have to use the overwritten version if you overwrite a vDevice")
|
|
113
|
+
|
|
114
|
+
def _get_method_based_for_vdevice_intersection(self, for_vdevice) -> List[Connection]:
|
|
115
|
+
"""helper method that determines the intersection connection list of all method based `@for_vdevice`
|
|
116
|
+
connections of the given `for_vdevice`"""
|
|
117
|
+
intersection = []
|
|
118
|
+
|
|
119
|
+
if self.get_method_based_for_vdevice() is not None:
|
|
120
|
+
for _, method_dict in self.get_method_based_for_vdevice().items():
|
|
121
|
+
for _, vdevice_dict in method_dict.items():
|
|
122
|
+
if for_vdevice not in vdevice_dict.keys():
|
|
123
|
+
continue
|
|
124
|
+
for cur_cnn in vdevice_dict[for_vdevice]:
|
|
125
|
+
if isinstance(cur_cnn, type):
|
|
126
|
+
cur_cnn = cur_cnn()
|
|
127
|
+
# clean metadata here because this is no connection between real devices
|
|
128
|
+
cur_cnn.set_metadata_for_all_subitems(None)
|
|
129
|
+
intersection.append(cur_cnn)
|
|
130
|
+
if len(intersection) == 0:
|
|
131
|
+
return [Connection()]
|
|
132
|
+
return intersection
|
|
133
|
+
|
|
134
|
+
def _determine_all_theoretically_unordered_method_variations(
|
|
135
|
+
self, of_method_name: str, for_vdevice: Type[VDevice],
|
|
136
|
+
with_connection: Union[Connection, Tuple[Connection]]) -> Dict[Callable, Connection]:
|
|
137
|
+
"""
|
|
138
|
+
This method returns all theoretically matching method variations. It returns more than one, if there are
|
|
139
|
+
multiple method variation for the given VDevice in this feature, where the given connection is part of the
|
|
140
|
+
connection described by the method variation.
|
|
141
|
+
|
|
142
|
+
:param of_method_name: the name of the method that should be returned
|
|
143
|
+
:param for_vdevice: the VDevice that is mapped
|
|
144
|
+
:param with_connection: the connection that is used between the device that uses the related feature and the
|
|
145
|
+
VDevice
|
|
146
|
+
:return: a dictionary that holds all available method variation that matches here
|
|
147
|
+
"""
|
|
148
|
+
all_vdevice_method_variations = self.get_method_based_for_vdevice()
|
|
149
|
+
|
|
150
|
+
if all_vdevice_method_variations is None:
|
|
151
|
+
raise ValueError("the current feature has no method variations")
|
|
152
|
+
|
|
153
|
+
if of_method_name not in all_vdevice_method_variations.keys():
|
|
154
|
+
raise ValueError(f"can not find the method `{of_method_name}` in method variation data dictionary")
|
|
155
|
+
|
|
156
|
+
all_possible_method_variations = {}
|
|
157
|
+
for cur_impl_method, cur_method_impl_dict in all_vdevice_method_variations[of_method_name].items():
|
|
158
|
+
if for_vdevice in cur_method_impl_dict.keys():
|
|
159
|
+
cur_impl_method_cnns = cur_method_impl_dict[for_vdevice].get_singles()
|
|
160
|
+
for cur_single_impl_method_cnn in cur_impl_method_cnns:
|
|
161
|
+
if cur_single_impl_method_cnn.contained_in(with_connection, ignore_metadata=True):
|
|
162
|
+
# this variation is possible
|
|
163
|
+
# ADD IT if it is not available yet
|
|
164
|
+
if cur_impl_method not in all_possible_method_variations.keys():
|
|
165
|
+
all_possible_method_variations[cur_impl_method] = cur_single_impl_method_cnn
|
|
166
|
+
# COMBINE IT if it is already available
|
|
167
|
+
else:
|
|
168
|
+
all_possible_method_variations[cur_impl_method] = Connection.based_on(
|
|
169
|
+
OrConnectionRelation(all_possible_method_variations[cur_impl_method],
|
|
170
|
+
cur_single_impl_method_cnn))
|
|
171
|
+
return all_possible_method_variations
|
|
172
|
+
|
|
173
|
+
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
def get_class_based_for_vdevice(self) -> Dict[Type[VDevice], Connection]:
|
|
176
|
+
"""
|
|
177
|
+
This method returns the class based data for the `@for_vdevice` decorator.
|
|
178
|
+
"""
|
|
179
|
+
return copy.copy(self._cls_for_vdevice)
|
|
180
|
+
|
|
181
|
+
def get_abs_class_based_for_vdevice(self) -> Dict[Type[VDevice], Connection]:
|
|
182
|
+
"""
|
|
183
|
+
This method returns the absolute calculated class-based-for-vdevice data for this feature.
|
|
184
|
+
"""
|
|
185
|
+
if self._abs_cls_for_vdevice is None:
|
|
186
|
+
raise RuntimeError('can not access the absolute class based for-vdevices because they are not set yet')
|
|
187
|
+
return self._abs_cls_for_vdevice
|
|
188
|
+
|
|
189
|
+
def set_class_based_for_vdevice(self, data: Dict[Type[VDevice], Connection]):
|
|
190
|
+
"""
|
|
191
|
+
This method allows to set the data of the class based `@for_vdevice` decorator.
|
|
192
|
+
"""
|
|
193
|
+
self._cls_for_vdevice = data
|
|
194
|
+
|
|
195
|
+
def determine_absolute_class_based_for_vdevice(self, print_warning):
|
|
196
|
+
"""
|
|
197
|
+
|
|
198
|
+
This method determines the absolute class based `@for_vdevice` value for the related feature.
|
|
199
|
+
|
|
200
|
+
First it checks if there is a direct class based `@for_vdevice` decorator for this feature. It will not change
|
|
201
|
+
anything, if the value was already set by an explicit class based `@for_vdevice` decorator. In this case the
|
|
202
|
+
method only checks that every given vDevice class is a real part of the current :class:`Feature` class (will be
|
|
203
|
+
returned by direct call of method `Feature.get_inner_vdevice_classes()`). Otherwise, it determines the class
|
|
204
|
+
based `@for_vdevice` value through analysing of the method based decorators and sets this determined value. If
|
|
205
|
+
the method has to determine the value, it throws a warning with a suggestion for a nice class based decorator.
|
|
206
|
+
Also, here the method will analyse the given vDevice classes and secures that they are defined in the current
|
|
207
|
+
:class:`Feature` class.
|
|
208
|
+
|
|
209
|
+
.. note::
|
|
210
|
+
This method automatically updates the values for the parent classes, too. Every time it searches for the
|
|
211
|
+
values it considers the parent values for the vDevice or the parent class of the vDevice, too.
|
|
212
|
+
|
|
213
|
+
.. note::
|
|
214
|
+
This method can throw a user warning (`throw_warning` has to be True for that), but only on the given list
|
|
215
|
+
of :class:`Feature` classes. All parent :class:`Feature` classes will be determined correctly, but will not
|
|
216
|
+
throw a waring.
|
|
217
|
+
"""
|
|
218
|
+
# first determine this for all parent classes
|
|
219
|
+
next_parent_feat = self.get_next_parent_feature()
|
|
220
|
+
# with the following recursive call we guarantee that the next parent class has the correct resolved class
|
|
221
|
+
# based @for_vdevice information
|
|
222
|
+
if next_parent_feat:
|
|
223
|
+
FeatureController.get_for(next_parent_feat).determine_absolute_class_based_for_vdevice(print_warning=False)
|
|
224
|
+
|
|
225
|
+
# validate if all used vDevice references in method and class based `@for_vdevice` decorators can be used,
|
|
226
|
+
# because they are members of this feature
|
|
227
|
+
self._validate_vdevice_reference_used_in_for_vdevice_decorators()
|
|
228
|
+
|
|
229
|
+
# now check if a definition for this class exists
|
|
230
|
+
all_vdevices = self.get_abs_inner_vdevice_classes()
|
|
231
|
+
|
|
232
|
+
cls_based_for_vdevice = self.get_class_based_for_vdevice()
|
|
233
|
+
for cur_vdevice in all_vdevices:
|
|
234
|
+
# determine the class based for_vdevice value only if there is no one defined for this vDevice
|
|
235
|
+
if cur_vdevice in cls_based_for_vdevice.keys():
|
|
236
|
+
# there already exists a definition for this vDevice -> IGNORE
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
# first determine the valid parent intersection (can also be extended itself)
|
|
240
|
+
|
|
241
|
+
# get the correct vDevice that is known in the parent feature class
|
|
242
|
+
vdevice_controller = VDeviceController.get_for(cur_vdevice)
|
|
243
|
+
if vdevice_controller.get_outer_class() == self.related_cls:
|
|
244
|
+
# this cur_vdevice is defined in this Feature class -> check if a parent class of it exists in
|
|
245
|
+
# parent classes of this feature
|
|
246
|
+
# -> search parent vDevice existence and check if this vDevice is also used in a parent class of
|
|
247
|
+
# this feature
|
|
248
|
+
# -> if it does not exist -> there is no parent class based definition
|
|
249
|
+
vdevice_of_interest = vdevice_controller.get_next_parent_vdevice()
|
|
250
|
+
|
|
251
|
+
else:
|
|
252
|
+
# the definition scope of the VDevice is in a higher Feature parent -> has to be in the
|
|
253
|
+
# `__cls_for_vdevice` in one of the next base classes
|
|
254
|
+
vdevice_of_interest = cur_vdevice
|
|
255
|
+
|
|
256
|
+
parent_values = []
|
|
257
|
+
# read vDevice connection tree of parent feature class (only if the vDevice already exists in the
|
|
258
|
+
# parent class)
|
|
259
|
+
if vdevice_of_interest is not None and next_parent_feat is not None:
|
|
260
|
+
next_parent_feat_cls_based_for_vdevice = \
|
|
261
|
+
FeatureController.get_for(next_parent_feat).get_abs_class_based_for_vdevice()
|
|
262
|
+
if vdevice_of_interest in next_parent_feat_cls_based_for_vdevice.keys():
|
|
263
|
+
cnn = next_parent_feat_cls_based_for_vdevice[vdevice_of_interest]
|
|
264
|
+
# clean metadata here because this is no connection between real devices
|
|
265
|
+
cnn.set_metadata_for_all_subitems(None)
|
|
266
|
+
parent_values.append(cnn)
|
|
267
|
+
|
|
268
|
+
this_vdevice_intersection = parent_values
|
|
269
|
+
|
|
270
|
+
# determine the class value automatically by discovering all method variations for this vDevice only
|
|
271
|
+
this_vdevice_intersection += self._get_method_based_for_vdevice_intersection(for_vdevice=cur_vdevice)
|
|
272
|
+
|
|
273
|
+
# print warning only if the printing is enabled and there is a sub connection tree (otherwise, the
|
|
274
|
+
# decorator is not necessary)
|
|
275
|
+
if print_warning and len(this_vdevice_intersection) > 0:
|
|
276
|
+
# only print the warning if there exists method variations for the feature
|
|
277
|
+
if self.get_method_based_for_vdevice() is not None:
|
|
278
|
+
logger.warning(
|
|
279
|
+
f"your used feature class `{self.related_cls.__name__}` doesn't provide a class based "
|
|
280
|
+
f"@for_vdevice decorator for the vDevice `{cur_vdevice.__name__}`\n"
|
|
281
|
+
f"Balder has determined a possible marker:\n\n"
|
|
282
|
+
f'@balder.for_vdevice("{cur_vdevice.__name__}", '
|
|
283
|
+
f'{", ".join([cur_cnn.get_tree_str() for cur_cnn in this_vdevice_intersection])})\n\n')
|
|
284
|
+
|
|
285
|
+
# set the determined data into the class based `@for_vdevice` class property
|
|
286
|
+
cls_based_for_vdevice[cur_vdevice] = Connection.based_on(OrConnectionRelation(*this_vdevice_intersection))
|
|
287
|
+
|
|
288
|
+
self._abs_cls_for_vdevice = cls_based_for_vdevice
|
|
289
|
+
|
|
290
|
+
def get_method_based_for_vdevice(self) -> \
|
|
291
|
+
Union[Dict[str, Dict[Callable, Dict[Type[VDevice], Connection]]], None]:
|
|
292
|
+
"""
|
|
293
|
+
This method returns the method based data for the `@for_vdevice` decorator or None, if there is no decorator
|
|
294
|
+
given
|
|
295
|
+
"""
|
|
296
|
+
return self._for_vdevice
|
|
297
|
+
|
|
298
|
+
def set_method_based_for_vdevice(
|
|
299
|
+
self, data: Union[Dict[str, Dict[Callable, Dict[Type[VDevice], Connection]]], None]):
|
|
300
|
+
"""
|
|
301
|
+
This method allows to set the data for the method based `@for_vdevice` decorator.
|
|
302
|
+
"""
|
|
303
|
+
self._for_vdevice = data
|
|
304
|
+
|
|
305
|
+
def get_method_variation(
|
|
306
|
+
self,
|
|
307
|
+
of_method_name: str,
|
|
308
|
+
for_vdevice: Type[VDevice],
|
|
309
|
+
with_connection: Connection,
|
|
310
|
+
ignore_no_findings: bool = False
|
|
311
|
+
) -> Union[Callable, None]:
|
|
312
|
+
"""
|
|
313
|
+
This method searches for the unique possible method variation and returns it. In its search, the method also
|
|
314
|
+
includes the parent classes of the related feature element of this controller.
|
|
315
|
+
|
|
316
|
+
.. note::
|
|
317
|
+
The method throws an exception if it can not find a valid unique method variation for the given data.
|
|
318
|
+
|
|
319
|
+
.. note::
|
|
320
|
+
Note, that the method does not check if the method name, the VDevice nor the given `connection` is really a
|
|
321
|
+
part of this object. Please secure that the data is validated before.
|
|
322
|
+
|
|
323
|
+
.. note::
|
|
324
|
+
The method determines all possible method-variations. If it finds more than one clear method variation it
|
|
325
|
+
tries to sort them hierarchical. This is done by checking if one possible method variation is contained in
|
|
326
|
+
the other. If this can be clearly done, the method returns the furthest out one. Otherwise, it throws an
|
|
327
|
+
`UnclearMethodVariationError`
|
|
328
|
+
|
|
329
|
+
:param of_method_name: the name of the method that should be returned
|
|
330
|
+
|
|
331
|
+
:param for_vdevice: the VDevice that is mapped
|
|
332
|
+
|
|
333
|
+
:param with_connection: the connection that is used between the device that uses the related feature and the
|
|
334
|
+
VDevice
|
|
335
|
+
|
|
336
|
+
:param ignore_no_findings: if this attribute is true, the method will not throw an exception if it can not find
|
|
337
|
+
something, it only returns None
|
|
338
|
+
|
|
339
|
+
:return: the method variation callable for the given data (or none, if the method does not exist in this object
|
|
340
|
+
or in a parent class of it)
|
|
341
|
+
"""
|
|
342
|
+
# first determine all possible method-variations
|
|
343
|
+
all_possible_method_variations = self._determine_all_theoretically_unordered_method_variations(
|
|
344
|
+
of_method_name=of_method_name, for_vdevice=for_vdevice, with_connection=with_connection)
|
|
345
|
+
|
|
346
|
+
# there are no method variations in this feature directly -> check parent classes
|
|
347
|
+
if len(all_possible_method_variations) == 0:
|
|
348
|
+
# try to execute this method in parent classes
|
|
349
|
+
for cur_base in self.related_cls.__bases__:
|
|
350
|
+
if issubclass(cur_base, Feature) and cur_base != Feature:
|
|
351
|
+
parent_meth_result = FeatureController.get_for(cur_base).get_method_variation(
|
|
352
|
+
of_method_name=of_method_name, for_vdevice=for_vdevice, with_connection=with_connection,
|
|
353
|
+
ignore_no_findings=True)
|
|
354
|
+
if parent_meth_result:
|
|
355
|
+
return parent_meth_result
|
|
356
|
+
if not ignore_no_findings:
|
|
357
|
+
raise UnclearMethodVariationError(
|
|
358
|
+
f"found no possible method variation for method "
|
|
359
|
+
f"`{self.related_cls.__name__}.{of_method_name}` with vDevice `{for_vdevice.__name__}` "
|
|
360
|
+
f"and usable connection `{with_connection.get_tree_str()}´")
|
|
361
|
+
return None
|
|
362
|
+
|
|
363
|
+
# we only have one -> selection is clear
|
|
364
|
+
if len(all_possible_method_variations) == 1:
|
|
365
|
+
return list(all_possible_method_variations.keys())[0]
|
|
366
|
+
|
|
367
|
+
# if there are more than one possible method variation, try to determine the outer one
|
|
368
|
+
remaining_possible_method_variations = list(
|
|
369
|
+
filter(
|
|
370
|
+
lambda meth: not max(all_possible_method_variations[meth].contained_in(cur_other_cnn)
|
|
371
|
+
for cur_other_cnn in all_possible_method_variations.values()
|
|
372
|
+
if cur_other_cnn != all_possible_method_variations[meth]),
|
|
373
|
+
all_possible_method_variations.keys())
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
if len(remaining_possible_method_variations) > 1:
|
|
377
|
+
raise UnclearMethodVariationError(
|
|
378
|
+
f"found more than one possible method variation for method "
|
|
379
|
+
f"`{self.related_cls.__name__}.{of_method_name}` with vDevice `{for_vdevice.__name__}` "
|
|
380
|
+
f"and usable connection `{with_connection.get_tree_str()}´")
|
|
381
|
+
return remaining_possible_method_variations[0]
|
|
382
|
+
|
|
383
|
+
def get_inner_vdevice_classes(self) -> List[Type[VDevice]]:
|
|
384
|
+
"""
|
|
385
|
+
This is a method that determines the inner VDevice classes for the related feature class. If the method can not
|
|
386
|
+
find some VDevices in the current class it returns an empty list. This method will never search in parent
|
|
387
|
+
classes.
|
|
388
|
+
|
|
389
|
+
If you want to get the absolute VDevices use :meth:`Feature.get_inner_vdevice_classes`.
|
|
390
|
+
"""
|
|
391
|
+
|
|
392
|
+
all_classes = inspect.getmembers(self.related_cls, inspect.isclass)
|
|
393
|
+
filtered_classes = []
|
|
394
|
+
for _, cur_class in all_classes:
|
|
395
|
+
if not issubclass(cur_class, VDevice):
|
|
396
|
+
# filter all classes and make sure that only the child classes of :class:`VDevice` remain
|
|
397
|
+
continue
|
|
398
|
+
|
|
399
|
+
if VDeviceController.get_for(cur_class).get_outer_class() != self.related_cls:
|
|
400
|
+
# filter all classes that do not match the setup name in __qualname__
|
|
401
|
+
continue
|
|
402
|
+
# otherwise, add this candidate
|
|
403
|
+
filtered_classes.append(cur_class)
|
|
404
|
+
|
|
405
|
+
if len(filtered_classes) == 0:
|
|
406
|
+
# do not found some VDevice classes -> search in parent
|
|
407
|
+
for cur_base in self.related_cls.__bases__:
|
|
408
|
+
if cur_base == Feature:
|
|
409
|
+
return []
|
|
410
|
+
if issubclass(cur_base, Feature):
|
|
411
|
+
return FeatureController.get_for(cur_base).get_abs_inner_vdevice_classes()
|
|
412
|
+
|
|
413
|
+
return filtered_classes
|
|
414
|
+
|
|
415
|
+
def get_inner_vdevice_class_by_string(self, device_str: str) -> Union[Type[VDevice], None]:
|
|
416
|
+
"""
|
|
417
|
+
This method returns the inner VDevice class for the given string.
|
|
418
|
+
|
|
419
|
+
:param device_str: the name string of the VDevice that should be returned
|
|
420
|
+
|
|
421
|
+
:return: the VDevice class or None, if the method has not found any class with this name
|
|
422
|
+
"""
|
|
423
|
+
possible_vdevs = [cur_vdevice for cur_vdevice in self.get_inner_vdevice_classes()
|
|
424
|
+
if cur_vdevice.__name__ == device_str]
|
|
425
|
+
if len(possible_vdevs) == 0:
|
|
426
|
+
return None
|
|
427
|
+
if len(possible_vdevs) > 1:
|
|
428
|
+
raise RuntimeError("found more than one possible vDevices - something unexpected happened")
|
|
429
|
+
|
|
430
|
+
return possible_vdevs[0]
|
|
431
|
+
|
|
432
|
+
def get_abs_inner_vdevice_classes(self) -> List[Type[VDevice]]:
|
|
433
|
+
"""
|
|
434
|
+
This is a method that determines the inner VDevice classes for the feature class. If the method can not find
|
|
435
|
+
some VDevices in the related feature class it also starts searching in the base classes. It always returns the
|
|
436
|
+
first existing definition in the relevant parent classes.
|
|
437
|
+
"""
|
|
438
|
+
|
|
439
|
+
filtered_classes = self.get_inner_vdevice_classes()
|
|
440
|
+
|
|
441
|
+
if len(filtered_classes) == 0:
|
|
442
|
+
# do not found some VDevice classes -> search in parent
|
|
443
|
+
for cur_base in self.related_cls.__bases__:
|
|
444
|
+
if cur_base == Feature:
|
|
445
|
+
return []
|
|
446
|
+
if issubclass(cur_base, Feature):
|
|
447
|
+
return FeatureController.get_for(cur_base).get_abs_inner_vdevice_classes()
|
|
448
|
+
|
|
449
|
+
return filtered_classes
|
|
450
|
+
|
|
451
|
+
def get_inner_referenced_features(self) -> Dict[str, Feature]:
|
|
452
|
+
"""
|
|
453
|
+
This method returns a dictionary with all referenced :class:`Feature` objects, where the variable name is the
|
|
454
|
+
key and the instantiated object the value.
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
result = {}
|
|
458
|
+
for cur_name in dir(self.related_cls):
|
|
459
|
+
cur_val = getattr(self.related_cls, cur_name)
|
|
460
|
+
if isinstance(cur_val, Feature):
|
|
461
|
+
result[cur_name] = cur_val
|
|
462
|
+
return result
|
|
463
|
+
|
|
464
|
+
def validate_inner_vdevice_inheritance(self):
|
|
465
|
+
"""
|
|
466
|
+
This method validates the inheritance of all inner :class:`VDevice` classes of the feature that belongs to this
|
|
467
|
+
controller.
|
|
468
|
+
|
|
469
|
+
It secures that new :class:`VDevice` classes are added or existing :class:`VDevice` classes are completely being
|
|
470
|
+
overwritten for every feature level. The method only allows the overwriting of :class:`VDevices`, which are
|
|
471
|
+
subclasses of another :class:`VDevice` that is defined in a parent :class:`Feature` class. In addition, the
|
|
472
|
+
class has to have the same name as its parent class.
|
|
473
|
+
|
|
474
|
+
The method also secures that the user overwrites instantiated :class:`Feature` classes in the VDevice (class
|
|
475
|
+
property name is the same) only with subclasses of the element that is being overwritten. New Features can be
|
|
476
|
+
added without consequences.
|
|
477
|
+
"""
|
|
478
|
+
|
|
479
|
+
all_direct_vdevices_of_this_feature_lvl = self.get_inner_vdevice_classes()
|
|
480
|
+
if len(all_direct_vdevices_of_this_feature_lvl) != 0:
|
|
481
|
+
# check that all absolute items of higher class are implemented
|
|
482
|
+
next_feature_parent = None
|
|
483
|
+
for cur_parent in self._related_cls.__bases__:
|
|
484
|
+
if issubclass(cur_parent, Feature) and cur_parent != Feature:
|
|
485
|
+
if next_feature_parent is not None:
|
|
486
|
+
raise MultiInheritanceError(
|
|
487
|
+
"can not select the next parent class, found more than one parent classes for feature "
|
|
488
|
+
f"`{self._related_cls.__name__}` that is a subclass of `{Feature.__name__}`")
|
|
489
|
+
next_feature_parent = cur_parent
|
|
490
|
+
# only continue if the current feature has a parent class
|
|
491
|
+
if next_feature_parent:
|
|
492
|
+
# first check the parent feature (secure that the inheritance chain is valid first)
|
|
493
|
+
parent_collector = FeatureController.get_for(next_feature_parent)
|
|
494
|
+
parent_collector.validate_inner_vdevice_inheritance()
|
|
495
|
+
|
|
496
|
+
# now continue with checking the inheritance between this feature and its direct parent
|
|
497
|
+
parent_vdevices = parent_collector.get_abs_inner_vdevice_classes()
|
|
498
|
+
# now check that every parent vDevice also exists in the current selection
|
|
499
|
+
for cur_parent_vdevice in parent_vdevices:
|
|
500
|
+
direct_namings = [cur_item.__name__ for cur_item in all_direct_vdevices_of_this_feature_lvl]
|
|
501
|
+
# check that the parent vDevice exists in the direct namings
|
|
502
|
+
if cur_parent_vdevice.__name__ not in direct_namings:
|
|
503
|
+
raise VDeviceOverwritingError(
|
|
504
|
+
f"missing overwriting of parent VDevice class `{cur_parent_vdevice.__qualname__}` in "
|
|
505
|
+
f"feature class `{self._related_cls.__name__}` - if you overwrite one or more VDevice(s) "
|
|
506
|
+
f"you have to overwrite all!")
|
|
507
|
+
|
|
508
|
+
# otherwise check if inheritance AND feature overwriting is correct
|
|
509
|
+
cur_child_idx = direct_namings.index(cur_parent_vdevice.__name__)
|
|
510
|
+
related_child_vdevice = all_direct_vdevices_of_this_feature_lvl[cur_child_idx]
|
|
511
|
+
if not issubclass(related_child_vdevice, cur_parent_vdevice):
|
|
512
|
+
# inherit from a parent device, but it has not the same naming -> NOT ALLOWED
|
|
513
|
+
raise VDeviceOverwritingError(
|
|
514
|
+
f"the inner vDevice class `{related_child_vdevice.__qualname__}` has the same "
|
|
515
|
+
f"name than the vDevice `{cur_parent_vdevice.__qualname__}` - it should also "
|
|
516
|
+
f"inherit from it")
|
|
517
|
+
# todo check that feature overwriting inside the VDevice is correct
|
|
518
|
+
# now check that the vDevice overwrites the existing properties only in a proper manner (to
|
|
519
|
+
# overwrite it, it has to have the same property name as the property in the next parent
|
|
520
|
+
# class)
|
|
521
|
+
cur_vdevice_features = \
|
|
522
|
+
VDeviceController.get_for(related_child_vdevice).get_all_instantiated_feature_objects()
|
|
523
|
+
cur_vdevice_base_features = \
|
|
524
|
+
VDeviceController.get_for(cur_parent_vdevice).get_all_instantiated_feature_objects()
|
|
525
|
+
for cur_base_property_name, cur_base_feature_instance in cur_vdevice_base_features.items():
|
|
526
|
+
# now check that every base property is available in the current vDevice too - check
|
|
527
|
+
# that the instantiated feature is the same or the feature of the child vDevice is a
|
|
528
|
+
# child of it -> ignore it, if the child vDevice has more features than the base -
|
|
529
|
+
# that doesn't matter
|
|
530
|
+
if cur_base_property_name not in cur_vdevice_features.keys():
|
|
531
|
+
raise VDeviceResolvingError(
|
|
532
|
+
f"can not find the property `{cur_base_property_name}` of "
|
|
533
|
+
f"parent vDevice `{cur_parent_vdevice.__qualname__}` in the "
|
|
534
|
+
f"current vDevice class `{related_child_vdevice.__qualname__}`")
|
|
535
|
+
cur_feature_instance = cur_vdevice_features[cur_base_property_name]
|
|
536
|
+
if not isinstance(cur_feature_instance, cur_base_feature_instance.__class__):
|
|
537
|
+
raise FeatureOverwritingError(
|
|
538
|
+
f"you are trying to overwrite an existing vDevice Feature property "
|
|
539
|
+
f"`{cur_base_property_name}` in vDevice `{related_child_vdevice.__qualname__}` "
|
|
540
|
+
f"from the parent vDevice class `{cur_parent_vdevice.__qualname__}` - this is "
|
|
541
|
+
f"only possible with a child (or with the same) feature class the parent "
|
|
542
|
+
f"uses (in this case the `{cur_base_feature_instance.__class__.__name__}`)")
|
|
543
|
+
|
|
544
|
+
def validate_inherited_class_based_vdevice_cnn_subset(self):
|
|
545
|
+
"""
|
|
546
|
+
This method checks that the class based for_vdevice values of a child :class:`Feature` class are contained_in
|
|
547
|
+
the related VDevice defined in a parent :class:`Feature` class.
|
|
548
|
+
"""
|
|
549
|
+
|
|
550
|
+
to_checking_parent_features = []
|
|
551
|
+
|
|
552
|
+
feature_vdevices = self.get_abs_inner_vdevice_classes()
|
|
553
|
+
for cur_vdevice in feature_vdevices:
|
|
554
|
+
cur_vdevice_cls_cnn = self.get_abs_class_based_for_vdevice().get(cur_vdevice)
|
|
555
|
+
# get parent class of vdevice
|
|
556
|
+
relevant_parent_class = VDeviceController.get_for(cur_vdevice).get_next_parent_vdevice()
|
|
557
|
+
|
|
558
|
+
# continue with next vdevice if no relevant parent class was found
|
|
559
|
+
if relevant_parent_class is None:
|
|
560
|
+
continue
|
|
561
|
+
|
|
562
|
+
relevant_parent_class_controller = VDeviceController.get_for(relevant_parent_class)
|
|
563
|
+
# only if there is a higher class which has to be considered
|
|
564
|
+
parent_vdevice_feature = relevant_parent_class_controller.get_outer_class()
|
|
565
|
+
if parent_vdevice_feature not in to_checking_parent_features:
|
|
566
|
+
to_checking_parent_features.append(parent_vdevice_feature)
|
|
567
|
+
parent_vdevice_cnn = \
|
|
568
|
+
FeatureController.get_for(
|
|
569
|
+
parent_vdevice_feature).get_abs_class_based_for_vdevice()[relevant_parent_class]
|
|
570
|
+
# check if VDevice connection elements are all contained in the parent connection
|
|
571
|
+
if not cur_vdevice_cls_cnn.contained_in(parent_vdevice_cnn, ignore_metadata=True):
|
|
572
|
+
raise VDeviceResolvingError(
|
|
573
|
+
f"the VDevice `{cur_vdevice.__name__}` is a child of the VDevice "
|
|
574
|
+
f"`{relevant_parent_class.__name__}`, which doesn't implements the connection of "
|
|
575
|
+
f"the child - the connection element `{cur_vdevice_cls_cnn.get_tree_str()})´ is not "
|
|
576
|
+
f"contained in the connection-tree of the parent VDevice")
|
|
577
|
+
|
|
578
|
+
# validate inheritance levels for all features with parent VDevices as inner-classes
|
|
579
|
+
for cur_feature in to_checking_parent_features:
|
|
580
|
+
FeatureController.get_for(cur_feature).validate_inherited_class_based_vdevice_cnn_subset()
|
|
581
|
+
|
|
582
|
+
def get_next_parent_feature(self) -> Union[Type[Feature], None]:
|
|
583
|
+
"""
|
|
584
|
+
This method returns the next parent class of this feature, which is still a subclass of :class:`Feature`. If
|
|
585
|
+
the next parent class is :class:`Feature`, None will be returned.
|
|
586
|
+
|
|
587
|
+
:return: the parent Feature class or None if no parent exists
|
|
588
|
+
"""
|
|
589
|
+
possible_parent_classes = []
|
|
590
|
+
for cur_parent in self.related_cls.__bases__:
|
|
591
|
+
if issubclass(cur_parent, Feature) and cur_parent != Feature:
|
|
592
|
+
possible_parent_classes.append(cur_parent)
|
|
593
|
+
|
|
594
|
+
if len(possible_parent_classes) > 1:
|
|
595
|
+
raise MultiInheritanceError(
|
|
596
|
+
f"the feature `{self.related_cls.__name__}` has more than one parent classes from type "
|
|
597
|
+
f"`Feature` - this is not allowed")
|
|
598
|
+
|
|
599
|
+
if len(possible_parent_classes) == 1:
|
|
600
|
+
# we have found one parent feature class
|
|
601
|
+
return possible_parent_classes[0]
|
|
602
|
+
|
|
603
|
+
# we have no parent class
|
|
604
|
+
return None
|
|
605
|
+
|
|
606
|
+
def set_active_method_variation(self, method_selection: Dict[str, Tuple[Type[VDevice], Connection, Callable]]):
|
|
607
|
+
"""
|
|
608
|
+
This method sets the active method variation selection for the related feature class.
|
|
609
|
+
:param method_selection: the method selection that should be set
|
|
610
|
+
"""
|
|
611
|
+
self._current_active_method_variation = method_selection
|
|
612
|
+
|
|
613
|
+
def get_active_method_variation(self, method_name: str) \
|
|
614
|
+
-> Union[Tuple[Type[VDevice], Connection, Callable], Tuple[None, None, None]]:
|
|
615
|
+
"""
|
|
616
|
+
This method returns the current active method variation for the given `method_name` for the related fixture.
|
|
617
|
+
|
|
618
|
+
.. note::
|
|
619
|
+
Please note, this method only returns the set active method variation for this related feature only. It does
|
|
620
|
+
not check parent classes of this feature.
|
|
621
|
+
|
|
622
|
+
:param method_name: the name of the method the current active method variation should be returned
|
|
623
|
+
|
|
624
|
+
:return: a tuple with the current active method selection or a tuple with `None` if no active method variation
|
|
625
|
+
exists on this feature class level
|
|
626
|
+
"""
|
|
627
|
+
return self._current_active_method_variation.get(method_name, tuple([None, None, None]))
|
|
628
|
+
|
|
629
|
+
def get_inherited_method_variation(self, parent_class: Type[Feature], method_var_name: str):
|
|
630
|
+
"""
|
|
631
|
+
This method will determine the correct inherited method-variation for the current object. For this, it searches
|
|
632
|
+
in the base classes of the given `parent_class` (which has to be a parent class of `self`) for the
|
|
633
|
+
method-variation that should be called.
|
|
634
|
+
It automatically detects if the parent class has a method-variation or is a single normal method. In case that
|
|
635
|
+
the method is a single normal method, it will directly return it, otherwise it searches the correct
|
|
636
|
+
method-variation according to the vDevice mapping of the current object and return the current active
|
|
637
|
+
method-variation.
|
|
638
|
+
|
|
639
|
+
:param parent_class: the parent class of this object, the method should start searching for the
|
|
640
|
+
`method_var_name` method (it searches in this class and all parents)
|
|
641
|
+
|
|
642
|
+
:param method_var_name: the name of the method or of the method variation that should be returned
|
|
643
|
+
"""
|
|
644
|
+
|
|
645
|
+
parent_class_controller = FeatureController.get_for(parent_class)
|
|
646
|
+
if parent_class_controller.get_method_based_for_vdevice() is not None and \
|
|
647
|
+
method_var_name in parent_class_controller.get_method_based_for_vdevice().keys():
|
|
648
|
+
# the parent class has a method-variation -> get the current active version of it
|
|
649
|
+
|
|
650
|
+
# first get the active data for the instantiated feature object
|
|
651
|
+
active_vdevice, active_cnn_intersection, _ = self.get_active_method_variation(method_var_name)
|
|
652
|
+
# get the vDevice object that is used in the given parent class
|
|
653
|
+
if hasattr(parent_class, active_vdevice.__name__):
|
|
654
|
+
parent_vdevice = getattr(parent_class, active_vdevice.__name__)
|
|
655
|
+
else:
|
|
656
|
+
return None
|
|
657
|
+
|
|
658
|
+
# then determine the correct method variation according to the data of the instantiated object
|
|
659
|
+
cur_method_variation = parent_class_controller.get_method_variation(
|
|
660
|
+
of_method_name=method_var_name, for_vdevice=parent_vdevice,
|
|
661
|
+
with_connection=active_cnn_intersection, ignore_no_findings=True)
|
|
662
|
+
return cur_method_variation
|
|
663
|
+
|
|
664
|
+
if hasattr(parent_class, method_var_name):
|
|
665
|
+
# we found one normal method in this object
|
|
666
|
+
return getattr(parent_class, method_var_name)
|
|
667
|
+
|
|
668
|
+
# execute this method for all based and check if there is exactly one
|
|
669
|
+
next_base_feature_class = parent_class_controller.get_next_parent_feature()
|
|
670
|
+
if next_base_feature_class is None:
|
|
671
|
+
return None
|
|
672
|
+
|
|
673
|
+
return self.get_inherited_method_variation(next_base_feature_class, method_var_name)
|
|
674
|
+
|
|
675
|
+
def validate_inner_classes(self):
|
|
676
|
+
"""
|
|
677
|
+
This method validates all inner classes of the related feature and secures that none of these subclasses are
|
|
678
|
+
subclass of :class:`Device` but not subclasses from :class:`VDevice`. Of course other inner-classes that are not
|
|
679
|
+
required for balder are allowed too.
|
|
680
|
+
"""
|
|
681
|
+
all_inner_classes = inspect.getmembers(self.related_cls, inspect.isclass)
|
|
682
|
+
for cur_inner_name, cur_inner_class in all_inner_classes:
|
|
683
|
+
if not issubclass(cur_inner_class, Device):
|
|
684
|
+
# ignore this element
|
|
685
|
+
continue
|
|
686
|
+
# do only check the inner classes that inherits from `Device`
|
|
687
|
+
if not issubclass(cur_inner_class, VDevice):
|
|
688
|
+
raise VDeviceResolvingError(
|
|
689
|
+
f"the inner class `{cur_inner_class.__name__}` with name `{cur_inner_name}` is a child "
|
|
690
|
+
f"class of `Device` but not from `VDevice` as expected")
|
|
691
|
+
cur_inner_class_instantiated_features = \
|
|
692
|
+
VDeviceController.get_for(cur_inner_class).get_all_instantiated_feature_objects()
|
|
693
|
+
for _, cur_vdevice_feature in cur_inner_class_instantiated_features.items():
|
|
694
|
+
if cur_vdevice_feature.active_vdevices != {}:
|
|
695
|
+
raise IllegalVDeviceMappingError(
|
|
696
|
+
f"the feature `{cur_vdevice_feature.__class__.__name__}` you have instantiated in your "
|
|
697
|
+
f"vDevice `{cur_inner_class.__name__}` of feature `{self.related_cls.__name__}` "
|
|
698
|
+
f"has a own vDevice mapping - vDevice mappings are allowed for features on Devices "
|
|
699
|
+
f"only")
|
|
700
|
+
|
|
701
|
+
def get_original_vdevice_definitions(self) -> Dict[str, Type[VDevice]]:
|
|
702
|
+
"""
|
|
703
|
+
This method returns the :class:`VDevice` definitions that are the original definitions for this feature.
|
|
704
|
+
"""
|
|
705
|
+
if self._original_vdevice_definitions is None:
|
|
706
|
+
raise RuntimeError('can not access the original VDevice definitions before they were set with '
|
|
707
|
+
'`save_all_current_vdevice_references_as_originals`')
|
|
708
|
+
return self._original_vdevice_definitions
|
|
709
|
+
|
|
710
|
+
def save_all_current_vdevice_references_as_originals(self):
|
|
711
|
+
"""
|
|
712
|
+
This method saves the current existing :class:`VDevice` definitions inside this feature as originals.
|
|
713
|
+
"""
|
|
714
|
+
new_originals = self.get_abs_inner_vdevice_classes()
|
|
715
|
+
self._original_vdevice_definitions = {cur_vdevice.__name__: cur_vdevice for cur_vdevice in new_originals}
|