Merge pull request #3000 from takluyver/stop-cmd-http

Send HTTP shutdown request on 'stop' subcommand
Thomas Kluyver 9 years ago committed by GitHub
commit 15f393b49c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -24,6 +24,7 @@ import signal
import socket
import sys
import threading
import time
import warnings
import webbrowser
@ -351,6 +352,51 @@ class NotebookPasswordApp(JupyterApp):
set_password(config_file=self.config_file)
self.log.info("Wrote hashed password to %s" % self.config_file)
def shutdown_server(server_info, timeout=5, log=None):
"""Shutdown a notebook server in a separate process.
*server_info* should be a dictionary as produced by list_running_servers().
Will first try to request shutdown using /api/shutdown .
On Unix, if the server is still running after *timeout* seconds, it will
send SIGTERM. After another timeout, it escalates to SIGKILL.
Returns True if the server was stopped by any means, False if stopping it
failed (on Windows).
"""
from tornado.httpclient import HTTPClient, HTTPRequest
url = server_info['url']
pid = server_info['pid']
req = HTTPRequest(url + 'api/shutdown', method='POST', body=b'', headers={
'Authorization': 'token ' + server_info['token']
})
if log: log.debug("POST request to %sapi/shutdown", url)
HTTPClient().fetch(req)
# Poll to see if it shut down.
for _ in range(timeout*10):
if check_pid(pid):
if log: log.debug("Server PID %s is gone", pid)
return True
time.sleep(0.1)
if sys.platform.startswith('win'):
return False
if log: log.debug("SIGTERM to PID %s", pid)
os.kill(pid, signal.SIGTERM)
# Poll to see if it shut down.
for _ in range(timeout * 10):
if check_pid(pid):
if log: log.debug("Server PID %s is gone", pid)
return True
time.sleep(0.1)
if log: log.debug("SIGKILL to PID %s", pid)
os.kill(pid, signal.SIGKILL)
return True # SIGKILL cannot be caught
class NbserverStopApp(JupyterApp):
version = __version__
@ -364,14 +410,18 @@ class NbserverStopApp(JupyterApp):
if self.extra_args:
self.port=int(self.extra_args[0])
def shutdown_server(self, server):
return shutdown_server(server, log=self.log)
def start(self):
servers = list(list_running_servers(self.runtime_dir))
if not servers:
self.exit("There are no running servers")
for server in servers:
if server['port'] == self.port:
self.log.debug("Shutting down notebook server with PID: %i", server['pid'])
os.kill(server['pid'], signal.SIGTERM)
print("Shutting down server on port", self.port, "...")
if not self.shutdown_server(server):
sys.exit("Could not stop server")
return
else:
print("There is currently no server running on port {}".format(self.port), file=sys.stderr)

@ -140,6 +140,15 @@ def test_notebook_password():
nt.assert_not_equal(nb.password, '')
passwd_check(nb.password, password)
class TestingStopApp(notebookapp.NbserverStopApp):
"""For testing the logic of NbserverStopApp."""
def __init__(self, **kwargs):
super(TestingStopApp, self).__init__(**kwargs)
self.servers_shut_down = []
def shutdown_server(self, server):
self.servers_shut_down.append(server)
return True
def test_notebook_stop():
def list_running_servers(runtime_dir):
@ -159,18 +168,18 @@ def test_notebook_stop():
mock_servers = patch('notebook.notebookapp.list_running_servers', list_running_servers)
# test stop with a match
with mock_servers, patch('os.kill') as os_kill:
app = notebookapp.NbserverStopApp()
with mock_servers:
app = TestingStopApp()
app.initialize(['105'])
app.start()
nt.assert_equal(os_kill.call_count, 1)
nt.assert_equal(os_kill.call_args, ((1105, signal.SIGTERM),))
nt.assert_equal(len(app.servers_shut_down), 1)
nt.assert_equal(app.servers_shut_down[0]['port'], 105)
# test no match
with mock_servers, patch('os.kill') as os_kill:
app = notebookapp.NbserverStopApp()
app = TestingStopApp()
app.initialize(['999'])
with nt.assert_raises(SystemExit) as exc:
app.start()
nt.assert_equal(exc.exception.code, 1)
nt.assert_equal(os_kill.call_count, 0)
nt.assert_equal(len(app.servers_shut_down), 0)

Loading…
Cancel
Save