Skip to content

Commit

Permalink
Emit deprecation warnings and add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
coretl committed Oct 24, 2024
1 parent 287fd4c commit 2a16755
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 42 deletions.
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ addopts = """
--doctest-glob="*.rst" --doctest-glob="*.md" --ignore=docs/examples
"""
# https://iscinumpy.gitlab.io/post/bound-version-constraints/#watch-for-warnings
filterwarnings = "error"
filterwarnings = [
"error",
"ignore:Use `ophyd_async.epics.core` instead of `ophyd_async.epics.signal` and `pvi`:DeprecationWarning",
"ignore:Use `StandardReadableFormat.CONFIG_SIGNAL` instead of `ConfigSignal`:DeprecationWarning",
"ignore:Use `StandardReadableFormat.HINTED_SIGNAL` instead of `HintedSignal`:DeprecationWarning",
"ignore:Use `StandardReadableFormat.HINTED_UNCACHED_SIGNAL` instead of `HintedSignal.uncached`:DeprecationWarning",
]
# Doctest python code in docs, python code in src docstrings, test functions in tests
testpaths = "docs src tests"
log_format = "%(asctime)s,%(msecs)03d %(levelname)s (%(threadName)s) %(message)s"
Expand Down
100 changes: 61 additions & 39 deletions src/ophyd_async/core/_readable.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import warnings
from collections.abc import Awaitable, Callable, Generator, Sequence
from contextlib import contextmanager
from enum import Enum
from typing import cast

from bluesky.protocols import HasHints, Hints, Reading
from event_model import DataKey
Expand All @@ -13,10 +15,26 @@


class StandardReadableFormat(Enum):
"""Declare how a `Device` should contribute to the `StandardReadable` verbs."""

#: Detect which verbs the child supports and contribute to:
#:
#: - ``read()``, ``describe()`` if it is `bluesky.protocols.Readable`
#: - ``read_configuration()``, ``describe_configuration()`` if it is
#: `bluesky.protocols.Configurable`
#: - ``stage()``, ``unstage()`` if it is `bluesky.protocols.Stageable`
#: - ``hints`` if it `bluesky.protocols.HasHints`
CHILD = "CHILD"
#: Contribute the `Signal` value to ``read_configuration()`` and
#: ``describe_configuration()``
CONFIG_SIGNAL = "CONFIG_SIGNAL"
#: Contribute the monitored `Signal` value to ``read()`` and ``describe()``` and
#: put the signal name in ``hints``
HINTED_SIGNAL = "HINTED_SIGNAL"
#: Contribute the uncached `Signal` value to ``read()`` and ``describe()```
UNCACHED_SIGNAL = "UNCACHED_SIGNAL"
#: Contribute the uncached `Signal` value to ``read()`` and ``describe()``` and
#: put the signal name in ``hints``
HINTED_UNCACHED_SIGNAL = "HINTED_UNCACHED_SIGNAL"

def __call__(self, parent: Device, child: Device):
Expand All @@ -26,9 +44,31 @@ def __call__(self, parent: Device, child: Device):


# Back compat
ConfigSignal = StandardReadableFormat.CONFIG_SIGNAL
HintedSignal = StandardReadableFormat.HINTED_SIGNAL
HintedSignal.uncached = StandardReadableFormat.HINTED_UNCACHED_SIGNAL # type: ignore
class _WarningMatcher:
def __init__(self, name: str, target: StandardReadableFormat):
self._name = name
self._target = target

def __eq__(self, value: object) -> bool:
warnings.warn(
DeprecationWarning(
f"Use `StandardReadableFormat.{self._target.name}` "
f"instead of `{self._name}`"
),
stacklevel=2,
)
return value == self._target


def _compat_format(name: str, target: StandardReadableFormat) -> StandardReadableFormat:
return cast(StandardReadableFormat, _WarningMatcher(name, target))


ConfigSignal = _compat_format("ConfigSignal", StandardReadableFormat.CONFIG_SIGNAL)
HintedSignal = _compat_format("HintedSignal", StandardReadableFormat.HINTED_SIGNAL)
HintedSignal.uncached = _compat_format( # type: ignore
"HintedSignal.uncached", StandardReadableFormat.HINTED_UNCACHED_SIGNAL
)


class StandardReadable(
Expand Down Expand Up @@ -112,27 +152,13 @@ def hints(self) -> Hints:
@contextmanager
def add_children_as_readables(
self,
wrapper: StandardReadableFormat = StandardReadableFormat.CHILD,
format: StandardReadableFormat = StandardReadableFormat.CHILD,
) -> Generator[None, None, None]:
"""Context manager to wrap adding Devices
"""Context manager that calls `add_readables` on child Devices added within.
Add Devices to this class instance inside the Context Manager to automatically
add them to the correct fields, based on the Device's interfaces.
The provided wrapper class will be applied to all Devices and can be used to
specify their behaviour.
Parameters
----------
wrapper:
Wrapper class to apply to all Devices created inside the context manager.
See Also
--------
:func:`~StandardReadable.add_readables`
:class:`ConfigSignal`
:class:`HintedSignal`
:meth:`HintedSignal.uncached`
Scans ``self.children()`` on entry and exit to context manager, and calls
`add_readables` on any that are added with the provided
`StandardReadableFormat`.
"""

dict_copy = dict(self.children())
Expand All @@ -152,38 +178,34 @@ def add_children_as_readables(
flattened_values.append(value)

new_devices = list(filter(lambda x: isinstance(x, Device), flattened_values))
self.add_readables(new_devices, wrapper)
self.add_readables(new_devices, format)

def add_readables(
self,
devices: Sequence[Device],
wrapper: StandardReadableFormat = StandardReadableFormat.CHILD,
format: StandardReadableFormat = StandardReadableFormat.CHILD,
) -> None:
"""Add the given devices to the lists of known Devices
"""Add devices to contribute to various bluesky verbs.
Add the provided Devices to the relevant fields, based on the Signal's
interfaces.
Use output from the given devices to contribute to the verbs of the following
interfaces:
The provided wrapper class will be applied to all Devices and can be used to
specify their behaviour.
- `bluesky.protocols.Readable`
- `bluesky.protocols.Configurable`
- `bluesky.protocols.Stageable`
- `bluesky.protocols.HasHints`
Parameters
----------
devices:
The devices to be added
wrapper:
Wrapper class to apply to all Devices created inside the context manager.
See Also
--------
:func:`~StandardReadable.add_children_as_readables`
:class:`ConfigSignal`
:class:`HintedSignal`
:meth:`HintedSignal.uncached`
format:
Determines which of the devices functions are added to which verb as per the
`StandardReadableFormat` documentation
"""

for device in devices:
match wrapper:
match format:
case StandardReadableFormat.CHILD:
if isinstance(device, AsyncConfigurable):
self._describe_config_funcs += (device.describe_configuration,)
Expand Down
4 changes: 2 additions & 2 deletions src/ophyd_async/epics/adcore/_single_trigger.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ def __init__(

self.add_readables(
[self.drv.array_counter, *read_uncached],
wrapper=HintedSignal.uncached,
HintedSignal.uncached, # type: ignore
)

self.add_readables([self.drv.acquire_time], wrapper=ConfigSignal)
self.add_readables([self.drv.acquire_time], ConfigSignal)

super().__init__(name=name)

Expand Down
9 changes: 9 additions & 0 deletions src/ophyd_async/epics/signal.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,11 @@
# back compat
import warnings

from .core import * # noqa: F403

warnings.warn(
DeprecationWarning(
"Use `ophyd_async.epics.core` instead of `ophyd_async.epics.signal` and `pvi`"
),
stacklevel=2,
)

0 comments on commit 2a16755

Please sign in to comment.