Show More
@@ -658,7 +658,9 b' class NotebookApp(BaseIPythonApplication):' | |||||
658 | kls = import_item(self.notebook_manager_class) |
|
658 | kls = import_item(self.notebook_manager_class) | |
659 | self.notebook_manager = kls(parent=self, log=self.log) |
|
659 | self.notebook_manager = kls(parent=self, log=self.log) | |
660 | kls = import_item(self.session_manager_class) |
|
660 | kls = import_item(self.session_manager_class) | |
661 |
self.session_manager = kls(parent=self, log=self.log |
|
661 | self.session_manager = kls(parent=self, log=self.log, | |
|
662 | kernel_manager=self.kernel_manager, | |||
|
663 | notebook_manager=self.notebook_manager) | |||
662 | kls = import_item(self.cluster_manager_class) |
|
664 | kls = import_item(self.cluster_manager_class) | |
663 | self.cluster_manager = kls(parent=self, log=self.log) |
|
665 | self.cluster_manager = kls(parent=self, log=self.log) | |
664 | self.cluster_manager.update_profiles() |
|
666 | self.cluster_manager.update_profiles() |
@@ -27,8 +27,16 b' class MainKernelHandler(IPythonHandler):' | |||||
27 | @web.authenticated |
|
27 | @web.authenticated | |
28 | @json_errors |
|
28 | @json_errors | |
29 | def post(self): |
|
29 | def post(self): | |
|
30 | model = self.get_json_body() | |||
|
31 | if model is None: | |||
|
32 | raise web.HTTPError(400, "No JSON data provided") | |||
|
33 | try: | |||
|
34 | name = model['name'] | |||
|
35 | except KeyError: | |||
|
36 | raise web.HTTPError(400, "Missing field in JSON data: name") | |||
|
37 | ||||
30 | km = self.kernel_manager |
|
38 | km = self.kernel_manager | |
31 | kernel_id = km.start_kernel() |
|
39 | kernel_id = km.start_kernel(kernel_name=name) | |
32 | model = km.kernel_model(kernel_id) |
|
40 | model = km.kernel_model(kernel_id) | |
33 | location = url_path_join(self.base_url, 'api', 'kernels', kernel_id) |
|
41 | location = url_path_join(self.base_url, 'api', 'kernels', kernel_id) | |
34 | self.set_header('Location', url_escape(location)) |
|
42 | self.set_header('Location', url_escape(location)) |
@@ -72,8 +72,8 b' class MappingKernelManager(MultiKernelManager):' | |||||
72 | os_path = os.path.dirname(os_path) |
|
72 | os_path = os.path.dirname(os_path) | |
73 | return os_path |
|
73 | return os_path | |
74 |
|
74 | |||
75 | def start_kernel(self, kernel_id=None, path=None, **kwargs): |
|
75 | def start_kernel(self, kernel_id=None, path=None, kernel_name='python', **kwargs): | |
76 | """Start a kernel for a session an return its kernel_id. |
|
76 | """Start a kernel for a session and return its kernel_id. | |
77 |
|
77 | |||
78 | Parameters |
|
78 | Parameters | |
79 | ---------- |
|
79 | ---------- | |
@@ -84,12 +84,16 b' class MappingKernelManager(MultiKernelManager):' | |||||
84 | path : API path |
|
84 | path : API path | |
85 | The API path (unicode, '/' delimited) for the cwd. |
|
85 | The API path (unicode, '/' delimited) for the cwd. | |
86 | Will be transformed to an OS path relative to root_dir. |
|
86 | Will be transformed to an OS path relative to root_dir. | |
|
87 | kernel_name : str | |||
|
88 | The name identifying which kernel spec to launch. This is ignored if | |||
|
89 | an existing kernel is returned, but it may be checked in the future. | |||
87 | """ |
|
90 | """ | |
88 | if kernel_id is None: |
|
91 | if kernel_id is None: | |
89 | kwargs['extra_arguments'] = self.kernel_argv |
|
92 | kwargs['extra_arguments'] = self.kernel_argv | |
90 | if path is not None: |
|
93 | if path is not None: | |
91 | kwargs['cwd'] = self.cwd_for_path(path) |
|
94 | kwargs['cwd'] = self.cwd_for_path(path) | |
92 |
kernel_id = super(MappingKernelManager, self).start_kernel( |
|
95 | kernel_id = super(MappingKernelManager, self).start_kernel( | |
|
96 | kernel_name=kernel_name, **kwargs) | |||
93 | self.log.info("Kernel started: %s" % kernel_id) |
|
97 | self.log.info("Kernel started: %s" % kernel_id) | |
94 | self.log.debug("Kernel args: %r" % kwargs) |
|
98 | self.log.debug("Kernel args: %r" % kwargs) | |
95 | # register callback for failed auto-restart |
|
99 | # register callback for failed auto-restart | |
@@ -111,7 +115,8 b' class MappingKernelManager(MultiKernelManager):' | |||||
111 | """Return a dictionary of kernel information described in the |
|
115 | """Return a dictionary of kernel information described in the | |
112 | JSON standard model.""" |
|
116 | JSON standard model.""" | |
113 | self._check_kernel_id(kernel_id) |
|
117 | self._check_kernel_id(kernel_id) | |
114 |
model = {"id":kernel_id |
|
118 | model = {"id":kernel_id, | |
|
119 | "name": self._kernels[kernel_id].kernel_name} | |||
115 | return model |
|
120 | return model | |
116 |
|
121 | |||
117 | def list_kernels(self): |
|
122 | def list_kernels(self): |
@@ -1,6 +1,6 b'' | |||||
1 | """Test the kernels service API.""" |
|
1 | """Test the kernels service API.""" | |
2 |
|
2 | |||
3 |
|
3 | import json | ||
4 | import requests |
|
4 | import requests | |
5 |
|
5 | |||
6 | from IPython.html.utils import url_path_join |
|
6 | from IPython.html.utils import url_path_join | |
@@ -30,8 +30,9 b' class KernelAPI(object):' | |||||
30 | def get(self, id): |
|
30 | def get(self, id): | |
31 | return self._req('GET', id) |
|
31 | return self._req('GET', id) | |
32 |
|
32 | |||
33 | def start(self): |
|
33 | def start(self, name='python'): | |
34 | return self._req('POST', '') |
|
34 | body = json.dumps({'name': name}) | |
|
35 | return self._req('POST', '', body) | |||
35 |
|
36 | |||
36 | def shutdown(self, id): |
|
37 | def shutdown(self, id): | |
37 | return self._req('DELETE', id) |
|
38 | return self._req('DELETE', id) | |
@@ -69,6 +70,7 b' class KernelAPITest(NotebookTestBase):' | |||||
69 | self.assertEqual(r.status_code, 200) |
|
70 | self.assertEqual(r.status_code, 200) | |
70 | assert isinstance(r.json(), list) |
|
71 | assert isinstance(r.json(), list) | |
71 | self.assertEqual(r.json()[0]['id'], kern1['id']) |
|
72 | self.assertEqual(r.json()[0]['id'], kern1['id']) | |
|
73 | self.assertEqual(r.json()[0]['name'], kern1['name']) | |||
72 |
|
74 | |||
73 | # create another kernel and check that they both are added to the |
|
75 | # create another kernel and check that they both are added to the | |
74 | # list of kernels from a GET request |
|
76 | # list of kernels from a GET request | |
@@ -89,6 +91,7 b' class KernelAPITest(NotebookTestBase):' | |||||
89 | self.assertEqual(r.headers['Location'], '/api/kernels/'+kern2['id']) |
|
91 | self.assertEqual(r.headers['Location'], '/api/kernels/'+kern2['id']) | |
90 | rekern = r.json() |
|
92 | rekern = r.json() | |
91 | self.assertEqual(rekern['id'], kern2['id']) |
|
93 | self.assertEqual(rekern['id'], kern2['id']) | |
|
94 | self.assertEqual(rekern['name'], kern2['name']) | |||
92 |
|
95 | |||
93 | def test_kernel_handler(self): |
|
96 | def test_kernel_handler(self): | |
94 | # GET kernel with given id |
|
97 | # GET kernel with given id |
@@ -45,27 +45,28 b' class SessionRootHandler(IPythonHandler):' | |||||
45 | # Creates a new session |
|
45 | # Creates a new session | |
46 | #(unless a session already exists for the named nb) |
|
46 | #(unless a session already exists for the named nb) | |
47 | sm = self.session_manager |
|
47 | sm = self.session_manager | |
48 | nbm = self.notebook_manager |
|
48 | ||
49 | km = self.kernel_manager |
|
|||
50 | model = self.get_json_body() |
|
49 | model = self.get_json_body() | |
51 | if model is None: |
|
50 | if model is None: | |
52 | raise web.HTTPError(400, "No JSON data provided") |
|
51 | raise web.HTTPError(400, "No JSON data provided") | |
53 | try: |
|
52 | try: | |
54 | name = model['notebook']['name'] |
|
53 | name = model['notebook']['name'] | |
55 | except KeyError: |
|
54 | except KeyError: | |
56 | raise web.HTTPError(400, "Missing field in JSON data: name") |
|
55 | raise web.HTTPError(400, "Missing field in JSON data: notebook.name") | |
57 | try: |
|
56 | try: | |
58 | path = model['notebook']['path'] |
|
57 | path = model['notebook']['path'] | |
59 | except KeyError: |
|
58 | except KeyError: | |
60 | raise web.HTTPError(400, "Missing field in JSON data: path") |
|
59 | raise web.HTTPError(400, "Missing field in JSON data: notebook.path") | |
|
60 | try: | |||
|
61 | kernel_name = model['kernel']['name'] | |||
|
62 | except KeyError: | |||
|
63 | raise web.HTTPError(400, "Missing field in JSON data: kernel.name") | |||
|
64 | ||||
61 | # Check to see if session exists |
|
65 | # Check to see if session exists | |
62 | if sm.session_exists(name=name, path=path): |
|
66 | if sm.session_exists(name=name, path=path): | |
63 | model = sm.get_session(name=name, path=path) |
|
67 | model = sm.get_session(name=name, path=path) | |
64 | else: |
|
68 | else: | |
65 | # allow nbm to specify kernels cwd |
|
69 | model = sm.create_session(name=name, path=path, kernel_name=kernel_name) | |
66 | kernel_path = nbm.get_kernel_path(name=name, path=path) |
|
|||
67 | kernel_id = km.start_kernel(path=kernel_path) |
|
|||
68 | model = sm.create_session(name=name, path=path, kernel_id=kernel_id) |
|
|||
69 | location = url_path_join(self.base_url, 'api', 'sessions', model['id']) |
|
70 | location = url_path_join(self.base_url, 'api', 'sessions', model['id']) | |
70 | self.set_header('Location', url_escape(location)) |
|
71 | self.set_header('Location', url_escape(location)) | |
71 | self.set_status(201) |
|
72 | self.set_status(201) | |
@@ -108,10 +109,7 b' class SessionHandler(IPythonHandler):' | |||||
108 | def delete(self, session_id): |
|
109 | def delete(self, session_id): | |
109 | # Deletes the session with given session_id |
|
110 | # Deletes the session with given session_id | |
110 | sm = self.session_manager |
|
111 | sm = self.session_manager | |
111 | km = self.kernel_manager |
|
|||
112 | session = sm.get_session(session_id=session_id) |
|
|||
113 | sm.delete_session(session_id) |
|
112 | sm.delete_session(session_id) | |
114 | km.shutdown_kernel(session['kernel']['id']) |
|
|||
115 | self.set_status(204) |
|
113 | self.set_status(204) | |
116 | self.finish() |
|
114 | self.finish() | |
117 |
|
115 |
@@ -23,12 +23,16 b' from tornado import web' | |||||
23 |
|
23 | |||
24 | from IPython.config.configurable import LoggingConfigurable |
|
24 | from IPython.config.configurable import LoggingConfigurable | |
25 | from IPython.utils.py3compat import unicode_type |
|
25 | from IPython.utils.py3compat import unicode_type | |
|
26 | from IPython.utils.traitlets import Instance | |||
26 |
|
27 | |||
27 | #----------------------------------------------------------------------------- |
|
28 | #----------------------------------------------------------------------------- | |
28 | # Classes |
|
29 | # Classes | |
29 | #----------------------------------------------------------------------------- |
|
30 | #----------------------------------------------------------------------------- | |
30 |
|
31 | |||
31 | class SessionManager(LoggingConfigurable): |
|
32 | class SessionManager(LoggingConfigurable): | |
|
33 | ||||
|
34 | kernel_manager = Instance('IPython.html.services.kernels.kernelmanager.MappingKernelManager') | |||
|
35 | notebook_manager = Instance('IPython.html.services.notebooks.nbmanager.NotebookManager', args=()) | |||
32 |
|
36 | |||
33 | # Session database initialized below |
|
37 | # Session database initialized below | |
34 | _cursor = None |
|
38 | _cursor = None | |
@@ -69,10 +73,15 b' class SessionManager(LoggingConfigurable):' | |||||
69 | "Create a uuid for a new session" |
|
73 | "Create a uuid for a new session" | |
70 | return unicode_type(uuid.uuid4()) |
|
74 | return unicode_type(uuid.uuid4()) | |
71 |
|
75 | |||
72 |
def create_session(self, name=None, path=None, kernel_ |
|
76 | def create_session(self, name=None, path=None, kernel_name='python'): | |
73 | """Creates a session and returns its model""" |
|
77 | """Creates a session and returns its model""" | |
74 | session_id = self.new_session_id() |
|
78 | session_id = self.new_session_id() | |
75 | return self.save_session(session_id, name=name, path=path, kernel_id=kernel_id) |
|
79 | # allow nbm to specify kernels cwd | |
|
80 | kernel_path = self.notebook_manager.get_kernel_path(name=name, path=path) | |||
|
81 | kernel_id = self.kernel_manager.start_kernel(path=kernel_path, | |||
|
82 | kernel_name=kernel_name) | |||
|
83 | return self.save_session(session_id, name=name, path=path, | |||
|
84 | kernel_id=kernel_id) | |||
76 |
|
85 | |||
77 | def save_session(self, session_id, name=None, path=None, kernel_id=None): |
|
86 | def save_session(self, session_id, name=None, path=None, kernel_id=None): | |
78 | """Saves the items for the session with the given session_id |
|
87 | """Saves the items for the session with the given session_id | |
@@ -170,8 +179,7 b' class SessionManager(LoggingConfigurable):' | |||||
170 | query = "UPDATE session SET %s WHERE session_id=?" % (', '.join(sets)) |
|
179 | query = "UPDATE session SET %s WHERE session_id=?" % (', '.join(sets)) | |
171 | self.cursor.execute(query, list(kwargs.values()) + [session_id]) |
|
180 | self.cursor.execute(query, list(kwargs.values()) + [session_id]) | |
172 |
|
181 | |||
173 | @staticmethod |
|
182 | def row_factory(self, cursor, row): | |
174 | def row_factory(cursor, row): |
|
|||
175 | """Takes sqlite database session row and turns it into a dictionary""" |
|
183 | """Takes sqlite database session row and turns it into a dictionary""" | |
176 | row = sqlite3.Row(cursor, row) |
|
184 | row = sqlite3.Row(cursor, row) | |
177 | model = { |
|
185 | model = { | |
@@ -180,9 +188,7 b' class SessionManager(LoggingConfigurable):' | |||||
180 | 'name': row['name'], |
|
188 | 'name': row['name'], | |
181 | 'path': row['path'] |
|
189 | 'path': row['path'] | |
182 | }, |
|
190 | }, | |
183 | 'kernel': { |
|
191 | 'kernel': self.kernel_manager.kernel_model(row['kernel_id']) | |
184 | 'id': row['kernel_id'], |
|
|||
185 | } |
|
|||
186 | } |
|
192 | } | |
187 | return model |
|
193 | return model | |
188 |
|
194 | |||
@@ -195,5 +201,6 b' class SessionManager(LoggingConfigurable):' | |||||
195 | def delete_session(self, session_id): |
|
201 | def delete_session(self, session_id): | |
196 | """Deletes the row in the session database with given session_id""" |
|
202 | """Deletes the row in the session database with given session_id""" | |
197 | # Check that session exists before deleting |
|
203 | # Check that session exists before deleting | |
198 | self.get_session(session_id=session_id) |
|
204 | session = self.get_session(session_id=session_id) | |
|
205 | self.kernel_manager.shutdown_kernel(session['kernel']['id']) | |||
199 | self.cursor.execute("DELETE FROM session WHERE session_id=?", (session_id,)) |
|
206 | self.cursor.execute("DELETE FROM session WHERE session_id=?", (session_id,)) |
@@ -5,79 +5,101 b' from unittest import TestCase' | |||||
5 | from tornado import web |
|
5 | from tornado import web | |
6 |
|
6 | |||
7 | from ..sessionmanager import SessionManager |
|
7 | from ..sessionmanager import SessionManager | |
|
8 | from IPython.html.services.kernels.kernelmanager import MappingKernelManager | |||
|
9 | ||||
|
10 | class DummyKernel(object): | |||
|
11 | def __init__(self, kernel_name='python'): | |||
|
12 | self.kernel_name = kernel_name | |||
|
13 | ||||
|
14 | class DummyMKM(MappingKernelManager): | |||
|
15 | """MappingKernelManager interface that doesn't start kernels, for testing""" | |||
|
16 | def __init__(self, *args, **kwargs): | |||
|
17 | super(DummyMKM, self).__init__(*args, **kwargs) | |||
|
18 | self.id_letters = iter(u'ABCDEFGHIJK') | |||
|
19 | ||||
|
20 | def _new_id(self): | |||
|
21 | return next(self.id_letters) | |||
|
22 | ||||
|
23 | def start_kernel(self, kernel_id=None, path=None, kernel_name='python', **kwargs): | |||
|
24 | kernel_id = kernel_id or self._new_id() | |||
|
25 | self._kernels[kernel_id] = DummyKernel(kernel_name=kernel_name) | |||
|
26 | return kernel_id | |||
|
27 | ||||
|
28 | def shutdown_kernel(self, kernel_id, now=False): | |||
|
29 | del self._kernels[kernel_id] | |||
8 |
|
30 | |||
9 | class TestSessionManager(TestCase): |
|
31 | class TestSessionManager(TestCase): | |
10 |
|
32 | |||
11 | def test_get_session(self): |
|
33 | def test_get_session(self): | |
12 | sm = SessionManager() |
|
34 | sm = SessionManager(kernel_manager=DummyMKM()) | |
13 | session_id = sm.new_session_id() |
|
35 | session_id = sm.create_session(name='test.ipynb', path='/path/to/', | |
14 | sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel_id='5678') |
|
36 | kernel_name='bar')['id'] | |
15 | model = sm.get_session(session_id=session_id) |
|
37 | model = sm.get_session(session_id=session_id) | |
16 | expected = {'id':session_id, 'notebook':{'name':u'test.ipynb', 'path': u'/path/to/'}, 'kernel':{'id':u'5678'}} |
|
38 | expected = {'id':session_id, | |
|
39 | 'notebook':{'name':u'test.ipynb', 'path': u'/path/to/'}, | |||
|
40 | 'kernel': {'id':u'A', 'name': 'bar'}} | |||
17 | self.assertEqual(model, expected) |
|
41 | self.assertEqual(model, expected) | |
18 |
|
42 | |||
19 | def test_bad_get_session(self): |
|
43 | def test_bad_get_session(self): | |
20 | # Should raise error if a bad key is passed to the database. |
|
44 | # Should raise error if a bad key is passed to the database. | |
21 | sm = SessionManager() |
|
45 | sm = SessionManager(kernel_manager=DummyMKM()) | |
22 | session_id = sm.new_session_id() |
|
46 | session_id = sm.create_session(name='test.ipynb', path='/path/to/', | |
23 | sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel_id='5678') |
|
47 | kernel_name='foo')['id'] | |
24 | self.assertRaises(TypeError, sm.get_session, bad_id=session_id) # Bad keyword |
|
48 | self.assertRaises(TypeError, sm.get_session, bad_id=session_id) # Bad keyword | |
25 |
|
49 | |||
26 | def test_list_sessions(self): |
|
50 | def test_list_sessions(self): | |
27 | sm = SessionManager() |
|
51 | sm = SessionManager(kernel_manager=DummyMKM()) | |
28 | session_id1 = sm.new_session_id() |
|
52 | sessions = [ | |
29 | session_id2 = sm.new_session_id() |
|
53 | sm.create_session(name='test1.ipynb', path='/path/to/1/', kernel_name='python'), | |
30 | session_id3 = sm.new_session_id() |
|
54 | sm.create_session(name='test2.ipynb', path='/path/to/2/', kernel_name='python'), | |
31 |
sm. |
|
55 | sm.create_session(name='test3.ipynb', path='/path/to/3/', kernel_name='python'), | |
32 | sm.save_session(session_id=session_id2, name='test2.ipynb', path='/path/to/2/', kernel_id='5678') |
|
56 | ] | |
33 | sm.save_session(session_id=session_id3, name='test3.ipynb', path='/path/to/3/', kernel_id='5678') |
|
|||
34 | sessions = sm.list_sessions() |
|
57 | sessions = sm.list_sessions() | |
35 |
expected = [{'id':session |
|
58 | expected = [{'id':sessions[0]['id'], 'notebook':{'name':u'test1.ipynb', | |
36 |
'path': u'/path/to/1/'}, 'kernel':{'id':u' |
|
59 | 'path': u'/path/to/1/'}, 'kernel':{'id':u'A', 'name':'python'}}, | |
37 |
{'id':session |
|
60 | {'id':sessions[1]['id'], 'notebook': {'name':u'test2.ipynb', | |
38 |
'path': u'/path/to/2/'}, 'kernel':{'id':u' |
|
61 | 'path': u'/path/to/2/'}, 'kernel':{'id':u'B', 'name':'python'}}, | |
39 |
{'id':session |
|
62 | {'id':sessions[2]['id'], 'notebook':{'name':u'test3.ipynb', | |
40 |
'path': u'/path/to/3/'}, 'kernel':{'id':u' |
|
63 | 'path': u'/path/to/3/'}, 'kernel':{'id':u'C', 'name':'python'}}] | |
41 | self.assertEqual(sessions, expected) |
|
64 | self.assertEqual(sessions, expected) | |
42 |
|
65 | |||
43 | def test_update_session(self): |
|
66 | def test_update_session(self): | |
44 | sm = SessionManager() |
|
67 | sm = SessionManager(kernel_manager=DummyMKM()) | |
45 | session_id = sm.new_session_id() |
|
68 | session_id = sm.create_session(name='test.ipynb', path='/path/to/', | |
46 | sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel_id=None) |
|
69 | kernel_name='julia')['id'] | |
47 | sm.update_session(session_id, kernel_id='5678') |
|
|||
48 | sm.update_session(session_id, name='new_name.ipynb') |
|
70 | sm.update_session(session_id, name='new_name.ipynb') | |
49 | model = sm.get_session(session_id=session_id) |
|
71 | model = sm.get_session(session_id=session_id) | |
50 | expected = {'id':session_id, 'notebook':{'name':u'new_name.ipynb', 'path': u'/path/to/'}, 'kernel':{'id':u'5678'}} |
|
72 | expected = {'id':session_id, | |
|
73 | 'notebook':{'name':u'new_name.ipynb', 'path': u'/path/to/'}, | |||
|
74 | 'kernel':{'id':u'A', 'name':'julia'}} | |||
51 | self.assertEqual(model, expected) |
|
75 | self.assertEqual(model, expected) | |
52 |
|
76 | |||
53 | def test_bad_update_session(self): |
|
77 | def test_bad_update_session(self): | |
54 | # try to update a session with a bad keyword ~ raise error |
|
78 | # try to update a session with a bad keyword ~ raise error | |
55 | sm = SessionManager() |
|
79 | sm = SessionManager(kernel_manager=DummyMKM()) | |
56 | session_id = sm.new_session_id() |
|
80 | session_id = sm.create_session(name='test.ipynb', path='/path/to/', | |
57 | sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel_id='5678') |
|
81 | kernel_name='ir')['id'] | |
58 | self.assertRaises(TypeError, sm.update_session, session_id=session_id, bad_kw='test.ipynb') # Bad keyword |
|
82 | self.assertRaises(TypeError, sm.update_session, session_id=session_id, bad_kw='test.ipynb') # Bad keyword | |
59 |
|
83 | |||
60 | def test_delete_session(self): |
|
84 | def test_delete_session(self): | |
61 | sm = SessionManager() |
|
85 | sm = SessionManager(kernel_manager=DummyMKM()) | |
62 | session_id1 = sm.new_session_id() |
|
86 | sessions = [ | |
63 | session_id2 = sm.new_session_id() |
|
87 | sm.create_session(name='test1.ipynb', path='/path/to/1/', kernel_name='python'), | |
64 | session_id3 = sm.new_session_id() |
|
88 | sm.create_session(name='test2.ipynb', path='/path/to/2/', kernel_name='python'), | |
65 |
sm. |
|
89 | sm.create_session(name='test3.ipynb', path='/path/to/3/', kernel_name='python'), | |
66 | sm.save_session(session_id=session_id2, name='test2.ipynb', path='/path/to/2/', kernel_id='5678') |
|
90 | ] | |
67 | sm.save_session(session_id=session_id3, name='test3.ipynb', path='/path/to/3/', kernel_id='5678') |
|
91 | sm.delete_session(sessions[1]['id']) | |
68 |
sm. |
|
92 | new_sessions = sm.list_sessions() | |
69 | sessions = sm.list_sessions() |
|
93 | expected = [{'id':sessions[0]['id'], 'notebook':{'name':u'test1.ipynb', | |
70 | expected = [{'id':session_id1, 'notebook':{'name':u'test1.ipynb', |
|
94 | 'path': u'/path/to/1/'}, 'kernel':{'id':u'A', 'name':'python'}}, | |
71 | 'path': u'/path/to/1/'}, 'kernel':{'id':u'5678'}}, |
|
95 | {'id':sessions[2]['id'], 'notebook':{'name':u'test3.ipynb', | |
72 | {'id':session_id3, 'notebook':{'name':u'test3.ipynb', |
|
96 | 'path': u'/path/to/3/'}, 'kernel':{'id':u'C', 'name':'python'}}] | |
73 | 'path': u'/path/to/3/'}, 'kernel':{'id':u'5678'}}] |
|
97 | self.assertEqual(new_sessions, expected) | |
74 | self.assertEqual(sessions, expected) |
|
|||
75 |
|
98 | |||
76 | def test_bad_delete_session(self): |
|
99 | def test_bad_delete_session(self): | |
77 | # try to delete a session that doesn't exist ~ raise error |
|
100 | # try to delete a session that doesn't exist ~ raise error | |
78 | sm = SessionManager() |
|
101 | sm = SessionManager(kernel_manager=DummyMKM()) | |
79 | session_id = sm.new_session_id() |
|
102 | sm.create_session(name='test.ipynb', path='/path/to/', kernel_name='python') | |
80 | sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel_id='5678') |
|
|||
81 | self.assertRaises(TypeError, sm.delete_session, bad_kwarg='23424') # Bad keyword |
|
103 | self.assertRaises(TypeError, sm.delete_session, bad_kwarg='23424') # Bad keyword | |
82 | self.assertRaises(web.HTTPError, sm.delete_session, session_id='23424') # nonexistant |
|
104 | self.assertRaises(web.HTTPError, sm.delete_session, session_id='23424') # nonexistant | |
83 |
|
105 |
@@ -37,8 +37,9 b' class SessionAPI(object):' | |||||
37 | def get(self, id): |
|
37 | def get(self, id): | |
38 | return self._req('GET', id) |
|
38 | return self._req('GET', id) | |
39 |
|
39 | |||
40 | def create(self, name, path): |
|
40 | def create(self, name, path, kernel_name='python'): | |
41 |
body = json.dumps({'notebook': {'name':name, 'path':path} |
|
41 | body = json.dumps({'notebook': {'name':name, 'path':path}, | |
|
42 | 'kernel': {'name': kernel_name}}) | |||
42 | return self._req('POST', '', body) |
|
43 | return self._req('POST', '', body) | |
43 |
|
44 | |||
44 | def modify(self, id, name, path): |
|
45 | def modify(self, id, name, path): |
@@ -59,6 +59,9 b' define([' | |||||
59 | this.keyboard_manager = options.keyboard_manager; |
|
59 | this.keyboard_manager = options.keyboard_manager; | |
60 | this.save_widget = options.save_widget; |
|
60 | this.save_widget = options.save_widget; | |
61 | this.tooltip = new tooltip.Tooltip(this.events); |
|
61 | this.tooltip = new tooltip.Tooltip(this.events); | |
|
62 | // default_kernel_name is a temporary measure while we implement proper | |||
|
63 | // kernel selection and delayed start. Do not rely on it. | |||
|
64 | this.default_kernel_name = 'python'; | |||
62 | // TODO: This code smells (and the other `= this` line a couple lines down) |
|
65 | // TODO: This code smells (and the other `= this` line a couple lines down) | |
63 | // We need a better way to deal with circular instance references. |
|
66 | // We need a better way to deal with circular instance references. | |
64 | this.keyboard_manager.notebook = this; |
|
67 | this.keyboard_manager.notebook = this; | |
@@ -1495,7 +1498,12 b' define([' | |||||
1495 | base_url: this.base_url, |
|
1498 | base_url: this.base_url, | |
1496 | notebook_path: this.notebook_path, |
|
1499 | notebook_path: this.notebook_path, | |
1497 | notebook_name: this.notebook_name, |
|
1500 | notebook_name: this.notebook_name, | |
|
1501 | // For now, create all sessions with the 'python' kernel, which is the | |||
|
1502 | // default. Later, the user will be able to select kernels. This is | |||
|
1503 | // overridden if KernelManager.kernel_cmd is specified for the server. | |||
|
1504 | kernel_name: this.default_kernel_name, | |||
1498 | notebook: this}); |
|
1505 | notebook: this}); | |
|
1506 | ||||
1499 | this.session.start($.proxy(this._session_started, this)); |
|
1507 | this.session.start($.proxy(this._session_started, this)); | |
1500 | }; |
|
1508 | }; | |
1501 |
|
1509 |
@@ -15,13 +15,14 b' define([' | |||||
15 | * A Kernel Class to communicate with the Python kernel |
|
15 | * A Kernel Class to communicate with the Python kernel | |
16 | * @Class Kernel |
|
16 | * @Class Kernel | |
17 | */ |
|
17 | */ | |
18 | var Kernel = function (kernel_service_url, notebook) { |
|
18 | var Kernel = function (kernel_service_url, notebook, name) { | |
19 | this.events = notebook.events; |
|
19 | this.events = notebook.events; | |
20 | this.kernel_id = null; |
|
20 | this.kernel_id = null; | |
21 | this.shell_channel = null; |
|
21 | this.shell_channel = null; | |
22 | this.iopub_channel = null; |
|
22 | this.iopub_channel = null; | |
23 | this.stdin_channel = null; |
|
23 | this.stdin_channel = null; | |
24 | this.kernel_service_url = kernel_service_url; |
|
24 | this.kernel_service_url = kernel_service_url; | |
|
25 | this.name = name; | |||
25 | this.running = false; |
|
26 | this.running = false; | |
26 | this.username = "username"; |
|
27 | this.username = "username"; | |
27 | this.session_id = utils.uuid(); |
|
28 | this.session_id = utils.uuid(); |
@@ -15,6 +15,7 b' define([' | |||||
15 | this.notebook = options.notebook; |
|
15 | this.notebook = options.notebook; | |
16 | this.name = options.notebook_name; |
|
16 | this.name = options.notebook_name; | |
17 | this.path = options.notebook_path; |
|
17 | this.path = options.notebook_path; | |
|
18 | this.kernel_name = options.kernel_name; | |||
18 | this.base_url = options.base_url; |
|
19 | this.base_url = options.base_url; | |
19 | }; |
|
20 | }; | |
20 |
|
21 | |||
@@ -24,6 +25,9 b' define([' | |||||
24 | notebook : { |
|
25 | notebook : { | |
25 | name : this.name, |
|
26 | name : this.name, | |
26 | path : this.path |
|
27 | path : this.path | |
|
28 | }, | |||
|
29 | kernel : { | |||
|
30 | name : this.kernel_name | |||
27 | } |
|
31 | } | |
28 | }; |
|
32 | }; | |
29 | var settings = { |
|
33 | var settings = { | |
@@ -87,7 +91,7 b' define([' | |||||
87 | Session.prototype._handle_start_success = function (data, status, xhr) { |
|
91 | Session.prototype._handle_start_success = function (data, status, xhr) { | |
88 | this.id = data.id; |
|
92 | this.id = data.id; | |
89 | var kernel_service_url = utils.url_path_join(this.base_url, "api/kernels"); |
|
93 | var kernel_service_url = utils.url_path_join(this.base_url, "api/kernels"); | |
90 | this.kernel = new kernel.Kernel(kernel_service_url, this.notebook); |
|
94 | this.kernel = new kernel.Kernel(kernel_service_url, this.notebook, this.kernel_name); | |
91 | this.kernel._kernel_started(data.kernel); |
|
95 | this.kernel._kernel_started(data.kernel); | |
92 | }; |
|
96 | }; | |
93 |
|
97 |
@@ -80,7 +80,7 b' casper.notebook_test(function () {' | |||||
80 | }); |
|
80 | }); | |
81 | return return_this_thing; |
|
81 | return return_this_thing; | |
82 | }, {nbname:nbname}); |
|
82 | }, {nbname:nbname}); | |
83 |
this.test.assertEquals(notebook_url |
|
83 | this.test.assertNotEquals(notebook_url, null, "Escaped URL in notebook list"); | |
84 | // open the notebook |
|
84 | // open the notebook | |
85 | this.open(notebook_url); |
|
85 | this.open(notebook_url); | |
86 | }); |
|
86 | }); |
@@ -63,7 +63,7 b' casper.kernel_running = function() {' | |||||
63 | casper.shutdown_current_kernel = function () { |
|
63 | casper.shutdown_current_kernel = function () { | |
64 | // Shut down the current notebook's kernel. |
|
64 | // Shut down the current notebook's kernel. | |
65 | this.thenEvaluate(function() { |
|
65 | this.thenEvaluate(function() { | |
66 |
IPython.notebook. |
|
66 | IPython.notebook.session.delete(); | |
67 | }); |
|
67 | }); | |
68 | // We close the page right after this so we need to give it time to complete. |
|
68 | // We close the page right after this so we need to give it time to complete. | |
69 | this.wait(1000); |
|
69 | this.wait(1000); |
@@ -92,7 +92,7 b' class MultiKernelManager(LoggingConfigurable):' | |||||
92 | def __contains__(self, kernel_id): |
|
92 | def __contains__(self, kernel_id): | |
93 | return kernel_id in self._kernels |
|
93 | return kernel_id in self._kernels | |
94 |
|
94 | |||
95 | def start_kernel(self, **kwargs): |
|
95 | def start_kernel(self, kernel_name='python', **kwargs): | |
96 | """Start a new kernel. |
|
96 | """Start a new kernel. | |
97 |
|
97 | |||
98 | The caller can pick a kernel_id by passing one in as a keyword arg, |
|
98 | The caller can pick a kernel_id by passing one in as a keyword arg, | |
@@ -111,7 +111,7 b' class MultiKernelManager(LoggingConfigurable):' | |||||
111 | # including things like its transport and ip. |
|
111 | # including things like its transport and ip. | |
112 | km = self.kernel_manager_factory(connection_file=os.path.join( |
|
112 | km = self.kernel_manager_factory(connection_file=os.path.join( | |
113 | self.connection_dir, "kernel-%s.json" % kernel_id), |
|
113 | self.connection_dir, "kernel-%s.json" % kernel_id), | |
114 | parent=self, autorestart=True, log=self.log |
|
114 | parent=self, autorestart=True, log=self.log, kernel_name=kernel_name, | |
115 | ) |
|
115 | ) | |
116 | km.start_kernel(**kwargs) |
|
116 | km.start_kernel(**kwargs) | |
117 | self._kernels[kernel_id] = km |
|
117 | self._kernels[kernel_id] = km |
General Comments 0
You need to be logged in to leave comments.
Login now