• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import itertools
2import os
3import sys
4from typing import Any, Callable, Dict, Iterable, List, NamedTuple, Tuple, \
5    TypeVar, Optional
6
7# local import
8sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(
9    os.path.abspath(__file__)))))
10import lib.print_utils as print_utils
11
12T = TypeVar('T')
13NamedTupleMeta = Callable[
14    ..., T]  # approximation of a (S : NamedTuple<T> where S() == T) metatype.
15FilterFuncType = Callable[[NamedTuple], bool]
16
17def dict_lookup_any_key(dictionary: dict, *keys: List[Any]):
18  for k in keys:
19    if k in dictionary:
20      return dictionary[k]
21
22
23  print_utils.debug_print("None of the keys {} were in the dictionary".format(
24      keys))
25  return [None]
26
27def generate_run_combinations(named_tuple: NamedTupleMeta[T],
28                              opts_dict: Dict[str, List[Optional[object]]],
29                              loop_count: int = 1) -> Iterable[T]:
30  """
31  Create all possible combinations given the values in opts_dict[named_tuple._fields].
32
33  :type T: type annotation for the named_tuple type.
34  :param named_tuple: named tuple type, whose fields are used to make combinations for
35  :param opts_dict: dictionary of keys to value list. keys correspond to the named_tuple fields.
36  :param loop_count: number of repetitions.
37  :return: an iterable over named_tuple instances.
38  """
39  combinations_list = []
40  for k in named_tuple._fields:
41    # the key can be either singular or plural , e.g. 'package' or 'packages'
42    val = dict_lookup_any_key(opts_dict, k, k + "s")
43
44    # treat {'x': None} key value pairs as if it was [None]
45    # otherwise itertools.product throws an exception about not being able to iterate None.
46    combinations_list.append(val or [None])
47
48  print_utils.debug_print("opts_dict: ", opts_dict)
49  print_utils.debug_print_nd("named_tuple: ", named_tuple)
50  print_utils.debug_print("combinations_list: ", combinations_list)
51
52  for i in range(loop_count):
53    for combo in itertools.product(*combinations_list):
54      yield named_tuple(*combo)
55
56def filter_run_combinations(named_tuple: NamedTuple,
57                            filters: List[FilterFuncType]) -> bool:
58  for filter in filters:
59    if filter(named_tuple):
60      return False
61  return True
62
63def generate_group_run_combinations(run_combinations: Iterable[NamedTuple],
64                                    dst_nt: NamedTupleMeta[T]) \
65    -> Iterable[Tuple[T, Iterable[NamedTuple]]]:
66  def group_by_keys(src_nt):
67    src_d = src_nt._asdict()
68    # now remove the keys that aren't legal in dst.
69    for illegal_key in set(src_d.keys()) - set(dst_nt._fields):
70      if illegal_key in src_d:
71        del src_d[illegal_key]
72
73    return dst_nt(**src_d)
74
75  for args_list_it in itertools.groupby(run_combinations, group_by_keys):
76    (group_key_value, args_it) = args_list_it
77    yield (group_key_value, args_it)
78