def normalise(self, meta, val): val = self.spec.normalise(meta, val) if "discovery_options" not in meta.everything: return val base = meta.everything["discovery_options"].clone() if val.hardcoded_discovery is not sb.NotSpecified: if not base.hardcoded_discovery or val.hardcoded_discovery is None: base.hardcoded_discovery = val.hardcoded_discovery else: hardcoded_discovery = val.hardcoded_discovery if isinstance(base.hardcoded_discovery, dict): opts = MergedOptions.using(base.hardcoded_discovery, val.hardcoded_discovery) hardcoded_discovery = opts.as_dict() base.hardcoded_discovery = hardcoded_discovery elif isinstance(base.hardcoded_discovery, dict): base.hardcoded_discovery = MergedOptions.using( base.hardcoded_discovery).as_dict() if val.serial_filter is not sb.NotSpecified: base.serial_filter = val.serial_filter elif isinstance(base.serial_filter, list): base.serial_filter = list(base.serial_filter) return base
async def execute(self): plans = self.sender.make_plans("hev_status", "hev_config") serials = await self.serials result = ihp.ResultBuilder() got = await self.sender.gatherer.gather_all( plans, serials, error_catcher=result.error, message_timeout=self.timeout) for serial, (complete, info) in got.items(): if not complete: continue if info["hev_status"] is Skip: continue if "hev_status" in info and "hev_config" in info: # Create a copy so we don't corrupt the gatherer cache final = result.result["results"][serial] = {} final["status"] = MergedOptions.using( info["hev_status"]).as_dict() final["status"]["last"]["result"] = final["status"]["last"][ "result"].name final["config"] = info["hev_config"] return result
def find_photons_app_options(self, configuration, args_dict): """Return us all the photons_app options""" d = lambda r: {} if r in (None, "", sb.NotSpecified) else r return MergedOptions.using( dict(d(configuration.get("photons_app")).items()), dict(d(args_dict.get("photons_app")).items()), ).as_dict()
def __init__(self, store, **options): self.store = store everything = MergedOptions.using(options, {"commander": self}, dont_prefix=[dictobj]) self.meta = Meta(everything, [])
async def execute_task(self, **kwargs): self("Usage: (<target>:)<task> <options> -- <extra>") target_register = self.target_register initial_restrictions = {} if self.specific_target is not sb.NotSpecified: initial_restrictions.update( dict(target_names=[self.specific_target])) target_register = target_register.restricted( **initial_restrictions) targets_by_name = defaultdict(list) for name, target in self.target_register.registered.items(): typ = self.target_register.type_for(name) desc = self.target_register.desc_for(name) targets_by_name[name] = (typ, desc) tasks = [] for task in task_register.registered: if (self.specific_task_groups is not None and task.task_group not in self.specific_task_groups): continue if self.specific_task is sb.NotSpecified or task.name == self.specific_task: _, restrictions = task_register.determine_target_restrictions( task.task) if not restrictions: tasks.append((task, restrictions)) continue restrict = MergedOptions.using(initial_restrictions, restrictions).as_dict() reg = target_register.restricted(**restrict) if reg.registered: tasks.append((task, restrictions)) if len(tasks) == 1: self() self.print_one_task(targets_by_name, *tasks[0]) elif tasks: self() self.print_tasks(targets_by_name, tasks) else: self("Found no tasks to print help for...")
def messages(self): if self.deps["c"]["cap"].has_chain: length = len(self.deps["chain"]["chain"]) width = self.deps["chain"]["width"] options = MergedOptions.using( { "target": serial, "tile_index": 0, "length": length, "x": 0, "y": 0, "width": width, }, collector.photons_app.extra_as_json, ) return [ TileMessages.Get64.empty_normalise(**options.as_dict()) ] return Skip
def msg(kls, components, overrides=None): """ Create a :ref:`SetWaveformOptional <LightMessages.SetWaveformOptional>` message that may be used to change the state of a device to what has been specified. .. code-block:: python from photons_control.colour import ColourParser async def my_action(target, reference): msg = ColourParser.msg("green") await target.send(msg, reference) """ h, s, b, k = kls.hsbk(components, overrides) colour = dict( hue=0 if h is None else h, set_hue=h is not None, saturation=0 if s is None else s, set_saturation=s is not None, brightness=0 if b is None else b, set_brightness=b is not None, kelvin=0 if k is None else int(k), set_kelvin=k is not None, ) other = dict( transient=0, cycles=1, skew_ratio=0, waveform=Waveform.SAW, period=0 if not overrides else overrides.get("duration", 0), ) other_override = Effects.make(**(overrides or {})) options = MergedOptions.using(other, other_override, overrides or {}, colour) return LightMessages.SetWaveformOptional.normalise( Meta.empty(), options)
async def start( self, identity, reference, *, run_options=sb.NotSpecified, animations=sb.NotSpecified, ): pauser = asyncio.Semaphore() final_future = hp.ChildOfFuture( self.final_future, name=f"Animations::start({identity})[final_future]") if run_options is sb.NotSpecified: run_options = {} if animations is not sb.NotSpecified: run_options = MergedOptions.using(run_options, { "animations": animations }).as_dict() runner = AnimationRunner( self.sender, reference, run_options, final_future=final_future, error_catcher=errors, animation_options=self.animation_options, ) runner.run_options.pauser = pauser def remove(res): if identity in self.animations: del self.animations[identity] self.animations[identity] = Animation(final_future, identity, runner, pauser).start( self.tasks, remove) return self.info(started=identity)
changer = attrs.attrs_path("many", 2).changer_to("nope") assert repr(changer) == "<Will change <Path many[2]> to nope>" assert changer == ChangeAttr.test("many[2]", "nope", attempted=False) await changer() assert repr(changer) == "<Changed many[2] to nope>" assert changer == ChangeAttr.test("many[2]", "nope") changer = attrs.attrs_path("stuff").changer_to("better") assert repr(changer) == "<Will change <Path stuff> to better>" assert changer == ChangeAttr.test("stuff", "better", attempted=False) await changer() assert repr(changer) == "<Changed stuff to better>" assert changer == ChangeAttr.test("stuff", "better") assert MergedOptions.using(attrs._attrs).as_dict() == { "one": { "list1": [{"blah": False}, {"blah": False}], "list2": [1, 2, 3], "thing3": 56, }, "stuff": "better", "many": ["yes", "yah", "nope"], } async it "can add attributes that don't already exist to the base of attrs", attrs: changer = attrs.attrs_path("new").changer_to("newer") assert repr(changer) == "<Will change <Path new> to newer>" await changer() assert repr(changer) == "<Changed new to newer>"
thing, val = await commander.executor(progress_cb, request_handler).execute( "/v1", {"command": "thing", "args": {"value": value}}, {"other": other2} ) assert val == value assert thing.other is other2 assert thing.store is store2 assert store is not store2 async it "can inject values that are dictobj's": class Other(dictobj): fields = ["one"] other = Other("twenty") progress_cb = mock.Mock(name="progress_cb") request_handler = mock.Mock(name="request_handler") commander = Commander(store, other=other) value = str(uuid.uuid1()) thing, val = await commander.executor(progress_cb, request_handler).execute( "/v1", {"command": "thing", "args": {"value": value}} ) assert val == value assert thing.other is other async it "allows commands to be retrieved from a MergedOptions": options = MergedOptions.using({"command": FieldsRequired}, dont_prefix=[dictobj]) assert options["command"] is FieldsRequired
collector.extra_prepare(configuration, args_dict) class AFuture: def __eq__(s, other): return isinstance(other, asyncio.Future) assert collector.register is register assert configuration.as_dict() == { "$@": extra, "collector": collector, "photons_app": photons_app, } describe "find_photons_app_options": it "returns us a dictionary with options from configuration and args_dict": configuration = MergedOptions.using({"photons_app": {"one": 1, "two": 2}}) args_dict = {"photons_app": {"one": 3, "three": 4}} photons_app = Collector().find_photons_app_options(configuration, args_dict) assert photons_app == {"one": 3, "two": 2, "three": 4} it "doesn't care if configuration has no photons_app": configuration = MergedOptions() args_dict = {"photons_app": {"one": 3, "three": 4}} photons_app = Collector().find_photons_app_options(configuration, args_dict) assert photons_app == {"one": 3, "three": 4} for v in (None, "", sb.NotSpecified): configuration = MergedOptions.using({"photons_app": v}) photons_app = Collector().find_photons_app_options(configuration, args_dict)
def alter_clone_args_dict(self, new_collector, new_args_dict, options=None): return MergedOptions.using( new_args_dict, {"photons_app": self.configuration["photons_app"].as_dict()}, options or {}, )
def meta(s): options = MergedOptions.using( {"target_register": s.target_register}, dont_prefix=[mock.Mock] ) return Meta(options, [])
async def execute(self, path, body, extra_options=None, allow_ws_only=False): """ Responsible for creating a command and calling execute on it. If command is not already a Command instance then we normalise it into one. We have available on the meta object: __init__ options Anything that is provided to the Commander and Executor at __init__ store The store of commands path The path that was passed in executor This executor request_future A future that is cancelled after execute is finished extra options Anything provided as extra_options to this function """ request_future = asyncio.Future() request_future._merged_options_formattable = True try: everything = MergedOptions.using( self.commander.meta.everything, { "path": path, "store": self.commander.store, "executor": self, "progress_cb": self.progress_cb, "request_future": request_future, "request_handler": self.request_handler, }, self.extra_options, extra_options or {}, dont_prefix=[dictobj], ) meta = Meta(everything, self.commander.meta.path).at("<input>") execute = self.commander.store.command_spec.normalise( meta, { "path": path, "body": body, "allow_ws_only": allow_ws_only }) return await execute() finally: request_future.cancel()
async def execute_task(self, **kwargs): if self.reference == "help": if self.artifact in register.animations: print_help( animation_kls=register.animations[self.artifact].Animation, animation_name=self.artifact, ) else: print_help() return if self.reference in register.available_animations(): ref = self.artifact self.artifact = self.reference self.reference = ref extra = self.collector.photons_app.extra_as_json reference = self.collector.reference_object(self.reference) options = {} specific_animation = self.artifact not in (None, "", sb.NotSpecified) if specific_animation: options = extra run_options = extra.pop("run_options", {}) else: run_options = extra if isinstance(run_options, list): run_options = {"animations": run_options} if specific_animation: background = sb.NotSpecified layered = { "animations": [[self.artifact, background, options]], "animation_limit": 1 } run_options = MergedOptions.using(layered, run_options).as_dict() def errors(e): if isinstance(e, KeyboardInterrupt): return if not isinstance(e, PhotonsAppError): log.exception(e) else: log.error(e) conf = self.collector.configuration photons_app = conf["photons_app"] with photons_app.using_graceful_future() as final_future: async with self.target.session() as sender: runner = AnimationRunner( sender, reference, run_options, final_future=final_future, error_catcher=errors, animation_options=conf.get("animation_options", {}), ) async with runner: await runner.run()