-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
signal: init value can be a ValueInfo structure with dtype, shape and… #1194
Conversation
Please comment , if you agree with the proposed change I will add a test. |
ophyd/signal.py
Outdated
@@ -95,7 +102,7 @@ def __init__( | |||
self, | |||
*, | |||
name, | |||
value=0.0, | |||
value=DEFAULT_EPICSSIGNAL_VALUE, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we move this here I suspect we will have to hoist the logic to handle this from EpicsSignal up to the base class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well, it looks like DEFAULT_EPICSSIGNAL_VALUE
is more used as a sentinel value to indicate if a default has been provided or not - it is currently already used in Signal
class, see line 475:
if self._readback is DEFAULT_EPICSSIGNAL_VALUE:
val = self.get()
or derived classes which do not have an explicit relation with EPICS (i.e not deriving from EpicsSignalBase
).
I propose to rename it, then ? Like UNSET_VALUE
or UNSPECIFIED_VALUE
?
It seems None
is considered as a potential ok value. Is it the case ? Otherwise we could initialize with None
.
In describe()
, I think the key point is to really read the value if it is not specified. Any way to do it like this is fine for me.
What do you think ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, if we are already using it is the base class than I'm 50/50 on adding an alias (the hassle of anyone downstream using the old name needing to update is not worth it to remove the old name) vs just using it.
The concern I still have is that in Signal.get
we just return self._readback
which means that there is a chance of returning DEFAULT_EPICSSIGNAL_VALUE
to the user (which we definitely should not do). The minimal fix is probably to raise in Signal.get
if the value has never been set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't it be better to initialize to None
and to return None
if it has not been set? I am very new to Ophyd, so I do not know the logic/philosophy behind reading signals and why DEFAULT_EPICSSIGNAL_VALUE
was introduced at first place
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Otherwise I can make an alias and raise in get()
as you suggest, no problem 👍🏻
Tell me what you prefer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tl;dr: I would prefer the alias + raise in get
path as the minimal change.
pyepics (the default wrapping we use to talk channel access over in Python) returns None
when it fails so defaulting to None
means we could not tell the difference between "failed to read" vs "never read". The logic was to have a unique sentinel that would never come to use from pyepics and would never be passed to us by the user so we could tell if we had ever been told a value for this signal. Basically detect the start up transient and allows lazy connection (each PV is its own connection dance with a server and if we never read it then it is better to never try to connect).
Going with a custom default object also helps makes clear that it is "default but invalid" where as None
is frequently "default and valid" or "placeholder to tell the system to do the default thing" and I think that is an important distinction here.
I'm a bit fuzzy remember choices we made probably 8 years ago now, but I am biased to trusted my previous self 🤣
Interesting idea! I have some concerns that this will box us out from ever fixing We have been using I lean +.75 on this being a good idea . |
I agree with this.
Ok I renamed shape and dtype vars to |
605ab8e
to
f1849e0
Compare
The value of DEFAULT_EPICSSIGNAL_VALUE = object()
DEFAULT_EPICSSIGNAL_VALUE = uuid.uuid4() |
6c7a836
to
c2be5d3
Compare
Hi @tacaswell , @prjemian 😄 This is ready for me. I don't know why doc would not build... ? I added specific tests for the feature. In addition to what we discussed above, I renamed I would be happy to work on having |
No news ; is there anything wrong with the proposal? |
@prjemian @tacaswell I apologize in advance if you're on holidays or too busy. I really need to have proper type info in Ophyd, sorry to insist - if you think this proposal can be improved do not hesitate to tell me, or if I have to do something to make it approved ? |
I'm OK with the part on which I commented. I'll leave it to Tom for the PR review. |
Hi @tacaswell! What's the status on this one? It would help us a lot if we could get this one merged soon. Thanks! |
c2be5d3
to
fd75ea7
Compare
Hi again... I am reviving this discussion, I really need the added functionality so can we find an agreement on this? |
Sorry I never found you again at NOBUGs! |
ophyd/signal.py
Outdated
@@ -95,7 +104,7 @@ def __init__( | |||
self, | |||
*, | |||
name, | |||
value=0.0, | |||
value=NumericValueInfo(float, (), 0.0), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than merging the expected info and the default value, can we add a new key(s) of expected_dtype_and_shape
or dtype
and shape
? In __init__
it seems reasonable to do a cast, but in describe
I wonder if we should be checking if the value we have in had is actually the correct dtype/shape or not?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my original proposal I didn't want to add more keyword args, but yeah sure - I introduced dtype
and shape
in latest version.
For the check in describe: I didn't change anything yet
ophyd/tests/test_signal.py
Outdated
|
||
original = Signal(name="original") | ||
original_desc = original.describe()["original"] | ||
assert original_desc["dtype"] == "float64" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is out of the schema for things that can be in descriptors: https://github.com/bluesky/event-model/blob/a1fdaec0c45b89e513d0be9dba0bfc453982e5bb/src/event_model/schemas/event_descriptor.json#L51-L62
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok so newest version has dtype
and dtype_numpy
The more-complete dtype information should be put in |
75c4f6e
to
7e33cc1
Compare
@tacaswell sorry for the delay... I struggled a bit with the changes (this is why test didn't pass last time). In fact I asked me a lot of questions, finally I think I get it all right. For the review: maybe the easiest is to have a look to the new test ( What I find relevant to know:
|
7e33cc1
to
cf6b4a5
Compare
Other remark:
|
Maybe we could get the RE to look at
That seems fine as |
ophyd/ophydobj.py
Outdated
@@ -588,14 +588,14 @@ def _repr_info(self): | |||
if self._parent is not None: | |||
yield ("parent", self.parent.name) | |||
|
|||
def __copy__(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did you remove the custom __copy__
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I said in the comment above:
- I removed a weird copy dunder
- with the original copy one test was not passing, because it recreates the object with different constructor arguments
- I could not see why it was like this, no specific test neither
But, with my newest code the failing test works again. So I re-enabled __copy__
, no prob.
(however, I still don't understand what it is for)
0012943
to
4537842
Compare
4537842
to
db157c5
Compare
@tacaswell Here we are. Should be ready to merge now ? |
Just to be precise: current code I sent, do not always provide About replacing 0.0 default value with
Ok. I will make another MR, then. It is not urgent for me. |
… default value to characterize the value
Solves issue #1193 (and #1178 ?)