Coverage for venv / lib / python3.12 / site-packages / combinatrix / models.py: 97%
154 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-05 00:09 +0100
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-05 00:09 +0100
1from combinatrix import constants
2from combinatrix.exceptions import CombinatrixException
5class Parameters(object):
7 def __init__(self, raw=None):
8 self.data = [] if raw is None else raw.get("parameters", [])
9 self._index = {}
10 self._value_index = {}
11 self._make_index()
13 def add_field(self, name, type):
14 self.data.append({"name": name, "type": type})
15 self._index[name] = len(self.data) - 1
17 def set_default(self, name, default):
18 obj = self.get(name)
19 if obj.get("type") not in [constants.CONDITIONAL]:
20 return
21 obj["default"] = default
23 def add_value(self, name, value):
24 obj = self.get(name)
25 if obj.get("type") in [constants.INDEX, constants.COMMENT]:
26 return
27 if value == "":
28 return
29 if "values" not in obj:
30 obj["values"] = {}
31 if value not in obj["values"]:
32 obj["values"][value] = {}
34 # keep an ordered list of values for iteration later
35 real_name = obj.get("name")
36 if real_name not in self._value_index:
37 self._value_index[real_name] = []
38 if value not in self._value_index[real_name]:
39 self._value_index[real_name].append(value)
41 def add_constraint(self, name, value, other_field, or_values=None, nor_values=None):
42 if or_values is not None and nor_values is not None:
43 raise CombinatrixException("you can't specify both or and nor values - choose one")
45 obj = self.get(name)
46 if obj.get("type") not in [constants.GENERATED]:
47 return
48 self.add_value(name, value)
49 if "constraints" not in obj["values"][value]:
50 obj["values"][value]["constraints"] = {}
51 context = obj["values"][value]["constraints"]
53 if other_field not in context:
54 context[other_field] = {}
56 if or_values is not None:
57 if "or" not in context[other_field]:
58 context[other_field]["or"] = []
59 context[other_field]["or"] += or_values
60 elif nor_values is not None:
61 if "nor" not in context[other_field]:
62 context[other_field]["nor"] = []
63 context[other_field]["nor"] += nor_values
65 def add_condition_set(self, name, value, conditions):
66 obj = self.get(name)
67 if obj.get("type") not in [constants.CONDITIONAL]:
68 return
69 self.add_value(name, value)
70 if "conditions" not in obj["values"][value]:
71 obj["values"][value]["conditions"] = []
72 context = obj["values"][value]["conditions"]
73 context.append(conditions)
75 def field_names(self, types=None):
76 if types is None:
77 return [p["name"] for p in self.data]
78 return [p["name"] for p in self.data if p["type"] in types]
80 def get_default(self, name):
81 return self.get(name).get("default", "")
83 def get_values(self, name):
84 obj = self.get(name)
85 return self._value_index.get(obj.get("name"))
87 def get_constraints(self, name, value):
88 obj = self.get(name)
89 return obj.get("values", {}).get(value, {}).get("constraints", {})
91 def get_conditions(self, name, value):
92 obj = self.get(name)
93 return obj.get("values", {}).get(value, {}).get("conditions", [])
95 def get(self, name):
96 if isinstance(name, int):
97 return self.data[name]
98 else:
99 return self.data[self._index[name]]
101 def as_dict(self):
102 return {"parameters": self.data}
104 def _make_index(self):
105 for i, obj in enumerate(self.data):
106 self._index[obj.get("name")] = i
107 self._value_index[obj.get("name")] = list(obj.get("values", {}).keys())
110class ComboIterator(object):
111 def __init__(self, parameters):
112 self.counters = {}
113 self._index = []
114 self._init = True
116 generated_fields = parameters.field_names(types=[constants.GENERATED])
117 for gf in generated_fields:
118 values = parameters.get_values(gf)
119 s = {}
120 for i, value in enumerate(values):
121 constraints = parameters.get_constraints(gf, value)
122 skip = {}
123 for field, rules in constraints.items():
124 if "nor" in rules:
125 skip[field] = self._values_to_indices(parameters, field, rules["nor"])
126 elif "or" in rules:
127 skip[field] = self._mirror_to_indices(parameters, field, rules["or"])
128 if len(list(skip.keys())) > 0:
129 s[i] = skip
131 self.counters[gf] = {"c": 0, "m": len(values) - 1, "s": s}
132 self._index.append(gf)
134 def _mirror_to_indices(self, parameters, field, values):
135 field_values = parameters.get_values(field)
136 return [field_values.index(v) for v in field_values if v not in values]
138 def _values_to_indices(self, parameters, field, values):
139 field_values = parameters.get_values(field)
140 return [field_values.index(v) for v in values]
142 def get_current(self, name):
143 return self.counters[name]["c"]
145 def reset_current(self, name):
146 self.counters[name]["c"] = 0
148 def increment_current(self, name):
149 self.counters[name]["c"] += 1
151 def get_max(self, name):
152 return self.counters[name]["m"]
154 def get_skip(self, name):
155 return self.counters[name]["s"]
157 def __next__(self):
158 if self._init:
159 self._init = False
160 if self._check_position():
161 return True
163 while True:
164 counted = False
165 for i in range(len(self._index) -1, -1, -1):
166 field = self._index[i]
167 current = self.get_current(field)
168 max = self.get_max(field)
169 if current == max:
170 self.reset_current(field)
171 else:
172 self.increment_current(field)
173 counted = True
174 break
175 if not counted:
176 break
177 if self._check_position():
178 break
179 return counted
181 def _check_position(self):
182 for i in range(len(self._index)):
183 field = self._index[i]
184 current = self.get_current(field)
185 skips = self.get_skip(field)
186 if current not in skips:
187 continue
188 for other_field, skip_list in skips[current].items():
189 if self.get_current(other_field) in skip_list:
190 return False
191 return True