Skip to content

Commit c0f12fb

Browse files
committed
pexpect-style wait for output in test_kernelapp
wait for the expected output to appear, instead of assuming 1 second is enough this should be both faster (where sleep(1) was enough) and more reliable
1 parent a212f90 commit c0f12fb

File tree

1 file changed

+76
-39
lines changed

1 file changed

+76
-39
lines changed
Lines changed: 76 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import os
2-
import shutil
32
import sys
43
import time
4+
from queue import Empty
5+
from queue import Queue
56
from subprocess import PIPE
67
from subprocess import Popen
7-
from tempfile import mkdtemp
8+
from threading import Thread
89

910

1011
def _launch(extra_env):
@@ -21,44 +22,80 @@ def _launch(extra_env):
2122
POLL_FREQ = 10
2223

2324

24-
def test_kernelapp_lifecycle():
25+
def test_kernelapp_lifecycle(request, tmpdir):
2526
# Check that 'jupyter kernel' starts and terminates OK.
26-
runtime_dir = mkdtemp()
27-
startup_dir = mkdtemp()
27+
runtime_dir = str(tmpdir.join("runtime").mkdir())
28+
startup_dir = str(tmpdir.join("startup").mkdir())
2829
started = os.path.join(startup_dir, "started")
29-
try:
30-
p = _launch(
31-
{
32-
"JUPYTER_RUNTIME_DIR": runtime_dir,
33-
"JUPYTER_CLIENT_TEST_RECORD_STARTUP_PRIVATE": started,
34-
}
35-
)
36-
# Wait for start
37-
for _ in range(WAIT_TIME * POLL_FREQ):
38-
if os.path.isfile(started):
39-
break
40-
time.sleep(1 / POLL_FREQ)
41-
else:
42-
raise AssertionError("No started file created in {} seconds".format(WAIT_TIME))
43-
44-
# Connection file should be there by now
45-
for _ in range(WAIT_TIME * POLL_FREQ):
46-
files = os.listdir(runtime_dir)
47-
if files:
30+
p = _launch(
31+
{
32+
"JUPYTER_RUNTIME_DIR": runtime_dir,
33+
"JUPYTER_CLIENT_TEST_RECORD_STARTUP_PRIVATE": started,
34+
}
35+
)
36+
request.addfinalizer(p.terminate)
37+
38+
# Wait for start
39+
for _ in range(WAIT_TIME * POLL_FREQ):
40+
if os.path.isfile(started):
41+
break
42+
time.sleep(1 / POLL_FREQ)
43+
else:
44+
raise AssertionError("No started file created in {} seconds".format(WAIT_TIME))
45+
46+
# Connection file should be there by now
47+
for _ in range(WAIT_TIME * POLL_FREQ):
48+
files = os.listdir(runtime_dir)
49+
if files:
50+
break
51+
time.sleep(1 / POLL_FREQ)
52+
else:
53+
raise AssertionError("No connection file created in {} seconds".format(WAIT_TIME))
54+
55+
assert len(files) == 1
56+
cf = files[0]
57+
assert cf.startswith("kernel")
58+
assert cf.endswith(".json")
59+
60+
# pexpect-style wait-for-text with timeout
61+
# use blocking background thread to read output
62+
# so this works on Windows
63+
t = time.perf_counter()
64+
deadline = t + WAIT_TIME
65+
remaining = WAIT_TIME
66+
67+
stderr = ""
68+
q = Queue()
69+
70+
def _readlines():
71+
while True:
72+
line = p.stderr.readline()
73+
q.put(line.decode("utf8", "replace"))
74+
if not line:
4875
break
49-
time.sleep(1 / POLL_FREQ)
50-
else:
51-
raise AssertionError("No connection file created in {} seconds".format(WAIT_TIME))
52-
assert len(files) == 1
53-
cf = files[0]
54-
assert cf.startswith("kernel")
55-
assert cf.endswith(".json")
56-
57-
# Send SIGTERM to shut down
58-
time.sleep(1)
76+
77+
stderr_thread = Thread(target=_readlines, daemon=True)
78+
stderr_thread.start()
79+
80+
while remaining >= 0 and p.poll() is None:
81+
try:
82+
line = q.get(timeout=remaining)
83+
except Empty:
84+
break
85+
stderr += line
86+
if cf in stderr:
87+
break
88+
remaining = deadline - time.perf_counter()
89+
90+
if p.poll() is None:
5991
p.terminate()
60-
_, stderr = p.communicate(timeout=WAIT_TIME)
61-
assert cf in stderr.decode("utf-8", "replace")
62-
finally:
63-
shutil.rmtree(runtime_dir)
64-
shutil.rmtree(startup_dir)
92+
93+
# finish reading stderr
94+
stderr_thread.join()
95+
while True:
96+
try:
97+
line = q.get_nowait()
98+
except Empty:
99+
break
100+
stderr += line
101+
assert cf in stderr

0 commit comments

Comments
 (0)