baldertest 0.1.0b10__py3-none-any.whl → 0.1.0b12__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.
Files changed (34) hide show
  1. _balder/_version.py +1 -1
  2. _balder/cnnrelations/__init__.py +7 -0
  3. _balder/cnnrelations/and_connection_relation.py +149 -0
  4. _balder/cnnrelations/base_connection_relation.py +270 -0
  5. _balder/cnnrelations/or_connection_relation.py +65 -0
  6. _balder/collector.py +10 -16
  7. _balder/connection.py +400 -881
  8. _balder/connection_metadata.py +255 -0
  9. _balder/controllers/device_controller.py +37 -16
  10. _balder/controllers/feature_controller.py +63 -99
  11. _balder/controllers/normal_scenario_setup_controller.py +5 -5
  12. _balder/controllers/scenario_controller.py +6 -6
  13. _balder/controllers/setup_controller.py +2 -3
  14. _balder/decorator_connect.py +12 -10
  15. _balder/decorator_for_vdevice.py +17 -25
  16. _balder/decorator_gateway.py +3 -3
  17. _balder/executor/testcase_executor.py +0 -1
  18. _balder/executor/variation_executor.py +212 -199
  19. _balder/feature.py +1 -1
  20. _balder/feature_replacement_mapping.py +69 -0
  21. _balder/feature_vdevice_mapping.py +88 -0
  22. _balder/fixture_manager.py +10 -9
  23. _balder/objects/connections/osi_3_network.py +2 -2
  24. _balder/objects/connections/osi_4_transport.py +2 -2
  25. _balder/routing_path.py +27 -31
  26. _balder/solver.py +1 -1
  27. _balder/testresult.py +1 -1
  28. _balder/utils.py +27 -1
  29. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/METADATA +2 -2
  30. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/RECORD +34 -27
  31. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/WHEEL +1 -1
  32. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/LICENSE +0 -0
  33. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/entry_points.txt +0 -0
  34. {baldertest-0.1.0b10.dist-info → baldertest-0.1.0b12.dist-info}/top_level.txt +0 -0
_balder/_version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '0.1.0b10'
15
+ __version__ = version = '0.1.0b12'
16
16
  __version_tuple__ = version_tuple = (0, 1, 0)
@@ -0,0 +1,7 @@
1
+ from .and_connection_relation import AndConnectionRelation
2
+ from .or_connection_relation import OrConnectionRelation
3
+
4
+ __all__ = [
5
+ 'AndConnectionRelation',
6
+ 'OrConnectionRelation'
7
+ ]
@@ -0,0 +1,149 @@
1
+ from __future__ import annotations
2
+ from typing import TYPE_CHECKING
3
+
4
+ import itertools
5
+
6
+ from .base_connection_relation import BaseConnectionRelation, BaseConnectionRelationT
7
+
8
+ if TYPE_CHECKING:
9
+ from ..connection import List, Union, Connection
10
+ from .or_connection_relation import OrConnectionRelation
11
+
12
+
13
+ class AndConnectionRelation(BaseConnectionRelation):
14
+ """
15
+ describes an AND relation between connections
16
+ """
17
+
18
+ def get_tree_str(self) -> str:
19
+ based_on_strings = [cur_elem.get_tree_str() for cur_elem in self._connections]
20
+ return f"({' | '.join(based_on_strings)})"
21
+
22
+ def get_simplified_relation(self) -> OrConnectionRelation:
23
+ from ..connection import Connection # pylint: disable=import-outside-toplevel
24
+ from .or_connection_relation import OrConnectionRelation # pylint: disable=import-outside-toplevel
25
+
26
+ # create template with all elements that are definitely contained in every new AND relation (only
27
+ # ``Connection`` objects here)
28
+ and_template = AndConnectionRelation()
29
+ # add all OR relations that needs to be further resolved to that list
30
+ self_simplified_or_relations = []
31
+
32
+ # first: go through all inner elements and convert all relations in simplified relations
33
+ for cur_elem in self.connections:
34
+ if isinstance(cur_elem, Connection):
35
+ # that is fine - add it to the template
36
+ and_template.append(cur_elem)
37
+ elif isinstance(cur_elem, (AndConnectionRelation, OrConnectionRelation)):
38
+ # simplify this AND/OR relation and add the items of it
39
+ # (the simplified version is always an OR relation!)
40
+ self_simplified_or_relations.append(cur_elem.get_simplified_relation().connections)
41
+ else:
42
+ raise TypeError(f'unexpected type for inner element `{cur_elem}`')
43
+ # now our `self_simplified_or_relations` can only consist of the following nesting: `List[OR[Conn, AND[Conn]]]`
44
+ # we need to resolve this constellation into `OR[Conn, AND[Conn]]` now
45
+
46
+ # now generate all possibilities out of the OR relations and add them to the AND template
47
+ all_new_ands = []
48
+ for cur_variation_tuple in itertools.product(*self_simplified_or_relations):
49
+ for cur_item in cur_variation_tuple:
50
+ if isinstance(cur_item, Connection):
51
+ # just add the single connection item to the AND template
52
+ new_full_and_relation = and_template.clone()
53
+ new_full_and_relation.append(cur_item)
54
+ all_new_ands.append(new_full_and_relation)
55
+ elif isinstance(cur_item, AndConnectionRelation):
56
+ new_full_and_relation = and_template.clone()
57
+ new_full_and_relation.extend(cur_item.connections)
58
+ all_new_ands.append(new_full_and_relation)
59
+ else:
60
+ #: note: inner element can not be an OR here!
61
+ raise TypeError(f'detect illegal type `{cur_item.__class__}` for inner element')
62
+ if not self_simplified_or_relations:
63
+ all_new_ands.append(and_template)
64
+ return OrConnectionRelation(*all_new_ands)
65
+
66
+ def is_single(self) -> bool:
67
+
68
+ if len(self._connections) == 0:
69
+ return True
70
+
71
+ return min(cnn.is_single() for cnn in self._connections)
72
+
73
+ def get_singles(self) -> List[Connection]:
74
+ from ..connection import Connection # pylint: disable=import-outside-toplevel
75
+
76
+ singles_and_relations = ()
77
+ for cur_elem in self._connections:
78
+ # get all singles of this AND relation element
79
+ singles_and_relations += (cur_elem.get_singles(),)
80
+ # now get the variations and add them to our results
81
+ return [
82
+ Connection.based_on(AndConnectionRelation(*cur_tuple))
83
+ for cur_tuple in itertools.product(*singles_and_relations)
84
+ ]
85
+
86
+ def cut_into_all_possible_subtree_branches(self) -> List[AndConnectionRelation]:
87
+ if not self.is_single():
88
+ raise ValueError('can not execute method, because relation is not single')
89
+
90
+ tuple_with_all_possibilities = (
91
+ tuple(cur_tuple_item.cut_into_all_possible_subtree_branches() for cur_tuple_item in self._connections))
92
+
93
+ cloned_tuple_list = []
94
+ for cur_tuple in list(itertools.product(*tuple_with_all_possibilities)):
95
+ cloned_tuple = AndConnectionRelation(*[cur_tuple_item.clone() for cur_tuple_item in cur_tuple])
96
+ cloned_tuple_list.append(cloned_tuple)
97
+ return cloned_tuple_list
98
+
99
+ def contained_in(self, other_conn: Union[Connection, BaseConnectionRelationT], ignore_metadata=False) -> bool:
100
+ # This method checks if the AND relation is contained in the `other_conn`. To ensure that an AND relation is
101
+ # contained in a connection tree, there has to be another AND relation into the `other_conn`, that has the same
102
+ # length or is bigger. In addition, there has to exist an order combination where every element of the this AND
103
+ # relation is contained in the found AND relation of the `other_cnn`. In this case it doesn't matter where the
104
+ # AND relation is in `other_elem` (will be converted to single, and AND relation will be searched in all
105
+ # BASED_ON elements). If the AND relation of `other_conn` has fewer items than this AND relation, it will be
106
+ # ignored. The method only search for a valid existing item in the `other_conn` AND relation for every item of
107
+ # this AND relation.
108
+ from ..connection import Connection # pylint: disable=import-outside-toplevel
109
+
110
+ if not self.is_resolved():
111
+ raise ValueError('can not execute method, because connection relation is not resolved')
112
+ if not other_conn.is_resolved():
113
+ raise ValueError('can not execute method, because other connection relation is not resolved')
114
+
115
+ if isinstance(other_conn, BaseConnectionRelation):
116
+ other_conn = Connection.based_on(other_conn)
117
+
118
+ self_singles = self.get_singles()
119
+ other_singles = other_conn.get_singles()
120
+
121
+ for cur_self_single, cur_other_single in itertools.product(self_singles, other_singles):
122
+ # check if we can find an AND relation in the other object -> go the single connection upwards and
123
+ # search for a `AndConnectionRelation`
124
+
125
+ # self is a container connection -> use raw inner AND list
126
+ cur_self_single_and_relation = cur_self_single.based_on_elements.connections[0]
127
+
128
+ cur_sub_other_single = cur_other_single
129
+ while cur_sub_other_single is not None:
130
+ if isinstance(cur_sub_other_single, AndConnectionRelation):
131
+ # found an AND relation -> check if length does match
132
+ if len(cur_sub_other_single) < len(cur_self_single_and_relation):
133
+ # this complete element is not possible - skip this single!
134
+ break
135
+ # length is okay, no check if every single element is contained in one of this tuple
136
+ for cur_inner_self_elem in cur_self_single_and_relation.connections:
137
+
138
+ if not cur_inner_self_elem.contained_in(cur_sub_other_single,
139
+ ignore_metadata=ignore_metadata):
140
+ # at least one element is not contained in other AND relation - this complete element
141
+ # is not possible - skip this single!
142
+ break
143
+ # all items are contained in the current other AND relation -> match
144
+ return True
145
+ # go further up, if this element is no AND relation
146
+ cur_sub_other_single = cur_sub_other_single.based_on_elements[0] \
147
+ if cur_sub_other_single.based_on_elements else None
148
+
149
+ return False
@@ -0,0 +1,270 @@
1
+ from __future__ import annotations
2
+ from typing import List, Union, Type, Dict, TypeVar, TYPE_CHECKING
3
+ from abc import ABC, abstractmethod
4
+ from ..utils import cnn_type_check_and_convert
5
+
6
+ if TYPE_CHECKING:
7
+ from ..connection import Connection
8
+ from ..connection import ConnectionMetadata
9
+ from ..device import Device
10
+ from .and_connection_relation import AndConnectionRelation
11
+ from .or_connection_relation import OrConnectionRelation
12
+
13
+ BaseConnectionRelationT = TypeVar('BaseConnectionRelationT', bound='BaseConnectionRelation')
14
+
15
+
16
+ class BaseConnectionRelation(ABC):
17
+ """
18
+ This is the base connection list class, which provides the general functionality of connection collections.
19
+ """
20
+
21
+ def __init__(self, *connections: Union[Type[Connection], Connection, BaseConnectionRelationT]):
22
+ self._connections = []
23
+ # add it over append for type checking
24
+ for connection in connections:
25
+ self.append(connection)
26
+
27
+ def __iter__(self):
28
+ return self._connections.__iter__()
29
+
30
+ def __getitem__(self, item):
31
+ return self._connections.__getitem__(item)
32
+
33
+ def __len__(self):
34
+ return len(self._connections)
35
+
36
+ def __hash__(self):
37
+ all_hashes = 0
38
+ for cur_elem in self.connections:
39
+ all_hashes += hash(cur_elem) + hash(self.__class__.__name__)
40
+ return all_hashes
41
+
42
+ def __and__(
43
+ self,
44
+ other: Union[Connection, Type[Connection], AndConnectionRelation, OrConnectionRelation]
45
+ ) -> AndConnectionRelation:
46
+
47
+ from .and_connection_relation import AndConnectionRelation # pylint: disable=import-outside-toplevel
48
+
49
+ new_list = AndConnectionRelation()
50
+ new_list.append(self)
51
+ new_list.append(cnn_type_check_and_convert(other))
52
+ return new_list
53
+
54
+ def __or__(
55
+ self,
56
+ other: Union[Connection, Type[Connection], AndConnectionRelation, OrConnectionRelation]
57
+ ) -> OrConnectionRelation:
58
+
59
+ from .or_connection_relation import OrConnectionRelation # pylint: disable=import-outside-toplevel
60
+
61
+ new_list = OrConnectionRelation()
62
+ new_list.append(self)
63
+ new_list.append(cnn_type_check_and_convert(other))
64
+ return new_list
65
+
66
+ @property
67
+ def connections(self) -> List[Union[Connection, BaseConnectionRelationT]]:
68
+ """
69
+ returns the components of this connection relation
70
+ """
71
+ return self._connections.copy()
72
+
73
+ @property
74
+ def metadata(self) -> ConnectionMetadata | None:
75
+ """
76
+ returns the metadata of this connection relation
77
+ """
78
+ if not self.connections:
79
+ return None
80
+
81
+ # get all unique metadata objects
82
+ existing_metadata = list({elem.metadata for elem in self.connections if elem})
83
+ if len(existing_metadata) > 1:
84
+ raise ValueError(f'different metadata detected: `{existing_metadata}`')
85
+ return existing_metadata[0]
86
+
87
+ @abstractmethod
88
+ def get_simplified_relation(self) -> OrConnectionRelation:
89
+ """
90
+ This method simplifies the connection relation. It will convert every possible relation into an
91
+ OrConnectionRelation[Connection, AndConnectionRelation].
92
+ """
93
+
94
+ def clone(self):
95
+ """
96
+ clones this connection relation
97
+ """
98
+ return self.__class__(*[cnn.clone() for cnn in self._connections])
99
+
100
+ def append(self, connection: Union[Type[Connection], Connection, BaseConnectionRelationT]):
101
+ """
102
+ appends a component to this connection relation
103
+ """
104
+ from ..connection import Connection # pylint: disable=import-outside-toplevel
105
+
106
+ if isinstance(connection, type):
107
+ if issubclass(connection, Connection):
108
+ connection = connection()
109
+ else:
110
+ raise TypeError(f'can not append a element `{connection.__name__}`')
111
+ if not isinstance(connection, Connection) and not isinstance(connection, BaseConnectionRelation):
112
+ raise TypeError('the element that should be appended to the relation needs to be a Connection or another '
113
+ 'relation')
114
+ if isinstance(connection, self.__class__):
115
+ # directly add children (because it has the same type)
116
+ for cur_inner_connection in connection.connections:
117
+ self._connections.append(cur_inner_connection)
118
+ else:
119
+ self._connections.append(connection)
120
+
121
+ def extend(self, relation: BaseConnectionRelationT):
122
+ """
123
+ extends the relation with the elements of provided relation
124
+ """
125
+ if not isinstance(relation, self.__class__):
126
+ raise TypeError(f'the relation with the elements that should be appended needs to be from same type (is '
127
+ f'`{relation.__class__}` | expected `{self.__class__}`)')
128
+ for cur_elem in relation.connections:
129
+ self.append(cur_elem)
130
+
131
+ def set_metadata_for_all_subitems(self, metadata: Union[Dict[str, Union[Device, str]], None]):
132
+ """
133
+ This method sets the metadata for all existing Connection items in this element.
134
+
135
+ :param metadata: the metadata that should be set (if the value is explicitly set to None, it removes the
136
+ metadata from every item)
137
+ """
138
+ for cur_cnn in self._connections:
139
+ cur_cnn.set_metadata_for_all_subitems(metadata)
140
+
141
+ def get_all_used_connection_types(self) -> List[Type[Connection]]:
142
+ """
143
+ This method returns all available connection types, that are used within this connection relation.
144
+ """
145
+ from ..connection import Connection # pylint: disable=import-outside-toplevel
146
+
147
+ result = []
148
+ for cur_inner_elem in self._connections:
149
+ if isinstance(cur_inner_elem, Connection):
150
+ result.append(cur_inner_elem.__class__)
151
+ elif isinstance(cur_inner_elem, BaseConnectionRelation):
152
+ result.extend(cur_inner_elem.get_all_used_connection_types())
153
+ else:
154
+ raise TypeError(f'unexpected type for inner item `{cur_inner_elem.__class__.__name__}`')
155
+ return list(set(result))
156
+
157
+ @abstractmethod
158
+ def get_tree_str(self) -> str:
159
+ """
160
+ returns the tree string for this part of the connection tree
161
+ """
162
+
163
+ def is_resolved(self) -> bool:
164
+ """
165
+ returns whether this connection relation is part of a single connection
166
+ """
167
+ if len(self._connections) == 0:
168
+ return True
169
+ return min(cnn.is_resolved() for cnn in self._connections)
170
+
171
+ def get_resolved(self) -> BaseConnectionRelationT:
172
+ """
173
+ This method returns a resolved Connection Tree. This means that it convert the based_on references so that
174
+ every based on connection is a direct parent of the current element. It secures that there are no undefined
175
+ connection layers between an object and the given parent.
176
+ """
177
+ return self.__class__(*[cnn.get_resolved() for cnn in self._connections])
178
+
179
+ @abstractmethod
180
+ def is_single(self) -> bool:
181
+ """
182
+ returns whether this connection relation is part of a single connection
183
+ """
184
+
185
+ @abstractmethod
186
+ def get_singles(self) -> List[Connection]:
187
+ """
188
+ returns the single connections of all components of this connection relation
189
+ """
190
+
191
+ def cnn_are_in_other(self, other: BaseConnectionRelationT, ignore_metadata: bool=False) -> bool:
192
+ """
193
+ This method validates that the elements from this relation are contained in the other relation. Elements matches
194
+ with each other, as soon as they are equal.
195
+
196
+ .. note::
197
+ This method only checks that every single connections from the first element is contained in the second too.
198
+ It does not check the other direction. If you want to validate this, you need to call this method with both
199
+ possibilities.
200
+
201
+ :param other: the first list of connections
202
+ :param other: the other relation object
203
+ :param ignore_metadata: True, if the metadata of the single connections should be ignored
204
+ :return: True in case that every connection of the first list is equal with one in the second list, otherwise
205
+ False
206
+ """
207
+ for cur_cnn in self._connections:
208
+ found_equal = False
209
+ for cur_other_cnn in other.connections:
210
+ if cur_cnn.equal_with(cur_other_cnn, ignore_metadata=ignore_metadata):
211
+ found_equal = True
212
+ break
213
+ if not found_equal:
214
+ return False
215
+ return True
216
+
217
+ @abstractmethod
218
+ def cut_into_all_possible_subtree_branches(self):
219
+ """
220
+ This method returns a list of all possible connection tree branches. A branch is a single connection, while
221
+ this method returns a list of all possible singles where every single connection has this connection as head.
222
+ """
223
+
224
+ def equal_with(self, other_relation: BaseConnectionRelationT, ignore_metadata=False) -> bool:
225
+ """
226
+ This method returns True if the current relation matches with the other relation object. It always converts the
227
+ elements to a resolved version and checks if both of them are exactly the same.
228
+
229
+ .. note::
230
+ Note that both connection relations need to be resolved.
231
+
232
+ .. note::
233
+ Note that both Connection objects have to have the same ending parent elements. Only the order of the inner
234
+ connection elements are irrelevant.
235
+
236
+ :param other_relation: the other connection relation (needs to be the same type)
237
+
238
+ :param ignore_metadata: if this value is true the method ignores the metadata
239
+
240
+ :return: returns True if both elements are same
241
+ """
242
+ if self.__class__ != other_relation.__class__:
243
+ return False
244
+
245
+ if not self.is_resolved():
246
+ raise ValueError('can not execute method, because connection relation is not resolved')
247
+ if not other_relation.is_resolved():
248
+ raise ValueError('can not execute method, because other connection relation is not resolved')
249
+
250
+ # check inner connection elements (if they match all in both directions)
251
+ return (self.cnn_are_in_other(other_relation, ignore_metadata=ignore_metadata)
252
+ and other_relation.cnn_are_in_other(self, ignore_metadata=ignore_metadata))
253
+
254
+ @abstractmethod
255
+ def contained_in(self, other_conn: Union[Connection, BaseConnectionRelationT], ignore_metadata=False) -> bool:
256
+ """
257
+ This method helps to find out whether this connection relation fits within a given connection tree. A connection
258
+ object is a certain part of the large connection tree that Balder has at its disposal. This method checks
259
+ whether a possibility of this connection tree fits in one possibility of the given connection tree.
260
+
261
+ .. note::
262
+ The method returns true if one single connection of this object fits in another single connection that is
263
+ given by `other_conn`.
264
+
265
+ :param other_conn: the other connection
266
+
267
+ :param ignore_metadata: if this value is true the method ignores the metadata
268
+
269
+ :return: true if the self object is contained in the `other_conn`, otherwise false
270
+ """
@@ -0,0 +1,65 @@
1
+ from __future__ import annotations
2
+ from typing import List, Union, TYPE_CHECKING
3
+
4
+ from .base_connection_relation import BaseConnectionRelation, BaseConnectionRelationT
5
+
6
+ if TYPE_CHECKING:
7
+ from ..connection import Connection
8
+
9
+
10
+ class OrConnectionRelation(BaseConnectionRelation):
11
+ """
12
+ describes a OR relation between connections
13
+ """
14
+
15
+ def get_tree_str(self) -> str:
16
+ based_on_strings = [cur_elem.get_tree_str() for cur_elem in self._connections]
17
+ return f"({' & '.join(based_on_strings)})"
18
+
19
+ def get_simplified_relation(self) -> OrConnectionRelation:
20
+ from ..connection import Connection # pylint: disable=import-outside-toplevel
21
+
22
+ result = OrConnectionRelation()
23
+ for cur_inner_elem in self.connections:
24
+ if isinstance(cur_inner_elem, Connection):
25
+ # that is fine - just add it again
26
+ result.append(cur_inner_elem)
27
+ elif isinstance(cur_inner_elem, BaseConnectionRelation):
28
+ # simplify this AND/OR relation and add the items of it
29
+ # (the simplified version is always an OR relation!)
30
+ result.extend(cur_inner_elem.get_simplified_relation())
31
+ else:
32
+ raise TypeError(f'detect unexpected element type `{cur_inner_elem.__class__}` in inner elements')
33
+ return result
34
+
35
+ def is_single(self) -> bool:
36
+ if len(self.connections) == 0:
37
+ return True
38
+ if len(self.connections) == 1:
39
+ return self.connections[0].is_single()
40
+ return False
41
+
42
+ def get_singles(self) -> List[Connection]:
43
+ result = []
44
+ for elem in self.connections:
45
+ result.extend(elem.get_singles())
46
+ return result
47
+
48
+ def cut_into_all_possible_subtree_branches(self) -> List[OrConnectionRelation]:
49
+ if not self.is_single():
50
+ raise ValueError('can not execute method, because relation is not single')
51
+ result = [OrConnectionRelation()]
52
+ if len(self.connections) == 0:
53
+ return result
54
+ result.extend(self.connections[0].cut_into_all_possible_subtree_branches())
55
+ return result
56
+
57
+ def contained_in(self, other_conn: Union[Connection, BaseConnectionRelationT], ignore_metadata=False) -> bool:
58
+ if not self.is_resolved():
59
+ raise ValueError('can not execute method, because connection relation is not resolved')
60
+ if not other_conn.is_resolved():
61
+ raise ValueError('can not execute method, because other connection relation is not resolved')
62
+ for cur_inner_cnn in self._connections:
63
+ if cur_inner_cnn.contained_in(other_conn, ignore_metadata=ignore_metadata):
64
+ return True
65
+ return False
_balder/collector.py CHANGED
@@ -27,7 +27,6 @@ from _balder.utils import get_scenario_inheritance_list_of
27
27
 
28
28
  if TYPE_CHECKING:
29
29
  from _balder.plugin_manager import PluginManager
30
- ConnectionType = Union[Type[Connection], Connection, Tuple[Union[Type[Connection], Connection]]]
31
30
 
32
31
  logger = logging.getLogger(__file__)
33
32
 
@@ -45,7 +44,7 @@ class Collector:
45
44
  # with the method `rework_method_variation_decorators()`
46
45
  _possible_method_variations: Dict[
47
46
  Callable,
48
- List[Tuple[Union[Type[VDevice], str], Union[ConnectionType, List[ConnectionType]]]]
47
+ List[Tuple[Union[Type[VDevice], str], Connection]]
49
48
  ] = {}
50
49
 
51
50
  # this static attribute will be managed by the decorator `@parametrize(..)`. It holds all functions/methods that
@@ -85,7 +84,7 @@ class Collector:
85
84
  def register_possible_method_variation(
86
85
  meth: Callable,
87
86
  vdevice: Union[Type[VDevice], str],
88
- with_connections: Union[ConnectionType, List[ConnectionType]]):
87
+ with_connections: Connection):
89
88
  """
90
89
  allows to register a new method variation - used by decorator `@balder.for_vdevice()`
91
90
 
@@ -509,11 +508,6 @@ class Collector:
509
508
  if name not in owner_for_vdevice.keys():
510
509
  owner_for_vdevice[name] = {}
511
510
 
512
- cur_decorator_cleaned_cnns = []
513
- for cur_cnn in cur_decorator_with_connections:
514
- cur_cnn = cur_cnn() if isinstance(cur_cnn, type) and issubclass(cur_cnn, Connection) else cur_cnn
515
- cur_decorator_cleaned_cnns.append(cur_cnn)
516
-
517
511
  if cur_fn in owner_for_vdevice[name].keys():
518
512
  old_dict = owner_for_vdevice[name][cur_fn]
519
513
  if cur_decorator_vdevice in old_dict.keys():
@@ -521,10 +515,10 @@ class Collector:
521
515
  f'`{cur_decorator_vdevice}` at method `{name}` of class '
522
516
  f'`{owner.__name__}` ')
523
517
 
524
- old_dict[cur_decorator_vdevice] = cur_decorator_cleaned_cnns
518
+ old_dict[cur_decorator_vdevice] = cur_decorator_with_connections
525
519
  owner_for_vdevice[name][cur_fn] = old_dict
526
520
  else:
527
- new_dict = {cur_decorator_vdevice: cur_decorator_cleaned_cnns}
521
+ new_dict = {cur_decorator_vdevice: cur_decorator_with_connections}
528
522
  owner_for_vdevice[name][cur_fn] = new_dict
529
523
 
530
524
  def owner_wrapper(the_owner_of_this_method, the_name, wrap_fn):
@@ -570,12 +564,12 @@ class Collector:
570
564
  for cur_field_name, cur_value_list in cur_decorator_data_dict.items():
571
565
  if isinstance(cur_value_list, FeatureAccessSelector):
572
566
  # make sure that all parameters exist in test method parametrization
573
- for cur_value_parameter in cur_value_list.parameters.values():
574
- if isinstance(cur_value_parameter, Parameter):
575
- if cur_value_parameter.name not in cur_decorator_data_dict.keys():
576
- raise AttributeError(f'can not find attribute `{cur_value_parameter.name}` that is '
577
- f'used in parametrization for attribute `{cur_field_name}` in '
578
- f'test method `{cur_fn.__qualname__}`')
567
+ value_parameters = filter(lambda p: isinstance(p, Parameter), cur_value_list.parameters.values())
568
+ for cur_value_parameter in value_parameters:
569
+ if cur_value_parameter.name not in cur_decorator_data_dict.keys():
570
+ raise AttributeError(
571
+ f'can not find attribute `{cur_value_parameter.name}` that is used in parametrization '
572
+ f'for attribute `{cur_field_name}` in test method `{cur_fn.__qualname__}`')
579
573
  if cur_field_name not in args_of_cur_fn:
580
574
  raise ValueError(f'the argument `{cur_field_name}` does not exist in test method '
581
575
  f'`{cur_fn.__qualname__}`')