baldertest 0.1.0b10__py3-none-any.whl → 0.1.0b11__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/_version.py +1 -1
- _balder/cnnrelations/__init__.py +7 -0
- _balder/cnnrelations/and_connection_relation.py +149 -0
- _balder/cnnrelations/base_connection_relation.py +270 -0
- _balder/cnnrelations/or_connection_relation.py +65 -0
- _balder/collector.py +10 -16
- _balder/connection.py +400 -881
- _balder/connection_metadata.py +255 -0
- _balder/controllers/device_controller.py +25 -11
- _balder/controllers/feature_controller.py +63 -99
- _balder/controllers/normal_scenario_setup_controller.py +5 -5
- _balder/controllers/scenario_controller.py +6 -6
- _balder/controllers/setup_controller.py +2 -3
- _balder/decorator_connect.py +12 -10
- _balder/decorator_for_vdevice.py +17 -25
- _balder/decorator_gateway.py +3 -3
- _balder/executor/testcase_executor.py +0 -1
- _balder/executor/variation_executor.py +122 -115
- _balder/feature.py +1 -1
- _balder/fixture_manager.py +10 -9
- _balder/objects/connections/osi_3_network.py +2 -2
- _balder/objects/connections/osi_4_transport.py +2 -2
- _balder/routing_path.py +18 -25
- _balder/solver.py +1 -1
- _balder/testresult.py +1 -1
- _balder/utils.py +27 -1
- {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b11.dist-info}/METADATA +2 -2
- {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b11.dist-info}/RECORD +32 -27
- {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b11.dist-info}/WHEEL +1 -1
- {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b11.dist-info}/LICENSE +0 -0
- {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b11.dist-info}/entry_points.txt +0 -0
- {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b11.dist-info}/top_level.txt +0 -0
|
@@ -4,6 +4,7 @@ from typing import Type, Dict, List, Tuple, Union, Callable, Iterable, Any
|
|
|
4
4
|
import logging
|
|
5
5
|
import inspect
|
|
6
6
|
from collections import OrderedDict
|
|
7
|
+
from _balder.cnnrelations import OrConnectionRelation
|
|
7
8
|
from _balder.device import Device
|
|
8
9
|
from _balder.scenario import Scenario
|
|
9
10
|
from _balder.connection import Connection
|
|
@@ -215,15 +216,15 @@ class ScenarioController(NormalScenarioSetupController):
|
|
|
215
216
|
|
|
216
217
|
# now check if one or more single of the classbased connection are CONTAINED IN the possible
|
|
217
218
|
# parallel connection (only if there exists more than one parallel)
|
|
218
|
-
feature_cnn =
|
|
219
|
-
|
|
219
|
+
feature_cnn = FeatureController.get_for(
|
|
220
|
+
cur_feature.__class__).get_abs_class_based_for_vdevice()[mapped_vdevice]
|
|
220
221
|
|
|
221
222
|
# search node names that is the relevant connection
|
|
222
223
|
relevant_cnns: List[Connection] = []
|
|
223
224
|
mapped_device_abs_cnns = DeviceController.get_for(mapped_device).get_all_absolute_connections()
|
|
224
225
|
for _, all_connections in mapped_device_abs_cnns.items():
|
|
225
226
|
relevant_cnns += [cur_cnn for cur_cnn in all_connections
|
|
226
|
-
if cur_cnn.has_connection_from_to(cur_from_device, mapped_device)]
|
|
227
|
+
if cur_cnn.has_connection_from_to(cur_from_device, end_device=mapped_device)]
|
|
227
228
|
|
|
228
229
|
if len(relevant_cnns) <= 1:
|
|
229
230
|
# ignore if there are not more than one relevant connection
|
|
@@ -338,10 +339,9 @@ class ScenarioController(NormalScenarioSetupController):
|
|
|
338
339
|
continue
|
|
339
340
|
|
|
340
341
|
# now try to reduce the scenario connections according to the requirements of the feature class
|
|
341
|
-
|
|
342
|
+
cur_feature_cnn = \
|
|
342
343
|
FeatureController.get_for(
|
|
343
344
|
cur_feature.__class__).get_abs_class_based_for_vdevice()[mapped_vdevice]
|
|
344
|
-
cur_feature_cnn = Connection.based_on(*cur_feature_class_based_for_vdevice)
|
|
345
345
|
|
|
346
346
|
device_cnn_singles = get_single_cnns_between_device_for_feature(
|
|
347
347
|
from_device=cur_from_device, to_device=mapped_device, relevant_feature_cnn=cur_feature_cnn)
|
|
@@ -395,7 +395,7 @@ class ScenarioController(NormalScenarioSetupController):
|
|
|
395
395
|
cur_from_device_controller = DeviceController.get_for(cur_from_device)
|
|
396
396
|
cur_to_device_controller = DeviceController.get_for(cur_to_device)
|
|
397
397
|
|
|
398
|
-
new_cnn = Connection.based_on(*cur_single_cnns)
|
|
398
|
+
new_cnn = Connection.based_on(OrConnectionRelation(*cur_single_cnns))
|
|
399
399
|
new_cnn.set_metadata_for_all_subitems(cur_single_cnns[0].metadata)
|
|
400
400
|
if cur_from_device == cur_single_cnns[0].from_device:
|
|
401
401
|
cur_from_device_controller.add_new_absolute_connection(new_cnn)
|
|
@@ -3,7 +3,6 @@ from typing import Type, Dict, Union, TYPE_CHECKING
|
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
5
|
from _balder.setup import Setup
|
|
6
|
-
from _balder.connection import Connection
|
|
7
6
|
from _balder.exceptions import IllegalVDeviceMappingError, MultiInheritanceError
|
|
8
7
|
from _balder.controllers.feature_controller import FeatureController
|
|
9
8
|
from _balder.controllers.device_controller import DeviceController
|
|
@@ -113,12 +112,12 @@ class SetupController(NormalScenarioSetupController):
|
|
|
113
112
|
continue
|
|
114
113
|
|
|
115
114
|
# there exists a class based requirement for this vDevice
|
|
116
|
-
class_based_cnn =
|
|
115
|
+
class_based_cnn = feature_class_based_for_vdevice[mapped_vdevice]
|
|
117
116
|
# search relevant connection
|
|
118
117
|
cur_device_controller = DeviceController.get_for(cur_device)
|
|
119
118
|
for _, cur_cnn_list in cur_device_controller.get_all_absolute_connections().items():
|
|
120
119
|
for cur_cnn in cur_cnn_list:
|
|
121
|
-
if not cur_cnn.has_connection_from_to(cur_device, mapped_device):
|
|
120
|
+
if not cur_cnn.has_connection_from_to(cur_device, end_device=mapped_device):
|
|
122
121
|
# this connection can be ignored, because it is no connection between the current device
|
|
123
122
|
# and the mapped device
|
|
124
123
|
continue
|
_balder/decorator_connect.py
CHANGED
|
@@ -5,10 +5,15 @@ import re
|
|
|
5
5
|
from _balder.device import Device
|
|
6
6
|
from _balder.connection import Connection
|
|
7
7
|
from _balder.controllers import DeviceController
|
|
8
|
+
from _balder.cnnrelations import AndConnectionRelation, OrConnectionRelation
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
def connect(
|
|
11
|
-
|
|
11
|
+
def connect(
|
|
12
|
+
with_device: Union[Type[Device], str],
|
|
13
|
+
over_connection: Union[Connection, Type[Connection], AndConnectionRelation, OrConnectionRelation],
|
|
14
|
+
self_node_name: str = None,
|
|
15
|
+
dest_node_name: str = None
|
|
16
|
+
):
|
|
12
17
|
"""
|
|
13
18
|
This decorator connects two devices with each other. It can be used for scenarios as well as setup devices.
|
|
14
19
|
|
|
@@ -27,11 +32,6 @@ def connect(with_device: Union[Type[Device], str], over_connection: Union[Type[C
|
|
|
27
32
|
if not isinstance(with_device, str) and not issubclass(with_device, Device):
|
|
28
33
|
raise ValueError("the value of `with_device` must be a `Device` (or a subclass thereof) or the device name "
|
|
29
34
|
"as a string")
|
|
30
|
-
if isinstance(over_connection, tuple):
|
|
31
|
-
# doesn't make sense, because we can describe what's needed in one scenario with several `@connect` decorations
|
|
32
|
-
# anyway. We are describing a setup, therefore an AND link makes no sense here either
|
|
33
|
-
raise TypeError("an AND link (signaled via the tuple) of the connection is not possible here - for further "
|
|
34
|
-
"separate connections use the `@connect` decorator again")
|
|
35
35
|
if isinstance(over_connection, type):
|
|
36
36
|
if not issubclass(over_connection, Connection):
|
|
37
37
|
raise TypeError("the type of `over_connection` must be a `Connection` (or a subclass of it)")
|
|
@@ -91,11 +91,13 @@ def connect(with_device: Union[Type[Device], str], over_connection: Union[Type[C
|
|
|
91
91
|
# in another `@connect()` decorator (and also remove possible metadata from the new clone)
|
|
92
92
|
cur_cnn_instance = over_connection.clone()
|
|
93
93
|
cur_cnn_instance.set_metadata_for_all_subitems(None)
|
|
94
|
-
elif issubclass(over_connection, Connection):
|
|
94
|
+
elif isinstance(over_connection, type) and issubclass(over_connection, Connection):
|
|
95
95
|
# not instantiated -> instantiate it
|
|
96
96
|
cur_cnn_instance = over_connection()
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
elif isinstance(over_connection, (AndConnectionRelation, OrConnectionRelation)):
|
|
98
|
+
over_connection = Connection.based_on(over_connection)
|
|
99
|
+
cur_cnn_instance.metadata.set_from(from_device=decorated_cls, from_device_node_name=self_node_name)
|
|
100
|
+
cur_cnn_instance.metadata.set_to(to_device=with_device, to_device_node_name=dest_node_name)
|
|
99
101
|
|
|
100
102
|
decorated_cls_device_controller.add_new_raw_connection(cur_cnn_instance)
|
|
101
103
|
return decorated_cls
|
_balder/decorator_for_vdevice.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import Union, Type
|
|
3
3
|
|
|
4
4
|
import inspect
|
|
5
|
+
from _balder.cnnrelations import AndConnectionRelation, OrConnectionRelation
|
|
5
6
|
from _balder.collector import Collector
|
|
6
7
|
from _balder.feature import Feature
|
|
7
8
|
from _balder.vdevice import VDevice
|
|
@@ -13,9 +14,9 @@ from _balder.exceptions import DuplicateForVDeviceError, UnknownVDeviceException
|
|
|
13
14
|
def for_vdevice(
|
|
14
15
|
vdevice: Union[str, Type[VDevice]],
|
|
15
16
|
with_connections: Union[
|
|
16
|
-
Type[Connection], Connection,
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
Type[Connection], Connection, AndConnectionRelation, OrConnectionRelation
|
|
18
|
+
] = Connection(),
|
|
19
|
+
):
|
|
19
20
|
"""
|
|
20
21
|
With the `@for_vdevice` you can limit the decorated object for a special allowed connection tree for every existing
|
|
21
22
|
vDevice. This decorator can be used to decorate whole :class:`Feature` classes just like single methods of a
|
|
@@ -37,26 +38,18 @@ def for_vdevice(
|
|
|
37
38
|
|
|
38
39
|
:param with_connections: the assigned connection trees for this class/method (default: a universal connection)
|
|
39
40
|
"""
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
raise ValueError(f"the given tuple element `{tuple_idx}` that is given on position `{idx}` "
|
|
53
|
-
f"has to be a subclass of `{Connection.__name__}`")
|
|
54
|
-
tuple_idx += 1
|
|
55
|
-
elif not isinstance(cur_conn, Connection):
|
|
56
|
-
raise ValueError(f"the given type on position `{idx}` has to be a subclass of `{Connection.__name__}` or "
|
|
57
|
-
f"a element of it")
|
|
58
|
-
|
|
59
|
-
idx += 1
|
|
41
|
+
if isinstance(with_connections, Connection):
|
|
42
|
+
# do nothing
|
|
43
|
+
pass
|
|
44
|
+
elif isinstance(with_connections, (AndConnectionRelation, OrConnectionRelation)):
|
|
45
|
+
# use container connection
|
|
46
|
+
with_connections = Connection.based_on(with_connections)
|
|
47
|
+
elif isinstance(with_connections, type) and issubclass(with_connections, Connection):
|
|
48
|
+
# instantiate it
|
|
49
|
+
with_connections = with_connections()
|
|
50
|
+
else:
|
|
51
|
+
raise TypeError(f"the given element ``with_connection`` needs to be from type `AndConnectionRelation`, "
|
|
52
|
+
f"`OrConnectionRelation` or `Connection` - `{type(with_connections)}` is not allowed")
|
|
60
53
|
|
|
61
54
|
# note: if `args` is an empty list - no special sub-connection-tree bindings
|
|
62
55
|
|
|
@@ -105,7 +98,6 @@ def for_vdevice(
|
|
|
105
98
|
|
|
106
99
|
vdevice = relevant_vdevices[0]
|
|
107
100
|
cls_for_vdevice = fn_feature_controller.get_class_based_for_vdevice()
|
|
108
|
-
cls_for_vdevice = {} if cls_for_vdevice is None else cls_for_vdevice
|
|
109
101
|
if vdevice in cls_for_vdevice.keys():
|
|
110
102
|
raise DuplicateForVDeviceError(
|
|
111
103
|
f'there already exists a decorator for the vDevice `{vdevice}` in the Feature class '
|
_balder/decorator_gateway.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from _balder.controllers.device_controller import DeviceController
|
|
3
4
|
from _balder.device import Device
|
|
4
5
|
from _balder.node_gateway import NodeGateway
|
|
5
6
|
|
|
@@ -25,10 +26,9 @@ def gateway(from_node: str, to_node: str, bidirectional: bool = True):
|
|
|
25
26
|
raise TypeError(
|
|
26
27
|
f"The decorator `gateway` may only be used for `Device` objects. This is not possible for the applied "
|
|
27
28
|
f"class `{cls.__name__}`.")
|
|
29
|
+
decorated_cls_device_controller = DeviceController.get_for(cls)
|
|
28
30
|
|
|
29
|
-
if not hasattr(cls, '_gateways'):
|
|
30
|
-
cls._gateways = []
|
|
31
31
|
new_gateway = NodeGateway(cls, from_node, to_node, bidirectional)
|
|
32
|
-
|
|
32
|
+
decorated_cls_device_controller.add_new_raw_gateway(new_gateway)
|
|
33
33
|
return cls
|
|
34
34
|
return decorator
|
|
@@ -12,7 +12,6 @@ from _balder.testresult import ResultState, TestcaseResult
|
|
|
12
12
|
from _balder.utils import inspect_method
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
|
-
from _balder.executor.unresolved_parametrized_testcase_executor import UnresolvedParametrizedTestcaseExecutor
|
|
16
15
|
from _balder.executor.variation_executor import VariationExecutor
|
|
17
16
|
from _balder.fixture_manager import FixtureManager
|
|
18
17
|
from _balder.scenario import Scenario
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
2
4
|
from typing import Type, Union, List, Dict, Tuple, TYPE_CHECKING
|
|
3
5
|
|
|
4
6
|
import inspect
|
|
5
7
|
import logging
|
|
8
|
+
from _balder.cnnrelations import OrConnectionRelation
|
|
6
9
|
from _balder.device import Device
|
|
7
10
|
from _balder.connection import Connection
|
|
8
11
|
from _balder.fixture_execution_level import FixtureExecutionLevel
|
|
@@ -21,6 +24,8 @@ if TYPE_CHECKING:
|
|
|
21
24
|
from _balder.feature import Feature
|
|
22
25
|
from _balder.scenario import Scenario
|
|
23
26
|
from _balder.vdevice import VDevice
|
|
27
|
+
from _balder.controllers.scenario_controller import ScenarioController
|
|
28
|
+
from _balder.controllers.setup_controller import SetupController
|
|
24
29
|
from _balder.executor.scenario_executor import ScenarioExecutor
|
|
25
30
|
from _balder.fixture_manager import FixtureManager
|
|
26
31
|
|
|
@@ -96,11 +101,25 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
96
101
|
"""property returns the current :class:`Scenario` for this variation"""
|
|
97
102
|
return self._parent_executor.base_scenario_class
|
|
98
103
|
|
|
104
|
+
@property
|
|
105
|
+
def cur_scenario_controller(self) -> ScenarioController:
|
|
106
|
+
"""
|
|
107
|
+
returns the current :class:`ScenarioController` for this variation
|
|
108
|
+
"""
|
|
109
|
+
return self._parent_executor.base_scenario_controller
|
|
110
|
+
|
|
99
111
|
@property
|
|
100
112
|
def cur_setup_class(self) -> Setup:
|
|
101
113
|
"""property returns the current :class:`Setup` for this variation"""
|
|
102
114
|
return self._parent_executor.parent_executor.base_setup_class
|
|
103
115
|
|
|
116
|
+
@property
|
|
117
|
+
def cur_setup_controller(self) -> SetupController:
|
|
118
|
+
"""
|
|
119
|
+
returns the current :class:`SetupController` for this variation
|
|
120
|
+
"""
|
|
121
|
+
return self._parent_executor.parent_executor.base_setup_controller
|
|
122
|
+
|
|
104
123
|
@property
|
|
105
124
|
def base_device_mapping(self) -> Dict[Type[Device], Type[Device]]:
|
|
106
125
|
"""
|
|
@@ -284,6 +303,39 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
284
303
|
f'can not find a valid routing on setup level for the connection `{scenario_cnn.get_tree_str()}` '
|
|
285
304
|
f'between scenario devices `{scenario_cnn.from_device}` and `{scenario_cnn.to_device}`')
|
|
286
305
|
|
|
306
|
+
def _get_matching_setup_features_for(
|
|
307
|
+
self,
|
|
308
|
+
scenario_feature_obj: Feature,
|
|
309
|
+
in_setup_device: Type[Device]
|
|
310
|
+
) -> List[Feature]:
|
|
311
|
+
"""
|
|
312
|
+
Helper method that returns all matching setup features for the provided scenario feature in the provided setup
|
|
313
|
+
device.
|
|
314
|
+
"""
|
|
315
|
+
cur_setup_features = DeviceController.get_for(in_setup_device).get_all_instantiated_feature_objects()
|
|
316
|
+
|
|
317
|
+
replacing_feature_candidates = [
|
|
318
|
+
cur_setup_feature for cur_setup_feature in cur_setup_features.values()
|
|
319
|
+
if isinstance(cur_setup_feature, scenario_feature_obj.__class__)
|
|
320
|
+
]
|
|
321
|
+
active_scenario_vdev, mapped_scenario_dev = scenario_feature_obj.active_vdevice_device_mapping
|
|
322
|
+
|
|
323
|
+
replacing_features = replacing_feature_candidates.copy()
|
|
324
|
+
if mapped_scenario_dev is not None:
|
|
325
|
+
# get the related setup device for the mapped scenario device (on scenario level)
|
|
326
|
+
setup_dev_of_mapped_scenario_dev = self.get_setup_device_for(mapped_scenario_dev)
|
|
327
|
+
|
|
328
|
+
# now check if there is a mapping on setup level too
|
|
329
|
+
for cur_replacing_feature in replacing_feature_candidates:
|
|
330
|
+
mapped_setup_vdev, mapped_setup_dev = cur_replacing_feature.active_vdevice_device_mapping
|
|
331
|
+
if mapped_setup_vdev is not None and not issubclass(mapped_setup_vdev, active_scenario_vdev):
|
|
332
|
+
# drop this feature matching, because we have different vdevice mapped
|
|
333
|
+
replacing_features.remove(cur_replacing_feature)
|
|
334
|
+
elif mapped_setup_dev is not None and mapped_setup_dev != setup_dev_of_mapped_scenario_dev:
|
|
335
|
+
# drop this feature matching, because it is not applicable here
|
|
336
|
+
replacing_features.remove(cur_replacing_feature)
|
|
337
|
+
return replacing_features
|
|
338
|
+
|
|
287
339
|
# ---------------------------------- METHODS -----------------------------------------------------------------------
|
|
288
340
|
|
|
289
341
|
def testsummary(self) -> ResultSummary:
|
|
@@ -336,75 +388,53 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
336
388
|
:raises NotApplicableVariationError: will be thrown if this variation cannot be applied, because the setup-/
|
|
337
389
|
scenario-device-features can not be resolved
|
|
338
390
|
"""
|
|
339
|
-
feature_replacement = {}
|
|
340
|
-
abs_setup_vdevice_mappings = {}
|
|
391
|
+
feature_replacement = {scenario_dev: {} for scenario_dev in self.base_device_mapping.keys()}
|
|
392
|
+
abs_setup_vdevice_mappings = {setup_dev: {} for setup_dev in self.base_device_mapping.values()}
|
|
341
393
|
for cur_scenario_device, cur_setup_device in self.base_device_mapping.items():
|
|
342
394
|
cur_setup_features = DeviceController.get_for(cur_setup_device).get_all_instantiated_feature_objects()
|
|
343
395
|
|
|
344
|
-
if cur_scenario_device not in feature_replacement.keys():
|
|
345
|
-
feature_replacement[cur_scenario_device] = {}
|
|
346
|
-
|
|
347
|
-
if cur_setup_device not in abs_setup_vdevice_mappings.keys():
|
|
348
|
-
abs_setup_vdevice_mappings[cur_setup_device] = {}
|
|
349
|
-
|
|
350
396
|
all_assigned_setup_features = []
|
|
351
397
|
cur_scenario_device_orig_features = \
|
|
352
398
|
DeviceController.get_for(cur_scenario_device).get_original_instanced_feature_objects()
|
|
353
|
-
for cur_attr_name,
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
if
|
|
361
|
-
# get the related setup device for the mapped scenario device (on scenario level)
|
|
362
|
-
to_scenarios_vdevice_mapped_setup_device = self.get_setup_device_for(mapped_scenario_device)
|
|
363
|
-
|
|
364
|
-
# now check if there is a mapping on setup level too
|
|
365
|
-
for cur_replacing_feature in replacing_features:
|
|
366
|
-
mapped_setup_vdevice, mapped_setup_device = cur_replacing_feature.active_vdevice_device_mapping
|
|
367
|
-
if mapped_setup_vdevice is not None and \
|
|
368
|
-
not issubclass(mapped_setup_vdevice, used_scenario_vdevice):
|
|
369
|
-
# drop this feature matching, because we have different vdevice mapped
|
|
370
|
-
cleanup_replacing_features.remove(cur_replacing_feature)
|
|
371
|
-
elif mapped_setup_device is not None and \
|
|
372
|
-
mapped_setup_device != to_scenarios_vdevice_mapped_setup_device:
|
|
373
|
-
# drop this feature matching, because it is not applicable here
|
|
374
|
-
cleanup_replacing_features.remove(cur_replacing_feature)
|
|
375
|
-
|
|
376
|
-
if len(cleanup_replacing_features) != 1:
|
|
399
|
+
for cur_attr_name, cur_scenario_feature_obj in cur_scenario_device_orig_features.items():
|
|
400
|
+
active_scenario_vdevice, mapped_scenario_device = cur_scenario_feature_obj.active_vdevice_device_mapping
|
|
401
|
+
|
|
402
|
+
cur_setup_feature_objs = self._get_matching_setup_features_for(
|
|
403
|
+
scenario_feature_obj=cur_scenario_feature_obj, in_setup_device=cur_setup_device
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
if len(cur_setup_feature_objs) != 1:
|
|
377
407
|
raise NotApplicableVariationException(
|
|
378
408
|
f'this variation can not be applicable because there was no setup feature implementation of '
|
|
379
|
-
f'`{
|
|
409
|
+
f'`{cur_scenario_feature_obj.__class__.__name__}` (used by scenario device '
|
|
380
410
|
f'`{cur_scenario_device.__name__}`) in setup device `{cur_setup_device.__name__}`')
|
|
411
|
+
cur_setup_feature_obj = cur_setup_feature_objs[0]
|
|
381
412
|
|
|
382
413
|
if mapped_scenario_device is None:
|
|
383
414
|
# we have exactly one matching candidate, but also no vDevice mapping
|
|
384
415
|
# check if the matching candidate has a vDevice mapping
|
|
385
|
-
_, mapped_setup_device =
|
|
386
|
-
cleanup_feature_controller = FeatureController.get_for(
|
|
416
|
+
_, mapped_setup_device = cur_setup_feature_obj.active_vdevice_device_mapping
|
|
417
|
+
cleanup_feature_controller = FeatureController.get_for(cur_setup_feature_obj.__class__)
|
|
387
418
|
if mapped_setup_device is None \
|
|
388
419
|
and len(cleanup_feature_controller.get_abs_inner_vdevice_classes()) > 0:
|
|
389
420
|
# there is no vDevice mapping on scenario and no vDevice mapping on setup level, but the
|
|
390
421
|
# feature defined vDevices -> NOT APPLICABLE
|
|
391
422
|
logger.warning(
|
|
392
423
|
f"missing vDevice mapping for feature "
|
|
393
|
-
f"`{
|
|
424
|
+
f"`{cur_scenario_feature_obj.__class__.__name__}` (used in scenario device "
|
|
394
425
|
f"`{cur_scenario_device.__name__}` and in setup device `{cur_setup_device.__name__}`) - "
|
|
395
426
|
f"VARIATION CAN NOT BE APPLIED")
|
|
396
427
|
raise NotApplicableVariationException(
|
|
397
|
-
f'this variation can not be
|
|
428
|
+
f'this variation can not be applied because there was no vDevice mapping given on '
|
|
398
429
|
f'scenario or on setup level for the feature '
|
|
399
|
-
f'`{
|
|
430
|
+
f'`{cur_scenario_feature_obj.__class__.__name__}` (used by scenario device '
|
|
400
431
|
f'`{cur_scenario_device.__name__}`) in setup device `{cur_setup_device.__name__}`')
|
|
401
432
|
|
|
402
|
-
all_assigned_setup_features.append(
|
|
433
|
+
all_assigned_setup_features.append(cur_setup_feature_obj)
|
|
403
434
|
if cur_attr_name not in feature_replacement[cur_scenario_device].keys():
|
|
404
|
-
|
|
405
|
-
cleanup_feature_controller = FeatureController.get_for(cleanup_feature.__class__)
|
|
435
|
+
cleanup_feature_controller = FeatureController.get_for(cur_setup_feature_obj.__class__)
|
|
406
436
|
|
|
407
|
-
used_setup_vdevice, mapped_setup_device =
|
|
437
|
+
used_setup_vdevice, mapped_setup_device = cur_setup_feature_obj.active_vdevice_device_mapping
|
|
408
438
|
|
|
409
439
|
# if there is a vDevice mapping on scenario level, but not on setup level, so update the
|
|
410
440
|
# VDevice-Device-Mapping there
|
|
@@ -413,21 +443,21 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
413
443
|
# because check was already done in collector-stage)
|
|
414
444
|
setup_vdevices = [cur_vdevice for cur_vdevice
|
|
415
445
|
in cleanup_feature_controller.get_abs_inner_vdevice_classes()
|
|
416
|
-
if cur_vdevice.__name__ ==
|
|
446
|
+
if cur_vdevice.__name__ == active_scenario_vdevice.__name__]
|
|
417
447
|
used_setup_vdevice = setup_vdevices[0]
|
|
418
448
|
# set the mapping
|
|
419
|
-
abs_setup_vdevice_mappings[cur_setup_device][
|
|
449
|
+
abs_setup_vdevice_mappings[cur_setup_device][cur_setup_feature_obj] = {
|
|
420
450
|
used_setup_vdevice: self.get_setup_device_for(mapped_scenario_device)}
|
|
421
451
|
# if there is a vDevice mapping on setup level, but not on scenario level, so directly update the
|
|
422
452
|
# VDevice-Device-Mapping there
|
|
423
453
|
elif mapped_scenario_device is None and mapped_setup_device is not None:
|
|
424
|
-
abs_setup_vdevice_mappings[cur_setup_device][
|
|
454
|
+
abs_setup_vdevice_mappings[cur_setup_device][cur_setup_feature_obj] = {
|
|
425
455
|
used_setup_vdevice: mapped_setup_device}
|
|
426
456
|
|
|
427
457
|
feature_replacement[cur_scenario_device][cur_attr_name] = \
|
|
428
|
-
(
|
|
458
|
+
(cur_scenario_feature_obj, cur_setup_feature_obj)
|
|
429
459
|
# also add all setup features that are not assigned as autonomous features
|
|
430
|
-
for
|
|
460
|
+
for cur_setup_feature in cur_setup_features.values():
|
|
431
461
|
if cur_setup_feature not in all_assigned_setup_features:
|
|
432
462
|
# determine free name
|
|
433
463
|
idx = 0
|
|
@@ -679,31 +709,16 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
679
709
|
|
|
680
710
|
def determine_absolute_scenario_device_connections(self):
|
|
681
711
|
"""
|
|
682
|
-
This method determines the
|
|
683
|
-
|
|
712
|
+
This method determines the absolute connections for this variation and sets the internal properties
|
|
713
|
+
`_abs_variation_*_device_connections` with them. This will be used to determine the real connection-subtree
|
|
684
714
|
(that can be used for this variation) by the method `create_all_valid_routings()`.
|
|
685
715
|
|
|
686
716
|
The method re-executes the algorithm to determine the absolute connections for a scenario/setup (see the method
|
|
687
717
|
:meth:`Collector.determine_absolute_device_connections_for`), but it considers the real applied vDevice and
|
|
688
718
|
their feature restrictions too.
|
|
689
719
|
"""
|
|
690
|
-
abs_var_scenario_device_cnns = {}
|
|
691
|
-
|
|
692
720
|
# first determine all relevant absolute connection depending on the current scenario
|
|
693
|
-
|
|
694
|
-
cur_scenario_device_abs_cnns = \
|
|
695
|
-
DeviceController.get_for(cur_scenario_device).get_all_absolute_connections()
|
|
696
|
-
for _, cur_cnn_list in cur_scenario_device_abs_cnns.items():
|
|
697
|
-
for cur_cnn in cur_cnn_list:
|
|
698
|
-
if cur_scenario_device not in abs_var_scenario_device_cnns.keys():
|
|
699
|
-
abs_var_scenario_device_cnns[cur_scenario_device] = {}
|
|
700
|
-
|
|
701
|
-
cur_to_device, _ = cur_cnn.get_conn_partner_of(cur_scenario_device)
|
|
702
|
-
|
|
703
|
-
if cur_to_device not in abs_var_scenario_device_cnns[cur_scenario_device].keys():
|
|
704
|
-
abs_var_scenario_device_cnns[cur_scenario_device][cur_to_device] = []
|
|
705
|
-
|
|
706
|
-
abs_var_scenario_device_cnns[cur_scenario_device][cur_to_device].append(cur_cnn)
|
|
721
|
+
abs_var_scenario_device_cnns = self.cur_scenario_controller.get_all_abs_connections()
|
|
707
722
|
|
|
708
723
|
# now iterate over every feature, that is used by the scenario and determine the class-based feature connections
|
|
709
724
|
# of the mapped scenario feature (and its vDevice)
|
|
@@ -726,31 +741,34 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
726
741
|
|
|
727
742
|
# get relevant class based connections for the current feature on setup level (this is really be used
|
|
728
743
|
# here)
|
|
729
|
-
|
|
744
|
+
feature_cnn = \
|
|
730
745
|
FeatureController.get_for(
|
|
731
746
|
cur_setup_feature.__class__).get_abs_class_based_for_vdevice()[cur_setup_feature_vdevice]
|
|
732
747
|
# connection that are relevant for this feature
|
|
733
|
-
relevant_cnns =
|
|
748
|
+
relevant_cnns = [
|
|
749
|
+
cnn for cnn in abs_var_scenario_device_cnns
|
|
750
|
+
if cnn.has_connection_from_to(cur_scenario_device, end_device=cur_mapped_scenario_device)
|
|
751
|
+
]
|
|
734
752
|
|
|
735
753
|
relevant_device_cnn = None
|
|
736
754
|
|
|
737
755
|
if len(relevant_cnns) > 1:
|
|
738
756
|
# we have parallel possibilities -> determine the selected one (only one is allowed to fit)
|
|
739
757
|
for cur_relevant_cnn in relevant_cnns:
|
|
740
|
-
for cur_relevant_single_cnn in
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
758
|
+
for cur_relevant_single_cnn, cur_feature_single_cnn in (
|
|
759
|
+
itertools.product(cur_relevant_cnn.get_singles(), feature_cnn.get_singles())):
|
|
760
|
+
if not cur_feature_single_cnn.contained_in(cur_relevant_single_cnn, ignore_metadata=True):
|
|
761
|
+
continue
|
|
762
|
+
if relevant_device_cnn is not None:
|
|
763
|
+
raise UnclearAssignableFeatureConnectionError(
|
|
764
|
+
f"the devices {cur_scenario_device.__name__} and "
|
|
765
|
+
f"{cur_mapped_scenario_device.__name__} have multiple parallel connections - the "
|
|
766
|
+
f"device `{cur_scenario_device.__name__}` uses a feature "
|
|
767
|
+
f"`{cur_scenario_feature.__class__.__name__}` that matches with the device "
|
|
768
|
+
f"`{cur_mapped_scenario_device.__name__}`, but it is not clear which of the "
|
|
769
|
+
f"parallel connection could be used"
|
|
770
|
+
)
|
|
771
|
+
relevant_device_cnn = cur_relevant_cnn
|
|
754
772
|
elif len(relevant_cnns) == 1:
|
|
755
773
|
relevant_device_cnn = relevant_cnns[0]
|
|
756
774
|
if relevant_device_cnn is None:
|
|
@@ -759,36 +777,19 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
759
777
|
|
|
760
778
|
# now cleanup the scenario-device connection `relevant_device_cnn` according to the class-based feature
|
|
761
779
|
# connection
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
relevant_device_cnn)
|
|
773
|
-
abs_var_scenario_device_cnns[cur_scenario_device][cur_mapped_scenario_device].append(new_cnn_to_replace)
|
|
774
|
-
|
|
775
|
-
# also search the connection in the other direction
|
|
776
|
-
other_dir_relevant_device_cnn = None
|
|
777
|
-
for cur_cnn in abs_var_scenario_device_cnns[cur_mapped_scenario_device][cur_scenario_device]:
|
|
778
|
-
if cur_cnn.equal_with(relevant_device_cnn):
|
|
779
|
-
other_dir_relevant_device_cnn = cur_cnn
|
|
780
|
-
break
|
|
781
|
-
# and also replace it
|
|
782
|
-
abs_var_scenario_device_cnns[cur_mapped_scenario_device][cur_scenario_device].remove(
|
|
783
|
-
other_dir_relevant_device_cnn)
|
|
784
|
-
abs_var_scenario_device_cnns[cur_mapped_scenario_device][cur_scenario_device].append(new_cnn_to_replace)
|
|
780
|
+
new_cnn_to_replace = Connection.based_on(OrConnectionRelation(*[
|
|
781
|
+
cur_old_cnn_single for cur_old_cnn_single in relevant_device_cnn.get_singles()
|
|
782
|
+
if feature_cnn.contained_in(cur_old_cnn_single, ignore_metadata=True)
|
|
783
|
+
]))
|
|
784
|
+
new_cnn_to_replace.set_metadata_for_all_subitems(relevant_device_cnn.metadata)
|
|
785
|
+
|
|
786
|
+
abs_var_scenario_device_cnns.remove(relevant_device_cnn)
|
|
787
|
+
abs_var_scenario_device_cnns.append(new_cnn_to_replace)
|
|
788
|
+
|
|
789
|
+
# we do not need to check other direction because `has_connection_from_to()` returns both possibilities
|
|
785
790
|
|
|
786
791
|
# set the determined values in variation object
|
|
787
|
-
self._abs_variation_scenario_device_connections =
|
|
788
|
-
for _, from_device_dict in abs_var_scenario_device_cnns.items():
|
|
789
|
-
for _, cur_cnn_list in from_device_dict.items():
|
|
790
|
-
for cur_cnn in cur_cnn_list:
|
|
791
|
-
self._abs_variation_scenario_device_connections.append(cur_cnn)
|
|
792
|
+
self._abs_variation_scenario_device_connections = abs_var_scenario_device_cnns
|
|
792
793
|
|
|
793
794
|
def create_all_valid_routings(self):
|
|
794
795
|
"""
|
|
@@ -828,16 +829,18 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
828
829
|
if virtual_routing_cnns[cur_cnn] is None:
|
|
829
830
|
virtual_routing_cnns[cur_cnn] = Connection.based_on(virtual_cnn)
|
|
830
831
|
else:
|
|
831
|
-
virtual_routing_cnns[cur_cnn] = Connection.based_on(
|
|
832
|
+
virtual_routing_cnns[cur_cnn] = Connection.based_on(
|
|
833
|
+
OrConnectionRelation(virtual_routing_cnns[cur_cnn], virtual_cnn))
|
|
832
834
|
virtual_routing_cnns[cur_cnn].set_metadata_for_all_subitems(virtual_cnn.metadata)
|
|
833
835
|
|
|
834
836
|
self._abs_variation_connections = []
|
|
835
837
|
for cur_cnn in self._abs_variation_scenario_device_connections:
|
|
836
838
|
cur_virtual_cnn = virtual_routing_cnns[cur_cnn]
|
|
837
839
|
new_intersection = cur_cnn.intersection_with(cur_virtual_cnn)
|
|
838
|
-
new_intersection
|
|
839
|
-
|
|
840
|
-
|
|
840
|
+
if new_intersection:
|
|
841
|
+
new_intersection.set_metadata_for_all_subitems(None)
|
|
842
|
+
# always set the metadata for setup devices
|
|
843
|
+
new_intersection.set_metadata_for_all_subitems(cur_virtual_cnn.metadata)
|
|
841
844
|
self._abs_variation_connections.append(new_intersection)
|
|
842
845
|
|
|
843
846
|
def set_conn_dependent_methods(self):
|
|
@@ -873,11 +876,15 @@ class VariationExecutor(BasicExecutableExecutor):
|
|
|
873
876
|
relevant_abs_conn = []
|
|
874
877
|
for cur_cnn in self._abs_variation_connections:
|
|
875
878
|
if cur_cnn.has_connection_from_to(start_device=setup_device, end_device=mapped_setup_device):
|
|
876
|
-
|
|
879
|
+
# add the children
|
|
880
|
+
relevant_abs_conn.extend(
|
|
881
|
+
cur_cnn.based_on_elements.connections if cur_cnn.__class__ == Connection else [cur_cnn]
|
|
882
|
+
)
|
|
883
|
+
|
|
877
884
|
if len(relevant_abs_conn) is None:
|
|
878
885
|
raise RuntimeError(f"detect empty absolute connection between device `{setup_device.__name__}` "
|
|
879
886
|
f"and device `{mapped_setup_device.__name__}`")
|
|
880
|
-
absolute_feature_method_var_cnn = Connection.based_on(*relevant_abs_conn)
|
|
887
|
+
absolute_feature_method_var_cnn = Connection.based_on(OrConnectionRelation(*relevant_abs_conn))
|
|
881
888
|
cur_method_variation = cur_setup_feature_controller.get_method_variation(
|
|
882
889
|
of_method_name=cur_method_name, for_vdevice=mapped_vdevice,
|
|
883
890
|
with_connection=absolute_feature_method_var_cnn)
|
_balder/feature.py
CHANGED
|
@@ -16,7 +16,7 @@ class Feature:
|
|
|
16
16
|
:param kwargs: contains a dictionary that references all vDevices of the feature and a real
|
|
17
17
|
scenario :meth:`Device` as value
|
|
18
18
|
"""
|
|
19
|
-
from _balder.controllers import FeatureController
|
|
19
|
+
from _balder.controllers import FeatureController # pylint: disable=import-outside-toplevel
|
|
20
20
|
|
|
21
21
|
#: this property contains the active mapping for the devices
|
|
22
22
|
self.active_vdevices: Dict[VDevice, Union[Device, str]] = {}
|