##// 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
36
37 _connection = None
37 def get_session(self, nb_name, nb_path=None):
38
38 """Get an existing session or create a new one"""
39 @property
39 model = None
40 def cursor(self):
40 for session in self.sessions:
41 """Start a cursor and create a database called 'session'"""
41 if session['name'] == nb_name and session['path'] == nb_path:
42 if self._cursor is None:
42 session_id = session['id']
43 self._cursor = self.connection.cursor()
43 model = session
44 self._cursor.execute("""CREATE TABLE session
44 if model != None:
45 (id, name, path, kernel)""")
45 return session_id, model
46 return self._cursor
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
66 else:
67 return True
68
69 def get_session_id(self):
70 "Create a uuid for a new session"
71 return unicode(uuid.uuid4())
72
73 def save_session(self, session_id, name=None, path=None, kernel=None):
74 """ Given a session_id (and any other of the arguments), this method
75 creates a row in the sqlite session database that holds the information
76 for a session.
77
78 Parameters
79 ----------
80 session_id : str
81 uuid for the session; this method must be given a session_id
82 name : str
83 the .ipynb notebook name that started the session
84 path : str
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)
46 else:
118 else:
47 session_id = unicode(uuid.uuid4())
119 model = None
48 return session_id, model
49
50 def session_model(self, session_id, notebook_name=None, notebook_path=None, kernel=None):
51 """ Create a session that links notebooks with kernels """
52 model = dict(id=session_id,
53 name=notebook_name,
54 path=notebook_path,
55 kernel=kernel)
56 if notebook_path == None:
57 model['path']=""
58 self.sessions.append(model)
59 return model
120 return model
60
121
61 def list_sessions(self):
122 def update_session(self, session_id, **kwargs):
62 """List all sessions and their information"""
123 """Updates the values in the session with the given session_id
63 return self.sessions
124 with the values from the keyword arguments.
64
125
65 def set_kernel_for_sessions(self, session_id, kernel_id):
126 Parameters
66 """Maps the kernel_ids to the session_id in session_mapping"""
127 ----------
67 for session in self.sessions:
128 session_id : str
68 if session['id'] == session_id:
129 a uuid that identifies a session in the sqlite3 database
69 session['kernel']['id'] = kernel_id
130 **kwargs : str
70 return self.sessions
131 the key must correspond to a column title in session database,
71
132 and the value replaces the current value in the session
72 def delete_mapping_for_session(self, session_id):
133 with session_id.
73 """Delete the session from session_mapping with the given session_id"""
134 """
74 i = 0
135 column = kwargs.keys()[0] # uses only the first kwarg that is entered
75 for session in self.sessions:
136 value = kwargs.values()[0]
76 if session['id'] == session_id:
137 try:
77 del self.sessions[i]
138 self.cursor.execute("UPDATE session SET %s=? WHERE id=?" %column, (value, session_id))
78 i = i + 1
139 self.connection.commit()
79 return self.sessions
140 except sqlite3.OperationalError:
80
141 raise TraitError("No session exists with ID: %s" %session_id)
81 def get_session_from_id(self, session_id):
142
82 for session in self.sessions:
143 def reply_to_dictionary_model(self, reply):
83 if session['id'] == session_id:
144 """Takes sqlite database session row and turns it into a dictionary"""
84 return session
145 model = {'id': reply['id'],
85
146 'name' : reply['name'],
86 def get_notebook_from_session(self, session_id):
147 'path' : reply['path'],
87 """Returns the notebook_path for the given session_id"""
148 'kernel' : {'id':reply['kernel'], 'ws_url': ''}}
88 for session in self.sessions:
149 return model
89 if session['id'] == session_id:
90 return session['name']
91
92 def get_kernel_from_session(self, session_id):
93 """Returns the kernel_id for the given session_id"""
94 for session in self.sessions:
95 if session['id'] == session_id:
96 return session['kernel']['id']
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