async def find(self, fltr): _, removed = await self._ensure_devices(fltr) def error(e): log.error( hp.lc( "Failed to determine if device matched filter", error=e, error_type=type(e).__name__, )) streamer = hp.ResultStreamer(self.final_future, error_catcher=error, exceptions_only_to_error_catcher=True) for device in removed: await streamer.add_coroutine(device.finish()) for serial, device in list(self.devices.items()): if fltr.matches_all: fut = asyncio.Future() fut.set_result(True) await streamer.add_task(fut, context=device) else: await streamer.add_coroutine(device.matches( self.sender, fltr, self.collections), context=device) streamer.no_more_work() async with streamer: async for result in streamer: if result.successful and result.value and result.context: yield result.context
async def info(self, fltr): def error(e): log.error( hp.lc("Failed to find information for device", error=e, error_type=type(e).__name__)) streamer = hp.ResultStreamer(self.final_future, error_catcher=error, exceptions_only_to_error_catcher=True) async def find(): async for device in self.find(fltr): await streamer.add_coroutine( device.matches( self.sender, Filter.empty(refresh_info=fltr.refresh_info), self.collections), context=device, ) await streamer.add_coroutine(find(), context=True) streamer.no_more_work() async with streamer: async for result in streamer: if result.context is True and not result.successful: raise result.value if result.successful and result.value and result.context: yield result.context
async def stream(self, animation_state): self.started = time.time() del self.ticker async def tick(): async with self.ticker as ticks: async for result in ticks: yield result def errors(e): if not isinstance(e, asyncio.CancelledError): log.error(hp.lc(error=e, error_type=type(e))) with hp.ChildOfFuture( self.final_future, name="Animation::streamer[stop_fut]") as stop_fut: async with hp.ResultStreamer( stop_fut, error_catcher=errors, exceptions_only_to_error_catcher=True, name=f"Animation({self.__class__.__name__})", ) as streamer: await streamer.add_generator(tick(), context=AnimationEvent.Types.TICK) await streamer.add_generator( self.make_user_events(animation_state), context=AnimationEvent.Types.USER_EVENT) streamer.no_more_work() async for result in streamer: if result.value is hp.ResultStreamer.GeneratorComplete: continue yield result
async def info(self, fltr): catcher = partial(log_errors, "Failed to find information for device") async def find(): async for device in self.find(fltr): await streamer.add_coroutine( device.matches( self.sender, Filter.empty(refresh_info=fltr.refresh_info), self.collections ), context=device, ) streamer = hp.ResultStreamer( self.final_future, error_catcher=catcher, name="Finder::info[streamer]" ) async with streamer: await streamer.add_coroutine(find(), context=True) streamer.no_more_work() async for result in streamer: if not result.successful: if result.context is True: try: raise result.value finally: del result elif result.value and result.context: yield result.context
async def retrieve_all(self, msg, complete): try: async with hp.ResultStreamer( self.stop_fut, name="FromGenerator>Runner::retrieve_all[streamer]", error_catcher=squash, ) as streamer: for item in self.item.simplifier(msg): await streamer.add_generator(self.retrieve(item), context="retrieve") streamer.no_more_work() async for result in streamer: if result.value is hp.ResultStreamer.GeneratorComplete: continue if not result.successful: if not isinstance(result.value, self.Unsuccessful): hp.add_error(self.error_catcher, result.value) if not complete.done(): complete.set_result(False) else: yield result.value finally: if not complete.done(): exc_info = sys.exc_info() if exc_info[0] is None: complete.set_result(True) else: complete.set_result(False)
async def make_streamer(**kwargs): streamer = None final_future = hp.create_future() try: streamer = hp.ResultStreamer(final_future, **kwargs) yield streamer finally: final_future.cancel() if streamer: await streamer.finish()
def __init__(self, item, stop_fut, reference, sender, kwargs): self.item = item self.kwargs = kwargs self.reference = reference self.sender = sender self.stop_fut = hp.ChildOfFuture( stop_fut, name="FromGenerator>Runner::__init__[stop_fut]") self.streamer = hp.ResultStreamer( self.stop_fut, name="FromGenerator>Runner::__init__[streamer]", error_catcher=squash, )
async def find(self, fltr): if self.final_future.done(): return refresh = False if fltr is None else fltr.refresh_discovery try: serials = await self._find_all_serials(refresh=refresh) except asyncio.CancelledError: raise except Exception: log.exception("Failed to find serials") serials = [] removed = self._ensure_devices(serials) catcher = partial(log_errors, "Failed to determine if device matched filter") async with hp.ResultStreamer( self.final_future, name="Finder::find[streamer]", error_catcher=catcher ) as streamer: for device in removed: await streamer.add_coroutine(device.finish()) for serial, device in list(self.devices.items()): if fltr.matches_all: fut = hp.create_future(name=f"Finder({serial})::find[fut]") fut.set_result(True) await streamer.add_task(fut, context=device) else: await streamer.add_coroutine( device.matches(self.sender, fltr, self.collections), context=device ) streamer.no_more_work() async with streamer: async for result in streamer: if result.successful and result.value and result.context: yield result.context
async def write_messages(self, sender, packets, kwargs): """Send all our packets and collect all the results""" error_catcher = kwargs["error_catcher"] async with hp.ResultStreamer( sender.stop_fut, error_catcher=silence_errors, name="Item::write_messages[streamer]") as streamer: count = 0 for original, packet in packets: count += 1 await streamer.add_coroutine(self.do_send( sender, original, packet, kwargs), context=packet) streamer.no_more_work() got = 0 async for result in streamer: got += 1 if result.successful: for msg in result.value: yield msg else: exc = result.value pkt = result.context if isinstance(exc, asyncio.CancelledError): hp.add_error( error_catcher, TimedOut( "Message was cancelled", sent_pkt_type=pkt.pkt_type, serial=pkt.serial, source=pkt.source, sequence=pkt.sequence, ), ) else: hp.add_error(error_catcher, exc)
async def gather(self, plans, reference, error_catcher=None, **kwargs): """ This is an async generator that yields tuples of ``(serial, label, info)`` where ``serial`` is the serial of the device, ``label`` is the label of the plan, and ``info`` is the result of executing that plan. The error handling of this function is the same as the async generator behaviour in :ref:`sender <sender_interface>` API. """ if not plans: return with catch_errors(error_catcher) as error_catcher: kwargs["error_catcher"] = error_catcher serials, missing = await find_serials(reference, self.sender, timeout=kwargs.get( "find_timeout", 20)) for serial in missing: hp.add_error(error_catcher, FailedToFindDevice(serial=serial)) async with hp.ResultStreamer( self.sender.stop_fut, error_catcher=error_catcher, exceptions_only_to_error_catcher=True, ) as streamer: for serial in serials: await streamer.add_generator( self._follow(plans, serial, **kwargs)) streamer.no_more_work() async for result in streamer: if result.successful: yield result.value
async def search_loop(self): refreshing = hp.ResettableFuture(name="DeviceFinderDaemon::search_loop[refreshing]") refresh_discovery_fltr = Filter.empty(refresh_discovery=True) async def add(streamer): if refreshing.done(): return refreshing.set_result(True) async for device in self.finder.find(refresh_discovery_fltr): await streamer.add_coroutine( device.refresh_information_loop( self.sender, self.time_between_queries, self.finder.collections ), context=device, ) async def ticks(): async with self.hp_tick(self.search_interval, final_future=self.final_future) as ticks: async for info in ticks: yield info catcher = partial(log_errors, "Something went wrong in a search") async with hp.ResultStreamer( self.final_future, name="DeviceFinderDaemon::search_loop[streamer]", error_catcher=catcher, ) as streamer: await streamer.add_generator(ticks(), context="finder_tick") streamer.no_more_work() async for result in streamer: if result.successful and result.context == "finder_tick": refreshing.reset() await streamer.add_coroutine(add(streamer))
async def send_single(self, original, packet, *, timeout, no_retry=False, broadcast=False, connect_timeout=10): transport, is_broadcast = await self._transport_for_send( None, packet, original, broadcast, connect_timeout) retry_gaps = self.retry_gaps(original, transport) writer = Writer( self, transport, self.receiver, original, packet, retry_gaps, did_broadcast=is_broadcast, connect_timeout=connect_timeout, ) results = [] unlimited = False if hasattr(original, "Meta"): unlimited = original.Meta.multi == -1 async def wait_for_remainders(tick_fut, streamer_fut): try: await hp.wait_for_all_futures(tick_fut) finally: if unlimited and any(result.wait_for_result() for result in results): await hp.wait_for_first_future(*results) streamer_fut.cancel() tick_fut = hp.ChildOfFuture( self.stop_fut, name= f"SendPacket({original.pkt_type, packet.serial})::send_single[tick_fut]", ) streamer_fut = hp.ChildOfFuture( self.stop_fut, name= f"SendPacket({original.pkt_type, packet.serial})::send_single[streamer_fut]", ) retry_ticker = retry_gaps.retry_ticker( name= f"{type(self).__name__}({type(transport).__name__})::retry_ticker") with tick_fut, streamer_fut: async with hp.ResultStreamer( streamer_fut, name= f"SendPacket({original.pkt_type, packet.serial}).send_single" ) as streamer: await streamer.add_generator( retry_ticker.tick(tick_fut, timeout), context="tick", on_done=lambda res: tick_fut.cancel(), ) await streamer.add_coroutine( wait_for_remainders(tick_fut, streamer_fut)) streamer.no_more_work() async for result in streamer: if not result.successful: try: raise result.value finally: del result if result.context == "tick": if not no_retry or not results: result = await writer() results.append(result) await streamer.add_task(result, context="write") elif result.context == "write": return result.value raise TimedOut( "Waiting for reply to a packet", serial=packet.serial, sent_pkt_type=packet.pkt_type, source=packet.source, sequence=packet.sequence, )
def streamer(s): return hp.ResultStreamer(s.final_future, error_catcher=s.error_catcher)
async def gen(reference, sender, **kwargs): with alter_called("primary"): async with hp.tick(0.3) as ticks: async for i, _ in ticks: called.append(("primary", i)) yield make_secondary_msg(i, m) return FromGenerator(gen, reference_override=True) msg = make_primary_msg(m) await FoundSerials().find(sender, timeout=1) sender.received.clear() fut = hp.create_future() async with hp.ResultStreamer(fut) as streamer: async def pkts(): with alter_called("pkts"): async for pkt in sender(msg, light1.serial): yield pkt t = await streamer.add_generator(pkts(), context="pkt") streamer.no_more_work() found = [] async for result in streamer: if result.context == "pkt": found.append(result) if len(found) == 18: t.cancel()
self.other = other return isinstance(other, asyncio.CancelledError) def __repr__(self): return f"<C: {getattr(self, 'other', None)}" describe "ResultStreamer": it "takes in final_future and other options": final_future = asyncio.Future() error_catcher = mock.Mock(name="error_catcher") exceptions_only_to_error_catcher = mock.Mock(name="exceptions_only_to_error_catcher") streamer = hp.ResultStreamer( final_future, error_catcher=error_catcher, exceptions_only_to_error_catcher=exceptions_only_to_error_catcher, ) assert not streamer.final_future.done() assert streamer.error_catcher is error_catcher assert streamer.exceptions_only_to_error_catcher is exceptions_only_to_error_catcher assert streamer.ts == [] assert isinstance(streamer.queue, asyncio.Queue) assert not streamer.stop_on_completion async it "has a final future as a child of that passed in": final_future = asyncio.Future() streamer = hp.ResultStreamer(final_future) streamer.final_future.cancel()
def streamer(s): return hp.ResultStreamer(s.final_future)
self.other = other return isinstance(other, asyncio.CancelledError) def __repr__(self): return f"<C: {getattr(self, 'other', None)}" describe "ResultStreamer": it "takes in final_future and other options": final_future = hp.create_future() error_catcher = mock.Mock(name="error_catcher") exceptions_only_to_error_catcher = mock.Mock(name="exceptions_only_to_error_catcher") streamer = hp.ResultStreamer( final_future, error_catcher=error_catcher, exceptions_only_to_error_catcher=exceptions_only_to_error_catcher, ) assert not streamer.final_future.done() assert streamer.error_catcher is error_catcher assert streamer.exceptions_only_to_error_catcher is exceptions_only_to_error_catcher assert len(streamer.ts.ts) == 0 assert isinstance(streamer.queue, hp.Queue) assert not streamer.stop_on_completion async it "has a final future as a child of that passed in": final_future = hp.create_future() streamer = hp.ResultStreamer(final_future) streamer.final_future.cancel()
async def __aenter__(s): s.streamer = hp.ResultStreamer(s.final_future, **s.kwargs) return s.streamer
assert (await searcher.discover()) is serials assert searcher.search_fut.result() is serials async it "does not do a search if one is already in progress", searcher: called = [] serials = mock.Mock(name="serials") continue_event = asyncio.Future() async def _serials(): called.append("get_serials") await continue_event assert not searcher.search_fut.done() return serials streamer = hp.ResultStreamer(asyncio.Future()) await streamer.add_coroutine(searcher.discover()) await streamer.add_coroutine(searcher.discover()) streamer.no_more_work() asyncio.get_event_loop().call_soon(continue_event.set_result, True) with mock.patch.object(searcher, "_serials", _serials): async with streamer: async for result in streamer: assert result.value is serials assert called == ["get_serials"] async it "does not search at all if the search_fut is already resolved", searcher: serials = mock.Mock(name="serials") searcher.search_fut.reset()