-
Notifications
You must be signed in to change notification settings - Fork 46
Add additional callbacks for SharedPV handlers #155
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
base: master
Are you sure you want to change the base?
Conversation
|
||
# 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.
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! |
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.
The code changes look fine to me, barring a few things that are little more than nitpicks, although as discussed offline I'm no expert on p4p's design philosophy so can't comment on whether the changes are fundamentally ok. I've tagged @mdavidsaver for further review.
I think the server.rst
file needs updating to properly include the newly defined methods for SharedPV; looks like you need to define automethod
for each new method.
|
||
# Because we have not set use_handler_post=False in the post this | ||
# will automatically trigger evaluation of the post rule and thus | ||
# the application of |
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.
Looks like the end of this comment is missing?
class PersistHandler(Handler): | ||
""" | ||
A handler that will allow simple persistence of values and timestamps | ||
across retarts. It requires a post handler in order to persist values |
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.
across retarts. It requires a post handler in order to persist values | |
across restarts. It requires a post handler in order to persist values |
|
||
# Create an SQLite dayabase to function as our persistence store | ||
conn = sqlite3.connect("persist_pvs.db", check_same_thread=False) | ||
# conn.execute("DROP TABLE IF EXISTS pvs") |
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 think this comment is unnecessary, looks like you do the same thing in the next line?
class Audited(Handler): | ||
"""Forward information about Put operations to the auditing PV""" | ||
|
||
def __init__(self, pv: SharedPV): |
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 note that this type hint doesn't work in Python2; I don't know how important it is for all the examples to still support Python2, but the overall library still does.
def tearDown(self): | ||
self.server.stop() | ||
_defaultWorkQueue.sync() | ||
#self.pv._handler._pv = None |
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.
#self.pv._handler._pv = None |
pass | ||
|
||
def setUp(self): | ||
# gc.set_debug(gc.DEBUG_LEAK) |
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.
Another comment that can probably be removed
def tearDown(self): | ||
self.server.stop() | ||
_defaultWorkQueue.sync() | ||
#self.pv._handler._pv = None |
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.
#self.pv._handler._pv = None |
Called each time an Open operation is performed on this Channel | ||
:param value: A Value, or appropriate object (see nt= and wrap= of the constructor). | ||
""" |
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.
Should this (and the post
and close
methods) call op.done(error='Not supported')
), as other methods do?
At the very least for consistency this one should have a pass
, to be the same as the other methods that don't call that method.
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_
.