def test_toggleable_bools(): g = ToggleableBooleanGroup() for i, v in enumerate((True, True, False, True, False, False)): g.add(name="seed{}".format(i), value=v) result = [] result.append([bool(c) for c in g]) for series in g.shuffle(): result.append([bool(c) for c in series]) assert series == list(g) assert len(g) == 6 assert result == [ [True, True, False, True, False, False], [True, True, True, True, True, True], [True, True, True, True, True, False], [True, True, True, True, False, True], [True, True, True, True, False, False], [True, True, True, False, True, True], [True, True, True, False, True, False], [True, True, True, False, False, True], [True, True, True, False, False, False], [True, True, False, True, True, True], [True, True, False, True, True, False], [True, True, False, True, False, True], [True, True, False, False, True, True], [True, True, False, False, True, False], [True, True, False, False, False, True], [True, True, False, False, False, False], [True, False, True, True, True, True], [True, False, True, True, True, False], [True, False, True, True, False, True], [True, False, True, True, False, False], [True, False, True, False, True, True], [True, False, True, False, True, False], [True, False, True, False, False, True], [True, False, True, False, False, False], [True, False, False, True, True, True], [True, False, False, True, True, False], [True, False, False, True, False, True], [True, False, False, True, False, False], [True, False, False, False, True, True], [True, False, False, False, True, False], [True, False, False, False, False, True], [True, False, False, False, False, False], [False, True, True, True, True, True], [False, True, True, True, True, False], [False, True, True, True, False, True], [False, True, True, True, False, False], [False, True, True, False, True, True], [False, True, True, False, True, False], [False, True, True, False, False, True], [False, True, True, False, False, False], [False, True, False, True, True, True], [False, True, False, True, True, False], [False, True, False, True, False, True], [False, True, False, True, False, False], [False, True, False, False, True, True], [False, True, False, False, True, False], [False, True, False, False, False, True], [False, True, False, False, False, False], [False, False, True, True, True, True], [False, False, True, True, True, False], [False, False, True, True, False, True], [False, False, True, True, False, False], [False, False, True, False, True, True], [False, False, True, False, True, False], [False, False, True, False, False, True], [False, False, True, False, False, False], [False, False, False, True, True, True], [False, False, False, True, True, False], [False, False, False, True, False, True], [False, False, False, True, False, False], [False, False, False, False, True, True], [False, False, False, False, True, False], [False, False, False, False, False, True], [False, False, False, False, False, False], ]
class Plan: """Electrolyt Plan. :ivar entry_points: list of entry points found in the plans :vartype entry_points: dict """ def __init__( self, data: Dict[str, Any], entry_point_cls: Optional[Dict[str, Callable[..., EntryPoint]]] = None, plan_ext: str = ".plan", ): """Initialize a new plan. :param data: a dictionary defining additional globals to push into plan associated module :param entry_point_cls: dict associating a list of decorator name with an entry point class :param plan_ext: plan extension, by default ".plan". This is used to detect whether a specific frame is in a plan or in our code. See PlanContext._add_action """ self.mod = types.ModuleType("_anod_plan_") # Some additional user symbols for k, v in data.items(): self.mod.__dict__[k] = v self.entry_points: Dict[str, EntryPoint] = {} if entry_point_cls is None: entry_point_cls = {} self.plan_ext = plan_ext self.mod.__dict__["machine"] = partial(entry_point, self.entry_points, Machine, "machine") for name, cls in entry_point_cls.items(): self.mod.__dict__[name] = partial(entry_point, self.entry_points, cls, name) self.plan_date = datetime.now(timezone.utc) self.mod.__dict__["cond"] = self.cond self.toggleable_bool_group = ToggleableBooleanGroup() def cond(self, name: str, date: Callable[[datetime], bool]) -> ToggleableBoolean: """Generate a new conditional boolean. :param name: variable name :param date: function that takes the plan date and return a boolean. This can be used to set a value depending on the day of the week, e.g. by setting the constant to True on weekend: lambda d: d.isoweekday() in [6, 7] """ return self.toggleable_bool_group.add(name, date(self.plan_date)) def load(self, filename: str) -> None: """Load python code from file. :param filename: path to the python code """ with open(filename, "rb") as fd: source_code = fd.read() self.load_chunk(source_code, filename) def check(self, code_ast: ast.AST) -> None: """Check plan coding style.""" del self, code_ast def load_chunk(self, source_code: bytes, filename: str = "<unknown>") -> None: """Load a chunk of Python code. :param source_code: python source code :param filename: filename associated with the Python code """ code_ast = ast.parse(source_code, filename) self.check(code_ast) code = compile(code_ast, filename, "exec") exec(code, self.mod.__dict__)