feat(ophyd): temporary until new Ophyd release, prevent Status objects threads

Monkey-patching of Ophyd library
This commit is contained in:
guijar_m 2024-03-18 15:32:12 +01:00 committed by guijar_m
parent c9d1e0db05
commit df8ce79ca0
3 changed files with 105 additions and 0 deletions

View File

@ -1,3 +1,7 @@
from .ophyd_patch import monkey_patch_ophyd
monkey_patch_ophyd()
from .eiger1p5m_csaxs.eiger1p5m import Eiger1p5MDetector
from .epics import *
from .galil.fgalil_ophyd import FlomniGalilMotor

View File

@ -0,0 +1,50 @@
import inspect
import threading
import types
from ophyd import status as ophyd_status_module
from ophyd.status import StatusBase
from unittest.mock import patch, Mock
dummy_thread = Mock(spec=threading.Thread)
class PatchedStatusBase(StatusBase):
def __init__(self, *args, **kwargs):
timeout = kwargs.get("timeout", None)
if not timeout:
with patch("threading.Thread", dummy_thread):
super().__init__(*args, **kwargs)
else:
super().__init__(*args, **kwargs)
def set_finished(self, *args, **kwargs):
super().set_finished(*args, **kwargs)
if isinstance(self._callback_thread, Mock):
if self.settle_time > 0:
def settle_done():
self._settled_event.set()
self._run_callbacks()
threading.Timer(self.settle_time, settle_done).start()
else:
self._run_callbacks()
def set_exception(self, *args, **kwargs):
super().set_exception(*args, **kwargs)
if isinstance(self._callback_thread, Mock):
self._run_callbacks()
def monkey_patch_ophyd():
if ophyd_status_module.StatusBase.__name__ == "PatchedStatusBase":
# prevent patching multiple times
return
for name, klass in inspect.getmembers(
ophyd_status_module, lambda x: inspect.isclass(x) and StatusBase in x.__mro__
):
mro = klass.mro()
bases = tuple(PatchedStatusBase if x is StatusBase else x for x in mro)
new_klass = type("Patched" + name, bases, {})
setattr(ophyd_status_module, name, new_klass)

View File

@ -0,0 +1,51 @@
import pytest
import threading
import time
from unittest.mock import Mock
import ophyd_devices # ensure we are patched
from ophyd.status import StatusBase, StatusTimeoutError
def test_ophyd_status_patch():
cb = Mock()
st = StatusBase(timeout=1)
assert isinstance(st._callback_thread, threading.Thread)
st.add_callback(cb)
with pytest.raises(StatusTimeoutError):
time.sleep(1.1)
st.wait()
cb.assert_called_once()
cb.reset_mock()
st = StatusBase()
assert isinstance(st._callback_thread, Mock)
st.add_callback(cb)
st.set_finished()
cb.assert_called_once()
cb.reset_mock()
st.wait()
st = StatusBase(settle_time=1)
st.add_callback(cb)
assert isinstance(st._callback_thread, Mock)
st.set_finished()
assert cb.call_count == 0
time.sleep(0.5)
assert cb.call_count == 0 # not yet!
time.sleep(0.6)
cb.assert_called_once()
cb.reset_mock()
st.wait()
class TestException(RuntimeError):
pass
st = StatusBase()
st.add_callback(cb)
st.set_exception(TestException())
cb.assert_called_once()
with pytest.raises(TestException):
st.wait()