##// END OF EJS Templates
session manager restructuring...
Zachary Sailer -
Show More
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,12 b''
1 """Tests for the content manager."""
2
3 import os
4 from unittest import TestCase
5 from tempfile import NamedTemporaryFile
6
7 from IPython.utils.tempdir import TemporaryDirectory
8 from IPython.utils.traitlets import TraitError
9
10 from ..contentmanager import ContentManager
11
12 #class TestContentManager(TestCase):
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -0,0 +1,86 b''
1 """Tests for the session manager."""
2
3 import os
4
5 from unittest import TestCase
6 from tempfile import NamedTemporaryFile
7
8 from IPython.utils.tempdir import TemporaryDirectory
9 from IPython.utils.traitlets import TraitError
10
11 from ..sessionmanager import SessionManager
12
13 class TestSessionManager(TestCase):
14
15 def test_get_session(self):
16 sm = SessionManager()
17 session_id = sm.get_session_id()
18 sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel='5678')
19 model = sm.get_session(id=session_id)
20 expected = {'id':session_id, 'name':u'test.ipynb', 'path': u'/path/to/', 'kernel':{'id':u'5678', 'ws_url': u''}}
21 self.assertEqual(model, expected)
22
23 def test_bad_get_session(self):
24 # Should raise error if a bad key is passed to the database.
25 sm = SessionManager()
26 session_id = sm.get_session_id()
27 sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel='5678')
28 self.assertRaises(TraitError, sm.get_session, bad_id=session_id) # Bad keyword
29
30 def test_list_sessions(self):
31 sm = SessionManager()
32 session_id1 = sm.get_session_id()
33 session_id2 = sm.get_session_id()
34 session_id3 = sm.get_session_id()
35 sm.save_session(session_id=session_id1, name='test1.ipynb', path='/path/to/1/', kernel='5678')
36 sm.save_session(session_id=session_id2, name='test2.ipynb', path='/path/to/2/', kernel='5678')
37 sm.save_session(session_id=session_id3, name='test3.ipynb', path='/path/to/3/', kernel='5678')
38 sessions = sm.list_sessions()
39 expected = [{'id':session_id1, 'name':u'test1.ipynb',
40 'path': u'/path/to/1/', 'kernel':{'id':u'5678', 'ws_url': u''}},
41 {'id':session_id2, 'name':u'test2.ipynb',
42 'path': u'/path/to/2/', 'kernel':{'id':u'5678', 'ws_url': u''}},
43 {'id':session_id3, 'name':u'test3.ipynb',
44 'path': u'/path/to/3/', 'kernel':{'id':u'5678', 'ws_url': u''}}]
45 self.assertEqual(sessions, expected)
46
47 def test_update_session(self):
48 sm = SessionManager()
49 session_id = sm.get_session_id()
50 sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel=None)
51 sm.update_session(session_id, kernel='5678')
52 sm.update_session(session_id, name='new_name.ipynb')
53 model = sm.get_session(id=session_id)
54 expected = {'id':session_id, 'name':u'new_name.ipynb', 'path': u'/path/to/', 'kernel':{'id':u'5678', 'ws_url': u''}}
55 self.assertEqual(model, expected)
56
57 def test_bad_update_session(self):
58 # try to update a session with a bad keyword ~ raise error
59 sm = SessionManager()
60 session_id = sm.get_session_id()
61 sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel='5678')
62 self.assertRaises(TraitError, sm.update_session, session_id=session_id, bad_kw='test.ipynb') # Bad keyword
63
64 def test_delete_session(self):
65 sm = SessionManager()
66 session_id1 = sm.get_session_id()
67 session_id2 = sm.get_session_id()
68 session_id3 = sm.get_session_id()
69 sm.save_session(session_id=session_id1, name='test1.ipynb', path='/path/to/1/', kernel='5678')
70 sm.save_session(session_id=session_id2, name='test2.ipynb', path='/path/to/2/', kernel='5678')
71 sm.save_session(session_id=session_id3, name='test3.ipynb', path='/path/to/3/', kernel='5678')
72 sm.delete_session(session_id2)
73 sessions = sm.list_sessions()
74 expected = [{'id':session_id1, 'name':u'test1.ipynb',
75 'path': u'/path/to/1/', 'kernel':{'id':u'5678', 'ws_url': u''}},
76 {'id':session_id3, 'name':u'test3.ipynb',
77 'path': u'/path/to/3/', 'kernel':{'id':u'5678', 'ws_url': u''}}]
78 self.assertEqual(sessions, expected)
79
80 def test_bad_delete_session(self):
81 # try to delete a session that doesn't exist ~ raise error
82 sm = SessionManager()
83 session_id = sm.get_session_id()
84 sm.save_session(session_id=session_id, name='test.ipynb', path='/path/to/', kernel='5678')
85 self.assertRaises(TraitError, sm.delete_session, session_id='23424') # Bad keyword
86
@@ -46,17 +46,16 b' class MappingKernelManager(MultiKernelManager):'
46 self.log.warn("Kernel %s died, removing from map.", kernel_id)
46 self.log.warn("Kernel %s died, removing from map.", kernel_id)
47 self.remove_kernel(kernel_id)
47 self.remove_kernel(kernel_id)
48
48
49 def start_kernel(self, **kwargs):
49 def start_kernel(self, kernel_id=None, **kwargs):
50 """Start a kernel for a session an return its kernel_id.
50 """Start a kernel for a session an return its kernel_id.
51
51
52 Parameters
52 Parameters
53 ----------
53 ----------
54 session_id : uuid
54 kernel_id : uuid
55 The uuid of the session to associate the new kernel with. If this
55 The uuid to associate the new kernel with. If this
56 is not None, this kernel will be persistent whenever the session
56 is not None, this kernel will be persistent whenever it is
57 requests a kernel.
57 requested.
58 """
58 """
59 kernel_id = None
60 if kernel_id is None:
59 if kernel_id is None:
61 kwargs['extra_arguments'] = self.kernel_argv
60 kwargs['extra_arguments'] = self.kernel_argv
62 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
61 kernel_id = super(MappingKernelManager, self).start_kernel(**kwargs)
@@ -28,7 +28,6 b' from ...base.handlers import IPythonHandler'
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30
30
31
32 class SessionRootHandler(IPythonHandler):
31 class SessionRootHandler(IPythonHandler):
33
32
34 @web.authenticated
33 @web.authenticated
@@ -45,12 +44,19 b' class SessionRootHandler(IPythonHandler):'
45 nbm = self.notebook_manager
44 nbm = self.notebook_manager
46 km = self.kernel_manager
45 km = self.kernel_manager
47 notebook_path = self.get_argument('notebook_path', default=None)
46 notebook_path = self.get_argument('notebook_path', default=None)
48 notebook_name, path = nbm.named_notebook_path(notebook_path)
47 name, path = nbm.named_notebook_path(notebook_path)
49 session_id, model = sm.get_session(notebook_name, path)
48 if sm.session_exists(name=name, path=path):
50 if model == None:
49 model = sm.get_session(name=name, path=path)
51 kernel_id = km.start_kernel()
50 kernel_id = model['kernel']['id']
51 km.start_kernel(kernel_id, cwd=nbm.notebook_dir)
52 else:
53 session_id = sm.get_session_id()
54 sm.save_session(session_id=session_id, name=name, path=path)
55 kernel_id = km.start_kernel(cwd=nbm.notebook_dir)
52 kernel = km.kernel_model(kernel_id, self.ws_url)
56 kernel = km.kernel_model(kernel_id, self.ws_url)
53 model = sm.session_model(session_id, notebook_name, path, kernel)
57 sm.update_session(session_id, kernel=kernel_id)
58 model = sm.get_session(id=session_id)
59 self.set_header('Location', '{0}kernels/{1}'.format(self.base_kernel_url, kernel_id))
54 self.finish(jsonapi.dumps(model))
60 self.finish(jsonapi.dumps(model))
55
61
56 class SessionHandler(IPythonHandler):
62 class SessionHandler(IPythonHandler):
@@ -60,20 +66,19 b' class SessionHandler(IPythonHandler):'
60 @web.authenticated
66 @web.authenticated
61 def get(self, session_id):
67 def get(self, session_id):
62 sm = self.session_manager
68 sm = self.session_manager
63 model = sm.get_session_from_id(session_id)
69 model = sm.get_session(id=session_id)
64 self.finish(jsonapi.dumps(model))
70 self.finish(jsonapi.dumps(model))
65
71
66 @web.authenticated
72 @web.authenticated
67 def patch(self, session_id):
73 def patch(self, session_id):
74 # Currently, this handler is strictly for renaming notebooks
68 sm = self.session_manager
75 sm = self.session_manager
69 nbm = self.notebook_manager
76 nbm = self.notebook_manager
70 km = self.kernel_manager
77 km = self.kernel_manager
71 notebook_path = self.request.body
78 notebook_path = self.request.body
72 notebook_name, path = nbm.named_notebook_path(notebook_path)
79 name, path = nbm.named_notebook_path(notebook_path)
73 kernel_id = sm.get_kernel_from_session(session_id)
80 sm.update_session(id=session_id, name=name)
74 kernel = km.kernel_model(kernel_id, self.ws_url)
81 model = sm.get_session(id=session_id)
75 sm.delete_mapping_for_session(session_id)
76 model = sm.session_model(session_id, notebook_name, path, kernel)
77 self.finish(jsonapi.dumps(model))
82 self.finish(jsonapi.dumps(model))
78
83
79 @web.authenticated
84 @web.authenticated
@@ -81,9 +86,9 b' class SessionHandler(IPythonHandler):'
81 sm = self.session_manager
86 sm = self.session_manager
82 nbm = self.notebook_manager
87 nbm = self.notebook_manager
83 km = self.kernel_manager
88 km = self.kernel_manager
84 kernel_id = sm.get_kernel_from_session(session_id)
89 session = sm.get_session(id=session_id)
85 km.shutdown_kernel(kernel_id)
90 sm.delete_session(session_id)
86 sm.delete_mapping_for_session(session_id)
91 km.shutdown_kernel(session['kernel']['id'])
87 self.set_status(204)
92 self.set_status(204)
88 self.finish()
93 self.finish()
89
94
@@ -99,6 +104,3 b' default_handlers = ['
99 (r"api/sessions", SessionRootHandler)
104 (r"api/sessions", SessionRootHandler)
100 ]
105 ]
101
106
102
103
104
@@ -18,6 +18,7 b' Authors:'
18
18
19 import os
19 import os
20 import uuid
20 import uuid
21 import sqlite3
21
22
22 from tornado import web
23 from tornado import web
23
24
@@ -31,67 +32,139 b' from IPython.utils.traitlets import List, Dict, Unicode, TraitError'
31
32
32 class SessionManager(LoggingConfigurable):
33 class SessionManager(LoggingConfigurable):
33
34
34 # Use session_ids to map notebook names to kernel_ids
35 # Session database initialized below
35 sessions = List()
36 _cursor = None
37 _connection = None
36
38
37 def get_session(self, nb_name, nb_path=None):
39 @property
38 """Get an existing session or create a new one"""
40 def cursor(self):
39 model = None
41 """Start a cursor and create a database called 'session'"""
40 for session in self.sessions:
42 if self._cursor is None:
41 if session['name'] == nb_name and session['path'] == nb_path:
43 self._cursor = self.connection.cursor()
42 session_id = session['id']
44 self._cursor.execute("""CREATE TABLE session
43 model = session
45 (id, name, path, kernel)""")
44 if model != None:
46 return self._cursor
45 return session_id, model
47
48 @property
49 def connection(self):
50 """Start a database connection"""
51 if self._connection is None:
52 self._connection = sqlite3.connect(':memory:')
53 self._connection.row_factory = sqlite3.Row
54 return self._connection
55
56 def __del__(self):
57 """Close connection once SessionManager closes"""
58 self.cursor.close()
59
60 def session_exists(self, name, path):
61 """Check to see if the session for the given notebook exists"""
62 self.cursor.execute("SELECT * FROM session WHERE name=? AND path=?", (name,path))
63 reply = self.cursor.fetchone()
64 if reply is None:
65 return False
46 else:
66 else:
47 session_id = unicode(uuid.uuid4())
67 return True
48 return session_id, model
68
49
69 def get_session_id(self):
50 def session_model(self, session_id, notebook_name=None, notebook_path=None, kernel=None):
70 "Create a uuid for a new session"
51 """ Create a session that links notebooks with kernels """
71 return unicode(uuid.uuid4())
52 model = dict(id=session_id,
72
53 name=notebook_name,
73 def save_session(self, session_id, name=None, path=None, kernel=None):
54 path=notebook_path,
74 """ Given a session_id (and any other of the arguments), this method
55 kernel=kernel)
75 creates a row in the sqlite session database that holds the information
56 if notebook_path == None:
76 for a session.
57 model['path']=""
58 self.sessions.append(model)
59 return model
60
61 def list_sessions(self):
62 """List all sessions and their information"""
63 return self.sessions
64
65 def set_kernel_for_sessions(self, session_id, kernel_id):
66 """Maps the kernel_ids to the session_id in session_mapping"""
67 for session in self.sessions:
68 if session['id'] == session_id:
69 session['kernel']['id'] = kernel_id
70 return self.sessions
71
77
72 def delete_mapping_for_session(self, session_id):
78 Parameters
73 """Delete the session from session_mapping with the given session_id"""
79 ----------
74 i = 0
80 session_id : str
75 for session in self.sessions:
81 uuid for the session; this method must be given a session_id
76 if session['id'] == session_id:
82 name : str
77 del self.sessions[i]
83 the .ipynb notebook name that started the session
78 i = i + 1
84 path : str
79 return self.sessions
85 the path to the named notebook
86 kernel : str
87 a uuid for the kernel associated with this session
88 """
89 self.cursor.execute("""INSERT INTO session VALUES
90 (?,?,?,?)""", (session_id, name, path, kernel))
91 self.connection.commit()
92
93 def get_session(self, **kwargs):
94 """ Takes a keyword argument and searches for the value in the session
95 database, then returns the rest of the session's info.
96
97 Parameters
98 ----------
99 **kwargs : keyword argument
100 must be given one of the keywords and values from the session database
101 (i.e. session_id, name, path, kernel)
102
103 Returns
104 -------
105 model : dict
106 returns a dictionary that includes all the information from the
107 session described by the kwarg.
108 """
109 column = kwargs.keys()[0] # uses only the first kwarg that is entered
110 value = kwargs.values()[0]
111 try:
112 self.cursor.execute("SELECT * FROM session WHERE %s=?" %column, (value,))
113 except sqlite3.OperationalError:
114 raise TraitError("The session database has no column: %s" %column)
115 reply = self.cursor.fetchone()
116 if reply is not None:
117 model = self.reply_to_dictionary_model(reply)
118 else:
119 model = None
120 return model
121
122 def update_session(self, session_id, **kwargs):
123 """Updates the values in the session with the given session_id
124 with the values from the keyword arguments.
80
125
81 def get_session_from_id(self, session_id):
126 Parameters
82 for session in self.sessions:
127 ----------
83 if session['id'] == session_id:
128 session_id : str
84 return session
129 a uuid that identifies a session in the sqlite3 database
85
130 **kwargs : str
86 def get_notebook_from_session(self, session_id):
131 the key must correspond to a column title in session database,
87 """Returns the notebook_path for the given session_id"""
132 and the value replaces the current value in the session
88 for session in self.sessions:
133 with session_id.
89 if session['id'] == session_id:
134 """
90 return session['name']
135 column = kwargs.keys()[0] # uses only the first kwarg that is entered
91
136 value = kwargs.values()[0]
92 def get_kernel_from_session(self, session_id):
137 try:
93 """Returns the kernel_id for the given session_id"""
138 self.cursor.execute("UPDATE session SET %s=? WHERE id=?" %column, (value, session_id))
94 for session in self.sessions:
139 self.connection.commit()
95 if session['id'] == session_id:
140 except sqlite3.OperationalError:
96 return session['kernel']['id']
141 raise TraitError("No session exists with ID: %s" %session_id)
142
143 def reply_to_dictionary_model(self, reply):
144 """Takes sqlite database session row and turns it into a dictionary"""
145 model = {'id': reply['id'],
146 'name' : reply['name'],
147 'path' : reply['path'],
148 'kernel' : {'id':reply['kernel'], 'ws_url': ''}}
149 return model
97
150
151 def list_sessions(self):
152 """Returns a list of dictionaries containing all the information from
153 the session database"""
154 session_list=[]
155 self.cursor.execute("SELECT * FROM session")
156 sessions = self.cursor.fetchall()
157 for session in sessions:
158 model = self.reply_to_dictionary_model(session)
159 session_list.append(model)
160 return session_list
161
162 def delete_session(self, session_id):
163 """Deletes the row in the session database with given session_id"""
164 # Check that session exists before deleting
165 model = self.get_session(id=session_id)
166 if model is None:
167 raise TraitError("The session does not exist: %s" %session_id)
168 else:
169 self.cursor.execute("DELETE FROM session WHERE id=?", (session_id,))
170 self.connection.commit() No newline at end of file
@@ -24,16 +24,15 b' var IPython = (function (IPython) {'
24 * A Kernel Class to communicate with the Python kernel
24 * A Kernel Class to communicate with the Python kernel
25 * @Class Kernel
25 * @Class Kernel
26 */
26 */
27 var Kernel = function (base_url, session_id) {
27 var Kernel = function (base_url) {
28 this.kernel_id = null;
28 this.kernel_id = null;
29 this.session_id = session_id
30 this.shell_channel = null;
29 this.shell_channel = null;
31 this.iopub_channel = null;
30 this.iopub_channel = null;
32 this.stdin_channel = null;
31 this.stdin_channel = null;
33 this.base_url = base_url;
32 this.base_url = base_url;
34 this.running = false;
33 this.running = false;
35 this.username = "username";
34 this.username= "username";
36 this.base_session_id = utils.uuid();
35 this.session_id = utils.uuid();
37 this._msg_callbacks = {};
36 this._msg_callbacks = {};
38
37
39 if (typeof(WebSocket) !== 'undefined') {
38 if (typeof(WebSocket) !== 'undefined') {
@@ -52,7 +51,7 b' var IPython = (function (IPython) {'
52 header : {
51 header : {
53 msg_id : utils.uuid(),
52 msg_id : utils.uuid(),
54 username : this.username,
53 username : this.username,
55 session : this.base_session_id,
54 session : this.session_id,
56 msg_type : msg_type
55 msg_type : msg_type
57 },
56 },
58 metadata : {},
57 metadata : {},
@@ -76,7 +75,6 b' var IPython = (function (IPython) {'
76 Kernel.prototype.start = function (params) {
75 Kernel.prototype.start = function (params) {
77 var that = this;
76 var that = this;
78 params = params || {};
77 params = params || {};
79 params.session = this.session_id;
80 if (!this.running) {
78 if (!this.running) {
81 var qs = $.param(params);
79 var qs = $.param(params);
82 var url = this.base_url + '?' + qs;
80 var url = this.base_url + '?' + qs;
@@ -66,7 +66,6 b' var IPython = (function (IPython) {'
66 this.kernel_content = json.kernel;
66 this.kernel_content = json.kernel;
67 var base_url = $('body').data('baseKernelUrl') + "api/kernels";
67 var base_url = $('body').data('baseKernelUrl') + "api/kernels";
68 this.kernel = new IPython.Kernel(base_url, this.session_id);
68 this.kernel = new IPython.Kernel(base_url, this.session_id);
69 // Now that the kernel has been created, tell the CodeCells about it.
70 this.kernel._kernel_started(this.kernel_content);
69 this.kernel._kernel_started(this.kernel_content);
71 };
70 };
72
71
General Comments 0
You need to be logged in to leave comments. Login now