-
Notifications
You must be signed in to change notification settings - Fork 38
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
Add additional callbacks for SharedPV handlers #155
base: master
Are you sure you want to change the base?
Conversation
@@ -154,10 +178,23 @@ def open(self, value, nt=None, wrap=None, unwrap=None, **kws): | |||
self._wrap = wrap or (nt and nt.wrap) or self._wrap | |||
self._unwrap = unwrap or (nt and nt.unwrap) or self._unwrap | |||
|
|||
# Intercept all arguments that start with 'handler_open_' and remove them from | |||
# the arguments that go to the wrap and send them instead to the handler.open() | |||
post_kws = {x: kws.pop(x) for x in [y for y in kws if y.startswith("handler_open_")]} |
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, and the equivalent line 212 for post()
, is probably one of the more important changes. It means that any arguments prefixed handler_open_
will be passed to the handler (if it exists) and not to _wrap()
. An earlier version of the code added a single new argument that enabled or disabled the handler altogether. But while a smaller change it was also much crude.
@property | ||
def onFirstConnect(self): | ||
def on_open(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.
This is a leftover from an earlier version in which I thought I'd needed to supply decorator names that wouldn't clash with the function names. It does make the names PEP8 compliant so I've left the change in for consideration.
# Aliases for decorators to maintain consistent new style | ||
# Required because post is already used and on_post seemed the best | ||
# alternative. | ||
put = on_put |
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.
Compatibility for previous decorator names.
self._handler.post(self, V, **post_kws) | ||
except AttributeError: | ||
pass | ||
|
||
_SharedPV.post(self, V) |
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.
Implementing the persist.py
example has made me think there may be a case to split the post()
callback into two. The one implemented in this PR that changes values before they reach this _SharedPV.post(self, V)
and one which is called afterwards. The after_post()
callback would then only be supplied the updated state of the PV and would not be triggered by unsuccessful posts.
It was suggested at a meeting on 9 Oct that I tag @coretl and @AlexanderWells-diamond for attention or review. Apologies if this comes as a surprise! |
This PR adds three new callback functions for use by SharedPV (
p4p.server.raw
) handlers. They are:open()
- called when a SharedPV is openedpost()
- called each time a post operation is performed on a SharedPVclose()
- called when a SharedPV is closed.The associated changes to the
p4p.server.raw.Handler
class, additional new decorators, and unit tests (src/p4p/test/test_sharedpv.py
) are included. The changes tosrc/p4p/server/raw.py
are relatively minor but the examples and unit tests make this look like a much bigger PR! There was some initial discussion of the idea at #150.Three examples that make use of the new callback functions are also included:
example/auditor.py
- demonstrates usingopen()
andclose()
callbacks along with the existingput()
callback to record when an auditing PV that records who's made changes to other PVs is or isn't available. Probably redundant compared to the more complexexample/persist.py
.example/ntscalar_control.py
- implements the Control field logic for an NTScalar. This illustrates howput()
,post()
, andopen()
lead to a natural separation of concerns. Logic that does not depend on the previous state of the PV may be implemented in the handleropen()
, e.g. altering thevalue
dependent oncontrol.limitHigh
andcontrol.limitLow
. Logic that depends on the current state of the PV and the proposed changes may be implemented in thepost()
, e.g. applyingcontrol.minStep
. Theput()
may then be solely concerned with authorisation.example/persist.py
- demonstrates using an SQLite3 database to automatically save and restore the values of PVs. Makes use of the newhandler_open_
andhandler_post_
prefix arguments to make configuration convenient and to transmit information from aput()
to apost()
respectively. Makes full use of the newopen()
callback to automatically restore the value and timeStamp information of a PV andpost()
to automatically record this information each time it changes.I believe the
example/persist.py
file probably makes the strongest case for the kind of new features that these callbacks allow or make easier.Breaking Changes
I believe these changes may be considered largely backwards compatible. They will only cause compatibility issues:
open()
,close()
, orpost()
functions.handler_open_
orhandler_post_
.