baldertest 0.1.0b6__py3-none-any.whl → 0.1.0b8__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 CHANGED
@@ -1,4 +1,16 @@
1
1
  # file generated by setuptools_scm
2
2
  # don't change, don't track in version control
3
- __version__ = version = '0.1.0b6'
3
+ TYPE_CHECKING = False
4
+ if TYPE_CHECKING:
5
+ from typing import Tuple, Union
6
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
7
+ else:
8
+ VERSION_TUPLE = object
9
+
10
+ version: str
11
+ __version__: str
12
+ __version_tuple__: VERSION_TUPLE
13
+ version_tuple: VERSION_TUPLE
14
+
15
+ __version__ = version = '0.1.0b8'
4
16
  __version_tuple__ = version_tuple = (0, 1, 0)
_balder/balder_session.py CHANGED
@@ -21,7 +21,7 @@ if TYPE_CHECKING:
21
21
  from _balder.scenario import Scenario
22
22
  from _balder.connection import Connection
23
23
 
24
-
24
+ # pylint: disable-next=too-many-instance-attributes
25
25
  class BalderSession:
26
26
  """
27
27
  This is the main balder executable object. It contains all information about the current session and executes the
@@ -60,6 +60,8 @@ class BalderSession:
60
60
  self.collect_only: Union[bool, None] = None
61
61
  #: specifies that the tests should only be collected and resolved but not executed
62
62
  self.resolve_only: Union[bool, None] = None
63
+ #: specifies that all discarded variations should be printed (with information why they were discarded)
64
+ self.show_discarded: Union[bool, None] = None
63
65
  #: contains a number of :class:`Setup` class strings that should only be considered for the execution
64
66
  self.only_with_setup: Union[List[str], None] = None
65
67
  #: contains a number of :class:`Scenario` class strings that should only be considered for the execution
@@ -246,6 +248,10 @@ class BalderSession:
246
248
  '--resolve-only', action='store_true',
247
249
  help="specifies that the tests are only collected and resolved but not executed")
248
250
 
251
+ self.cmd_arg_parser.add_argument(
252
+ '--show-discarded', action='store_true',
253
+ help="specifies that all discarded variations should be printed (with information why they were discarded)")
254
+
249
255
  self.cmd_arg_parser.add_argument(
250
256
  '--only-with-setup', nargs="*",
251
257
  help="defines a number of Setup classes which should only be considered for the execution")
@@ -262,14 +268,10 @@ class BalderSession:
262
268
 
263
269
  self.parsed_args = self.cmd_arg_parser.parse_args(self._alt_cmd_args)
264
270
 
265
- if self.parsed_args.only_with_scenario is not None:
266
- raise NotImplementedError("the parameter `--only-with-scenario` is not supported in this version of balder")
267
- if self.parsed_args.only_with_setup is not None:
268
- raise NotImplementedError("the parameter `--only-with-setup` is not supported in this version of balder")
269
-
270
271
  self.working_dir = self.parsed_args.working_dir
271
272
  self.collect_only = self.parsed_args.collect_only
272
273
  self.resolve_only = self.parsed_args.resolve_only
274
+ self.show_discarded = self.parsed_args.show_discarded
273
275
  self.only_with_setup = self.parsed_args.only_with_setup
274
276
  self.only_with_scenario = self.parsed_args.only_with_scenario
275
277
  self.force_covered_by_duplicates = self.parsed_args.force_covered_by_duplicates
@@ -278,7 +280,10 @@ class BalderSession:
278
280
  """
279
281
  This method collects all data.
280
282
  """
281
- self.collector.collect(plugin_manager=self.plugin_manager)
283
+ self.collector.collect(
284
+ plugin_manager=self.plugin_manager,
285
+ scenario_filter_patterns=self.only_with_scenario,
286
+ setup_filter_patterns=self.only_with_setup)
282
287
 
283
288
  def solve(self):
284
289
  """
@@ -295,15 +300,10 @@ class BalderSession:
295
300
  .. note::
296
301
  Note that the method creates an :class:`ExecutorTree`, that hasn't to be completely resolved yet.
297
302
  """
298
- self.executor_tree = self.solver.get_executor_tree(plugin_manager=self.plugin_manager)
303
+ self.executor_tree = self.solver.get_executor_tree(plugin_manager=self.plugin_manager,
304
+ add_discarded=self.show_discarded)
299
305
  self.plugin_manager.execute_filter_executor_tree(executor_tree=self.executor_tree)
300
306
 
301
- def execute_executor_tree(self):
302
- """
303
- This method executes the :class:`ExecutorTree`.
304
- """
305
- self.executor_tree.execute()
306
-
307
307
  def run(self):
308
308
  """
309
309
  This method executes the whole session
@@ -326,11 +326,14 @@ class BalderSession:
326
326
  if not self.collect_only:
327
327
  self.solve()
328
328
  self.create_executor_tree()
329
- print(f" resolve them to {len(self.executor_tree.get_all_variation_executors())} mapping candidates")
329
+ count_valid = len(self.executor_tree.get_all_variation_executors())
330
+ count_discarded = len(self.executor_tree.get_all_variation_executors(return_discarded=True)) - count_valid
331
+ addon_text = f" ({count_discarded} discarded)" if self.show_discarded else ""
332
+ print(f" resolve them to {count_valid} valid variations{addon_text}")
330
333
  print("")
331
334
  if not self.resolve_only:
332
- self.execute_executor_tree()
335
+ self.executor_tree.execute(show_discarded=self.show_discarded)
333
336
  else:
334
- self.executor_tree.print_tree()
337
+ self.executor_tree.print_tree(show_discarded=self.show_discarded)
335
338
 
336
339
  self.plugin_manager.execute_session_finished(self.executor_tree)
_balder/collector.py CHANGED
@@ -1,10 +1,11 @@
1
1
  from __future__ import annotations
2
- from typing import List, Type, Union, Dict, Callable, Tuple, TYPE_CHECKING
2
+ from typing import List, Type, Union, Dict, Callable, Tuple, TYPE_CHECKING, Any
3
3
 
4
4
  import os
5
5
  import sys
6
6
  import types
7
7
  import logging
8
+ import fnmatch
8
9
  import inspect
9
10
  import pathlib
10
11
  import functools
@@ -677,27 +678,58 @@ class Collector:
677
678
  for cur_setup in self.all_setups:
678
679
  SetupController.get_for(cur_setup).validate_feature_possibility()
679
680
 
680
- def collect(self, plugin_manager: PluginManager):
681
+ def _filter_paths_after_allowed_paths(self, paths: List[pathlib.Path], filter_patterns: List[str]) -> List[pathlib.Path]:
682
+ """
683
+ This method filters the given list of filepaths for the given filter_patterns. It returns a list with all
684
+ remaining paths that are mathing the filter statements in `filter_paths`.
685
+
686
+ Patterns are the same like for `fnmatch <https://docs.python.org/3/library/fnmatch.html#module-fnmatch>`_.
687
+
688
+ :param paths: a list of all filepaths that should be filtered
689
+ :param filter_patterns: a list of relative filter patterns that should be applied on the files of the classes
690
+ :return: returns all classes of items that match any of the given patterns.
691
+ """
692
+ remaining = []
693
+
694
+ for cur_pattern in filter_patterns:
695
+ remaining += [cur_abs_path for cur_abs_path in paths
696
+ if fnmatch.fnmatch(str(cur_abs_path.relative_to(self.working_dir)), cur_pattern)]
697
+ return list(set(remaining))
698
+
699
+
700
+ def collect(self, plugin_manager: PluginManager, scenario_filter_patterns: Union[List[str], None],
701
+ setup_filter_patterns: Union[List[str], None]):
681
702
  """
682
703
  This method manages the entire collection process.
683
704
 
684
705
  :param plugin_manager: contains the reference to the used plugin manager
706
+ :param scenario_filter_patterns: a list with filter patterns for scenarios
707
+ :param setup_filter_patterns: a list with filter patterns for setups
685
708
  """
686
709
  # load all py files
687
710
  self.load_balderglob_py_file()
688
711
  self._all_py_files = self.get_all_py_files()
689
712
  self._all_py_files = plugin_manager.execute_modify_collected_pyfiles(self._all_py_files)
690
713
 
714
+ if scenario_filter_patterns:
715
+ all_scenario_filepaths = self._filter_paths_after_allowed_paths(
716
+ paths=self._all_py_files, filter_patterns=scenario_filter_patterns)
717
+ else:
718
+ all_scenario_filepaths = self._all_py_files
719
+ if setup_filter_patterns:
720
+ all_setup_filepaths = self._filter_paths_after_allowed_paths(
721
+ paths=self._all_py_files, filter_patterns=setup_filter_patterns)
722
+ else:
723
+ all_setup_filepaths = self._all_py_files
724
+
691
725
  # collect all `Connection` classes (has to be first, because scenarios/setups can use them)
692
726
  self.load_all_connection_classes(py_file_paths=self._all_py_files)
693
727
  self._all_connections = self.get_all_connection_classes()
694
728
 
695
729
  # collect all `Scenario` classes
696
- self._all_scenarios = self.get_all_scenario_classes(
697
- py_file_paths=self._all_py_files, filter_abstracts=True)
730
+ self._all_scenarios = self.get_all_scenario_classes(py_file_paths=all_scenario_filepaths, filter_abstracts=True)
698
731
  # collect all `Setup` classes
699
- self._all_setups = self.get_all_setup_classes(
700
- py_file_paths=self._all_py_files, filter_abstracts=True)
732
+ self._all_setups = self.get_all_setup_classes(py_file_paths=all_setup_filepaths, filter_abstracts=True)
701
733
 
702
734
  self._all_scenarios, self._all_setups = plugin_manager.execute_collected_classes(
703
735
  scenarios=self._all_scenarios, setups=self._all_setups)
_balder/connection.py CHANGED
@@ -88,6 +88,17 @@ class Connection:
88
88
  all_hashes += hash(cur_child)
89
89
  return hash(all_hashes)
90
90
 
91
+ def clone_without_based_on_elements(self) -> Connection:
92
+ """
93
+ This method returns a copied version of this element, while all `_based_on_connections` are removed (the copied
94
+ element has an empty list here).
95
+
96
+ :return: a python copied object of this item
97
+ """
98
+ self_copy = copy.copy(self)
99
+ self_copy._based_on_connections = [] # pylint: disable=protected-access
100
+ return self_copy
101
+
91
102
  def clone(self) -> Connection:
92
103
  """
93
104
  This method returns an exact clone of this connection. For this clone every inner connection object will be
@@ -95,18 +106,15 @@ class Connection:
95
106
  same for this object and the clone). The method will make a normal copy for every connection object in the
96
107
  `_based_on_elements` list.
97
108
  """
98
- self_copy = copy.copy(self)
99
- self_copy._based_on_connections = []
109
+ self_copy = self.clone_without_based_on_elements()
100
110
 
101
111
  for cur_based_on in self._based_on_connections:
102
112
  if isinstance(cur_based_on, tuple):
103
- cloned_tuple = ()
104
- for cur_tuple_element in cur_based_on:
105
- cloned_tuple = cloned_tuple + (cur_tuple_element.clone(), )
106
- self_copy._based_on_connections.append(cloned_tuple)
113
+ cloned_tuple = tuple([cur_tuple_element.clone() for cur_tuple_element in cur_based_on])
114
+ self_copy.append_to_based_on(cloned_tuple)
107
115
  elif isinstance(cur_based_on, Connection):
108
116
  cloned_cur_based_on = cur_based_on.clone()
109
- self_copy._based_on_connections.append(cloned_cur_based_on)
117
+ self_copy.append_to_based_on(cloned_cur_based_on)
110
118
  else:
111
119
  raise TypeError('based on element is not from valid type')
112
120
  return self_copy
@@ -132,22 +140,21 @@ class Connection:
132
140
  for cur_tuple in Connection.__cut_tuple_from_only_parent_to_child(elem.based_on_elements[0]):
133
141
  # for this we do not have to use `clone`, because we copy the base object with `copy.copy` and
134
142
  # completely replace the `_based_on_connections` by our own
135
- copied_conn = copy.copy(elem)
136
- copied_conn._based_on_connections = [cur_tuple]
143
+ copied_conn = elem.clone_without_based_on_elements()
144
+ copied_conn.append_to_based_on(cur_tuple)
137
145
  all_pieces.append(copied_conn)
138
146
  return all_pieces
139
147
  # if this element is the last element with a parent -> copy it and remove the parent, return it
140
148
  if len(elem.based_on_elements[0].based_on_elements) == 0:
141
- new_elem = copy.copy(elem)
142
- new_elem._based_on_connections = []
149
+ new_elem = elem.clone_without_based_on_elements()
143
150
  all_pieces.append(new_elem)
144
151
  return all_pieces
145
152
  # otherwise, the current item has grandparents, so call the method recursively for parents and add a copy of
146
153
  # this object as child
147
154
  all_possible_parents = Connection.__cut_conn_from_only_parent_to_child(elem.based_on_elements[0])
148
155
  for cur_parent in all_possible_parents:
149
- copied_conn = copy.copy(elem)
150
- copied_conn._based_on_connections = [cur_parent]
156
+ copied_conn = elem.clone_without_based_on_elements()
157
+ copied_conn.append_to_based_on(cur_parent)
151
158
  all_pieces.append(copied_conn)
152
159
  return all_pieces
153
160
 
@@ -165,15 +172,12 @@ class Connection:
165
172
  Note that this method also returns every possible ordering
166
173
  """
167
174
 
168
- tuple_with_all_possibilities = ()
169
- for cur_tuple_item in elem:
170
- tuple_with_all_possibilities += (Connection.__cut_conn_from_only_parent_to_child(cur_tuple_item), )
175
+ tuple_with_all_possibilities = (
176
+ tuple([Connection.__cut_conn_from_only_parent_to_child(cur_tuple_item) for cur_tuple_item in elem]))
171
177
 
172
178
  cloned_tuple_list = []
173
179
  for cur_tuple in list(itertools.product(*tuple_with_all_possibilities)):
174
- cloned_tuple: Tuple[Connection] = ()
175
- for cur_tuple_item in cur_tuple:
176
- cloned_tuple += (cur_tuple_item.clone(), )
180
+ cloned_tuple = tuple([cur_tuple_item.clone() for cur_tuple_item in cur_tuple])
177
181
  cloned_tuple_list.append(cloned_tuple)
178
182
  return cloned_tuple_list
179
183
 
@@ -215,11 +219,9 @@ class Connection:
215
219
  while len(cur_sub_other_single) > 0:
216
220
  if isinstance(cur_sub_other_single[0], tuple):
217
221
  # found a tuple -> check if length does match
218
- if len(cur_sub_other_single[0]) >= len(cur_tuple_single):
219
- # length does match
220
- if tuple_is_contained_in_other(
221
- cur_tuple_single, contained_in_tuple=cur_sub_other_single[0]):
222
- return True
222
+ if len(cur_sub_other_single[0]) >= len(cur_tuple_single) and tuple_is_contained_in_other(
223
+ cur_tuple_single, contained_in_tuple=cur_sub_other_single[0]):
224
+ return True
223
225
  # otherwise, this complete element is not possible - skip this single!
224
226
  break
225
227
  cur_sub_other_single = cur_sub_other_single[0].based_on_elements
@@ -371,6 +373,75 @@ class Connection:
371
373
  this_instance.append_to_based_on(*new_items)
372
374
  return this_instance
373
375
 
376
+ @classmethod
377
+ def check_equal_connections_are_in(
378
+ cls, cnns_from: List[Connection], are_in_cnns_from: List[Connection], ignore_metadata: bool=False) -> bool:
379
+ """
380
+ This method validates that the elements from the first list are contained (are equal) with one of the elements
381
+ in the second list.
382
+
383
+ .. note::
384
+ This method only checks that every single connections from the first element is contained in the second too.
385
+ It does not check the other direction. If you want to validate this, you need to call this method with both
386
+ possibilities.
387
+
388
+ :param cnns_from: the first list of connections
389
+ :param are_in_cnns_from: the second list of connection
390
+ :param ignore_metadata: True, if the metadata of the single connections should be ignored
391
+ :return: True in case that every connection of the first list is equal with one in the second list, otherwise
392
+ False
393
+ """
394
+ for cur_cnn in cnns_from:
395
+ found_equal = False
396
+ for cur_other_cnn in are_in_cnns_from:
397
+ if cur_cnn.equal_with(cur_other_cnn, ignore_metadata=ignore_metadata):
398
+ found_equal = True
399
+ break
400
+ if not found_equal:
401
+ return False
402
+ return True
403
+
404
+ @classmethod
405
+ def check_equal_connection_tuples_are_in(
406
+ cls, tuples_from: List[Tuple[Connection]], are_in_tuples_from: List[Tuple[Connection]],
407
+ ignore_metadata: bool=False) -> bool:
408
+ """
409
+ This method validates that the connection tuples from the first list are contained (are equal) within the tuple
410
+ elements of the second list.
411
+
412
+ .. note::
413
+ This method only checks that every single tuple from the first element is contained in the second list.
414
+ It does not check the other direction. If you want to validate this, you need to call this method with both
415
+ possibilities.
416
+
417
+ :param tuples_from: the first list of connection-tuples
418
+ :param are_in_tuples_from: the second list of connection-tuples
419
+ :param ignore_metadata: True, if the metadata of the single connections should be ignored
420
+ :return: True in case that every tuple connections of the first list is equal with one tuple in the second,
421
+ otherwise False
422
+ """
423
+ for cur_search_tuple in tuples_from:
424
+ found_match_for_cur_search_tuple = False
425
+ # go through each unmatched other tuple
426
+ for cur_other_tuple in are_in_tuples_from:
427
+ cur_search_tuple_is_completely_in_other_tuple = True
428
+ for cur_search_tuple_elem in cur_search_tuple:
429
+ fount_it = False
430
+ for cur_other_tuple_elem in cur_other_tuple:
431
+ if cur_search_tuple_elem.equal_with(cur_other_tuple_elem, ignore_metadata=ignore_metadata):
432
+ fount_it = True
433
+ break
434
+ if not fount_it:
435
+ cur_search_tuple_is_completely_in_other_tuple = False
436
+ break
437
+ if cur_search_tuple_is_completely_in_other_tuple:
438
+ # here we have no match
439
+ found_match_for_cur_search_tuple = True
440
+ break
441
+ if not found_match_for_cur_search_tuple:
442
+ return False
443
+ return True
444
+
374
445
  # ---------------------------------- PROPERTIES --------------------------------------------------------------------
375
446
 
376
447
  @property
@@ -484,7 +555,7 @@ class Connection:
484
555
 
485
556
  return False
486
557
 
487
- def _get_intersection_with_other_single(self, other_conn: Union[Connection, Tuple[Connection]]) \
558
+ def get_intersection_with_other_single(self, other_conn: Union[Connection, Tuple[Connection]]) \
488
559
  -> List[Connection, Tuple[Connection]]:
489
560
  """
490
561
  A helper method that returns an intersection between the two connections (self and the given one).
@@ -519,27 +590,23 @@ class Connection:
519
590
 
520
591
  intersection = []
521
592
 
593
+ def determine_for(pieces: List[Union[Connection, Tuple[Connection]]], in_other_cnn: Connection):
594
+ for cur_piece in pieces:
595
+ if isinstance(cur_piece, Connection):
596
+ if cur_piece.contained_in(in_other_cnn, ignore_metadata=True):
597
+ intersection.append(cur_piece)
598
+ else:
599
+ # isinstance of tuple
600
+ if Connection.check_if_tuple_contained_in_connection(cur_piece, in_other_cnn):
601
+ intersection.append(cur_piece)
602
+
522
603
  #: check if some sub elements of self connection are contained in `other_conn`
523
604
  self_pieces = self.cut_into_all_possible_subtrees()
524
- for cur_piece in self_pieces:
525
- if isinstance(cur_piece, Connection):
526
- if cur_piece.contained_in(other_conn, ignore_metadata=True):
527
- intersection.append(cur_piece)
528
- else:
529
- # isinstance of tuple
530
- if Connection.check_if_tuple_contained_in_connection(cur_piece, other_conn):
531
- intersection.append(cur_piece)
605
+ determine_for(pieces=self_pieces, in_other_cnn=other_conn)
532
606
 
533
607
  #: check if some sub elements of `other_conn` are contained in self connection
534
608
  other_pieces = other_conn.cut_into_all_possible_subtrees()
535
- for cur_piece in other_pieces:
536
- if isinstance(cur_piece, Connection):
537
- if cur_piece.contained_in(self, ignore_metadata=True):
538
- intersection.append(cur_piece)
539
- else:
540
- # isinstance of tuple
541
- if Connection.check_if_tuple_contained_in_connection(cur_piece, self):
542
- intersection.append(cur_piece)
609
+ determine_for(pieces=other_pieces, in_other_cnn=self)
543
610
 
544
611
  #: filter all duplicated (and contained in each other) connections
545
612
  intersection_without_duplicates = []
@@ -779,19 +846,19 @@ class Connection:
779
846
  method returns this child connection directly without any :class:`Connection` container otherwise the
780
847
  :class:`Connection` container class with all resolved child classes will be returned.
781
848
  """
782
- copied_base = copy.copy(self)
783
- if not copied_base.is_resolved():
784
- copied_base._based_on_connections = []
849
+ if self.is_resolved():
850
+ copied_base = self.clone()
851
+ else:
852
+ copied_base = self.clone_without_based_on_elements()
853
+
785
854
  if self.__class__ == Connection:
786
855
  # the base object is a container Connection - iterate over the items and determine the values for them
787
856
  for cur_item in self.based_on_elements.copy():
788
857
  if isinstance(cur_item, tuple):
789
- new_tuple = ()
790
- for cur_tuple_item in cur_item:
791
- new_tuple += (cur_tuple_item.get_resolved(), )
792
- copied_base._based_on_connections.append(new_tuple)
858
+ new_tuple = tuple([cur_tuple_item.get_resolved() for cur_tuple_item in cur_item])
859
+ copied_base.append_to_based_on(new_tuple)
793
860
  else:
794
- copied_base._based_on_connections.append(cur_item.get_resolved())
861
+ copied_base.append_to_based_on(cur_item.get_resolved())
795
862
  else:
796
863
  for next_higher_parent in self.based_on_elements:
797
864
  if isinstance(next_higher_parent, tuple):
@@ -811,21 +878,20 @@ class Connection:
811
878
  # resolve the opportunities and create multiple possible tuples where all elements are direct
812
879
  # parents
813
880
  for cur_possibility in itertools.product(*direct_ancestors_tuple):
814
- new_child_tuple = ()
815
- for cur_tuple_element in cur_possibility:
816
- new_child_tuple += (cur_tuple_element.get_resolved(), )
817
- copied_base._based_on_connections.append(new_child_tuple)
881
+ new_child_tuple = (
882
+ tuple([cur_tuple_element.get_resolved() for cur_tuple_element in cur_possibility]))
883
+ copied_base.append_to_based_on(new_child_tuple)
818
884
  else:
819
885
  if next_higher_parent.__class__ in self.__class__.get_parents():
820
886
  # is already a direct parent
821
- copied_base._based_on_connections.append(next_higher_parent.get_resolved())
887
+ copied_base.append_to_based_on(next_higher_parent.get_resolved())
822
888
  else:
823
889
  # only add the first level of direct parents - deeper will be added by recursively call of
824
890
  # `get_resolved`
825
891
  for cur_self_direct_parent in self.__class__.get_parents():
826
892
  if next_higher_parent.__class__.is_parent_of(cur_self_direct_parent):
827
893
  new_child = cur_self_direct_parent.based_on(next_higher_parent)
828
- copied_base._based_on_connections.append(new_child.get_resolved())
894
+ copied_base.append_to_based_on(new_child.get_resolved())
829
895
  # if it is a connection container, where only one element exists that is no tuple -> return this directly
830
896
  # instead of the container
831
897
  if copied_base.__class__ == Connection and len(copied_base.based_on_elements) == 1 and not \
@@ -865,8 +931,8 @@ class Connection:
865
931
  return [resolved_obj]
866
932
  for cur_child in resolved_obj.based_on_elements:
867
933
  for cur_single_child in cur_child.get_singles():
868
- copied_base = copy.copy(resolved_obj)
869
- copied_base._based_on_connections = [cur_single_child]
934
+ copied_base = resolved_obj.clone_without_based_on_elements()
935
+ copied_base.append_to_based_on(cur_single_child)
870
936
  all_singles.append(copied_base)
871
937
  # convert all tuple objects in a connection object and set metadata of self object
872
938
  cleaned_singles = []
@@ -979,40 +1045,6 @@ class Connection:
979
1045
  resolved_self = self.get_resolved()
980
1046
  resolved_other = other_conn.get_resolved()
981
1047
 
982
- def check_elems_if(elems_from, are_in_elems_from):
983
- for cur_elem in elems_from:
984
- found_equal = False
985
- for cur_other_elem in are_in_elems_from:
986
- if cur_elem.equal_with(cur_other_elem, ignore_metadata=ignore_metadata):
987
- found_equal = True
988
- break
989
- if not found_equal:
990
- return False
991
- return True
992
-
993
- def check_tuples_if(tuples_from, are_in_tuples_from):
994
- for cur_search_tuple in tuples_from:
995
- found_match_for_cur_search_tuple = False
996
- # go through each unmatched other tuple
997
- for cur_other_tuple in are_in_tuples_from:
998
- cur_search_tuple_is_completely_in_other_tuple = True
999
- for cur_search_tuple_elem in cur_search_tuple:
1000
- fount_it = False
1001
- for cur_other_tuple_elem in cur_other_tuple:
1002
- if cur_search_tuple_elem.equal_with(cur_other_tuple_elem, ignore_metadata=ignore_metadata):
1003
- fount_it = True
1004
- break
1005
- if not fount_it:
1006
- cur_search_tuple_is_completely_in_other_tuple = False
1007
- break
1008
- if cur_search_tuple_is_completely_in_other_tuple:
1009
- # here we have no match
1010
- found_match_for_cur_search_tuple = True
1011
- break
1012
- if not found_match_for_cur_search_tuple:
1013
- return False
1014
- return True
1015
-
1016
1048
  self_based_on_elems = [
1017
1049
  cur_elem for cur_elem in resolved_self.based_on_elements if isinstance(cur_elem, Connection)]
1018
1050
  self_based_on_tuples = [
@@ -1023,13 +1055,20 @@ class Connection:
1023
1055
  cur_elem for cur_elem in resolved_other.based_on_elements if isinstance(cur_elem, tuple)]
1024
1056
 
1025
1057
  # check single connection elements (if they match all in both directions)
1026
- if not check_elems_if(elems_from=self_based_on_elems, are_in_elems_from=other_based_on_elems) or \
1027
- not check_elems_if(elems_from=other_based_on_elems, are_in_elems_from=self_based_on_elems):
1058
+ if (not self.__class__.check_equal_connections_are_in(
1059
+ cnns_from=self_based_on_elems, are_in_cnns_from=other_based_on_elems, ignore_metadata=ignore_metadata)
1060
+ or not self.__class__.check_equal_connections_are_in(
1061
+ cnns_from=other_based_on_elems, are_in_cnns_from=self_based_on_elems,
1062
+ ignore_metadata=ignore_metadata)):
1028
1063
  return False
1029
1064
 
1030
1065
  # check tuple connection elements (if they match all in both directions)
1031
- if not check_tuples_if(tuples_from=self_based_on_tuples, are_in_tuples_from=other_based_on_tuples) or \
1032
- not check_tuples_if(tuples_from=other_based_on_tuples, are_in_tuples_from=self_based_on_tuples):
1066
+ if (not self.__class__.check_equal_connection_tuples_are_in(
1067
+ tuples_from=self_based_on_tuples, are_in_tuples_from=other_based_on_tuples,
1068
+ ignore_metadata=ignore_metadata)
1069
+ or not self.__class__.check_equal_connection_tuples_are_in(
1070
+ tuples_from=other_based_on_tuples, are_in_tuples_from=self_based_on_tuples,
1071
+ ignore_metadata=ignore_metadata)):
1033
1072
  return False
1034
1073
 
1035
1074
  return True
@@ -1208,7 +1247,7 @@ class Connection:
1208
1247
  # determine intersections between all of these single components
1209
1248
  for cur_self_conn in self_conn_singles:
1210
1249
  for cur_other_conn in other_conn_singles:
1211
- for cur_intersection in cur_self_conn._get_intersection_with_other_single(cur_other_conn):
1250
+ for cur_intersection in cur_self_conn.get_intersection_with_other_single(cur_other_conn):
1212
1251
  if isinstance(cur_intersection, tuple):
1213
1252
  cur_intersection = Connection.based_on(cur_intersection)
1214
1253
  if cur_intersection not in intersections:
@@ -1236,7 +1275,8 @@ class Connection:
1236
1275
 
1237
1276
  return intersection_filtered[0].clone()
1238
1277
 
1239
- def append_to_based_on(self, *args: Union[Tuple[Union[Type[Connection], Connection]]]) -> None:
1278
+ def append_to_based_on(
1279
+ self, *args: Union[Connection, Type[Connection], Tuple[Union[Type[Connection], Connection]]]) -> None:
1240
1280
  """
1241
1281
  with this method you can extend the internal based_on list with the transferred elements. Any number of
1242
1282
  :meth:`Connection` objects or tuples with :meth:`Connection` objects can be given to this method
_balder/console/balder.py CHANGED
@@ -15,6 +15,7 @@ def console_balder(cmd_args: Optional[List[str]] = None, working_dir: Union[str,
15
15
  _console_balder_debug(cmd_args=cmd_args, working_dir=working_dir)
16
16
 
17
17
 
18
+ # pylint: disable-next=too-many-arguments
18
19
  def _console_balder_debug(cmd_args: Optional[List[str]] = None, working_dir: Union[str, pathlib.Path, None] = None,
19
20
  cb_session_created: Optional[Callable] = None, cb_run_finished: Optional[Callable] = None,
20
21
  cb_balder_exc: Optional[Callable] = None, cb_unexpected_exc: Optional[Callable] = None):
@@ -58,9 +58,8 @@ class BaseDeviceController(Controller, ABC):
58
58
  This method returns the original instanced feature objects of the related device
59
59
  """
60
60
  if self._original_instanced_features is None:
61
- # todo we should use a balder exception here!!
62
- raise EnvironmentError('can not access the original instantiated features before they were set with '
63
- '`save_all_original_instanced_features`')
61
+ raise RuntimeError('can not access the original instantiated features before they were set with '
62
+ '`save_all_original_instanced_features`')
64
63
  return self._original_instanced_features
65
64
 
66
65
  def save_all_original_instanced_features(self):
@@ -117,13 +117,14 @@ class FeatureController(Controller):
117
117
  if self.get_method_based_for_vdevice() is not None:
118
118
  for _, method_dict in self.get_method_based_for_vdevice().items():
119
119
  for _, vdevice_dict in method_dict.items():
120
- if for_vdevice in vdevice_dict.keys():
121
- for cur_cnn in vdevice_dict[for_vdevice]:
122
- if isinstance(cur_cnn, type):
123
- cur_cnn = cur_cnn()
124
- # clean metadata here because this is no connection between real devices
125
- cur_cnn.set_metadata_for_all_subitems(None)
126
- intersection.append(cur_cnn)
120
+ if for_vdevice not in vdevice_dict.keys():
121
+ continue
122
+ for cur_cnn in vdevice_dict[for_vdevice]:
123
+ if isinstance(cur_cnn, type):
124
+ cur_cnn = cur_cnn()
125
+ # clean metadata here because this is no connection between real devices
126
+ cur_cnn.set_metadata_for_all_subitems(None)
127
+ intersection.append(cur_cnn)
127
128
  if len(intersection) == 0:
128
129
  return [Connection()]
129
130
  return intersection
@@ -151,8 +152,7 @@ class FeatureController(Controller):
151
152
  This method returns the absolute calculated class-based-for-vdevice data for this feature.
152
153
  """
153
154
  if self._abs_cls_for_vdevice is None:
154
- # todo we should use a balder exception here!!
155
- raise EnvironmentError('can not access the absolute class based for-vdevices because they are not set')
155
+ raise RuntimeError('can not access the absolute class based for-vdevices because they are not set yet')
156
156
  return self._abs_cls_for_vdevice
157
157
 
158
158
  def set_class_based_for_vdevice(
@@ -718,9 +718,8 @@ class FeatureController(Controller):
718
718
  This method returns the :class:`VDevice` definitions that are the original definitions for this feature.
719
719
  """
720
720
  if self._original_vdevice_definitions is None:
721
- # todo we should use a balder exception here!!
722
- raise EnvironmentError('can not access the original VDevice definitions before they were set with '
723
- '`save_all_current_vdevice_references_as_originals`')
721
+ raise RuntimeError('can not access the original VDevice definitions before they were set with '
722
+ '`save_all_current_vdevice_references_as_originals`')
724
723
  return self._original_vdevice_definitions
725
724
 
726
725
  def save_all_current_vdevice_references_as_originals(self):
_balder/exceptions.py CHANGED
@@ -158,7 +158,7 @@ class IllegalVDeviceMappingError(BalderException):
158
158
  """
159
159
 
160
160
 
161
- class NotApplicableVariationError(BalderException):
161
+ class NotApplicableVariationException(BalderException):
162
162
  """
163
163
  is thrown internally after the current variation is not applicable
164
164
  """