bluesky / ophyd

hardware abstraction in Python with an emphasis on EPICS
https://blueskyproject.io/ophyd
BSD 3-Clause "New" or "Revised" License
51 stars 79 forks source link

BUG: isinstance calls property success before status object is done #1099

Closed tizayi closed 1 year ago

tizayi commented 1 year ago

Causes problems in ophyd-epics-devices when tying to create stage methods that return AsyncStatus objects.

    Traceback (most recent call last):
      File "/path/ophyd-epics-devices/tests/test_area_detector.py", line 233, in test_async_stage_single_trigger
        RE(async_stage_plan())
      File "/path/bluesky/bluesky/run_engine.py", line 895, in __call__
        plan_return = self._resume_task(init_func=_build_task)
      File "/path/bluesky/bluesky/run_engine.py", line 1034, in _resume_task
        raise exc
      File "/path/bluesky/bluesky/run_engine.py", line 1664, in _run
        raise err
      File "/path/mrbluesky/bluesky/bluesky/run_engine.py", line 1499, in _run
        msg = self._plan_stack[-1].throw(stashed_exception or resp)
      File "/path/ophyd-epics-devices/tests/test_area_detector.py", line 228, in async_stage_plan
        yield from bps.stage_all(det1, det2)
      File "/path/bluesky/bluesky/plan_stubs.py", line 753, in stage_all
        ret = yield Msg('stage', obj, group=group)
      File "/path/bluesky/bluesky/run_engine.py", line 1584, in _run
        new_response = await coro(msg)
      File "/path/bluesky/bluesky/run_engine.py", line 2368, in _stage
        if not isinstance(ret, Status):
      File "/path/python/anaconda/4.6.14/64/envs/python3.8/lib/python3.8/typing.py", line 1014, in __instancecheck__
        if all(hasattr(instance, attr) and
      File "/path/python/anaconda/4.6.14/64/envs/python3.8/lib/python3.8/typing.py", line 1014, in <genexpr>
        if all(hasattr(instance, attr) and
      File "/path/ophyd/ophyd/v2/core.py", line 84, in success
        assert self.done, "Status has not completed yet"
    AssertionError: Status has not completed yet

In the protocol part of the typing module:

    class _ProtocolMeta(ABCMeta):
        # This metaclass is really unfortunate and exists only because of
        # the lack of __instancehook__.
        def __instancecheck__(cls, instance):
            # We need this method for situations where attributes are
            # assigned in __init__.
            if ((not getattr(cls, '_is_protocol', False) or
                    _is_callable_members_only(cls)) and
                    issubclass(instance.__class__, cls)):
                return True
            if cls._is_protocol:
                if all(hasattr(instance, attr) and
                        # All *methods* can be blocked by setting them to None.
                        (not callable(getattr(cls, attr, None)) or
                         getattr(instance, attr) is not None)
                        for attr in _get_protocol_attrs(cls)):
                    return True
            return super().__instancecheck__(instance)
tacaswell commented 1 year ago

I can also see the case for returning None here, its falsy, but makes no other claim...

coretl commented 1 year ago

Decided to leave it as False