Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/fastcs/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ async def _start_scan_tasks(
):
self._scan_tasks = {self._loop.create_task(coro()) for coro in coros}

for task in self._scan_tasks:
task.add_done_callback(self._scan_done)

def _scan_done(self, task: asyncio.Task):
try:
task.result()
except Exception as e:
raise FastCSException(
"Exception raised in scan method of "
f"{self._controller.__class__.__name__}"
) from e

def _stop_scan_tasks(self):
for task in self._scan_tasks:
if not task.done():
Expand Down
38 changes: 38 additions & 0 deletions tests/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from fastcs.controller import Controller
from fastcs.cs_methods import Command
from fastcs.datatypes import Int
from fastcs.exceptions import FastCSException
from fastcs.wrappers import command, scan


Expand Down Expand Up @@ -127,3 +128,40 @@ async def test_wrapper():

assert len(backend._scan_tasks) == 1
assert len(backend._initial_coros) == 2


def test_scan_raises_exception_via_callback():
class MyTestController(Controller):
def __init__(self):
super().__init__()

@scan(0.1)
async def raise_exception(self):
raise ValueError("Scan Exception")

controller = MyTestController()
loop = asyncio.get_event_loop()
backend = Backend(controller, loop)

exception_info = {}
# This will intercept the exception raised in _scan_done
loop.set_exception_handler(
lambda _loop, context: exception_info.update(
{"exception": context.get("exception")}
)
)

async def test_scan_wrapper():
await backend.serve()
# This allows scan time to run
await asyncio.sleep(0.2)
# _scan_done should raise an exception
assert isinstance(exception_info["exception"], FastCSException)
for task in backend._scan_tasks:
internal_exception = task.exception()
assert internal_exception
# The task exception comes from scan method raise_exception
assert isinstance(internal_exception, ValueError)
assert "Scan Exception" == str(internal_exception)

loop.run_until_complete(test_scan_wrapper())
Loading