Skip to content

Commit 57f9b9e

Browse files
committed
basic testing of server launching
1 parent b863be3 commit 57f9b9e

File tree

7 files changed

+142
-9
lines changed

7 files changed

+142
-9
lines changed

anaconda-project.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ commands:
1010
env_spec: rfjl37
1111

1212
list:
13-
unix: jupyter serverextension list
13+
unix: jupyter serverextension list && jupyter notebook list --json
1414
env_spec: rfjl37
1515

1616
setup:
File renamed without changes.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
*** Settings ***
2+
Library JupyterLibrary
3+
Library Process
4+
Test Teardown Terminate All Processes
5+
6+
*** Test Cases ***
7+
Start one server
8+
${nbserver} = Start New Jupyter Server
9+
${ready} = Wait for Jupyter Server to be Ready
10+
Should be equal as integers ${ready} 1
11+
... msg=One server should be ready
12+
${terminated} = Terminate All Jupyter Servers
13+
Should be equal as integers ${terminated} 1
14+
... msg=One server should have been terminated
15+
${log} = Get Process Result ${nbserver} stderr=${True}
16+
Should Contain ${log} The Jupyter Notebook is running
17+
... msg=Log should contain expected status message
18+
19+
Start three servers
20+
${nb1} = Start New Jupyter Server
21+
${nb2} = Start New Jupyter Server
22+
${ready} = Wait for Jupyter Server to be Ready ${nb2} ${nb1}
23+
Should be equal as integers ${ready} 2
24+
... msg=Three servers should be ready
25+
${nb3} = Start New Jupyter Server
26+
${terminated} = Terminate All Jupyter Servers
27+
Should be equal as integers ${terminated} 3
28+
... msg=Three servers should have been terminated
29+
${log1} = Get Process Result ${nb1} stderr=${True}
30+
Should Contain ${log1} The Jupyter Notebook is running
31+
... msg=Log should contain expected status message
32+
${log2} = Get Process Result ${nb2} stderr=${True}
33+
Should Contain ${log2} The Jupyter Notebook is running
34+
... msg=Log should contain expected status message
35+
${log3} = Get Process Result ${nb3} stderr=${True}
36+
Should Not Contain ${log3} The Jupyter Notebook is running
37+
... msg=Unawaited server log should not contain expected status message
38+
${terminated} = Terminate All Jupyter Servers
39+
Should be equal as integers ${terminated} 0
40+
... msg=No servers should have been terminated
41+
Run Keyword And Expect Error Only 0 of 3* Wait for Jupyter Server to be Ready ${nb2} ${nb1} ${nb3}

atest/run.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@
88

99

1010
def run_tests(*robot_args):
11-
return subprocess.check_call([
11+
proc = subprocess.Popen([
1212
"python", "-m", "robot", "-d", out, tests
1313
], cwd=here)
1414

15+
try:
16+
return proc.wait()
17+
except KeyboardInterrupt:
18+
proc.kill()
19+
return proc.wait()
20+
1521

1622
if __name__ == "__main__":
1723
sys.exit(run_tests(sys.argv[1:]))

setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ install_requires =
3030
robotframework >3
3131
seleniumlibrary
3232
pillow
33+
six
3334
package_dir =
3435
= src
3536
packages = find:

src/JupyterLibrary/core.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
from SeleniumLibrary import SeleniumLibrary
22

3-
from .keywords import (
4-
server,
5-
)
3+
from .keywords import server
64

75

86
class JupyterLibrary(SeleniumLibrary):
@@ -28,5 +26,5 @@ def __init__(self, timeout=5.0, implicit_wait=0.0,
2826
screenshot_root_directory=None
2927
)
3028
self.add_library_components([
31-
server
29+
server.ServerKeywords(self)
3230
])
Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,90 @@
1-
class ServerKeywords(object):
2-
def start_jupyter_notebook_server():
3-
pass
1+
import subprocess
2+
import time
3+
from six.moves.urllib.request import urlopen
4+
5+
from robot.libraries.BuiltIn import BuiltIn
6+
from tornado.escape import json_decode
7+
8+
from SeleniumLibrary.base import keyword, LibraryComponent
9+
10+
11+
class NBServer(object):
12+
process = None
13+
14+
15+
class ServerKeywords(LibraryComponent):
16+
_nbserver_handles = []
17+
18+
@keyword
19+
def start_new_jupyter_server(self, command="jupyter", *arguments, **configuration):
20+
""" Start a Jupyter server
21+
"""
22+
plib = BuiltIn().get_library_instance('Process')
23+
if not arguments:
24+
arguments = self.build_jupyter_server_arguments()
25+
26+
handle = plib.start_process("jupyter", *arguments, **configuration)
27+
self._nbserver_handles += [handle]
28+
return handle
29+
30+
@keyword
31+
def build_jupyter_server_arguments(self):
32+
return ["notebook", "--no-browser"]
33+
34+
@keyword
35+
def wait_for_jupyter_server_to_be_ready(self, *nbservers, **kwargs):
36+
""" Wait for the most-recently started Jupyter server to be ready
37+
"""
38+
interval = float(kwargs.get("interval", 0.25))
39+
retries = int(kwargs.get("retries", 20))
40+
41+
plib = BuiltIn().get_library_instance('Process')
42+
43+
if not nbservers:
44+
if not self._nbserver_handles:
45+
return 0
46+
nbservers = [self._nbserver_handles[-1]]
47+
48+
ready = 0
49+
last_error = None
50+
51+
while retries and ready != len(nbservers):
52+
retries -= 1
53+
ready = 0
54+
55+
try:
56+
nbservers_json = self.get_jupyter_servers()
57+
for nbhandle in nbservers:
58+
nbpopen = plib.get_process_object(nbhandle)
59+
nbj = nbservers_json[nbpopen.pid]
60+
urlopen("{url}favicon.ico".format(**nbj))
61+
ready += 1
62+
except Exception as err:
63+
time.sleep(interval)
64+
last_error = err
65+
66+
assert ready == len(nbservers), (
67+
"Only {} of {} servers were ready: {}".format(
68+
ready, len(nbservers), last_error
69+
))
70+
return ready
71+
72+
@keyword
73+
def terminate_all_jupyter_servers(self, kill=False):
74+
""" Close all Jupyter servers started by JupyterLibrary
75+
"""
76+
plib = BuiltIn().get_library_instance('Process')
77+
terminated = 0
78+
for handle in self._nbserver_handles:
79+
plib.terminate_process(handle, kill=kill)
80+
terminated += 1
81+
82+
self._nbserver_handles = []
83+
84+
return terminated
85+
86+
def get_jupyter_servers(self):
87+
nbservers = list(map(json_decode, subprocess.check_output(
88+
["jupyter", "notebook", "list", "--json"]
89+
).decode("utf-8").strip().split("\n")))
90+
return {nbserver["pid"]: nbserver for nbserver in nbservers}

0 commit comments

Comments
 (0)