##// END OF EJS Templates
allow kernel_name to be undefined in requests...
MinRK -
Show More
@@ -1,112 +1,113 b''
1 1 """Tornado handlers for the sessions web service."""
2 2
3 3 # Copyright (c) IPython Development Team.
4 4 # Distributed under the terms of the Modified BSD License.
5 5
6 6 import json
7 7
8 8 from tornado import web
9 9
10 10 from ...base.handlers import IPythonHandler, json_errors
11 11 from IPython.utils.jsonutil import date_default
12 12 from IPython.html.utils import url_path_join, url_escape
13 13
14 14
15 15 class SessionRootHandler(IPythonHandler):
16 16
17 17 @web.authenticated
18 18 @json_errors
19 19 def get(self):
20 20 # Return a list of running sessions
21 21 sm = self.session_manager
22 22 sessions = sm.list_sessions()
23 23 self.finish(json.dumps(sessions, default=date_default))
24 24
25 25 @web.authenticated
26 26 @json_errors
27 27 def post(self):
28 28 # Creates a new session
29 29 #(unless a session already exists for the named nb)
30 30 sm = self.session_manager
31 31 cm = self.contents_manager
32 32 km = self.kernel_manager
33 33
34 34 model = self.get_json_body()
35 35 if model is None:
36 36 raise web.HTTPError(400, "No JSON data provided")
37 37 try:
38 38 name = model['notebook']['name']
39 39 except KeyError:
40 40 raise web.HTTPError(400, "Missing field in JSON data: notebook.name")
41 41 try:
42 42 path = model['notebook']['path']
43 43 except KeyError:
44 44 raise web.HTTPError(400, "Missing field in JSON data: notebook.path")
45 45 try:
46 46 kernel_name = model['kernel']['name']
47 47 except KeyError:
48 raise web.HTTPError(400, "Missing field in JSON data: kernel.name")
48 self.log.debug("No kernel name specified, using default kernel")
49 kernel_name = None
49 50
50 51 # Check to see if session exists
51 52 if sm.session_exists(name=name, path=path):
52 53 model = sm.get_session(name=name, path=path)
53 54 else:
54 55 model = sm.create_session(name=name, path=path, kernel_name=kernel_name)
55 56 location = url_path_join(self.base_url, 'api', 'sessions', model['id'])
56 57 self.set_header('Location', url_escape(location))
57 58 self.set_status(201)
58 59 self.finish(json.dumps(model, default=date_default))
59 60
60 61 class SessionHandler(IPythonHandler):
61 62
62 63 SUPPORTED_METHODS = ('GET', 'PATCH', 'DELETE')
63 64
64 65 @web.authenticated
65 66 @json_errors
66 67 def get(self, session_id):
67 68 # Returns the JSON model for a single session
68 69 sm = self.session_manager
69 70 model = sm.get_session(session_id=session_id)
70 71 self.finish(json.dumps(model, default=date_default))
71 72
72 73 @web.authenticated
73 74 @json_errors
74 75 def patch(self, session_id):
75 76 # Currently, this handler is strictly for renaming notebooks
76 77 sm = self.session_manager
77 78 model = self.get_json_body()
78 79 if model is None:
79 80 raise web.HTTPError(400, "No JSON data provided")
80 81 changes = {}
81 82 if 'notebook' in model:
82 83 notebook = model['notebook']
83 84 if 'name' in notebook:
84 85 changes['name'] = notebook['name']
85 86 if 'path' in notebook:
86 87 changes['path'] = notebook['path']
87 88
88 89 sm.update_session(session_id, **changes)
89 90 model = sm.get_session(session_id=session_id)
90 91 self.finish(json.dumps(model, default=date_default))
91 92
92 93 @web.authenticated
93 94 @json_errors
94 95 def delete(self, session_id):
95 96 # Deletes the session with given session_id
96 97 sm = self.session_manager
97 98 sm.delete_session(session_id)
98 99 self.set_status(204)
99 100 self.finish()
100 101
101 102
102 103 #-----------------------------------------------------------------------------
103 104 # URL to handler mappings
104 105 #-----------------------------------------------------------------------------
105 106
106 107 _session_id_regex = r"(?P<session_id>\w+-\w+-\w+-\w+-\w+)"
107 108
108 109 default_handlers = [
109 110 (r"/api/sessions/%s" % _session_id_regex, SessionHandler),
110 111 (r"/api/sessions", SessionRootHandler)
111 112 ]
112 113
@@ -1,227 +1,211 b''
1 """A base class session manager.
1 """A base class session manager."""
2 2
3 Authors:
4
5 * Zach Sailer
6 """
7
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2013 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
18 5
19 6 import uuid
20 7 import sqlite3
21 8
22 9 from tornado import web
23 10
24 11 from IPython.config.configurable import LoggingConfigurable
25 12 from IPython.utils.py3compat import unicode_type
26 13 from IPython.utils.traitlets import Instance
27 14
28 #-----------------------------------------------------------------------------
29 # Classes
30 #-----------------------------------------------------------------------------
31 15
32 16 class SessionManager(LoggingConfigurable):
33 17
34 18 kernel_manager = Instance('IPython.html.services.kernels.kernelmanager.MappingKernelManager')
35 19 contents_manager = Instance('IPython.html.services.contents.manager.ContentsManager', args=())
36 20
37 21 # Session database initialized below
38 22 _cursor = None
39 23 _connection = None
40 24 _columns = {'session_id', 'name', 'path', 'kernel_id'}
41 25
42 26 @property
43 27 def cursor(self):
44 28 """Start a cursor and create a database called 'session'"""
45 29 if self._cursor is None:
46 30 self._cursor = self.connection.cursor()
47 31 self._cursor.execute("""CREATE TABLE session
48 32 (session_id, name, path, kernel_id)""")
49 33 return self._cursor
50 34
51 35 @property
52 36 def connection(self):
53 37 """Start a database connection"""
54 38 if self._connection is None:
55 39 self._connection = sqlite3.connect(':memory:')
56 40 self._connection.row_factory = sqlite3.Row
57 41 return self._connection
58 42
59 43 def __del__(self):
60 44 """Close connection once SessionManager closes"""
61 45 self.cursor.close()
62 46
63 47 def session_exists(self, name, path):
64 48 """Check to see if the session for a given notebook exists"""
65 49 self.cursor.execute("SELECT * FROM session WHERE name=? AND path=?", (name, path))
66 50 reply = self.cursor.fetchone()
67 51 if reply is None:
68 52 return False
69 53 else:
70 54 return True
71 55
72 56 def new_session_id(self):
73 57 "Create a uuid for a new session"
74 58 return unicode_type(uuid.uuid4())
75 59
76 def create_session(self, name=None, path=None, kernel_name='python'):
60 def create_session(self, name=None, path=None, kernel_name=None):
77 61 """Creates a session and returns its model"""
78 62 session_id = self.new_session_id()
79 63 # allow nbm to specify kernels cwd
80 64 kernel_path = self.contents_manager.get_kernel_path(name=name, path=path)
81 65 kernel_id = self.kernel_manager.start_kernel(path=kernel_path,
82 66 kernel_name=kernel_name)
83 67 return self.save_session(session_id, name=name, path=path,
84 68 kernel_id=kernel_id)
85 69
86 70 def save_session(self, session_id, name=None, path=None, kernel_id=None):
87 71 """Saves the items for the session with the given session_id
88 72
89 73 Given a session_id (and any other of the arguments), this method
90 74 creates a row in the sqlite session database that holds the information
91 75 for a session.
92 76
93 77 Parameters
94 78 ----------
95 79 session_id : str
96 80 uuid for the session; this method must be given a session_id
97 81 name : str
98 82 the .ipynb notebook name that started the session
99 83 path : str
100 84 the path to the named notebook
101 85 kernel_id : str
102 86 a uuid for the kernel associated with this session
103 87
104 88 Returns
105 89 -------
106 90 model : dict
107 91 a dictionary of the session model
108 92 """
109 93 self.cursor.execute("INSERT INTO session VALUES (?,?,?,?)",
110 94 (session_id, name, path, kernel_id)
111 95 )
112 96 return self.get_session(session_id=session_id)
113 97
114 98 def get_session(self, **kwargs):
115 99 """Returns the model for a particular session.
116 100
117 101 Takes a keyword argument and searches for the value in the session
118 102 database, then returns the rest of the session's info.
119 103
120 104 Parameters
121 105 ----------
122 106 **kwargs : keyword argument
123 107 must be given one of the keywords and values from the session database
124 108 (i.e. session_id, name, path, kernel_id)
125 109
126 110 Returns
127 111 -------
128 112 model : dict
129 113 returns a dictionary that includes all the information from the
130 114 session described by the kwarg.
131 115 """
132 116 if not kwargs:
133 117 raise TypeError("must specify a column to query")
134 118
135 119 conditions = []
136 120 for column in kwargs.keys():
137 121 if column not in self._columns:
138 122 raise TypeError("No such column: %r", column)
139 123 conditions.append("%s=?" % column)
140 124
141 125 query = "SELECT * FROM session WHERE %s" % (' AND '.join(conditions))
142 126
143 127 self.cursor.execute(query, list(kwargs.values()))
144 128 try:
145 129 row = self.cursor.fetchone()
146 130 except KeyError:
147 131 # The kernel is missing, so the session just got deleted.
148 132 row = None
149 133
150 134 if row is None:
151 135 q = []
152 136 for key, value in kwargs.items():
153 137 q.append("%s=%r" % (key, value))
154 138
155 139 raise web.HTTPError(404, u'Session not found: %s' % (', '.join(q)))
156 140
157 141 return self.row_to_model(row)
158 142
159 143 def update_session(self, session_id, **kwargs):
160 144 """Updates the values in the session database.
161 145
162 146 Changes the values of the session with the given session_id
163 147 with the values from the keyword arguments.
164 148
165 149 Parameters
166 150 ----------
167 151 session_id : str
168 152 a uuid that identifies a session in the sqlite3 database
169 153 **kwargs : str
170 154 the key must correspond to a column title in session database,
171 155 and the value replaces the current value in the session
172 156 with session_id.
173 157 """
174 158 self.get_session(session_id=session_id)
175 159
176 160 if not kwargs:
177 161 # no changes
178 162 return
179 163
180 164 sets = []
181 165 for column in kwargs.keys():
182 166 if column not in self._columns:
183 167 raise TypeError("No such column: %r" % column)
184 168 sets.append("%s=?" % column)
185 169 query = "UPDATE session SET %s WHERE session_id=?" % (', '.join(sets))
186 170 self.cursor.execute(query, list(kwargs.values()) + [session_id])
187 171
188 172 def row_to_model(self, row):
189 173 """Takes sqlite database session row and turns it into a dictionary"""
190 174 if row['kernel_id'] not in self.kernel_manager:
191 175 # The kernel was killed or died without deleting the session.
192 176 # We can't use delete_session here because that tries to find
193 177 # and shut down the kernel.
194 178 self.cursor.execute("DELETE FROM session WHERE session_id=?",
195 179 (row['session_id'],))
196 180 raise KeyError
197 181
198 182 model = {
199 183 'id': row['session_id'],
200 184 'notebook': {
201 185 'name': row['name'],
202 186 'path': row['path']
203 187 },
204 188 'kernel': self.kernel_manager.kernel_model(row['kernel_id'])
205 189 }
206 190 return model
207 191
208 192 def list_sessions(self):
209 193 """Returns a list of dictionaries containing all the information from
210 194 the session database"""
211 195 c = self.cursor.execute("SELECT * FROM session")
212 196 result = []
213 197 # We need to use fetchall() here, because row_to_model can delete rows,
214 198 # which messes up the cursor if we're iterating over rows.
215 199 for row in c.fetchall():
216 200 try:
217 201 result.append(self.row_to_model(row))
218 202 except KeyError:
219 203 pass
220 204 return result
221 205
222 206 def delete_session(self, session_id):
223 207 """Deletes the row in the session database with given session_id"""
224 208 # Check that session exists before deleting
225 209 session = self.get_session(session_id=session_id)
226 210 self.kernel_manager.shutdown_kernel(session['kernel']['id'])
227 211 self.cursor.execute("DELETE FROM session WHERE session_id=?", (session_id,))
General Comments 0
You need to be logged in to leave comments. Login now