Raise RuntimeError if watcher.cancel() is called after client.close()#17
Raise RuntimeError if watcher.cancel() is called after client.close()#17mk-fg wants to merge 1 commit intomartyanov:masterfrom
Conversation
19bf4b2 to
20f45a6
Compare
If trying to do something with watcher after client is already closed via watcher.cancel() should be considered a bug, then I think opening any new iterators on it should almost certainly be a bug as well. |
be1ff05 to
41ed34f
Compare
41ed34f to
122265c
Compare
|
Haven't thought of it at first, but there's also one behavior around client.close() that kinda bothers me, indicated by I.e. if event gets received from etcd, followed by closing the client, iterators can get the event or not, somewhat randomly, depending on whether consumer or close() gets scheduled first. I think this will lead to inconsistent behavior in apps, and potential bugs, if a dev would expect this to go one specific way or another. |
Currently client.watch() will return a watcher object that can be used separately from the client.
Current behavior when watcher.close() is called after client.close() -
AttributeError: 'NoneType' object has no attribute 'cancel'- is confusing and unclear.I think two ways to handle this better are:
Make watcher.cancel() a no-op after client is closed.
This mimics how
async for kv in watcher:iterator currently works.Have watcher.cancel() raise a clear "must not be called when client is already closed" exception.
This PR does the latter, assuming that it is a programming error to call watcher.cancel() for closed client.
In my use-case closing the client should cancel all watcher iterators already, so such call indeed indicates some code bug.
Picked RuntimeError because it's used for same types of bugs in asyncio itself and also in client.py already, and there should never be a need to handle it in any way (i.e. handling RuntimeError is almost certainly a bug in the code).
Alternative viewpoint on the API can be that
w = client.watch(); try: ... finally: w.cancel()pattern should be supported, in which case .cancel() should be a no-op (to avoid raising exception in "finally").If this is the case, rtypes.Watch should also support aenter/aexit to work in a more conventional
with client.watch() as w: ...pattern. Assuming that this is not implemented on purpose, RuntimeError variant above seem to be a more fitting option.