Skip to content

Commit

Permalink
Merge pull request #907 from BCDA-APS/881-listdevice-defaults
Browse files Browse the repository at this point in the history
listdevice(show_pv=True) alternate defaults
  • Loading branch information
prjemian committed Jan 9, 2024
2 parents e5231a4 + f74dcf0 commit d4bb2cf
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Fixes
Maintenance
-----------

* In ``listdevice(show_pv=True)``, set ``cname=True, dname=False`` if not provided by caller.
* Move ``.OVAL`` field from ``EpicsRecordOutputFields to new ``EpicsRecordAnalogOutputFields``
* Write tables of plot statistics in most compact form.

Expand Down
1 change: 1 addition & 0 deletions apstools/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from .log_utils import setup_IPython_console_logging
from .log_utils import stream_log_handler
from .memory import rss_mem
from .misc import call_signature_decorator
from .misc import cleanupText
from .misc import connect_pvlist
from .misc import count_child_devices_and_signals
Expand Down
11 changes: 11 additions & 0 deletions apstools/utils/device_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from ophyd.signal import EpicsSignalBase

from ._core import TableStyle
from .misc import call_signature_decorator

logger = logging.getLogger(__name__)
pd.set_option("display.max_rows", None)
Expand Down Expand Up @@ -92,6 +93,7 @@ def _list_epics_signals(obj):
return items


@call_signature_decorator
def listdevice(
obj,
scope=None,
Expand All @@ -102,6 +104,7 @@ def listdevice(
show_ancient=True,
max_column_width=None,
table_style=TableStyle.pyRestTable,
_call_args=None,
):
"""Describe the signal information from device ``obj`` in a pandas DataFrame.
Expand Down Expand Up @@ -135,6 +138,11 @@ def listdevice(
column ``PV``.
default: ``False``
.. note:: Special case when ``show_pv=True``:
If ``cname`` is not provided, it will be set ``True``.
If ``dname`` is not provided, it will be set ``False``.
use_datetime *bool* :
Show the EPICS timestamp (time of last update) in
column ``timestamp``.
Expand Down Expand Up @@ -192,6 +200,9 @@ def listdevice(
)
# fmt: on

if show_pv:
cname = cname if "cname" in _call_args else True
dname = dname if "dname" in _call_args else False
if not cname and not dname:
cname = True

Expand Down
41 changes: 41 additions & 0 deletions apstools/utils/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
.. autosummary::
~call_signature_decorator
~cleanupText
~connect_pvlist
~count_child_devices_and_signals
Expand All @@ -25,6 +26,7 @@
~unix
"""

import inspect
import logging
import pathlib
import re
Expand All @@ -35,6 +37,7 @@
import warnings
from collections import OrderedDict
from collections import defaultdict
from functools import wraps

import databroker
import ophyd
Expand All @@ -51,6 +54,44 @@
logger = logging.getLogger(__name__)


def call_signature_decorator(f):
"""
Get the names of all function parameters supplied by the caller.
This is used to differentiate user-supplied parameters from as-defined
parameters with the same value.
HOW TO USE THIS DECORATOR:
Decorate a function or method with this decorator *and* add an additional
`_call_args=None` kwarg to the function. The function can test `_call_args`
if a specific kwarg was supplied by the caller.
EXAMPLE::
@call_signature_decorator
def func1(a, b=1, c=True, _call_args=None):
if 'c' in _call_args: # Caller supplied this kwarg?
pass
.. note:: With ``call_signature_decorator``, it is not possible to get the names
of the positional arguments. Since positional parameters are not specified by
name, such capability is not expected to become a requirement.
:see: https://stackoverflow.com/questions/14749328#58166804
(how-to-check-whether-optional-function-parameter-is-set)
"""
key = "_call_args"
varnames = inspect.getfullargspec(f)[0]

@wraps(f)
def wrapper(*a, **kw):
kw[key] = set(list(varnames[: len(a)]) + list(kw.keys()))
return f(*a, **kw)

return wrapper


def cleanupText(text):
"""
convert text so it can be used as a dictionary key
Expand Down
40 changes: 37 additions & 3 deletions apstools/utils/tests/test_listdevice.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
from ophyd import Signal
from ophyd.signal import EpicsSignalBase

from .._core import TableStyle
from ...devices import SwaitRecord
from ...tests import IOC_GP
from ..device_info import _list_epics_signals
from .._core import TableStyle
from ..device_info import DEFAULT_COLUMN_WIDTH
from ..device_info import listdevice
from ..device_info import NOT_CONNECTED_VALUE
from ..device_info import _list_epics_signals
from ..device_info import listdevice


class MySignals(Device):
Expand Down Expand Up @@ -208,3 +208,37 @@ def test_maximum_column_width(width):
if width is not None:
for column, content in enumerate(result[0].split()):
assert len(content) <= (width), f"{column=} {content=}"


@pytest.mark.parametrize(
"sig, has_cname, has_dname, has_PV",
[
[{}, False, True, False],
[{"cname": True, "dname": False}, True, False, False],
[{"cname": True}, True, True, False],
[{"show_pv": False}, False, True, False],
[{"show_pv": True, "cname": False, "dname": True}, False, True, True],
[{"show_pv": True, "cname": False}, True, False, True],
[{"show_pv": True, "cname": True, "dname": False}, True, False, True],
[{"show_pv": True, "cname": True}, True, False, True],
[{"show_pv": True, "dname": False}, True, False, True],
[{"show_pv": True, "dname": True}, True, True, True],
[{"show_pv": True}, True, False, True],
],
)
def test_listdevice_show_pv(sig, has_cname, has_dname, has_PV):
line = str(listdevice(motor, **sig)).splitlines()[1].strip()
if has_cname:
assert line.split()[0] == "name", f"{line=}"
else:
assert line.split()[0] != "name", f"{line=}"

if has_dname:
assert "data name" in line, f"{line=}"
else:
assert "data name" not in line, f"{line=}"

if has_PV:
assert "PV" in line, f"{line=}"
else:
assert "PV" not in line, f"{line=}"
34 changes: 34 additions & 0 deletions apstools/utils/tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ... import utils
from .._core import MAX_EPICS_STRINGOUT_LENGTH
from .._core import TableStyle
from ..misc import call_signature_decorator

CATALOG = "usaxs_test"
COUNT = "555a604" # <-- uid, scan_id: 2
Expand Down Expand Up @@ -530,3 +531,36 @@ def test_utils_getStreamValues_Exception(
def test_count_common_subdirs(p1, p2, expected):
icommon = utils.count_common_subdirs(p1, p2)
assert icommon == expected, f"{p1=} {p2=}"


@pytest.mark.parametrize(
"args, kwargs, expect",
[
[[1], {}, (False, False, False, False)],
[[1], {}, (False, False, False, False)],
[[1, 2], {}, (True, False, False, False)],
[[1, 2], {"show_pv": 1}, (True, False, False, True)],
[[1], {"cname": 1}, (False, True, False, False)],
[[1], {"dname": 1}, (False, False, True, False)],
[[1], {"b": 1, "cname": 1}, (True, True, False, False)],
[[1], {"b": 1, "dname": 1}, (True, False, True, False)],
[[1], {"show_pv": 1, "cname": False}, (False, True, False, True)],
[[1], {"show_pv": 1, "cname": True}, (False, True, False, True)],
[[1], {"show_pv": 1, "dname": False}, (False, False, True, True)],
[[1], {"show_pv": 1, "dname": True}, (False, False, True, True)],
[[1], {"show_pv": 1, "dname": False, "cname": False}, (False, True, True, True)],
[[1], {"show_pv": 1, "dname": True, "cname": True}, (False, True, True, True)],
],
)
def test_call_signature_decorator(args, kwargs, expect):
@call_signature_decorator
def func1(a, b=1, *, cname=False, dname=True, show_pv=None, _call_args=None):
return (
"b" in _call_args,
"cname" in _call_args,
"dname" in _call_args,
"show_pv" in _call_args,
)

result = func1(*args, **kwargs)
assert result == expect, f"{result=} {expect=}"
1 change: 1 addition & 0 deletions docs/source/api/_utils.rst
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ General

.. autosummary::

~apstools.utils.misc.call_signature_decorator
~apstools.utils.misc.cleanupText
~apstools.plans.command_list.command_list_as_table
~apstools.utils.misc.connect_pvlist
Expand Down

0 comments on commit d4bb2cf

Please sign in to comment.