baldertest 0.1.0b7__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.0b7'
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
@@ -268,11 +268,6 @@ class BalderSession:
268
268
 
269
269
  self.parsed_args = self.cmd_arg_parser.parse_args(self._alt_cmd_args)
270
270
 
271
- if self.parsed_args.only_with_scenario is not None:
272
- raise NotImplementedError("the parameter `--only-with-scenario` is not supported in this version of balder")
273
- if self.parsed_args.only_with_setup is not None:
274
- raise NotImplementedError("the parameter `--only-with-setup` is not supported in this version of balder")
275
-
276
271
  self.working_dir = self.parsed_args.working_dir
277
272
  self.collect_only = self.parsed_args.collect_only
278
273
  self.resolve_only = self.parsed_args.resolve_only
@@ -285,7 +280,10 @@ class BalderSession:
285
280
  """
286
281
  This method collects all data.
287
282
  """
288
- 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)
289
287
 
290
288
  def solve(self):
291
289
  """
@@ -302,7 +300,8 @@ class BalderSession:
302
300
  .. note::
303
301
  Note that the method creates an :class:`ExecutorTree`, that hasn't to be completely resolved yet.
304
302
  """
305
- 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)
306
305
  self.plugin_manager.execute_filter_executor_tree(executor_tree=self.executor_tree)
307
306
 
308
307
  def run(self):
_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
@@ -158,8 +158,7 @@ class BasicExecutor(ABC):
158
158
  if cur_child.has_runnable_tests(consider_discarded_too):
159
159
  return True
160
160
  return False
161
- else:
162
- return True
161
+ return True
163
162
 
164
163
  def get_all_base_instances_of_this_branch(
165
164
  self, with_type: Union[Type[Setup], Type[Scenario], Type[types.FunctionType]],
@@ -193,10 +192,12 @@ class BasicExecutor(ABC):
193
192
  return list(set(result))
194
193
 
195
194
  @abstractmethod
196
- def cleanup_empty_executor_branches(self):
195
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
197
196
  """
198
197
  This method searches the whole tree and removes branches where an executor item has no own children. It can
199
198
  remove these branches, because they have no valid matchings.
199
+
200
+ :param consider_discarded: true if this method should consider discarded branches, otherwise False
200
201
  """
201
202
 
202
203
  def filter_tree_for_user_filters(self):
@@ -129,10 +129,10 @@ class ExecutorTree(BasicExecutor):
129
129
  # can not find some
130
130
  return None
131
131
 
132
- def cleanup_empty_executor_branches(self):
132
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
133
133
  to_remove_executor = []
134
134
  for cur_setup_executor in self.get_setup_executors():
135
- cur_setup_executor.cleanup_empty_executor_branches()
135
+ cur_setup_executor.cleanup_empty_executor_branches(consider_discarded=consider_discarded)
136
136
  if len(cur_setup_executor.get_scenario_executors()) == 0:
137
137
  # remove this whole executor because it has no children anymore
138
138
  to_remove_executor.append(cur_setup_executor)
@@ -192,7 +192,8 @@ class ExecutorTree(BasicExecutor):
192
192
  for cur_key, cur_val in mapping_printings.items():
193
193
  print(("{} {:<" + str(max_len) + "} = {}").format(start_char, cur_key, cur_val))
194
194
  for cur_testcase_excutor in cur_variation_executor.get_testcase_executors():
195
- print(f"{start_char} -> Testcase<{cur_testcase_excutor.base_testcase_callable.__qualname__}>")
195
+ print(f"{start_char} -> Testcase<"
196
+ f"{cur_testcase_excutor.base_testcase_callable.__qualname__}>")
196
197
  if cur_variation_executor.prev_mark == PreviousExecutorMark.DISCARDED:
197
198
  print(f"{start_char}")
198
199
  print(f"{start_char} DISCARDED BECAUSE "
@@ -116,12 +116,12 @@ class ScenarioExecutor(BasicExecutor):
116
116
  if cur_executor.prev_mark != PreviousExecutorMark.DISCARDED]
117
117
  return self._variation_executors
118
118
 
119
- def cleanup_empty_executor_branches(self):
119
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
120
120
  """
121
121
  This method removes all sub executors that are empty and not relevant anymore.
122
122
  """
123
123
  to_remove_executor = []
124
- for cur_variation_executor in self.get_variation_executors():
124
+ for cur_variation_executor in self.get_variation_executors(return_discarded=consider_discarded):
125
125
  if len(cur_variation_executor.get_testcase_executors()) == 0:
126
126
  # remove this whole executor because it has no children anymore
127
127
  to_remove_executor.append(cur_variation_executor)
@@ -88,11 +88,11 @@ class SetupExecutor(BasicExecutor):
88
88
  """returns a list with all scenario executors that belongs to this setup executor"""
89
89
  return self._scenario_executors
90
90
 
91
- def cleanup_empty_executor_branches(self):
91
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
92
92
  to_remove_executor = []
93
93
  for cur_scenario_executor in self.get_scenario_executors():
94
- cur_scenario_executor.cleanup_empty_executor_branches()
95
- if len(cur_scenario_executor.get_variation_executors()) == 0:
94
+ cur_scenario_executor.cleanup_empty_executor_branches(consider_discarded=consider_discarded)
95
+ if len(cur_scenario_executor.get_variation_executors(return_discarded=consider_discarded)) == 0:
96
96
  # remove this whole executor because it has no children anymore
97
97
  to_remove_executor.append(cur_scenario_executor)
98
98
  for cur_scenario_executor in to_remove_executor:
@@ -7,7 +7,6 @@ import traceback
7
7
  from _balder.utils import inspect_method
8
8
  from _balder.testresult import ResultState, TestcaseResult
9
9
  from _balder.executor.basic_executor import BasicExecutor
10
- from _balder.previous_executor_mark import PreviousExecutorMark
11
10
 
12
11
  if TYPE_CHECKING:
13
12
  from _balder.executor.variation_executor import VariationExecutor
@@ -132,7 +131,7 @@ class TestcaseExecutor(BasicExecutor):
132
131
  return True
133
132
  return False
134
133
 
135
- def cleanup_empty_executor_branches(self):
134
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
136
135
  """
137
136
  This method searches the whole tree and removes branches where an executor item has no own children. It can
138
137
  remove these branches, because they have no valid matchings.
@@ -134,6 +134,7 @@ class VariationExecutor(BasicExecutor):
134
134
 
135
135
  @property
136
136
  def not_applicable_variation_exc(self) -> Union[NotApplicableVariationException, None]:
137
+ """holds the :class:`NotApplicableVariationException` that describes why this variation in not applicable"""
137
138
  return self._not_applicable_variation_exc
138
139
 
139
140
  # ---------------------------------- PROTECTED METHODS -------------------------------------------------------------
@@ -483,7 +484,7 @@ class VariationExecutor(BasicExecutor):
483
484
  # can not find some
484
485
  return None
485
486
 
486
- def cleanup_empty_executor_branches(self):
487
+ def cleanup_empty_executor_branches(self, consider_discarded=False):
487
488
  """
488
489
  This method searches the whole tree and removes branches where an executor item has no own children. It can
489
490
  remove these branches, because they have no valid matchings.
@@ -394,6 +394,41 @@ class FixtureManager:
394
394
  if exception:
395
395
  raise exception
396
396
 
397
+ def get_fixture_for_class(
398
+ self,
399
+ execution_level: Union[Type[ExecutorTree], Type[SetupExecutor], Type[ScenarioExecutor],
400
+ Type[VariationExecutor], Type[TestcaseExecutor]],
401
+ setup_or_scenario_class: Union[None, Type[Setup], Type[Scenario]],
402
+ parent_classes: bool = True
403
+ ) -> List[Tuple[MethodLiteralType, Callable]]:
404
+ """
405
+ This method returns all classes of a specific Setup/Scenario class for a specific execution-level.
406
+
407
+ :param execution_level: the execution level the fixture should be returned for
408
+ :param setup_or_scenario_class: the scenario or setup class, the fixtures should be returned for
409
+ :param parent_classes: true if the method should look for fixtures in parent classes too
410
+ :return: list with all fixtures that are matching search criteria
411
+ """
412
+ # current relevant EXECUTION LEVEL - all other levels are not relevant for this call
413
+ cur_execution_level = self.resolve_type_level[execution_level]
414
+ # get all fixtures of the current relevant level
415
+ fixtures_of_exec_level = self.fixtures.get(cur_execution_level, {})
416
+ if setup_or_scenario_class is not None and parent_classes:
417
+ all_fixtures = []
418
+ for cur_parent_class in inspect.getmro(setup_or_scenario_class):
419
+ if issubclass(cur_parent_class, (Scenario, Setup)):
420
+ all_fixtures += self.get_fixture_for_class(execution_level, cur_parent_class, False)
421
+ # go through list and remove all overwritten fixtures
422
+ _added_fixtures = []
423
+ remaining_fixtures = []
424
+ for cur_fixture_tuple in all_fixtures:
425
+ if cur_fixture_tuple[1].__name__ not in _added_fixtures:
426
+ _added_fixtures.append(cur_fixture_tuple[1].__name__)
427
+ remaining_fixtures.append(cur_fixture_tuple)
428
+ return remaining_fixtures
429
+ else:
430
+ return fixtures_of_exec_level.get(setup_or_scenario_class, [])
431
+
397
432
  def get_all_fixtures_for_current_level(
398
433
  self, branch: Union[ExecutorTree, SetupExecutor, ScenarioExecutor, VariationExecutor, TestcaseExecutor]) \
399
434
  -> Dict[Union[Type[ExecutorTree], Type[Scenario], Type[Setup]],
@@ -416,28 +451,24 @@ class FixtureManager:
416
451
  argument (this list is ordered after the call hierarchy)
417
452
  """
418
453
  from _balder.executor.executor_tree import ExecutorTree
419
- # current relevant EXECUTION LEVEL - all other levels are not relevant for this call
420
- cur_execution_level = self.resolve_type_level[branch.__class__]
421
- # get all fixtures of the current relevant level
422
- fixtures_of_exec_level = self.fixtures.get(cur_execution_level, {})
423
454
 
424
455
  all_fixtures = {}
425
456
  # get all relevant fixtures of `balderglob.py` (None is key for balderglob fixtures)
426
- glob_fixtures = fixtures_of_exec_level.get(None, [])
457
+ glob_fixtures = self.get_fixture_for_class(branch.__class__, None)
427
458
  all_fixtures[ExecutorTree] = {}
428
459
  all_fixtures[ExecutorTree][ExecutorTree] = glob_fixtures
429
460
  # get all relevant fixtures with definition scope "setup"
430
461
  all_fixtures[Setup] = {}
431
462
  for cur_setup in branch.get_all_base_instances_of_this_branch(Setup, only_runnable_elements=True):
432
463
  # check if there exists fixtures for the current setup
433
- cur_setup_fixtures = fixtures_of_exec_level.get(cur_setup.__class__, [])
464
+ cur_setup_fixtures = self.get_fixture_for_class(branch.__class__, cur_setup.__class__)
434
465
  if cur_setup_fixtures:
435
466
  all_fixtures[Setup][cur_setup.__class__] = cur_setup_fixtures
436
467
 
437
468
  # get all relevant fixtures with definition scope "scenario"
438
469
  all_fixtures[Scenario] = {}
439
470
  for cur_scenario in branch.get_all_base_instances_of_this_branch(Scenario, only_runnable_elements=True):
440
- cur_scenario_fixtures = fixtures_of_exec_level.get(cur_scenario.__class__, [])
471
+ cur_scenario_fixtures = self.get_fixture_for_class(branch.__class__, cur_scenario.__class__)
441
472
  if cur_scenario_fixtures:
442
473
  all_fixtures[Scenario][cur_scenario.__class__] = cur_scenario_fixtures
443
474
 
_balder/routing_path.py CHANGED
@@ -284,8 +284,6 @@ class RoutingPath:
284
284
 
285
285
  if elem_before is not None:
286
286
  # check if device and nodes of the two elements are the same -> have to be a chain
287
- last_device, last_node_name = self._get_end_device_and_node()
288
-
289
287
  if isinstance(elem, Connection):
290
288
  if (self.end_device, self.end_node_name) not in \
291
289
  [(elem.from_device, elem.from_node_name), (elem.to_device, elem.to_node_name)]:
_balder/solver.py CHANGED
@@ -170,10 +170,14 @@ class Solver:
170
170
  self._mapping = initial_mapping
171
171
  self._resolving_was_executed = True
172
172
 
173
- def get_executor_tree(self, plugin_manager: PluginManager) -> ExecutorTree: # pylint: disable=unused-argument
173
+ # pylint: disable-next=unused-argument
174
+ def get_executor_tree(self, plugin_manager: PluginManager, add_discarded=False) -> ExecutorTree:
174
175
  """
175
176
  This method builds the ExecutorTree from the resolved data and returns it
176
177
 
178
+ :param plugin_manager: the related plugin manager object
179
+ :param add_discarded: True in case discarded elements should be added to the tree, otherwise False
180
+
177
181
  :return: the executor tree is built on the basis of the mapping data
178
182
  """
179
183
 
@@ -215,7 +219,7 @@ class Solver:
215
219
  # now filter all elements that have no child elements
216
220
  # -> these are items that have no valid matching, because no variation can be applied for it (there are no
217
221
  # required :class:`Feature` matching or there exists no possible routing for the variation)
218
- executor_tree.cleanup_empty_executor_branches()
222
+ executor_tree.cleanup_empty_executor_branches(consider_discarded=add_discarded)
219
223
 
220
224
  self._set_data_for_covered_by_in_tree(executor_tree=executor_tree)
221
225
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: baldertest
3
- Version: 0.1.0b7
3
+ Version: 0.1.0b8
4
4
  Summary: balder: reusable scenario based test framework
5
5
  Home-page: https://docs.balder.dev
6
6
  Author: Max Stahlschmidt and others
@@ -1,10 +1,10 @@
1
1
  _balder/__init__.py,sha256=Qk4wkVInPlXLFV36Yco5K7PDawJoeeWQVakzj6g5pmA,195
2
- _balder/_version.py,sha256=Mlz6U-dFHSBS29dqTLVb3_npm2vadOIIiwZaPdAO_Ag,162
2
+ _balder/_version.py,sha256=IWxE2VF11qpQh0kbBboGimtTYBFAxLyQVgAo81Of17o,413
3
3
  _balder/balder_plugin.py,sha256=EQzJP1dwwVDydhMLJtAmTCXOczlDuXBJur05lalmK_k,3136
4
- _balder/balder_session.py,sha256=bgpgvpYqFmtUlPFYEXQ10Mvj0IhLO86Yut__Gmc3l2c,16211
4
+ _balder/balder_session.py,sha256=6Y2o2MR78MZHIsGCkSA9BLMu4pNhB1i8Y1HWMQOQtAc,16130
5
5
  _balder/balder_settings.py,sha256=U96PVep7dGSaTXrMfeZMYf6oCIcEDPEqrBlFcoX476s,582
6
- _balder/collector.py,sha256=PfGVY34wrz2oauDEu-HY53S7w5HlQoaYwuNHQE0HuRk,40336
7
- _balder/connection.py,sha256=T2g63SbtTRT7d8MvabTSbPQYP9DhIhn2zo8zc54OwoY,69633
6
+ _balder/collector.py,sha256=G_vrZ0nQRbIw6IjKOlGC9y7EgEn1oE7QjsJrW8t1EIs,42147
7
+ _balder/connection.py,sha256=MNazK97CIEJCA4o1krjKjtilVhOYMWD8TZT0YT5nCJs,71778
8
8
  _balder/decorator_connect.py,sha256=TvyJNIslBAYVQWhsfSeFgXKp_DP7sZF1BmcP6RhIdKo,5988
9
9
  _balder/decorator_covered_by.py,sha256=Y6WMUuyn_uvFkjGfbts8OE5Bsir_7LTRB-jxYGaYk4Y,4069
10
10
  _balder/decorator_fixture.py,sha256=Nvr3H-r8LlPiGSGEst3D1M-KQaBqSA1u1u13qzZrSwk,1138
@@ -15,14 +15,14 @@ _balder/device.py,sha256=5O3tqj_iLKfHb5Zi_viJ76VH82cMOzX58OzRrMRRv0k,833
15
15
  _balder/exceptions.py,sha256=d0YSw8zVax2bFakG_1hcLlAXlyQh5jXnOuIPL1HLfI4,4202
16
16
  _balder/exit_code.py,sha256=P0oFWKfjMo36Frv13ADRcm8eSPN3kE-WmZBE9qZJHdA,513
17
17
  _balder/feature.py,sha256=B3yPc-WZwLt1Q4dO9s2j9g1MBTcMBjn6oWoLnRgrwSs,3845
18
- _balder/fixture_manager.py,sha256=rsepVAp0BRbakcwWtY59NtFDipTLvFVxPqJoCXYrliM,26843
18
+ _balder/fixture_manager.py,sha256=_fJZAaBK8Pej96juvdkigi7rjKJ-RTbfxhkSs78OBvg,28656
19
19
  _balder/node_gateway.py,sha256=64mv7Nx82JVknnQ09UXC-AcdDl6i_OB6NOsq_uBxeYo,4710
20
20
  _balder/plugin_manager.py,sha256=Ev2jnx4NtFHDsZ3C6h0HrJtQisqLO-V34JRM3wzTnFM,6921
21
21
  _balder/previous_executor_mark.py,sha256=gwpGu7d-kwPzQT8CmaPfuEG6fess2Upf5Q-zX6Oi6NY,835
22
- _balder/routing_path.py,sha256=7geuwPqBVnB-wAZ1-CjSLedNClVWb5rS628u8fRbvFw,17247
22
+ _balder/routing_path.py,sha256=6MJkhzBTHow2ESXzKQ2otwRFbPcKhLTYVy-zh7c5HeE,17172
23
23
  _balder/scenario.py,sha256=ATowBUl2HYQBmJHZ-eBpliqjPsWPnZAme9kwIeX3Tak,840
24
24
  _balder/setup.py,sha256=zSgtzNIWTVBjiZ5mn-qfpqIAnP3Im73t3Lqoaw0gWEI,763
25
- _balder/solver.py,sha256=m4BiQxnTG7qsKlSM6tSpVFICocRohUyTH729VcM5QqE,11764
25
+ _balder/solver.py,sha256=qgcCRyLg3nsO2d3H0pukxmm2oe_h6t0pBGyZ-FgQYKc,11998
26
26
  _balder/testresult.py,sha256=6yo3EcuLlpM23vdrYzn5VDcG35qelAQudmyoLpwZoHM,5251
27
27
  _balder/unmapped_vdevice.py,sha256=oKr01YVTLViWtZkYz8kx8ccTx-KmwgNrHuQqqD4eLQw,513
28
28
  _balder/utils.py,sha256=2kcX6bZIJW9Z8g-Hv0ue2mdOLBQYq4T2XSZpH2in1MQ,3092
@@ -39,12 +39,12 @@ _balder/controllers/scenario_controller.py,sha256=gpN_wxEYcHlh6DVk7pRbPj_x2cmM6i
39
39
  _balder/controllers/setup_controller.py,sha256=iNIMFjawYJWaSToUUfpmRK6ssycPyZGNlcvms8c7GKM,7135
40
40
  _balder/controllers/vdevice_controller.py,sha256=6-PidCKgvUteZVJsbGkKX69f3cYYYnolONl5Gja16W8,5777
41
41
  _balder/executor/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
42
- _balder/executor/basic_executor.py,sha256=ujG5HMu7dZMmpFA3EHDpi3mBIW-b_kWtzsJFK1Dy9-I,11480
43
- _balder/executor/executor_tree.py,sha256=cwhdM4rdvf52B4DFT9IdZhWh4TW3Dd4BlyZevFH-Yew,9687
44
- _balder/executor/scenario_executor.py,sha256=stAi1ulXxa60XND5Ot8jXRG_4tGGHKI31EyuNNPiwYE,10807
45
- _balder/executor/setup_executor.py,sha256=z6naHYh4kzkAR3RuRjTymstuJ6lic-iKM7rWx6jNgzM,5747
46
- _balder/executor/testcase_executor.py,sha256=VVQsMu15w3IYhPt4nvWfcQIH_RM1BHyJfUs4amOUxWI,6677
47
- _balder/executor/variation_executor.py,sha256=gSqQCTZkQwKvll_9yWP65cmUYR_egniG4gntraj_JSE,55374
42
+ _balder/executor/basic_executor.py,sha256=qINIXLSL8uO3QYRXT3kb5_sG6Ub6DOKyvw21EKYN13Y,11596
43
+ _balder/executor/executor_tree.py,sha256=0vGGO2HsXRlxRB6zJJCM0vPIOPVU8VrjqLDXOqfxavw,9784
44
+ _balder/executor/scenario_executor.py,sha256=PopGXe6cwkSoNLrRMCWOTO320mrXf5DVMZSmRu-TgHY,10868
45
+ _balder/executor/setup_executor.py,sha256=VEU0qAy9e3A0qjKpmiV5MbAdqRkoiysMvR4zq_xvL0o,5845
46
+ _balder/executor/testcase_executor.py,sha256=jNfACO-AuOktIgCVcsavh0rHbUr5MmI-jML-LPMvo0w,6639
47
+ _balder/executor/variation_executor.py,sha256=lwSAaDQKsXqQSbg3udLDClctxvIZSIKNj1BgR8fWNcE,55517
48
48
  _balder/objects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
49
  _balder/objects/connections/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  _balder/objects/connections/osi_1_physical.py,sha256=74lKWJd6ETEtvNXH0_dmTbkZlStJ_af218pQkUht0aA,2189
@@ -60,9 +60,9 @@ balder/__init__.py,sha256=BfbdkW_CZCXWGqtdg1gpAjO0AY8b4h-P-flWsIMG-X0,1003
60
60
  balder/connections.py,sha256=H6rf7UsiVY_FeZLngZXCT9WDw9cQqpiDiPbz_0J4yjM,2331
61
61
  balder/devices.py,sha256=zupHtz8yaiEjzR8CrvgZU-RzsDQcZFeN5mObfhtjwSw,173
62
62
  balder/exceptions.py,sha256=z_vlipJIsFwwJy9Ae_oGDJGPTINiAInMNZuCvEy6SUE,1884
63
- baldertest-0.1.0b7.dist-info/LICENSE,sha256=Daz9qTpqbiq-klWb2Q9lYOmn3rJ5oIQnbs62sGcqOZ4,1084
64
- baldertest-0.1.0b7.dist-info/METADATA,sha256=wB5TDyLXJNzyQvWmXqxuCD5VB9Voum3iTtY06jpFjlk,15783
65
- baldertest-0.1.0b7.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
66
- baldertest-0.1.0b7.dist-info/entry_points.txt,sha256=hzqu_nrMKTCi5IJqzS1fhIXWEiL7mTGZ-kgj2lUYlRU,65
67
- baldertest-0.1.0b7.dist-info/top_level.txt,sha256=RUkIBkNLqHMemx2C9aEpoS65dpqb6_jU-oagIPxGQEA,15
68
- baldertest-0.1.0b7.dist-info/RECORD,,
63
+ baldertest-0.1.0b8.dist-info/LICENSE,sha256=Daz9qTpqbiq-klWb2Q9lYOmn3rJ5oIQnbs62sGcqOZ4,1084
64
+ baldertest-0.1.0b8.dist-info/METADATA,sha256=nMxgYubNYoSw45qZmXUlk13GJ5v93zOXCI__mlVVSs4,15783
65
+ baldertest-0.1.0b8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
66
+ baldertest-0.1.0b8.dist-info/entry_points.txt,sha256=hzqu_nrMKTCi5IJqzS1fhIXWEiL7mTGZ-kgj2lUYlRU,65
67
+ baldertest-0.1.0b8.dist-info/top_level.txt,sha256=RUkIBkNLqHMemx2C9aEpoS65dpqb6_jU-oagIPxGQEA,15
68
+ baldertest-0.1.0b8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.40.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5