async def finish(self, exc_typ=None, exc=None, tb=None): self.final_future.cancel() if hasattr(self, "_task"): await asyncio.wait([self._task]) if hasattr(self.server.finder.finish, "mock_calls"): assert len(self.server.finder.finish.mock_calls) == 0 waited = [] async with hp.TaskHolder(hp.create_future()) as ts: for cleaner in self.cleaners: waited.append(ts.add(cleaner())) async with hp.TaskHolder(hp.create_future()) as ts: if hasattr(self, "Wrapped"): waited.append(ts.add(self.Wrapped.__aexit__(exc_typ, exc, tb))) waited.append(ts.add(pytest.helpers.wait_for_no_port(self.port))) if hasattr(self, "ws"): waited.append(ts.add(self.ws.__aexit__(None, None, None))) await self.ts.finish() # make sure none failed for w in waited: await w # Prevent coroutine not awaited error await asyncio.sleep(0.01) if hasattr(self.server.finder.finish, "mock_calls"): self.server.finder.finish.assert_called_once_with()
class V: fut1 = hp.create_future() fut2 = hp.create_future() fut3 = hp.create_future() fut4 = hp.create_future() final_fut = hp.create_future() @hp.memoized_property def available_futs(s): return [s.fut1, s.fut2, s.fut3, s.fut4]
async def execute_task(self, collector, **kwargs): called.append(1) async def cleanup1(): called.append("c1a") fut = hp.create_future() fut.set_result(True) await fut called.append("c1b") async def cleanup2(): called.append("c2a") fut = hp.create_future() fut.set_result(True) await fut called.append("c2b") collector.photons_app.cleaners.extend([cleanup1, cleanup2]) called.append(2) try: hp.get_event_loop().call_later(0.02, collector.photons_app.final_future.cancel) await asyncio.sleep(10) called.append("unexpected") except: fut = hp.create_future() fut.set_result(True) await fut called.append(3) raise finally: called.append(4)
async def execute_task(self, collector, **kwargs): called.append(1) async def cleanup1(): called.append("c1a") fut = hp.create_future() fut.set_result(True) await fut called.append("c1b") async def cleanup2(): called.append("c2a") fut = hp.create_future() fut.set_result(True) await fut called.append("c2b") collector.photons_app.cleaners.extend([cleanup1, cleanup2]) called.append(2) try: raise PhotonsAppError("YO") except: fut = hp.create_future() fut.set_result(True) await fut called.append(3) raise finally: called.append(4)
class V: final_future = hp.create_future() default_broadcast = "1.2.3.255" @hp.memoized_property def transport_target(s): transport_target = mock.Mock( name="target", spec=[ "script", "final_future", "default_broadcast", "discovery_options", "gaps", ], ) transport_target.gaps = s.gaps transport_target.final_future = s.final_future transport_target.default_broadcast = s.default_broadcast transport_target.discovery_options = ( NoDiscoveryOptions.FieldSpec().empty_normalise() ) return transport_target @hp.memoized_property def session(s): return NetworkSession(s.transport_target) @hp.memoized_property def gaps(s): return Gaps( gap_between_ack_and_res=0.2, gap_between_results=0.35, timeouts=[(1, 1)] )
def wait_for_event(self, want): fut = hp.create_future(name=f"EventWaiter::wait_for_event[{repr(want)}]") def match(event): return event | want self.waiters.append((fut, match)) return fut
async def finish(self, exc_typ=None, exc=None, tb=None): self.final_future.cancel() async with hp.TaskHolder( hp.create_future(name="Finder::finish[task_holder_final_future]"), name="Finder::finish[task_holder]", ) as ts: for serial, device in sorted(self.devices.items()): ts.add(device.finish(exc_typ, exc, tb)) del self.devices[serial]
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()
async def execute_task(self, notify, output, **kwargs): try: fut = hp.create_future() fut.cancel() notify() await fut except asyncio.CancelledError: with open(output, "w") as fle: fle.write("CANCELLED") raise finally: with open(output, "a") as fle: fle.write(str(sys.exc_info()[0]))
async def retrieve(self, streamer): streamer.no_more_work() started = hp.create_future() async def retrieve(): results = [] started.set_result(True) async for result in streamer: results.append(result) return results return started, hp.async_as_background(retrieve())
def wait_for_incoming(self, io, pkt): if isinstance(pkt, type): desc = pkt.__name__ elif isinstance(pkt, tuple): desc = f"{pkt[0].__name__}({pkt[1]})" else: desc = "{pkt.__class__.__name__}({repr(pkt.payload)})" fut = hp.create_future(name=f"EventWaiter::wait_incoming[{io}, {desc}]") def match(event): return event == Events.INCOMING(self.device, io, pkt=pkt) self.waiters.append((fut, match)) return fut
class V: final_future = hp.create_future() @hp.memoized_property def transport_target(s): return mock.Mock( name="transport_target", final_future=s.final_future, protocol_register=protocol_register, spec=["final_future", "protocol_register"], ) @hp.memoized_property def communication(s): return Communication(s.transport_target)
async def separate_final_future(self, sleep=0): other_future = hp.create_future( name="PhotonsApp::separate_final_future") def stop(): hp.get_event_loop().call_soon_threadsafe(other_future.cancel) self.loop.remove_signal_handler(signal.SIGTERM) self.loop.add_signal_handler(signal.SIGTERM, stop) try: yield other_future finally: if sleep > 0: await asyncio.sleep(sleep) self.final_future.cancel()
class V: final_future = hp.create_future() error_catcher = mock.Mock(name="error_catcher") @hp.memoized_property def streamer(s): return hp.ResultStreamer(s.final_future, error_catcher=s.error_catcher) async def retrieve(s): results = [] s.streamer.no_more_work() async for result in s.streamer: results.append(result) return results
def resolve(self): def make_animation(final_future, pauser=None): options = self.animator.Options.FieldSpec().normalise(Meta.empty(), self.options) animation = self.animator.Animation(final_future, options, pauser=pauser) if self.options is not sb.NotSpecified: for attr in animation.overridable: if attr in self.options: setattr(animation, attr, self.options[attr]) return animation # Make sure the options can be resolved fut = hp.create_future(name="Animator>Resolver::resolve[fut]") fut.cancel() make_animation(fut) return make_animation, self.background
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 run_prompt(daemon, final_future, loop): readline.parse_and_bind("") def get_command(done): try: nxt = input() except Exception as error: if isinstance(error, (EOFError, KeyboardInterrupt)): error = UserQuit() if not final_future.done(): final_future.set_exception(error) if final_future.done(): return loop.call_soon_threadsafe(done.set_result, nxt.strip()) while True: write_prompt() done = hp.create_future(name="||run_prompt[done]") thread = threading.Thread(target=get_command, args=(done, )) thread.daemon = True thread.start() nxt = await done if nxt: try: await process_command(daemon, nxt) except asyncio.CancelledError: raise except: log.exception("Unexpected error") if final_future.done(): return
async def consume(self, gen, streamer): complete = None try: while True: try: msg = await gen.asend(complete) if isinstance(msg, Exception): hp.add_error(self.error_catcher, msg) continue if self.stop_fut.done(): break complete = hp.create_future( name="FromGenerator>Runner::getter[complete]") await streamer.add_generator(self.retrieve_all( msg, complete), context=self.Value) except StopAsyncIteration: break finally: exc_info = sys.exc_info() if exc_info[0] not in (None, asyncio.CancelledError): hp.add_error(self.error_catcher, exc_info[1]) await streamer.add_coroutine( hp.stop_async_generator( gen, complete, name="FromGenerator>Runner::consume[finally_stop_gen]", exc=exc_info[1], ), force=True, ) if exc_info[0] is not asyncio.CancelledError: return False
async def spawn(self, packet, *, timeout=10, create=True): if self.transport is not None: if self.transport.done() and (self.transport.cancelled() or self.transport.exception()): self.transport = None if self.transport is not None and self.transport.done(): if not await self.is_transport_active(packet, self.transport.result()): self.transport = None if not create and (self.transport is None or not self.transport.done()): return None if self.transport is None: self.transport = hp.create_future( name= f"Transport::{self.session.__class__.__name__}::spawn[transport]" ) t = hp.async_as_background(self.spawn_transport(timeout)) t.add_done_callback(hp.transfer_result(self.transport)) return await self.transport
async def execute_task(s, **kwargs): fut = hp.create_future() got.append((time.time(), "first")) async def wait(): try: got.append((time.time(), "wait")) await fut except asyncio.CancelledError: got.append((time.time(), "wait stopped")) s.task_holder.add(wait()) async def sleeper(): await asyncio.sleep(2) got.append((time.time(), "blink")) await asyncio.sleep(8) got.append((time.time(), "rested")) s.task_holder.add(sleeper()) async def cleaner1(): got.append((time.time(), "cleaner1")) s.photons_app.cleaners.append(cleaner1) async def cleaner2(): await asyncio.sleep(1) got.append((time.time(), "cleaner2")) s.photons_app.cleaners.append(cleaner2) await asyncio.sleep(3) got.append((time.time(), "wait now")) s.photons_app.loop.call_later(0.5, fut.cancel)
class V: final_future = hp.create_future() @hp.memoized_property def streamer(s): return hp.ResultStreamer(s.final_future)
def make_primary_msg(m): 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:
def final_future(self): fut = hp.create_future() try: yield fut finally: fut.cancel()
def final_future(): final_future = hp.create_future() try: yield final_future finally: final_future.cancel()
def final_future(): ff = hp.create_future() try: yield ff finally: ff.cancel()
import pytest import sys class C: def __eq__(self, other): 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)
import pytest import uuid import time @pytest.fixture() def loop(): return hp.get_event_loop() describe "creating a future": it "can create a future from a provided loop": fut = mock.Mock(name="future") loop = mock.Mock(name="loop") loop.create_future.return_value = fut assert hp.create_future(loop=loop) is fut assert fut.name is None loop.create_future.assert_called_once_with() it "can create a future from current loop": fut = hp.create_future() assert isinstance(fut, asyncio.Future) assert fut.name is None it "can give a name to the future": fut = hp.create_future(name="hi") assert fut.name == "hi" describe "fut_has_callback": async it "says no if fut has no callbacks":
# coding: spec from photons_control.device_finder import DeviceFinderDaemon, Finder, Filter, DeviceFinder, Device from photons_app import helpers as hp from photons_products import Products from unittest import mock import asyncio import pytest import time describe "DeviceFinderDaemon": it "takes in a sender": stop_fut = hp.create_future() sender = mock.Mock(name="sender", stop_fut=stop_fut) daemon = DeviceFinderDaemon(sender) assert daemon.sender is sender assert isinstance(daemon.final_future, hp.ChildOfFuture) assert daemon.final_future.original_fut is sender.stop_fut assert daemon.search_interval == 20 assert daemon.time_between_queries is None assert isinstance(daemon.finder, Finder) assert daemon.finder.sender is sender assert daemon.finder.final_future.original_fut is daemon.final_future assert daemon.finder.forget_after == 30
async def run( self, sig=None, expected_stdout=None, expected_stderr=None, expected_output=None, extra_argv=None, *, code, ): fut = hp.create_future() def ready(signum, frame): hp.get_event_loop().call_soon_threadsafe(fut.set_result, True) signal.signal(signal.SIGUSR1, ready) pipe = subprocess.PIPE cmd = [ sys.executable, self.fle.name, self.out.name, str(os.getpid()), *[str(a) for a in (extra_argv or [])], ] p = subprocess.Popen( cmd, stdout=pipe, stderr=pipe, ) await fut if sig is not None: p.send_signal(sig) p.wait(timeout=1) got_stdout = p.stdout.read().decode() got_stderr = p.stderr.read().decode().split("\n") redacted_stderr = [] in_tb = False last_line = None for line in got_stderr: if last_line == "During handling of the above exception, another exception occurred:": redacted_stderr = redacted_stderr[:-5] last_line = line continue if in_tb and not line.startswith(" "): in_tb = False if line.startswith("Traceback"): in_tb = True redacted_stderr.append(line) redacted_stderr.append(" <REDACTED>") if not in_tb: redacted_stderr.append(line) last_line = line got_stderr = "\n".join(redacted_stderr) print("=== STDOUT:") print(got_stdout) print("=== STDERR:") print(got_stderr) def assertOutput(out, regex): if regex is None: return regex = dedent(regex).strip() out = out.strip() regex = regex.strip() assert len(out.split("\n")) == len(regex.split("\n")) pytest.helpers.assertRegex(f"(?m){regex}", out) if expected_output is not None: got = None if os.path.exists(self.out.name): with open(self.out.name) as o: got = o.read().strip() assert got == expected_output.strip() assertOutput(got_stdout.strip(), expected_stdout) assertOutput(got_stderr.strip(), expected_stderr) assert p.returncode == code return got_stdout, got_stderr
@pytest.fixture(autouse=True) async def reset_devices(sender): for device in devices: await device.reset() devices.store(device).clear() sender.gatherer.clear_cache() describe "Pipeline": @pytest.mark.parametrize("reference", [devices.serials, FoundSerials()]) async it "does all messages at once if pipeline isn't used", sender, reference: called = [] wait = hp.create_future() async def see_request(event): if event | DeviceMessages.SetPower: called.append(event.pkt.serial) if len(called) == 3 and not wait.done(): wait.set_result(True) await asyncio.sleep(0) await wait isr1 = light1.io["MEMORY"].packet_filter.intercept_see_request(see_request) isr2 = light2.io["MEMORY"].packet_filter.intercept_see_request(see_request) isr3 = light3.io["MEMORY"].packet_filter.intercept_see_request(see_request) msgs = [ DeviceMessages.SetPower(level=0),