Repository navigation
FIX: improve how we interact with the asyncio event loop - #1125
Conversation
|
This is essentially bait left to nerd-snipe @tacaswell into finishing the Qt integration sketched in #1062. |
|
Status update: Basic scanning and live-updating plots now works in both IPython and Jupyter (including with tornado > 5). Pause/resume is currently broken. |
|
Now that this PR has rendered |
bf6c1e6 to
94dcb68
Compare
|
This is I think, now basically correct. Several tests that stopped/restarted the event loop need to be updated, and then we're done. To surface some in-person comments by @tacaswell --- his work on https://github.com/NSLS-II/mily has surfaces several other ways that we could consider architecting this. The current approach may not be the best one in the long term, though it's unclear yet which approach is best. This is detail-oriented work that requires focus that is in short supply at the moment, and I propose completing and merging the current approach. Our incompatibility with |
|
If this PR makes it possible to work with |
cca48a1 to
eb7aa90
Compare
|
I can locally sometimes get the test to run through without hanging or segfaulting. |
78ee3c5 to
c617c47
Compare
|
force-pushed to fix ci typo. |
bac7ce5 to
77d083c
Compare
04f25e7 to
09550e2
Compare
|
rebased to account for conflicts in .travis.yml. Will likely amend and force-push the last commit as well. |
|
The justification for the API change to Old way: import asyncio
import time
start = time.monotonic()
async def f1():
loop = asyncio.get_event_loop()
ev = asyncio.Event()
print('launch the task', time.monotonic() - start)
tsk = loop.create_task(f2(ev.wait()))
loop.call_later(2, ev.set)
print('sleep for a bit (1s)', time.monotonic() - start)
await asyncio.sleep(1)
print('cancel the task', time.monotonic() - start)
tsk.cancel()
print('sleep for a bit', time.monotonic() - start)
print('really cancelled?: ', tsk.cancelled(), time.monotonic() - start)
ex = await tsk
print('victory!, should be ~2s', time.monotonic() - start)
if ex:
raise ex
async def f2(fut):
while True:
try:
print(' try to wait', time.monotonic() - start)
(t,), _ = (await asyncio.wait([fut]))
print(' and escape later', time.monotonic() - start)
return t.exception()
except Exception as ex:
print(' Should fail once', time.monotonic() - start)
print(repr(ex))
loop = asyncio.get_event_loop()
loop.run_until_complete(f1())
New way: import asyncio
import time
start = time.monotonic()
async def f1():
loop = asyncio.get_event_loop()
ev = asyncio.Event()
print('launch the task', time.monotonic() - start)
tsk = loop.create_task(f2(ev.wait))
loop.call_later(2, ev.set)
print('sleep for a bit (1s)', time.monotonic() - start)
await asyncio.sleep(1)
print('cancel the task', time.monotonic() - start)
tsk.cancel()
print('sleep for a bit', time.monotonic() - start)
print('really cancelled?: ', tsk.cancelled(), time.monotonic() - start)
ex = await tsk
print('victory!, should be ~2s', time.monotonic() - start)
if ex:
raise ex
async def f2(fut):
while True:
try:
print(' try to wait', time.monotonic() - start)
(t,), _ = (await asyncio.wait([fut()]))
print(' and escape later', time.monotonic() - start)
return t.exception()
except Exception as ex:
print(' Should fail once', time.monotonic() - start)
print(repr(ex))
loop = asyncio.get_event_loop()
loop.run_until_complete(f1())The old way "works" on py3.6+ in that the timing is correct, but the inner task still sets a |
09550e2 to
e09e1e4
Compare
|
However import asyncio
import time
from asyncio.futures import Future
start = time.monotonic()
async def f1():
loop = asyncio.get_event_loop()
fut = Future()
print('launch the task', time.monotonic() - start)
tsk = loop.create_task(f2(fut))
loop.call_later(2, fut.set_result, 3)
print('sleep for a bit (1s)', time.monotonic() - start)
await asyncio.sleep(1)
print('cancel the task', time.monotonic() - start)
tsk.cancel()
print('sleep for a bit', time.monotonic() - start)
print('really cancelled?: ', tsk.cancelled(), time.monotonic() - start)
ex = await tsk
print('victory!, should be ~2s', time.monotonic() - start)
if ex:
raise ex
async def f2(fut):
while True:
try:
print(' try to wait', time.monotonic() - start)
(t,), _ = (await asyncio.wait([fut]))
print(' and escape later', time.monotonic() - start)
return t.exception()
except Exception as ex:
print(' Should fail once', time.monotonic() - start)
print(repr(ex))
loop = asyncio.get_event_loop()
loop.run_until_complete(f1())Works correctly on all versions so the problem is passing on co-routines as futures. We should have a bit of a think about what to change this API to. |
17a1feb to
93fc013
Compare
| - pip install -r requirements.txt | ||
| - pip install -r dev-requirements.txt | ||
| - pip install -U ophyd --pre | ||
| - pip install pyqt5 |
There was a problem hiding this comment.
Should this be added to dev-requirements.txt?
| Expected signature: f(md) | ||
| Expected return: updated scan_id value | ||
|
|
||
| during_task : callable, optional |
|
That comment reminded me that interrupters exist and that this PR may break them. Would you be able to manually test this locally? We really need to move "interuptors" upstream. |
e87fc03 to
d6c0fce
Compare
|
force-pushed to hide a dumb mistake |
Codecov Report
@@ Coverage Diff @@
## master #1125 +/- ##
==========================================
+ Coverage 89.24% 89.33% +0.08%
==========================================
Files 52 52
Lines 8600 8766 +166
==========================================
+ Hits 7675 7831 +156
- Misses 925 935 +10
Continue to review full report at Codecov.
|
3deaa7c to
c30c157
Compare
a98ad7d to
34bbb59
Compare
klauer
left a comment
There was a problem hiding this comment.
This looks to be a great improvement, but also a very hard to review one...
Take these comments with a grain of salt, as I'm distractedly reviewing during our weekly meeting ;)
| futs, = msg.args | ||
| yield from asyncio.wait(futs, loop=self.loop, **msg.kwargs) | ||
| futs = [f() for f in futs] | ||
| await asyncio.wait(futs, loop=self.loop, **msg.kwargs) |
There was a problem hiding this comment.
https://docs.python.org/3/library/asyncio-task.html#asyncio.wait
The loop argument is deprecated and scheduled for removal in Python 3.10
There was a problem hiding this comment.
Same comment as the other one, want to deal with this in a follow up PR as we need to sort out what the minimum version of python that correctly works without loop is.
| will do nothing. | ||
|
|
||
| It is safe to call this function multiple times. | ||
| DEPRECATED: This functionality is now handled automatically by default and |
| wakeupsn.activated.connect(handleWakeup) | ||
|
|
||
| else: | ||
| # On Windows, non-blocking anonymous pipe or socket is |
There was a problem hiding this comment.
Has any interactive testing been done on Windows with this?
There was a problem hiding this comment.
We do have at least one collection machine on the floor of NSLS-II, but we haven't tried. I don't know if we have a chance to test it from the PR branch before it gets merged.
There was a problem hiding this comment.
this was adapted from tortioushg which does get heavy windows use so I am optimistic.
- make bad sleep longer - make timeout condition less stringent - add more time details to the debugging prints
When we moved the table logic below the plotting we no longer were over-writing the `columns` local which change which code path we went down in the plotting.
|
The mac test failures are very confusing as they are |
mrakitin
left a comment
There was a problem hiding this comment.
Here are a few comments for the first part. run_engine.py and tests will go next.
| - pip install -r requirements.txt | ||
| - pip install -r dev-requirements.txt | ||
| - pip install -U ophyd --pre | ||
| - pip install pyqt5 |
|
|
||
| services: | ||
| - xvfb | ||
|
|
There was a problem hiding this comment.
I think this section is not needed for testing, as discovered in #1203.
There was a problem hiding this comment.
Pushed a commit to see if it works (but am skeptical).
There was a problem hiding this comment.
It seems to pass: https://travis-ci.org/bluesky/bluesky/builds/540895701?utm_source=github_status&utm_medium=notification, so it should be OK.
| # plot everything, independent or dependent variables | ||
| self._table = LiveTable(list(self.all_dim_fields) + columns) | ||
| self._table('start', self._start_doc) | ||
| self._table('descriptor', doc) |
There was a problem hiding this comment.
👍
That particular change resolves the issue with arranging live table and live plot in a Jupyter notebook.
| wakeupsn.activated.connect(handleWakeup) | ||
|
|
||
| else: | ||
| # On Windows, non-blocking anonymous pipe or socket is |
There was a problem hiding this comment.
We do have at least one collection machine on the floor of NSLS-II, but we haven't tried. I don't know if we have a chance to test it from the PR branch before it gets merged.
|
The issue was due to pytest trying to un-roll |
|
I think this is ready to merge! |
Do not merge. Needs work.
Closes #1062
Closes #1087
TODO:
run_matplotlib_qApp, it also handles the non Qt cases (semi-) gracefullywait_forapi changes_delayed_partialwhich is a poorly implemented version ofthreading.Timer